spring的条件装配bean

1 应用程序环境的迁移java

问题:程序员

开发软件时,有一个很大的挑战,就是将应用程序从一个环境迁移到另外一个环境。web

例如,开发环境中不少方式的处理并不适合生产环境,迁移后须要修改,这个过程可能会莫名的出现不少bug,一个常常出如今程序员间有意思的问题是:在我那明明没问题啊,为何到你那就不行了?spring

举个栗子,数据库配置,在开发环境咱们可能使用一个嵌入式的数据源并在启动的时候加载进来,可是在生产环境中这是糟糕的作法,咱们但愿数据库能直接从JNDI中获取,或者是从链接池中获取。那么问题来了,当从开发环境迁移到生产环境中时,咱们应该怎么作?sql

一个很好想到的办法是:配置多个xml文件,每一个xml里面配置一种数据源。而后在构建阶段,肯定哪种数据源编译到可部署的应用中。数据库

可是这样的作法缺点要为每种环境从新构建应用,从开发阶段迁移到QA(质量保证)阶段肯能没什么大问题,可是从QA迁移到生产阶段重新构建可能会出现bug,还QA个毛啊~ide

解答:测试

Spring为环境迁移提供的解决方案是profile功能,为bean配置profile注解,而后激活对应的profile。spa

怎么配置profile?下面有两种方式:code

(1)基于Java配置

@Configuration
public class DataSourceConfig {

    @Bean
    @Profile("dev")
    public DataSource embeddedDataSource() {

        // 配置开发环境     嵌入式数据源
        
    }


    @Bean
    @Profile("prod")
    public DataSource jndiDataSource() {

        // 配置生产环境    JNDI数据源
        
    }

}

(2)基于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" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
  xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="
    http://www.springframework.org/schema/jee
    http://www.springframework.org/schema/jee/spring-jee.xsd
    http://www.springframework.org/schema/jdbc
    http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

  <beans profile="dev">
    <jdbc:embedded-database id="dataSource" type="H2">
      <jdbc:script location="classpath:schema.sql" />
      <jdbc:script location="classpath:test-data.sql" />
    </jdbc:embedded-database>
  </beans>
  
  <beans profile="prod">
    <jee:jndi-lookup id="dataSource"
      lazy-init="true"
      jndi-name="jdbc/myDatabase"
      resource-ref="true"
      proxy-interface="javax.sql.DataSource" />
  </beans>
</beans>

怎么激活profile?

Spring在肯定哪一个profile处于激活状态时,须要依赖两个独立的属性:spring.profiles.active和spring.profiles.default。

若是设置了spring.profiles.active属性的话,那么它的值就会用来肯定哪一个profile是激活的。但若是没有设置spring.profiles.active,那Spring将会查找spring.profiles.default的值。若是两个属性都没配置,那么被@Profile注解的类都不会被加载。

有多种方式来设置这两个属性:

  • 做为DispatcherServlet的初始化参数;
  • 做为Web应用的上下文参数;
  • 做为JNDI条目;
  • 做为环境变量;
  • 做为JVM的系统属性;
  • 在集成测试类上,使用@ActiveProfiles注解设置。

如何配置这些初始化参数不是本文讨论的内容。但为了把例子解释清楚,这里以web应用上下文参数为例梳理整个流程,配置以下:

这里默认激活配置为dev的profile,因此启动应用时,以Java配置为例,注解@Profile("dev")标记的数据库链接类会被加载到spring容器中,@Profile("prod")标记的类不会被加载。

profile是spring3.1中出现的新功能,只能用于环境迁移的条件装配,可是spring4引入了Conditional功能,咱们能灵活的处理条件化装配bean了,并且spring4也使用Conditional重构了profile,也就是说spring4以后,prefile是基于Conditional实现的。

 

 

2 条件装配bean

假如你但愿一个或多个bean只有在应用的类路径下包含特定的库时才建立。或者咱们但愿某个bean只有当另外某个特定的bean也声明了以后才会建立。咱们还可能要求只有某个特定的环境变量设置以后,才会建立某个bean。这些使用Conditional都能实现。

举个例子:假设有一个名为MagicBean的类,咱们但愿只有设置了magic环境属性的时候,Spring才会实例化这个类。若是环境中没有这个属性,那么MagicBean将会被忽略。

首先要有一个MagicBean,咱们不关注它的功能与实现,只须要知道有这个bean就行。接下来配置:

 

@Configuration
public class MagicConfig {

  @Bean
  @Conditional(MagicExistsCondition.class)
  public MagicBean magicBean() {
    return new MagicBean();
  }
  
}

 

只有知足MagicExistsCondition这个条件时,咱们才实例化MagicBeanMagicExistsCondition须要实现Condition接口。Condition接口里面只有matches()方法,当实现类的matches()返回true时,条件才知足。不然条件不知足。MagicExistsCondition实现以下:

 

public class MagicExistsCondition implements Condition {

  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    Environment env = context.getEnvironment();
    return env.containsProperty("magic");
  }
  
}

 

只有环境变量magic存在,matches才会返回true,条件才知足,MagicBean才会实例化。

 

参考文章《spring in action》

 

————完—————

相关文章
相关标签/搜索