programing

스프링 주석을 사용하여 동일한 클래스의 여러 콩 인스턴스화

newstyles 2023. 8. 27. 08:58

스프링 주석을 사용하여 동일한 클래스의 여러 콩 인스턴스화

XML로 구성된 Springbean 팩토리를 사용하면 동일한 클래스의 여러 인스턴스를 다른 매개 변수로 쉽게 인스턴스화할 수 있습니다.주석을 사용하여 동일한 작업을 수행하려면 어떻게 해야 합니까?저는 다음과 같은 것을 원합니다.

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

그건 불가능해요.중복된 예외가 발생합니다.

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

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

@Configuration
public class PersonConfig {

    @Bean
    public Person personOne() {
        return new Person("Joe", "Smith");
    }

    @Bean
    public Person personTwo() {
        return new Person("Mary", "Williams");
    }
}

예, 사용자 지정 BeanFactory PostProcessor 구현의 도움을 받아 이 작업을 수행할 수 있습니다.

여기 간단한 예가 있습니다.

두 가지 성분이 있다고 가정합니다.하나는 다른 하나에 대한 의존입니다.

첫 번째 구성 요소:

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 beans의 이름이 포함된 주석.이것은 표준적인 것이지만, 당신은 당신 자신의 것을 자유롭게 구현할 수 있습니다.그 이유는 나중에 알게 될 것입니다.

두 번째로 언급할 것은 팩토리빈 구현입니다.이 인터페이스를 구현하는 경우 다른 인스턴스를 생성합니다.이 경우 MySecondComponent 유형으로 인스턴스를 만듭니다.

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

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로 주석이 달린 모든 빈을 통과하고 주석에서 이름을 추출한 다음 지정된 이름을 사용하여 이 유형의 빈을 수동으로 만듭니다.

스프링 구성은 다음과 같습니다.

<?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 구성을 유지하는 것이 좋습니다.

저는 단지 비슷한 사건을 해결해야만 했습니다.클래스를 재정의할 수 있는 경우 이 방법이 사용될 수 있습니다.

// 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");
   }
}

그런 다음 특정 인스턴스를 자동으로 연결해야 할 때마다 Person1 또는 Person2를 사용하고, 다른 모든 경우에는 Person을 사용합니다.

왁스의 답변에서 영감을 얻은 이 구현은 생성된 싱글톤이 아닌 정의를 추가하면 다른 사후 처리를 건너뛸 수 있고 더 안전할 수 있습니다.

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");
      }
    };
  }
}

콩의 이름은 여전히 왁스의 이름과 같이 어디에서나 나올 수 있습니다.@Qualifier 예문.콩 정의에는 공장 자체에서 상속할 수 있는 기능을 포함하여 다양한 속성이 있습니다.

@espen 답변을 계속하고, 콩에 한정자를 주입하고 외부 값을 사용하여 다르게 구성합니다.

public class Person{
  @Configuration
  public static class PersonConfig{
    @Bean
    //@Qualifier("personOne") - doesn't work - bean qualifier is method name
    public Person personOne() {
        return new Person("Joe", "Smith");
    }

    @Bean
    //@Qualifier("personTwo") - doesn't work - bean qualifier is method name
    public Person personTwo(@Value("${myapp.second.lastName}") String lastName) {
        return new Person("Mary", lastName);
    }
  }
  /* blah blah */
}

@Component
public class SomePersonReference{
  @Autowired
  @Qualifier("personTwo")
  Person marry;
}

스프링 컨텍스트에서 새로 생성된 개체, 콩 또는 속성을 주입해야 하는 경우 스프링 컨텍스트에서 생성된 콩을 주입하여 에스펜 답변을 확장한 코드의 다음 섹션을 볼 수 있습니다.

@Configuration
public class PersonConfig {

@Autowired 
private OtherBean other;

@Bean
public Person personOne() {
    return new Person("Joe", "Smith", other);
    }
}

가능한 모든 시나리오에 대해 이 기사를 살펴보십시오.

언급URL : https://stackoverflow.com/questions/2902335/instantiating-multiple-beans-of-the-same-class-with-spring-annotations