问题描述:

I have an application that consists of 2 modules.

First of them is main one and it can work without second module installed.

I have beans with default implementation defined in beans.xml file of main module. And when installing second module I want to keep the ids of those beans but change the implementation to use new classes.

What is the best way to do that?

beans.xml of first module:

...

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

...

and after the installation of second module I want to use the implementation of myCoolService that is defined in second module.

Upd:

Spring version is 3.2.4.

I need to make as little changes as possible so I need to continue using xml-driven configuration.

网友答案:

One way of doing this is introducing a common interface (I guess one should already be present):

public interface MyInterface {
    //...
}

And then in the main module annotate the default implementation with @Service

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

Then, if one of your modules needs to override this implementation, use the @Primary-annotation:

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

Then, the following code:

@Inject
private MyInterface myInterface;

will inject DefaultImplementation if OverridingImplementation is not scanned, and inject OverridingImplementation (without complaining about multiple beans) if it is scanned.

网友答案:

One way to achieve this is going through a proxy, that redirects to the proper implementation. The proxy would normally redirect to the default. It will redirect to module 2 if it is available.

To help the proxy figure out what is available, you may need to have

  1. a member that always points to the default implementation using "name" property.

  2. have a method to register a different bean as the alternate implementation.

For example

Inside 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;
}

In Module 1, your default implementation bean should be declared as having the defaultWorker as name

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

Module 2 can register itself to the proxy registry on startup using SmartLifeCycle.

网友答案:

if possible,use :

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

Define impl class in a property file.

IMPL_CLASS_NAME=com.blabla.SecondMduleCoolServiceImpl 

OR other approach could be :

Lets say your defaultCoolServiceImpl and SecondMduleCoolServiceImpl implement ICoolService interface

You define these bean and an implementation of FactoryBean as below :

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

Then you can access myCoolService via autowiring or applicationContext.getBean("myCoolService", ICoolService.class);

相关阅读:
Top