【问题标题】:Best way to override beans in Spring在 Spring 中覆盖 bean 的最佳方法
【发布时间】:2016-01-19 11:35:48
【问题描述】:

我有一个包含 2 个模块的应用程序。

其中第一个是主要的,它可以在没有安装第二个模块的情况下工作。

我在主模块的beans.xml 文件中定义了默认实现的bean。在安装第二个模块时,我想保留这些 bean 的 id,但更改实现以使用新类。

最好的方法是什么?

第一个模块的beans.xml

...
<bean id="myCoolService" class="com.blabla.defaultCoolServiceImpl">
...

在安装第二个模块后,我想使用第二个模块中定义的myCoolService 的实现。

更新: 春季版本是 3.2.4。 我需要做尽可能少的更改,所以我需要继续使用 xml 驱动的配置。

【问题讨论】:

  • JavaConfig(例如使用@Bean注解)是一个选项吗?哪个春季版本?
  • 您想将第二个模块中的 bean 用于主模块吗?如果是这样,您可以创建新的 Newbeans.xml 并将 contextConfigLocation 设置为 Newbeans.xml

标签: java spring


【解决方案1】:

这样做的一种方法是引入一个通用接口(我想应该已经存在):

public interface MyInterface {
    //...
}

然后在主模块中用@Service注释默认实现

@Service
public class DefaultImplementation implements MyInterface {
    //...
}

然后,如果您的某个模块需要覆盖此实现,请使用@Primary-annotation:

@Service
@Primary
public class OverridingImplementation implements MyInterface {
    //...
}

然后,下面的代码:

@Inject
private MyInterface myInterface;

如果OverridingImplementation 未被扫描,将注入DefaultImplementation,如果被扫描则注入OverridingImplementation(不抱怨多个bean)。

【讨论】:

  • 谢谢!这就是我们一直在寻找的 - 具有最小副作用的优雅解决方案 :)
【解决方案2】:

实现此目的的一种方法是通过代理,该代理重定向到正确的实现。代理通常会重定向到默认值。如果可用,它将重定向到模块 2。

为了帮助代理找出可用的内容,您可能需要拥有

  1. 一个始终使用“name”属性指向默认实现的成员。

  2. 有一种方法可以将不同的 bean 注册为替代实现。

例如

MyProxy 内部:

@Autowired
public void setDefaultWorker(Worker defaultWorker) {
    this.defaultWorker = defaultWorker;
}

private Worker defaultWorker;

private Worker alternateWorker;

public void registerAlternateWorker(Worker alternateWorker) {
    this.alternateWorker = alternateWorker;
}

//To use the worker
private Worker getWorker() {
    return alternateWorker == null? defaultWorker : alternateWorker;
}

在模块 1 中,您的默认实现 bean 应声明为具有 defaultWorker 作为名称

<bean id="defaultWorker" class="MyDefaultWorkerImpl"/>

模块 2 可以在启动时使用 SmartLifeCycle 将自身注册到代理注册表。

【讨论】:

    【解决方案3】:

    如果可能,请使用:

    <bean id="myCoolService" class="${IMPL_CLASS_NAME}"/>
    

    在属性文件中定义 impl 类。

    IMPL_CLASS_NAME=com.blabla.SecondMduleCoolServiceImpl 
    

    或者其他方法可以是:

    假设你的 defaultCoolServiceImpl 和 SecondMduleCoolServiceImpl 实现了 ICoolService 接口

    您定义这些 bean 和 FactoryBean 的实现如下:

    <bean id="mydefaultServiceimpl" class="com.blabla.defaultCoolServiceImpl">
    <bean id="secondModuleCoolserviceimpl" class="com.blabla.SecondMduleCoolServiceImpl">
    <bean id="myCoolService" class="com.blabla.ImplSelector"/>
    
    public class ImplSelector implements FactoryBean<ICoolService>, ApplicationContextAware {
        private ApplicationContext iApplicationContext;
        // @Value("#{corePropertyConfigurer['defaultOrCool']}") you can injcet via property file.
        private String defaultOrCool = "cool" ;
        @Override
        public ICoolService getObject() throws Exception {
            if (StringUtils.equals(defaultOrCool, "default")) {
                return iApplicationContext.getBean("mydefaultServiceimpl", ICoolService.class);
            }
            return iApplicationContext.getBean("secondModuleCoolserviceimpl", ICoolService.class);
        }
        @Override
        public Class<?> getObjectType() {
            return ICoolService.class;
        }
        @Override
        public boolean isSingleton() {
            return true;
        }
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            iApplicationContext = applicationContext;
        }
    }
    

    然后就可以通过自动装配或者applicationContext.getBean("myCoolService", ICoolService.class);访问myCoolService了

    【讨论】:

      猜你喜欢
      • 2011-06-25
      • 1970-01-01
      • 2020-07-22
      • 1970-01-01
      • 1970-01-01
      • 2020-08-07
      • 1970-01-01
      • 2015-12-03
      • 1970-01-01
      相关资源
      最近更新 更多