1、环境与profile
在开发软件的时候,有一个很大的挑战就是将应用软件从一个环境迁移到另一个环境,开发阶段中,某些环境相关做法可能并不适合迁移到生产环境,甚至即便迁移过去也无法正常运行。为此Spring可根据环境(在运行时)去决定该创建那个bean和不创建那个bean。这样同一个部署单元(可能是war文件)能够运用于所有的环境,没有必要进行重新构建。
例如:开发环境下的DataSource 用EmbeddedDatabaseBuilder 创建,生产环境下的DataSource用JndiObjectFactoryBean创建更加合理。
1.1、配置profile bean
在3.1版本中, Spring引入了bean profile的功能。要使用profile,你首先要将所有不同的bean定义整理到一个或多个profile之中,在将应用部署到每个环境时,要确保对应的profile处于激活(active)的状态。
- 通过Java配置类配置——@Profile
@Configuration public class DataSourceConfig { @Bean(destroyMethod="shutdown") @Profile("dev") // 为dev profile装配的bean public DataSource embeddedDataSource() { return new ... } @Bean @Profile("prod") // 为prod profile装配的bean public DataSource jndiDataSource() { return new ... } }
- 通过XML配置profile
<beans ...> <beans profile="dev"> <bean>...</bean> </beans> <beans profile="qa"> <bean>...</bean> </beans> <beans profile="prod"> <bean>...</bean> </beans> </beans>
1.2、激活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的初始化参数;
- 作为Web应用的上下文参数;
- 作为JNDI条目;
- 作为环境变量;
- 作为JVM的系统属性;
- 在集成测试类上,使用@ActiveProfiles注解设置。
例如:在Web应用的web.xml文件中设置默认的profile
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>ssm-spring</display-name> <context-param><!-- 为上下文设置默认profile --> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </context-param> <!-- springmvc前端控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframwork.web.servlet.DispatcherServlet</servlet-class> <init-param><!-- 为Servlet设置默认profile --> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
2、条件化的Bean
【条件】
- 希望一个或多个bean只有在应用的类路径下包含特定的库时才创建;
- 希望某个bean只有当另外某个特定的bean也声明了之后才会创建 ;
- 要求只有某个特定的环境变量设置之后,才会创建某个bean。
解决办法:Spring 4引入了一个新的@Conditional注解,它可以用到带有@Bean注解的方法上。如果给定的条件计算结果为true,就会创建这个bean,否则的话,这个bean会被忽略。
@Configuration @ComponentScan public class BeanConfig { @Bean @Conditional(MagicExistsCondition.class) // 设置创建bean的条件 public MagicBean magicBean() { return new MagicBean(); } } public class MagicExistsCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment ev = context.getEnvironment(); return ev.containsProperty("magic"); // 检查magic属性是否存在 } }
3、处理自动装配的歧义性
当在Spring上下文中存在多个bean能够匹配所需的结果时,@autowired自动装配将产生歧义,Spring将抛出NoUniqueBeanDefinitionException异常。
解决办法:
- 标示首选的bean
@Component @Primary public class IceCrean implements Dessert {...}
<bean id="iceCream" class="..." primary="true" />
- 限定自动装配的bean——使用@Qualifier
(1)、使用@Qualifier("iceCream")限定bean的范围。@Qualifier注解所设置的参数就是想要注入的bean的ID;
(2)、使用自定义的限定符注解
@Component @Qualifier("cold") public class Poosicle implements Dessert {...} @Bean @Qualifier("cold") public Dessert iceCream() { return new IceCream(); }
4、bean的作用域
在默认情况下, Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。 也就是说,不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同一个实例。
Spring定义了多种作用域:
- 单例(Singleton):在整个应用中,只创建bean的一个实例(默认作用域)。
- 原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
- 会话(Session):在Web应用中,为每个会话创建一个bean实例。
- 请求(Rquest):在Web应用中,为每个请求创建一个bean实例。
4.1、使用@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE )设置bean的作用域;
4.2、使用<bean id="xxx" class="xxxx" scope="" />设置bean的作用域。
5、运行时值注入
运行时注入的两种方式:
- 属性占位符(Property placeholder);
- Spring表达式语言(SpEL)。
5.1、注入外部的值
(1)、通过Java配置类注入
@Configuration @PropertySource("classpath:com/zhux/ssm/app.properties") // 声明属性源 public class ExpressiveConfig { @Autowired Environment env; @Bean public MagicBean magicBean() { return new MagicBean(env.getProperty("magic.title"), env.getProperty("magic.arttist")); // 检索属性值 } }
(2)、通过XML注入
<context:property-placeholder location="classpath:db.properties"/> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean>
(3)、使用@Value注解——如果我们依赖于组件扫描和自动装配来创建和初始化应用组件的话,那么就没有指定占位符的配置文件或类了 。此时我们可以使用@Value注解。为了使用占位符,我们必须要配置一个PropertyPlaceholderConfigurer bean或PropertySourcesPlaceholderConfigurer bean。
public class MagicBean {
public MagicBean(@Value("${magic.title}") title, @Value("${magic.artist}") artist) { ... }
}
// Java配置类中
@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer {
return new PropertySourcesPlaceholderConfigurer();
}
5.2、使用Spring表达式语言进行装配——SpEL