복붙노트

[SPRING] Spring - 프로그래밍 방식으로 콩 세트 생성

SPRING

Spring - 프로그래밍 방식으로 콩 세트 생성

구성 목록의 각 구성에 대해 12 개 또는 그 이상의 빈을 생성해야하는 Dropwizard 응용 프로그램이 있습니다. 건강 진단, 석영 스케쥴러 등과 같은 것들.

이 같은:

@Component
class MyModule {
    @Inject
    private MyConfiguration configuration;

    @Bean
    @Lazy
    public QuartzModule quartzModule() {
        return new QuartzModule(quartzConfiguration());
    }


    @Bean
    @Lazy
    public QuartzConfiguration quartzConfiguration() {
        return this.configuration.getQuartzConfiguration();
    }

    @Bean
    @Lazy
    public HealthCheck healthCheck() throws SchedulerException {
        return this.quartzModule().quartzHealthCheck();
    }
}

나는 MyConfiguration의 여러 인스턴스를 가지고 있는데, 모두 이런 bean이 필요하다. 지금은 이러한 정의를 복사하여 붙여 넣고 각각의 새로운 구성에 대해 이름을 바꿔야합니다.

어떻게 든 구성 클래스를 반복하고 각각에 대한 Bean 정의 세트를 생성 할 수 있습니까?

서브 클래 싱 (subclassing) 솔루션이나 형식이 안전한 모든 것이 동일한 코드를 복사하여 붙여 넣지 않고 메소드 이름을 변경하지 않고도 새로운 서비스를 추가해야하는 경우라면 괜찮습니다.

편집 : 나는이 빈에 의존하는 다른 구성 요소를 가지고 있다고 덧붙여 야합니다 (예를 들어 Collection 를 주입합니다).

