内容概述
- Spring Profile
- 条件化的bean声明
- 自动装配与歧义性
- bean的作用域
- Spring表达式语言
1、Spring Profile
在开发软件的时候,有一个很大的挑战就是将应用程序从一个环境迁移到另一个环境。开发阶段,某些环境相关做法可能并不适合迁移到生产环境中,甚至几遍迁移过去也无法正常工作。数据库配置、加密算法以及与外部系统的集成是跨环境部署时会发生变化的几个典型例子。
以dataSource bean为例,在开发环境中,使用一个嵌入式数据库会大大提高开发效率,减少dataSource配置的复杂性,并且可以使得每次启动数据库时,它都处于一个给定的状态:
@Bean(destroyMethod="shutdown")
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder()
.addScript("classpath:schema.sqsl")
.addScript("classpath:test-data.sql")
.build();
}
使用EmbeddedDatabaseBuilder会搭建一个嵌入式的Hypersonic数据库,它的模式(schema)定义在schema.sql中,测试数据则是通过test-data.sql加载的。当在开发环境中运行集成测试或者启动应用进行手动测试的时候,这个DataSource是很有用的。
尽管该嵌入式数据库创建的DataSource非常适合开发环境,但是对于生产环境来说,这会是一个糟糕的选择。在生产环境中,你可能会希望使用JCDI从容器中获取一个DataSource。在这样的场景下,如下的@Bean方法会更加合适:
@Bean
public DataSource dataSource(){
JndiObjectFactoryBean jndi=new JndiObjectFactoryBean();
jndi.setJndiName("jdbc/myDS");
jndi.setResourceRef(true);
jndi.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndi.getObject();
}
显然,这里展现的连个版本的dataSource()方法互不相同。虽然他们都会生成一个类型为javax.sql.DataSource的bean,但他们的相似点也仅限于此了。每个方法都使用了完全不同的策略来生成DataSource bean。这是一个很好的例子,它表现了在不同的环境中某个bean会有所不同。我们必须要有一种方法来配置DataSource,使其在每种环境下都会选择最适合的配置。
其中一种方式就是在单独的配置类(或XML文件)中配置每个bean,然后在构建阶段(可能会使用Maven的profiles)确定要将哪一个配置编译到可部署的应用中。这种方式在QA阶段迁移到生产阶段时会带来诸多弊端,并且工作量也会大增。而Spring提供一种更为简便的方式,那就是配置profile bean。
1.1在JavaConfig配置profile bean
Spring为环境相关的bean所提供的解决方案其实与构建时的方案没有太大的差别。当然,在这个过程中需要根据环境决定该创建那个bean和不创建哪个bean。不过Sprig并不是在构建的时候做出这样的决策的,而是等到运行时再来确定(这样的话就必须确保每一种环境的配置都必须随时存在,这会增加项目文件体积的吧?一般配置文件都相对较小,而且拿空间换效率也是当下流行的做法)。这样的结果是同一个部署单元(可能会是war包)能够适用于所有的环境,没有必要进行重新构建。
在3.1版本中,Spring引入了bean profile功能。要使用profile,你首先要将所有不同的bean定义整理到一个或多个profile之中,在将应用部署到每个环境时,要确保对应的profile处于激活(active)状态。
在Java配置中,可以使用@Profile注解指定那个bean属于哪一个profile。例如,在配置类中,嵌入式数据库的DataSource可能会配置成如下所示:
@Configuration
@Profile("dev")
public class DevelopmentProfileConfig{
@Bean(destroyMethod="shutdown")
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder()
.addScript("classpath:schema.sqsl")
.addScript("classpath:test-data.sql")
.build();
}
}
我希望你能够注意的是@Profile注解应用在了类级别上。它会告诉Spring这个配置类中的bean只有在dev profile激活时才会创建。同时你能还需要一个适用于生成环境的配置,如下所示:
@Configuration
@Profile("prod")
public class ProductionProfileConfig{
@Bean
public DataSource dataSource(){
JndiObjectFactoryBean jndi=new JndiObjectFactoryBean();
jndi.setJndiName("jdbc/myDS");
jndi.setResourceRef(true);
jndi.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndi.getObject();
}
}
在Spring3.1中,只能在类级别上使用@Profile注解。不过,从Spring3.2开始,你也可以在方法级别上使用@Profile注解,与@Bean注解一起使用(这下可就方便多了!!!)。这样的话,就能将这两个bean的声明放在同一个配置类中,如下所示:
@Configuration
public class DataSourceConfig{
@Bean(destroyMethod="shutdown")
@Profile("dev")
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder()
.addScript("classpath:schema.sqsl")
.addScript("classpath:test-data.sql")
.build();
}
@Bean
@Profile("prod")
public DataSource dataSource(){
JndiObjectFactoryBean jndi=new JndiObjectFactoryBean();
jndi.setJndiName("jdbc/myDS");
jndi.setResourceRef(true);
jndi.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndi.getObject();
}
}
这里有个问题需要注意,尽管每个DataSource bean都被声明在一个profile中,并且只有当规定的profile激活时,相应的bean才会被创建,但是可能会有其他的bean并没有声明在一个给定的profile范围内。没有指定profile的bean始终都会被创建,与激活那个profile没有关系。
1.2在XML中配置profile
我们也可以通过元素的profile属性,在XML中配置profile bean。例如,为了在XML中定义适用于开发阶段的嵌入式数据库DataSource bean,我们可以创建如下所