Spring学习笔记-高级装配-03
主要内容:
●Spring profile
●条件化的bean声明
●自动装配与歧义性
● Spring表达式语言
本章介绍一些高级的装配技术,可实现更为高级的装配功能。
环境与profile
软件开发时,有一个很大的挑战就是讲应用程序从一个环境迁移到另外一个环境,开发环境可能并不适合迁移到生产环境,比如:数据库配置、加密算法以及外部系统的集成可能会发生变化。
比如数据库配置:
● 在开发环境中可能使用的是嵌入式数据库,并预先加载测试数据,bean配置类可能会在一个带有@Bean注解的方法上使用EmbeddedDatabasebuilder(嵌入式数据源)。
● 在生产环境中可能使用JNDI从容器中获取一个DataSource。
● 在测试环境中可能选择另外一个不同的DataSource。
在不同的还击某个bean因此不同,我们需要一种方法来配置DataSource,使其在不同环境中都会选择最合适的配置。
配置profile
Spring3.1版本中,引入了bean profile功能,要使用它,首先要将所有不同的bean定义整理到一个或多个profile中,在将应用部署到每个环境时,要确保对应的profile处于激活的状态。
使用配置类配置profile
在Java配置中,可使用@Profile注解指定某个bean属于哪一个profile,例如,在配置类中,
嵌入式数据库的DataSource配置
@Configuration @Profile("dev") //告诉Spring这个配置类的bean只有在dev profile激活时才会创建,如果dev profile没有激活那么带有@Bean注解的方法都会被忽略掉 public class DevelopmentProfileConfig {
@Bean public DataSource dataSource(){ return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classppath:schema.sql") .addScript("classpath:test-data.sql") .build(); } }
一个适用于生产环境的配置
@Configuration @Profile("prod") //告诉Spring这个配置类的bean只有在prod profile激活时才会创建,如果dev profile没有激活那么带有@Bean注解的方法都会被忽略掉 public class ProductionProfileConfig { @Bean public DataSource dataSource(){ JndiObjectFactoryBean jndiObjectFactoryBean= new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/DS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean; } }
在Spring中,只能在类级别上使用@Profile注解,不过从3.2版本开始,也可以再方法级别使用@Profile,这样的话就能和@Bean同时使用,同时将两个bean的声明放在同一个配置类中。
@Profile注解基于激活的profile实现bean的装配
@Configuration public class DataSourceProfile { @Bean @Profile("dev") //为dev profile装配的bean public DataSource embededDataSource(){ return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classppath:schema.sql") .addScript("classpath:test-data.sql") .build(); } @Bean @Profile("prod") //为prod profile装配的bean public DataSource jndiDataSource(){ JndiObjectFactoryBean jndiObjectFactoryBean= new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/DS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean; } }
使用XML配置profile
了解即可
激活profile
String在确定哪个profile处于激活状态时,需要依赖两个独立的属性:spring.profile.active和spring.profiles.default,如果设置了spring.profile.active属性,那么它的值就会用来确定哪个profile是激活的,如果没有设置,那就会查找spring.profiles.default的值,如果均没有设置那就没有激活的profile,则只会创建那些没用定义在profile中的bean。
有多种方式来设置这两个属性:
● 作为DispatcherServlet的初始化参数;
● 作为Web应用的上下文参数;
● 作为JNDI条目;
● 作为环境变量;
● 作为JVM的系统属性;
● 在集成测试类上,使用@ActiveProfiles注解设置。
在Web.xml文件中设置默认的profile
<Web-app version="2.5"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/root-context.xml</param-value> </context-param> <context-param> <param-name>spring.profiles.default</param-name> <!-- 为上下文设置默认的profile --> <param-value>dev</param-value> </context-param> <listener> <listent-class> org.springframework.web.context.ContextLoaderListener </listent-class> </listener> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>spring.profiles.default</param-name> <!-- 为Servlet设置默认的profile --> <param-value>dev</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </Web-app>
按照这种方式设置spring.profiles.default,所有开发文员都能才能够版本控制软件获得应用程序代码,并且使用开发环境的设置运行代码,而不需要任何额外设置。
当程序部署到测试、生产或其他环境中,负责部署人员根据情况使用系统属性、环境变量或JNDI设置spring.profiles.active即可,此时spring.profiles.default值会被忽略,因为spring会优先使用spring.profiles.active的值。
使用profile进行测试
当集成运行测试时,通常会希望采用与生产环境相同的配置进行测试,但如果配置中的bean定义在profile中,那么在运行测试中,就需要有一种方式来启用合适的profile。
Spring提供了@ActiveProfiles注解,我们可使用它指定运行测试时要激活那个Profile。在集成测试时,通常想要激活的是开发环境的profile,例如,下面的测试类片段展现如何使用@ActiveProfile激活dev profile:
@RunWith(SpringJUnit4ClassRunner.class) @ContextCOnfiguration(classes={PersistenceTestConfig.class}) @ActiveProfiles("dev") public class ActiveProfileTest { ....
条件化的Bean
假设希望一个或多个bean只有在应用的类路径下包含特定的库时才会创建。或希望某个bean只有当另外某个特定的bean也声明后才会创建,以及等等需要满足各种条件才会创建。
在Spring4引入了一个新的@Conditional注解,它可以用到带有@Bean注解的方法上,如果给定的条件计算结果为true,就会创建这个bean,否则就会被忽略。
未完待续...