环境相关
profile的使用
@Profile(“profile名字”)来标注某个bean属于哪个profile领域。标注于类上,代表只有在特定profile激活时,该类中的bean才会被创建,没有标注profile的类始终会被创建。可用于使用在不同环境下,比如开发过程中、QA过程中或者生产过程中。现也可标注于方法级别。
在XML中使用时,在顶部声明中添加profile=“…”。例如:
<beans
…
profile=“dev” >
也可在同一个XML文件中,定义多个profile,即定义多个beans,每个beans添加不同的profile属性。当实际运行时只会根据环境,选择创建一个。
如何激活profile:
设置spring.profiles.active设置想激活的profile;设置spring.profiles.default设置默认激活的profile。
1、在DispatcherServlet的初始化参数中设置
例如在web.xml中:
<!— 为Servlet设置默认profiles —>
<servlet>
…
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</init-param>
</servlet>
2、作为Web应用的上下文参数(Context)
例如在web.xml中:
<!— 为Context设置默认profiles —>
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>
3、JNDI条目
同样在web.xml中:
<env-entry>
<env-entry-name>spring.profiles.default</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>dev</env-entry-value>
</env-entry>
4、环境变量
例如在Linux系统下,设置.bash_profile文件,将spring.profiles.default=dev添加进去。
5、JVM系统属性
可通过命令行-D<name>=<value>来设置,例如java -Dspring.profiles.default=dev。
6、在测试类中使用@ActiveProfiles注解
在测试类级别标注@ActiveProfiles(“dev”)。
条件化bean
为实现bean级别的条件化配置,使用标注@Conditional,只有符合条件的bean才会被创建。@Conditional(condition.class),传入的condition类需实现Condition接口,调用其中的matches()方法,通过返回的布尔值进行判断。matches方法有两个传入参数,一个是ConditionContext,一个是AnnotatedTypeMetadata。前者可以调用其中的方法获得一些环境变量,如getRegistry()检查bean定义,getBeanFactory()检查bean是否存在,getEnvironment()获得环境变量,getResourceLoader()获得加载的资源,getClassLoader()获得类加载器。后者获得标注@Conditional的bean上的其他注解。
解决注入的二义性
当被注入的对象存在多个匹配的bean时,无法进行正确装配,需要处理注入bean的二义性。
有两种处理方法:
一,可以在bean处进行@Primary标注,则有该注解的bean成为首选的bean,但是如果存在多个Primary注解,则二义性仍会发生。
二,在被注入的对象、方法进行@Qualifier标注,例如@Qualifier(“beanName”),则能明确该地方选择ID为beanName的bean。在bean处进行@Qualifier的标注,则可以使被标注的bean获得限定符。例如
@Component
@Qualifier(“name”)
public class BeanName {…}
表示当被注入的对象进行@Qualifier(“name”)标注时,注入的是BeanName这个对象,即使之后bean产生修改,不叫BeanName这个名字了,也可完成注入。如果同一限定符指向多个bean,需要用多个限定符来制定唯一的bean,则需避开同一标记(即@Qualifier)不能多次使用这个限定。可以创造自己的标注,使用模版
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Rentention(RententionPolicy.RUNTIME)
@Qualifier
public @interface <name>{}
第一行表示该注解可作用的领域;第二行表示该注解可保留的阶段,共有三个值(SOURCE,CLASS,RUNTIME),分别表示在源码阶段(被编译时即被忽略,在class文件中不可见)、编译阶段(在class文件中可见,但被JVM忽略)、运行阶段(JVM可见,并且可被读取、使用)保留;第三行表示该注解是一个限定符。
bean的作用域
为什么需要规定bean的作用域?在一般情况下,注入的bean都作为一个单例,如果这个实例应用在多个场景下,一些场景要求对它属性进行修改,一些不希望它的属性发生改变,就会产生冲突。因此针对不同的bean的应用场景,需要规定不同的作用域。
一般有四种作用域:
1、单例(Singleton),最常见的场景
2、原型(Prototype),每次注入都是重新创建的实例
3、会话(Session),每个会话创建一个实例
4、请求(Request),每个请求创建一个实例
在bean上使用@Scope标注,例如@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE),不标注默认为单例。如果使用XML,则在<bean>中添加属性scope=“prototype”。
对于3和4,一个例子是购物车,如果使用单例,则每个人看到的购物车是所有人一起拥有的,即你的购物车中有其他人添加的东西;如果使用原型,则每访问一个新的页面,添加产品进购物车,之前添加的商品将会不见。这里的使用需要将@Scope注解标注于被注入对象之上,例如
@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart() {…}
该模式只有当某个用户创建完会话后,才会进行注入,创建实例。@Scope中的value制定了作用域,proxyMode指定了代理模式,使用接口代理,如果注入的不是接口,则改为TARGET_CLASS。使用XML时,在<bean>中添加元素<aop:scoped-proxy />。
为什么要使用代理?在1和2中,不必使用代理,但在3和4中,不使用代理就会在装配过程中报错。因为对于被注入的对象,它可能是一个单例bean,因此不能将多个不同的注入的bean的实例注入到其中。原理是注入的代理会根据当前的会话或请求,将调用委托给实际的bean。这样代理对象只用存在一个,但是实际注入的bean存在多个。