해결법

  1. ==============================

    1.따라서 새로운 빈을 즉시 선언하고, 이들을 보통 빈처럼, 스프링의 애플리케이션 컨텍스트에 삽입해야합니다. 즉, 프록시, 사후 처리 등을 거쳐야한다는 것을 의미합니다. 즉, 스프링 빈 수명주기의 영향을 받아야합니다. .

    따라서 새로운 빈을 즉시 선언하고, 이들을 보통 빈처럼, 스프링의 애플리케이션 컨텍스트에 삽입해야합니다. 즉, 프록시, 사후 처리 등을 거쳐야한다는 것을 의미합니다. 즉, 스프링 빈 수명주기의 영향을 받아야합니다. .

    BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry () 메소드 javadocs를 참조하십시오. 이것은 정상적인 bean 정의가로드 된 후 하나의 bean이 인스턴스화되기 전에 Spring의 응용 프로그램 컨텍스트를 수정할 수있게 해주기 때문에 필요로하는 것입니다.

    @Configuration
    public class ConfigLoader implements BeanDefinitionRegistryPostProcessor {
    
        private final List<String> configurations;
    
        public ConfigLoader() {
            this.configurations = new LinkedList<>();
            // TODO Get names of different configurations, just the names!
            // i.e. You could manually read from some config file
            // or scan classpath by yourself to find classes 
            // that implement MyConfiguration interface.
            // (You can even hardcode config names to start seeing how this works)
            // Important: you can't autowire anything yet, 
            // because Spring has not instantiated any bean so far!
            for (String readConfigurationName : readConfigurationNames) {
                this.configurations.add(readConfigurationName);
            }
        }
    
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            // iterate over your configurations and create the beans definitions it needs
            for (String configName : this.configurations) {
                this.quartzConfiguration(configName, registry);
                this.quartzModule(configName, registry);
                this.healthCheck(configName, registry);
                // etc.
            }
        }
    
        private void quartzConfiguration(String configName, BeanDefinitionRegistry registry) throws BeansException {
            String beanName = configName + "_QuartzConfiguration";
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(QuartzConfiguration.class).setLazyInit(true); 
            // TODO Add what the bean needs to be properly initialized
            // i.e. constructor arguments, properties, shutdown methods, etc
            // BeanDefinitionBuilder let's you add whatever you need
            // Now add the bean definition with given bean name
            registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
        }
    
        private void quartzModule(String configName, BeanDefinitionRegistry registry) throws BeansException {
            String beanName = configName + "_QuartzModule";
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(QuartzModule.class).setLazyInit(true); 
            builder.addConstructorArgReference(configName + "_QuartzConfiguration"); // quartz configuration bean as constructor argument
            // Now add the bean definition with given bean name
            registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
        }
    
        private void healthCheck(String configName, BeanDefinitionRegistry registry) throws BeansException {
            String beanName = configName + "_HealthCheck";
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(HealthCheck.class).setLazyInit(true); 
            // TODO Add what the bean needs to be properly initialized
            // i.e. constructor arguments, properties, shutdown methods, etc
            // BeanDefinitionBuilder let's you add whatever you need
            // Now add the bean definition with given bean name
            registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
        }
    
        // And so on for other beans...
    }
    

    이렇게하면 필요한 빈을 효과적으로 선언하고이를 각 구성에 대한 빈 집합 인 Spring의 응용 프로그램 컨텍스트에 삽입 할 수 있습니다. 몇 가지 이름 지정 패턴을 사용하고 필요에 따라 빈을 이름으로 autowire해야합니다.

    @Service
    public class MyService {
    
        @Resource(name="config1_QuartzConfiguration")
        private QuartzConfiguration config1_QuartzConfiguration;
    
        @Resource(name="config1_QuartzModule")
        private QuartzModule config1_QuartzModule;
    
        @Resource(name="config1_HealthCheck")
        private HealthCheck config1_HealthCheck;
    
        ...
    
    }
    

    노트:

  2. ==============================

    2.이런 식으로 할 수 있어야합니다 :

    이런 식으로 할 수 있어야합니다 :

    @Configuration
    public class MyConfiguration implements BeanFactoryAware {
    
        private BeanFactory beanFactory;
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) {
            this.beanFactory = beanFactory;
        }
    
        @PostConstruct
        public void onPostConstruct() {
            ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
            for (..) {
                // setup beans programmatically
                String beanName= ..
                Object bean = ..
                configurableBeanFactory.registerSingleton(beanName, bean);
            }
         }
    
    }
    
  3. ==============================

    3.미차스의 답변을 확대하면 - 그의 솔루션은 내가 이렇게 설정하면 작동합니다 :

    미차스의 답변을 확대하면 - 그의 솔루션은 내가 이렇게 설정하면 작동합니다 :

    public class ToBeInjected {
    
    }
    
    public class PropertyInjected {
    
        private ToBeInjected toBeInjected;
    
        public ToBeInjected getToBeInjected() {
            return toBeInjected;
        }
    
        @Autowired
        public void setToBeInjected(ToBeInjected toBeInjected) {
            this.toBeInjected = toBeInjected;
        }
    
    }
    
    public class ConstructorInjected {
        private final ToBeInjected toBeInjected;
    
        public ConstructorInjected(ToBeInjected toBeInjected) {
            this.toBeInjected = toBeInjected;
        }
    
        public ToBeInjected getToBeInjected() {
            return toBeInjected;
        }
    
    }
    
    @Configuration
    public class BaseConfig implements BeanFactoryAware{
    
        private ConfigurableBeanFactory beanFactory;
    
        protected ToBeInjected toBeInjected;
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = (ConfigurableBeanFactory) beanFactory;
        }
    
        @PostConstruct
        public void addCustomBeans() {
            toBeInjected = new ToBeInjected();
            beanFactory.registerSingleton(this.getClass().getSimpleName() + "_quartzConfiguration", toBeInjected);
        }
    
        @Bean
        public ConstructorInjected test() {
            return new ConstructorInjected(toBeInjected);
        }
    
        @Bean
        public PropertyInjected test2() {
            return new PropertyInjected();
        }
    
    }
    

    한 가지주의 할 점은 구성 클래스의 특성으로 사용자 지정 bean을 만들고 @PostConstruct 메서드에서 초기화하는 것입니다. 이 방법은 bean으로 등록 된 객체를 가지고 있으므로 (@Autowire와 @Inject는 예상대로 작동 함) 나중에 bean을 필요로하는 생성자 삽입에서 같은 인스턴스를 사용할 수 있습니다. 속성 가시성이 protected로 설정되어 있으므로 하위 클래스에서 생성 된 객체를 사용할 수 있습니다.

    우리가 보유하고있는 인스턴스가 실제로 Spring 프록시가 아니기 때문에 몇 가지 문제가 발생할 수 있습니다 (aspect가 실행되지 않습니다). 다음과 같이 Bean을 등록한 후 검색하는 것이 실제로 좋은 생각 일 수 있습니다.

    toBeInjected = new ToBeInjected();
    String beanName = this.getClass().getSimpleName() + "_quartzConfiguration";
    beanFactory.registerSingleton(beanName, toBeInjected);
    toBeInjected = beanFactory.getBean(beanName, ToBeInjected.class);
    
  4. ==============================

    4.여기에 칩을 넣을거야. 다른 사람들은 당신이 당신의 설정이 주입되는 빈을 만들어야한다고 언급했다. 그런 다음 해당 bean은 사용자의 config를 사용하여 다른 bean을 작성하고이를 컨텍스트에 삽입합니다 (하나의 양식 또는 다른 양식으로 주입해야 함).

    여기에 칩을 넣을거야. 다른 사람들은 당신이 당신의 설정이 주입되는 빈을 만들어야한다고 언급했다. 그런 다음 해당 bean은 사용자의 config를 사용하여 다른 bean을 작성하고이를 컨텍스트에 삽입합니다 (하나의 양식 또는 다른 양식으로 주입해야 함).

    다른 사람들이이 동적으로 생성 된 bean에 의존 할 것이라고 당신이 말했던 것입니다. 이는 동적 빈 팩토리가 종속 Bean보다 먼저 인스턴스화되어야 함을 의미합니다. 당신은 (annotations에서) 이것을 사용하여

    @DependsOn("myCleverBeanFactory")
    

    당신의 영리한 콩 공장이 어떤 종류의 물건인지에 관해서, 다른 사람들은 이것을하는 더 좋은 방법을 추천 해 왔습니다. 그러나 내가 올바르게 기억한다면 당신은 올 봄 2 세계에서 실제로 이렇게 할 수 있습니다 :

    public class MyCleverFactoryBean implements ApplicationContextAware, InitializingBean {
      @Override
      public void afterPropertiesSet() {
        //get bean factory from getApplicationContext()
        //cast bean factory as necessary
        //examine your config
        //create beans
        //insert beans into context
       } 
    

    ..

  5. ==============================

    5.모든 구성 클래스에 의해 확장되는 기본 구성 클래스를 만들어야합니다. 그런 다음, 다음과 같이 모든 구성 클래스를 반복 할 수 있습니다.

    모든 구성 클래스에 의해 확장되는 기본 구성 클래스를 만들어야합니다. 그런 다음, 다음과 같이 모든 구성 클래스를 반복 할 수 있습니다.

    // Key - name of the configuration class
    // value - the configuration object
    Map<String, Object> configurations = applicationContext.getBeansWithAnnotation(Configuration.class);
    Set<String> keys = configurations.keySet();
    for(String key: keys) {
        MyConfiguration conf = (MyConfiguration) configurations.get(key);
    
        // Implement the logic to use this configuration to create other beans.
    }
    
  6. ==============================

    6.필자가 생각할 수있는 "최선의"방법은 Quartz 구성 및 스케줄러를 모두 1 개의 uber bean에 랩핑하고 수동으로 모두 연결 한 다음 uber bean 인터페이스로 작업하도록 코드를 리팩토링하는 것이 었습니다.

    필자가 생각할 수있는 "최선의"방법은 Quartz 구성 및 스케줄러를 모두 1 개의 uber bean에 랩핑하고 수동으로 모두 연결 한 다음 uber bean 인터페이스로 작업하도록 코드를 리팩토링하는 것이 었습니다.

    uber bean은 PostConstruct에 필요한 모든 객체를 생성하고 ApplicationContextAware를 구현하여 객체를 자동 배선 할 수 있습니다. 이상적은 아니지만 내가 생각할 수있는 최선이었습니다.

    스프링은 유형이 안전한 방식으로 빈을 동적으로 추가하는 좋은 방법이 없습니다.

  7. from https://stackoverflow.com/questions/28374000/spring-programmatically-generate-a-set-of-beans by cc-by-sa and MIT license