【问题标题】:Autowire setter override with Java Config使用 Java Config 覆盖 Autowire 设置器
【发布时间】:2014-03-14 21:34:36
【问题描述】:

考虑以下类:

public class MyBean {
    private A a;

    @Autowired(required=true)
    public void setA(A a) {
        this.a = a;
    }

    public A getA() {
        return a;
    }
}

在某些情况下需要覆盖自动装配的注入,例如当 Spring 无法找到注入的单个候选者时。在 XML 中,我可以有以下示例:

<?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 id="first" class="my.pkg.AImpl"/>
    <bean id="second" class="my.pkg.AImpl"/>
    <bean id="myBeanFirst" class="my.pkg.MyBean">
        <property name="a" ref="first"/>
    </bean>
    <bean id="myBeanSecond" class="my.pkg.MyBean">
        <property name="a" ref="second"/>
    </bean>

</beans>

有没有办法用 Java Config 做同样的事情?以下内容不起作用(我理解为什么),因为 Spring 在从 myBean 方法返回后尝试自动装配该属性,但它因 NoUniqueBeanDefinitionException 而失败:

@Configuration
public class MyConfig {
    @Bean
    public A first() {
        return new AImpl();
    }

    @Bean
    public A second() {
        return new AImpl();
    }

    @Bean
    public MyBean myBeanFirst(A first) {
        MyBean myBean = new MyBean();
        myBean.setA(first);
        return myBean;
    }

    @Bean
    public MyBean myBeanSecond(A second) {
        MyBean myBean = new MyBean();
        myBean.setA(first);
        return myBean;
    }
}

修改 MyBean 类并不总是一种选择,例如因为它来自外部库。 这是我必须使用 XML 配置的情况吗?

谢谢, 安德烈亚·波尔奇

更新 感谢您提供两种解决方案(按名称注入和使用@Primary),但它们不能解决我的用例,所以我认为我需要更具体。

在我的用例中,MyBean 类来自外部库,因此无法对其进行任何更改。我还需要有多个 MyBean 实例,每个实例都注入不同的 A 接口实例。我已经更新了上面的代码以反映这一点(xml 和 java)。

有没有使用 java config 的解决方案?是否可以避免自动装配对 MyBean 的依赖? (仅在该类的 bean 上,不会为上下文中的每个 bean 完全禁用自动装配)

【问题讨论】:

  • 否决这个问题的人能否解释一下原因?

标签: java spring spring-java-config


【解决方案1】:

好的,我相信这个答案会满足您的需求。

我们需要的是MergedBeanDefinitionPostProcessor 的实现,它将为MyBean 类的属性a 设置正确的值。这可以通过下面的类来完成

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;


@Component
public class MyBeanPostProcessor implements MergedBeanDefinitionPostProcessor {


    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if(beanName.equals("myBeanFirst")) {
            beanDefinition.getPropertyValues().add("a", getMyBeanFirstAImpl());
        }
        else if(beanName.equals("myBeanSecond")) {
            beanDefinition.getPropertyValues().add("a", getMyBeanSecondAImpl());
        }
    }

    private Object getMyBeanFirstAImpl() {
        return new AImpl();
    }

    private Object getMyBeanSecondAImpl() {
        return new AImpl();
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

正如您所见,此处的 bean 名称是硬编码的,但它们可以设置为静态 final String,以下代码中的 @Bean 注释也将使用这些名称

@Configuration
public class Configuration {



    @Bean
    public MyBean myBeanFirst() {
        return new MyBean();
    }

    @Bean
    public MyBean myBeanSecond() {
        return new MyBean();
    }

}

您会注意到在下面的代码中,在 MyBean 创建方法中没有调用 setA,因为无论我们设置什么值(或者在这种情况下不设置),当 Spring 执行 bean 后处理器时都会被覆盖。

如果您需要 A 的默认值(例如,如果您将其注入其他 bean),请继续在之前的配置中定义 @Bean

【讨论】:

  • 如果这是使用 Java Config 执行此操作的更简单方法,我认为真正的答案应该是“在这种情况下使用 xml 配置”。同样感谢。
  • 不知道是不是最简单的,也许有更好的答案!但是我确实知道 xml 配置有效,因为 xml 配置会覆盖任何以前的配置。
  • @geoand - 如果提问者知道他在做什么,则支持您的答案,可行的方法。
  • @ike_love 我从来没有说过这是不可行的。我的意思是,如果我需要定义一个自定义后处理器来设置依赖项,这表明我正在尝试做一些框架不打算做的事情,我需要改变我的方法(例如使用 xml 配置在这种情况下)。不要以为我是反对这个答案的人,我不是。
  • @AndreaPolci 谢谢!这种情况可能是JavaConfig不方便开发者的好案例
【解决方案2】:

如果您有多个候选注入,您可以通过在要成为默认 AImpl 的 bean 上使用 @Primary 来指定默认一个。这样就不需要对 MyBean 进行任何更改

【讨论】:

  • 这并不总能解决问题。如果我需要两个 MyBean 实例,并在其中注入不同的 A 实例怎么办?如果存在,我需要的是一种在 Java Config 中复制我在问题中发布的 xml 配置行为的方法。
【解决方案3】:

如果您希望首先使用 myBeanFirst,则只需调用 first()。 myBeanSecond 也一样,希望它使用第二个,然后在设置 A 时调用second()

@Configuration
public class MyConfig {
    @Bean
    public A first() {
        return new AImpl();
    }

    @Bean
    public A second() {
        return new AImpl();
    }

    @Bean
    public MyBean myBeanFirst() {
        MyBean myBean = new MyBean();
        myBean.setA(first());
        return myBean;
    }

    @Bean
    public MyBean myBeanSecond() {
        MyBean myBean = new MyBean();
        myBean.setA(second());
        return myBean;
    }
}

【讨论】:

  • 这不起作用,因为在 myBeanFirst() 创建了 bean 之后,Spring 尝试注入 @Autowire 注释字段(他不知道它已被设置)。如前所述,我无法修改 MyBean 以删除注释,因为它来自外部库。
  • 我只投了反对票,因为它不起作用。我不是拒绝你另一个答案的人。我真的很感谢你的帮助,如果我给了不同的印象,对不起。此外,我并没有抱怨投反对票,我只是好奇原因是什么,以及我是否可以对此做点什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-26
  • 2023-03-26
  • 1970-01-01
相关资源
最近更新 更多