使用XML配置的Spring bean工厂,我可以轻松地使用不同的参数实例化同一个类的多个实例。我怎么能用注释做同样的事情?我想要这样的东西:
@Component(firstName="joe", lastName="smith")
@Component(firstName="mary", lastName="Williams")
public class Person { /* blah blah */ }
答案 0 :(得分:31)
这是不可能的。你得到一个重复的例外。
在您的实现类中,这与配置数据相差甚远。
如果您想使用注释,可以使用Java config配置您的课程:
@Configuration
public class PersonConfig {
@Bean
public Person personOne() {
return new Person("Joe", "Smith");
}
@Bean
public Person personTwo() {
return new Person("Mary", "Williams");
}
}
答案 1 :(得分:16)
是的,您可以在自定义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注释的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>
最后要注意的是,虽然可以这样做但不应该,除非必须这样做,因为这不是一种非常自然的配置方式。如果您有多个类实例,最好坚持使用XML配置。
答案 2 :(得分:8)
我只需解决类似案件。如果您可以重新定义该课程,这可能会有效。
// 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。
答案 3 :(得分:1)
受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定义还有其他各种属性,包括从工厂本身继承的能力。
答案 4 :(得分:0)
是否需要在新创建的对象中注入spring上下文中的bean或属性,可以查看下面的代码部分,其中我通过注入一个bean扩展了Espen answer是从spring上下文创建的:
@Configuration
public class PersonConfig {
@Autowired
private OtherBean other;
@Bean
public Person personOne() {
return new Person("Joe", "Smith", other);
}
}
在所有可能的情况下查看此article。
答案 5 :(得分:0)
继续@espen答案,为带限定符的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;
}