복붙노트

[SPRING] Spring 어노테이션으로 같은 클래스의 여러 빈 인스턴스화하기

SPRING

Spring 어노테이션으로 같은 클래스의 여러 빈 인스턴스화하기

XML로 구성된 스프링 빈 팩토리를 사용하면 동일한 클래스의 여러 인스턴스를 다른 매개 변수로 쉽게 인스턴스화 할 수 있습니다. 특수 효과를 사용하여 동일한 작업을 수행하려면 어떻게해야합니까? 나는 이런 것을 원한다.

@Component(firstName="joe", lastName="smith")
@Component(firstName="mary", lastName="Williams")
public class Person { /* blah blah */ }

해결법

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

    1.예, 사용자 정의 BeanFactoryPostProcessor 구현의 도움으로이 작업을 수행 할 수 있습니다.

    예, 사용자 정의 BeanFactoryPostProcessor 구현의 도움으로이 작업을 수행 할 수 있습니다.

    다음은 간단한 예입니다.

    두 가지 구성 요소가 있다고 가정 해보십시오. 하나는 다른 것에 대한 의존성입니다.

    첫 번째 구성 요소 :

    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.util.Assert;
    
     public class MyFirstComponent implements InitializingBean{
    
        private MySecondComponent asd;
    
        private MySecondComponent qwe;
    
        public void afterPropertiesSet() throws Exception {
            Assert.notNull(asd);
            Assert.notNull(qwe);
        }
    
        public void setAsd(MySecondComponent asd) {
            this.asd = asd;
        }
    
        public void setQwe(MySecondComponent qwe) {
            this.qwe = qwe;
        }
    }
    

    보시다시피이 구성 요소에는 특별한 것이 없습니다. MySecondComponent의 두 개의 다른 인스턴스에 종속됩니다.

    두 번째 구성 요소 :

    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.beans.factory.annotation.Qualifier;
    
    
    @Qualifier(value = "qwe, asd")
    public class MySecondComponent implements FactoryBean {
    
        public Object getObject() throws Exception {
            return new MySecondComponent();
        }
    
        public Class getObjectType() {
            return MySecondComponent.class;
        }
    
        public boolean isSingleton() {
            return true;
        }
    }
    

    좀 더 까다 롭습니다. 설명 할 두 가지 사항이 있습니다. 첫 번째 - @Qualifier - MySecondComponent Bean의 이름을 포함하는 주석. 그것은 표준적인 것이지만, 직접 구현할 수 있습니다. 나중에 조금 볼 수 있습니다.

    두 번째 언급해야 할 것은 FactoryBean 구현입니다. Bean이이 인터페이스를 구현하면 다른 인스턴스를 생성하기위한 것입니다. 여기서는 MySecondComponent 유형으로 인스턴스를 작성합니다.

    가장 까다로운 부분은 BeanFactoryPostProcessor 구현입니다.

    import java.util.Map;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    
    
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
            Map<String, Object> map =  configurableListableBeanFactory.getBeansWithAnnotation(Qualifier.class);
            for(Map.Entry<String,Object> entry : map.entrySet()){
                createInstances(configurableListableBeanFactory, entry.getKey(), entry.getValue());
            }
    
        }
    
        private void createInstances(
                ConfigurableListableBeanFactory configurableListableBeanFactory,
                String beanName,
                Object bean){
            Qualifier qualifier = bean.getClass().getAnnotation(Qualifier.class);
            for(String name : extractNames(qualifier)){
                Object newBean = configurableListableBeanFactory.getBean(beanName);
                configurableListableBeanFactory.registerSingleton(name.trim(), newBean);
            }
        }
    
        private String[] extractNames(Qualifier qualifier){
            return qualifier.value().split(",");
        }
    }
    

    그것은 무엇을합니까? @Qualifier로 주석 된 모든 빈을 거치고 주석에서 이름을 추출한 다음 지정된 이름으로이 유형의 빈을 수동으로 작성합니다.

    다음은 Spring 설정입니다.

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean class="MyBeanFactoryPostProcessor"/>
    
        <bean class="MySecondComponent"/>
    
    
        <bean name="test" class="MyFirstComponent">
            <property name="asd" ref="asd"/>
            <property name="qwe" ref="qwe"/>
        </bean>
    
    </beans>
    

    마지막으로주의해야 할 점은 필수적인 경우가 아니라면 그렇게 할 수는 있지만 그렇게 할 수는 없다는 것입니다. 이는 실제로 자연스러운 구성 방식이기 때문입니다. 클래스 인스턴스가 두 개 이상인 경우 XML 구성을 사용하는 것이 좋습니다.

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

    2.그것은 불가능합니다. 중복 예외가 발생합니다.

    그것은 불가능합니다. 중복 예외가 발생합니다.

    또한 구현 클래스에서 이와 같은 구성 데이터를 사용하는 것이 최적이 아닙니다.

    주석을 사용하려면 Java 구성을 사용하여 클래스를 구성 할 수 있습니다.

    @Configuration
    public class PersonConfig {
    
        @Bean
        public Person personOne() {
            return new Person("Joe", "Smith");
        }
    
        @Bean
        public Person personTwo() {
            return new Person("Mary", "Williams");
        }
    }
    
  3. ==============================

    3.나는 비슷한 사건을 해결해야만했다. 클래스를 재정의 할 수 있으면이 방법이 효과가있을 수 있습니다.

    나는 비슷한 사건을 해결해야만했다. 클래스를 재정의 할 수 있으면이 방법이 효과가있을 수 있습니다.

    // This is not a @Component
    public class Person {
    
    }
    
    @Component
    public PersonOne extends Person {
       public PersonOne() {
           super("Joe", "Smith");
       }
    }
    
    @Component
    public PersonTwo extends Person {
       public PersonTwo() {
        super("Mary","Williams");
       }
    }
    

    그런 다음 PersonOne 또는 PersonTwo를 사용하면 특정 인스턴스를 autowire 할 필요가있을 때마다 Person을 사용할 수 있습니다.

  4. ==============================

    4.왁스의 대답에 영감을 얻은 구현은보다 안전 할 수 있으며 정의가 추가 된 경우가 아닌 추가 된 후 처리를 건너 뛰지 않습니다.

    왁스의 대답에 영감을 얻은 구현은보다 안전 할 수 있으며 정의가 추가 된 경우가 아닌 추가 된 후 처리를 건너 뛰지 않습니다.

    public interface MultiBeanFactory<T> {  // N.B. should not implement FactoryBean
      T getObject(String name) throws Exception;
      Class<?> getObjectType();
      Collection<String> getNames();
    }
    
    public class MultiBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        Map<String, MultiBeanFactory> factories = beanFactory.getBeansOfType(MultiBeanFactory.class);
    
        for (Map.Entry<String, MultiBeanFactory> entry : factories.entrySet()) {
          MultiBeanFactory factoryBean = entry.getValue();
          for (String name : factoryBean.getNames()) {
            BeanDefinition definition = BeanDefinitionBuilder
                .genericBeanDefinition(factoryBean.getObjectType())
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .setFactoryMethod("getObject")
                .addConstructorArgValue(name)
                .getBeanDefinition();
            definition.setFactoryBeanName(entry.getKey());
            registry.registerBeanDefinition(entry.getKey() + "_" + name, definition);
          }
        }
      }
    }
    
    @Configuration
    public class Config {
      @Bean
      public static MultiBeanFactoryPostProcessor() {
        return new MultiBeanFactoryPostProcessor();
      }
    
      @Bean
      public MultiBeanFactory<Person> personFactory() {
        return new MultiBeanFactory<Person>() {
          public Person getObject(String name) throws Exception {
            // ...
          }
          public Class<?> getObjectType() {
            return Person.class;
          }
          public Collection<String> getNames() {
            return Arrays.asList("Joe Smith", "Mary Williams");
          }
        };
      }
    }
    

    Bean 이름은 여전히 ​​wax의 @Qualifier 예제와 같이 어디서나 올 수 있습니다. Bean 정의에는 공장 자체에서 상속하는 기능을 포함하여 다양한 속성이 있습니다.

  5. from https://stackoverflow.com/questions/2902335/instantiating-multiple-beans-of-the-same-class-with-spring-annotations by cc-by-sa and MIT license