XMLが設定されたSpring Beanファクトリを使用すると、同じクラスの複数のインスタンスを異なるパラメーターで簡単にインスタンス化できます。アノテーションでも同じことができますか?私はこのようなものが欲しいです:
@Component(firstName="joe", lastName="smith")
@Component(firstName="mary", lastName="Williams")
public class Person { /* blah blah */ }
はい、カスタムBeanFactoryPostProcessor実装の助けを借りてそれを行うことができます。
以下に簡単な例を示します。
2つのコンポーネントがあるとします。 1つは別の依存関係です。
最初のコンポーネント:
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の2つの異なるインスタンスに依存しています。
2番目のコンポーネント:
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;
}
}
少し注意が必要です。以下に2つのことを説明します。最初のもの-@Qualifier-MySecondComponent Beanの名前を含むアノテーション。それは標準的なものですが、自由に独自のものを実装できます。少し後で説明します。
2番目に言及するのは、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アノテーションが付けられたすべてのBeanを調べ、アノテーションから名前を抽出し、指定された名前でこのタイプのBeanを手動で作成します。
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>
ここで最後に気づくのは、あなたがcanやるshould n'tする必要がある場合を除きます。これは本当に自然な設定方法ではないからです。クラスのインスタンスが複数ある場合は、XML構成に固執することをお勧めします。
不可能です。重複した例外が発生します。
また、実装クラスのこのような構成データでは最適とはほど遠いものです。
注釈を使用する場合は、クラスを Java config で構成できます。
@Configuration
public class PersonConfig {
@Bean
public Person personOne() {
return new Person("Joe", "Smith");
}
@Bean
public Person personTwo() {
return new Person("Mary", "Williams");
}
}
同様のケースを解決する必要がありました。これは、クラスを再定義できる場合に機能する場合があります。
// 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を使用し、それ以外の場合はすべてPersonを使用します。
wax's answer に触発されて、実装はより安全になり、シングルトンではなく、定義が追加された場合に他の後処理をスキップしません。
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の名前は、ワックスの@Qualifier
例。ファクトリーから継承する機能など、Bean定義には他のさまざまなプロパティがあります。
Springコンテキストから新しく作成されたオブジェクト、Bean、またはプロパティに注入する必要がある場合は、Beanを注入して Espen answer を拡張したコードの次のセクションを参照してください。春のコンテキストから作成されます:
@Configuration
public class PersonConfig {
@Autowired
private OtherBean other;
@Bean
public Person personOne() {
return new Person("Joe", "Smith", other);
}
}
これをご覧ください 記事 すべての可能なシナリオについて。
@espenの回答を続け、修飾子を使用してBeanを注入し、外部値を使用してBeanを異なるように構成します。
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;
}