简体   繁体   中英

Spring: how to reuse the same configuration multiple times

I have several similar sets of beans. For example, I could have a connection to database, monitoring for that database, and DAO for that database, thread pool for that database and so on. And I'd like to instantiate this set of beans several times in the same context with slightly different properties (eg with different host name). I'd like to have something like this:


abstract class ContextTemplate {
    abstract String dbHost();

    @Bean
    DataSource dataSource() { return new DataSourceImpl(dbHost()); }

    @Bean
    DbMonitoring dbMonitoring() { return new DbMonitoring(dataSource()); }}

    // and ten more db-specific beans
}

@Configuration(prefix = "primary-")
class PrimaryDbContext extends ContextTemplate {
    @Override
    String dbHost() { return "primary.host"; }
}

@Configuration(prefix = "slow-reqs-")
class SlowRequestsContext extends ContextTemplate {
    @Override
    String dbHost() { return "slow.requests.host"; }
}

@Configuration
@Import({
    PrimaryContext.class,
    SlowRequestsContext.class,
})
class MyContext {
}

With this imaginary configuration instantiated I'd like to have a single context with beans primary-dataSource , primary-monitoring , slow-reqs-dataSource , slow-reqs-monitoring .

The point is that each of PrimaryDbContext and SlowRequestsContext should emit several very similar bean definitions in one resuling application context.

Is something like this possible with Spring?

Edit: created a feature request in the Spring tracker .

I know this solution is not looking like your example. But you can't change bean names with any other ways, only manually. Create custom factory post processor:

public class DbRegistryPostProcessor implements BeanFactoryPostProcessor{

    private String hostName;
    private String prefix;

    public BeanFactoryPostProcessor(Strin prefix, String hostName){
        this.prefix = prefix;
        this.hostName = hostName;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
        factory.registerSingleton(prefix+"dataSource", dataSource());
        factory.registerSingleton(prefix+"dbMonitoring", dbMonitoring());
    }


    DataSource dataSource() { return new DataSourceImpl(hostName); }

    DbMonitoring dbMonitoring() { return new DbMonitoring(dataSource()); }}

    // and ten more db-specific beans
}

And register post-processors in configuration:

@Configuration
public class MyContext {

    @Bean
    public DbRegistryPostProcessor primaryDbProcessor(){
         return new DbRegistryPostProcessor("primary-", "primary.host");
    }

    @Bean
    public DbRegistryPostProcessor secondaryDbProcessor(){
         return new DbRegistryPostProcessor("secondary-", "secondary.host");
    }

}

Of course, with this approach your beans will not be processed by other post processors (for example with PropertySourcesPlaceholderConfigurer ). If you need beans to be processed by other post processors, you can implement custom BeanDefinitionRegistryPostProcessor instead of BeanFactoryPostProcessor .

Dynamically you can switch between different data sources by maintaining a flag that you can change by hitting a Servlet that sets the flag in cookie or application context scope.

Just register one bean of ContextTemplate that internally keeps two ContextTemplate .

  • instantiate both primary and secondary ContextTemplate once
  • override the methods
  • decide based on flag.

For example:

@Component
public class MyContextTemplate implements InitializingBean, ContextTemplate {

    private ContextTemplate primary;
    private ContextTemplate secondary;

    @Override
    public void afterPropertiesSet() {
        // initialize primary and secondary ContextTemplate  
        // you can initialize them in default constructor of this class also
    }

    public DataSource dataSource() {
       if(flat-set) {
          return primary.dataSource();
       }
       return secondary.dataSource();
    }

    public DbMonitoring dbMonitoring() {
       if(flat-set) {
          return primary.dbMonitoring();
       }
       return secondary.dbMonitoring();
    }
    // implement other methods
}

You can register multiple bean of ContextTemplate also and mark MyContextTemplate as primary bean to resolve the auto-wiring conflict issue.


Think about some of design patterns that can fit as per your requirement:

A snapshot of State pattern:

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM