环境与profile


在开发软件的时候, 有一个很大的挑战就是将应用程序从一个环境迁移到另外一个环境。 数据库的配置会因为环境不同而改变。
不同环境下的数据库配置:
    
开发环境:
    
我们可能会使用嵌入式数据库, 并预先加载测试数据。 例如, 在Spring配置类中, 我们可能会在一个带有@Bean注解的方法上使用EmbeddedDatabaseBuilder: 
    环境与profile
这会创建一个类型为javax.sql.DataSource的bean, 这个bean是如何创建出来的才是最有意思的。 使用EmbeddedDatabaseBuilder会搭建一个嵌入式的Hypersonic数据库, 它的模式(schema) 定义在schema.sql中, 测试数据则是通过test-data.sql加载的。

当你在开发环境中运行集成测试或者启动应用进行手动测试的时候, 这个DataSource是很有用的。 每次启动它的时候, 都能让数据库处于一个给定的状态。 
    
生产环境:
    
在生产环境的配置中, 你可能会希望使用JNDI从容器中获取一个DataSource。 在这样场景中, 如下的@Bean方法会更加合适: 
    环境与profile
    
通过JNDI获取DataSource能够让容器决定该如何创建这个DataSource, 甚至包括切换为容器管理的连接池。 即便如此, JNDI管理的DataSource更加适合于生产环境, 对于简单的集成和开发测试环境来说, 这会带来不必要的复杂性。 
同时, 在测试环境中, 你可以选择完全不同的DataSource配置, 可以配置为Commons DBCP连接池, 如下所示: 
    环境与profile
显然, 这里展现的三个版本的dataSource()方法互不相同。 虽然它们都会生成一个类型为javax.sql.DataSource的bean, 但它们的相似点也仅限于此了。 每个方法都使用了完全不同的策略来生成DataSource bean。
 
其中一种方式就是在单独的配置类(或XML文件) 中配置每个bean, 然后在构建阶段(可能会使用Maven的profiles) 确定要将哪一个配置编译到可部署的应用中。 这种方式的问题在于要为每种环境重新构建应用。 当从开发阶段迁移到QA阶段时, 重新构建也许算不上什么大问题。 

但是, 从QA阶段迁移到生产阶段时, 重新构建可能会引入bug并且会在QA团队的成员中带来不安的情绪。 

值得庆幸的是, Spring所提供的解决方案并不需要重新构建。 

Spring为环境相关的bean所提供的解决方案其实与构建时的方案没有太大的差别。 当然, 在这个过程中需要根据环境决定该创建哪个bean和不创建哪个bean。 不过Spring并不是在构建的时候做出这样的决策, 而是等到运行时再来确定。 这样的结果就是同一个部署单元(可能会是WAR文件) 能够适用于所有的环境, 没有必要进行重新构建 。
    
在3.1版本中, Spring引入了bean profile的功能。 要使用profile, 你首先要将所有不同的bean定义整理到一个或多个profile之中, 在将应用部署    到每个环境时, 要确保对应的profile处于**(active) 的状态。 
在Java配置中, 可以使用@Profile注解指定某个bean属于哪一个profile。 例如, 在配置类中, 嵌入式数据库的DataSource可能会配置成如下所示: 
    环境与profile
@Profile注解应用在了类级别上。 它会告诉Spring这个配置类中的bean只有在dev profile**时才会创建。 如果dev profile没有**的话, 那么带有@Bean注解的方法都会被忽略掉。 
同时, 你可能还需要有一个适用于生产环境的配置, 如下所示: 
    环境与profile
在本例中, 只有prod profile**的时候, 才会创建对应的bean。
在Spring 3.1中, 只能在类级别上使用@Profile注解。 不过, 从Spring 3.2开始, 你也可以在方法级别上使用@Profile注解, 与@Bean注解一同使用。 这样的话, 就能将这两个bean的声明放到同一个配置类之中, 如下所示: 
    
@Profile注解基于**的profile实现bean的装配 :
    环境与profile
这里有个问题需要注意, 尽管每个DataSource bean都被声明在一个profile中, 并且只有当规定的profile**时, 相应的bean才会被创建, 但是可能会有其他的bean并没有声明在一个给定的profile范围内。 没有指定profile的bean始终都会被创建, 与**哪个profile没有关系。 
    
    
在XML中配置profile
 
我们也可以通过<beans>元素的profile属性, 在XML中配置profile bean。 例如, 为了在XML中定义适用于开发阶段的嵌入式数据库DataSourcebean, 我们可以创建如下所示的XML文件: 
    环境与profile
与之类似, 我们也可以将profile设置为prod, 创建适用于生产环境的从JNDI获取的DataSource bean。 同样, 可以创建基于连接池定义的DataSource bean, 将其放在另外一个XML文件中, 并标注为qaprofile。 所有的配置文件都会放到部署单元之中(如WAR文件) , 但是只有profile属性与当前**profile相匹配的配置文件才会被用到。 
你还可以在根<beans>元素中嵌套定义<beans>元素, 而不是为每个环境都创建一个profile XML文件。 这能够将所有的profile bean定义放到
同一个XML文件中, 如下所示: 
    
重复使用元素来指定多个profile :
    环境与profile
除了所有的bean定义到了同一个XML文件之中, 这种配置方式与定义在单独的XML文件中的实际效果是一样的。 这里有三个bean, 类型都是javax.sql.DataSource, 并且ID都是dataSource。 但是在运行时, 只会创建一个bean, 这取决于处于**状态的是哪个profile。 


那么问题来了: 我们该怎样**某个profile呢? 

 

**profile :
Spring在确定哪个profile处于**状态时, 需要依赖两个独立的属性: spring.profiles.active和spring.profiles.default。 如果设置了spring.profiles.active属性的话, 那么它的值就会用来确定哪个profile是**的。 但如果没有设置spring.profiles.active属性的话, 那Spring将会查找spring.profiles.default的值。 如果spring.profiles.active和spring.profiles.default均没有设置的话, 那就没有**的profile, 因此只会创建那些没有定义在profile中的bean。 
    
有多种方式来设置这两个属性:
作为DispatcherServlet的初始化参数;
    环境与profile
作为Web应用的上下文参数;
    环境与profile
作为JNDI条目;

作为环境变量;
    环境与profile
作为JVM的系统属性;
    环境与profile
在集成测试类上, 使用@ActiveProfiles注解设置。 
    环境与profile
    web.xml配置:

    环境与profile
当应用程序部署到QA、 生产或其他环境之中时, 负责部署的人根据情况使用系统属性、 环境变量或JNDI设置spring.profiles.active即可。 当设置spring.profiles.active以后, 至于spring.profiles.default置成什么值就已经无所谓了; 系统会优先使用spring.profiles.active中所设置的profile。 

在spring.profiles.active和spring.profiles.default中, profile使用的都是复数形式。 这意味着你可以同时**多个profile, 这可以通过列出多个profile名称, 并以逗号分隔来实现。 当然, 同时启用dev和prod profile可能也没有太大的意义, 不过你可以同时设置多个彼此不相关的profile。
 
当运行集成测试时, 通常会希望采用与生产环境(或者是生产环境的部分子集) 相同的配置进行测试。 但是, 如果配置中的bean定义在了profile中, 那么在运行测试时, 我们就需要有一种方式来启用合适的profile。 

Spring提供了@ActiveProfiles注解, 我们可以使用它来指定运行测试时要**哪个profile。 在集成测试时, 通常想要**的是开发环境的
profile。 例如, 下面的测试类片段展现了使用@ActiveProfiles**dev profile: 
    环境与profile