使用Spring profile 能够设定在不一样的环境中启用不一样的bean.因为环境的不一样,数据库配置,加密算法以及与外部系统的集成可能会有不一样的表现,特别是数据库,通常开发环境,QA环境,预发布环境,生产环境会分开java
Spring并非在构建的时候作出这样的决策的,而是等到运行时再来肯定web
使用@Porfile
注解指定某个bean属于哪个profile,算法
@Profile("dev")
表示profile环境是dev.spring
只有相应的profile激活的时候,才会建立对应的bean,可是,没有指定profile的bean始终都会被建立,与激活哪一个profile没有关系数据库
<beans xmlns="http://springframework.org/schema/beans" xmlns:... profile="dev"> </beans>
建立不一样的XML文件设置不一样的profile,也能够在根<bean>
元素中嵌套定义<beans>
元素,而不是为每个环境建立一个profile XML文件.app
Spring在肯定哪一个profile处于激活状态时,须要依赖两个独立的属性:spring.profiles.active和spring.profiles.default,若是设置了 spring.profiles.active 属性,那么它的值就会用来肯定哪一个profile是激活的,可是若是没有设置,那 Spring会查找spring.profiles.defalut的值.若是二者均为设置,那么就没有激活的profile,Spring只会建立哪些没有定义在profile中的bean.oop
示例:
在web.xml中为上下文设置默认的profile测试
<context-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </context-param>
在web.xml中为Servlet设置默认的profileui
<servlet> <servlet-name>appServlet</servlet-name> <servlet-class> prg.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </init-param> </servlet>
Spring提供了@ActiveProfiles
注解指定运行测试时要激活哪一个profile.this
Spring4之后,引入了@Conditional
,能够用到带有@Bean
注解的方法上,用于计算给定的条件若是为true,就会建立这个bean,不然的话这个bean会被忽略
例若有一个Test的类,咱们但愿只有设置了 test 环境属性的时候,Spring才会实例化这个类,若是没有,就会被忽略.下面的配置展现了注解的使用方式
@Bean @Conditional(TestExistsCondition.class) public Test test() { return new Test(); }
@Condition
中给定了一个Class,指明建立该bean的条件,设置给它的类能够是任意实现了Condition接口的类型.下面展示了TestExistsCondition类的实现
public class TestExistsCondition() { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = context.getEnvironment(); return env.containsProperty("test"); } }
其中的两个参数 ConditionContext, AnnotatedTypeMetadata 都是接口,具体请看P77
在使用自动装配时,若是一个接口有多个实现类而且都被定义为bean,当Spring试图装配的时候就会没法作出选择从而抛出 NoUniqueBeanDefinitionException, Spring提供了两种方式来解决这样的问题:
在 bean 上使用@Primary注解标示当前的bean是首选bean,若是你是用XML来实例化bean
,能够用下面的方式:
<bean id="iceCream" class="com.IceCream" primary="true"> </bean>
可是,若是你设置了两个或者更多的首选bean,那么它就没法正常工做了
@Qualifier注解是使用限定符的主要方式:
@Autowired @Qualifier("iceCream") public void setDessert(Dessert dessert) { this.dessert = dessert }
上面的代码片断是最简单的例子,为@Qualifier注解所设置的参数就是想要注入的bean的ID,全部的@Component注解建立的类的ID都是首字母变为小写的类名.须要注意的是,若是没有指定其余的限定符的话,全部的bean都会给定一个默认的限定符,这个限定符与bean的ID相同
这里有个问题就是,指定的限定符与要注入的bean的名称是紧耦合的,若是bean的名字修改了,那么就会没法注入,解决方案是在bean上使用@qualifier
指定该bean的限定符
若是iceCream同时被使用在两个或更多的bean上,那怎么办呢?你可能会想到的是使用更多的限定符来缩小范围:
@Component @Qualifier("iceCream") @Qualifier("cold") public class IceCream implements Dessert {...}
这里只有一个小问题,Java不容许在同一个条目上重复出现相同类型的多个注解.若是你这么作了,编译将会报错.可是,咱们能够建立自定义的限定符注解:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Qualifier public @interface Cold{}
上面的代码片断定义了一个自定义注解,它自己要使用@Qualifier来标注,这样就能够不断缩小范围,直到惟一.
在默认状况下,Spring应用上下文中全部的bean都是做为以单利的形式建立的,不过,Spring提供了多种做用域,能够基于这些做用域建立bean:
默认是单例模式,若是选择其余的做用域,要使用@Scope注解,例如,要将一个bean生命为原型,须要这样作:
@bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Notepad{...}
使用组件扫描来发现和声明bean,只须要加上@Component
注解替换@Bean
,一样,若是使用XML来配置bean的话,可使用scope属性来设置做用域:
<bean id="notepad" class ="com.notepad" scope="prototype" />
对于会话做用域,在当前会话的相关操做中,bean其实是单例的:
@Component @Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES) public ShoppingCart cart() {...}
要注意的是,同时还有一个proxyMode属性被设置,请看下面的代码:
@Component public class StoreService { @Autowired public void setShoppingCart(ShoppingCart shoopingCart) { this.shoppingCart = shoppingCart } }
这个service是一个单例的bean,当它建立的时候,Spring会试图将ShoppingCart bean注入,可是ShoppingCart是会话做用域的,此时并不存在,知道某个用户进入到系统才会出现实例,另外,系统中将会有多个ShoppingCart实例被建立,咱们但愿注入的bean恰好是当前会话对应的那一个,所以,Spring会向StroeService中注入一个到ShoppingCart的代理.关于具体的描述,请看P87
在代码中,最好的方式是不要在代码中出现硬代码,所以咱们须要借助Spring的一些配置来注入外部的值:
@Configuration @PropertySource("") public class ExpressiveConfig { @Autowired Environment env; @Bean public BlankDisc disc() { return new BlankDisc(env.getProperty("disc.title")); } }
咱们使用了@ PropertySource
注解引用了外部的一个属性文件,其中有disc.title属性,会被读取并配置到bean中,若是这个属性没有被定义,获取到的值会是null,若是你但愿这个属性必须被定义,可使用getRequiredProperty()方法.
另外,可使用Environment.containsProperty()方法来判断是否有某一个属性.最后,若是想将属性解析为类的话,可使用getPropertyAdClass()方法.
若是咱们依赖组件扫描和自动装配来建立和初始化应用组件的话,娜美可使用@Value
注解,它与@Autowired
很是类似:
public BlankDisc(@Value("${disc.title}") String title) { this.title = title; }
为了使用占位符,咱们必需要配置一个PropertyPlaceHolderConfigurer bean或 PropertySourcePlaceHolderConfigurer bean,具体请看P92
Spring 3.2之后,注解支持在类和方法级别上使用
@Profile("...")
括号中能够定义任意的字符串,但建议用有意义的字符串