本章内容:
- Spring profile
- 条件化的bean声明
- 自动化装配与歧义性
- bean的作用域
- Spring表达式语言
1.环境与profile
在不同的环境中某个bean会有所不同(比如,DateSource),我们可以为不同的生产环境添加不同profile的设置文件,在构建项目的时候通过给spring.profiles.active属性不同的profile值来决定加载那个配置文件的信息来解决这个问题,同样在高级配置中spring给出了其他的解决方法(profile bean),我们可以选着性使用
配置profile bean:
Spring为环境相关的bean所提供的方案其实与构建时的方案没有太大的差别。当然,在这个过程中需要根据环境决定该创建哪个bean和不创建哪个bean。不过Spring并不是在构建的时候做出这样的决策,而是等到运行时再来确定。这样的结果就是同一个部署单元(可能是war文件,也可能是jar文件)能够适用于所有的环境,没有必要重新构建
在Java配置中,可以使用@Profile注解指定某个bean属于哪一个profile
激活profile:
Spring在确定那个profile处于激活状态时,需要依赖两个独立的属性:spring.profiles.active和spring.profiles.default如果设置了spring.profiles.active属性的话,那么它的值就会用来确定那个profile是激活的。但如果没有设置spring.profiles.active属性的话,那Spring将会查找spring.profiles.default的值。都没设置的话,那就没有激活的profile,因此只会创建哪些没有定义在profile中的bean
有多种方式来设置这两个属性:
作为DispatcherServlet的初始化参数;
作为Web应用的上下文参数;
作为JNDI条目;
作为环境变量;
作为JVM的系统属性;
在集成测试类上,使用@ActiveProfiles注解设置
2.条件化的bean
假设你希望一个或多个bean只有在应用的类路径下包含特定的库时才创建。或者我们希望某个bean只有当另外特定的bean也声明了之后才会创建。我们还可能要求只有某个特定的环境变量设置之后,才会创建某个bean。
@Conditional注解能够实现这个需求,它可以用到带有@Bean注解的方法上。如果给定的条件计算结果为true,就会创建这个bean,否则的话,这个bean会被忽略
条件计算类需要事项Condition接口,该接口中只有一个matches()方法,方法中包含ConditionContext得到的Environment两个参数
通过ConditionContext,我们可以做到如下几点:
借助getRegistry()返回的BeanDefinitionRegistry检查bean定义;
借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性;
借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么;
读取并探查getResourceLoader()返回的ResourceLoader所加载的资源;
借助getClassLoader()返回的ClassLoader加载并检查类是否存在。
3.处理自动装配的歧义性
自动化装配时,仅有一个匹配所需的结构时,才是有小的。如果不仅有一个bean能够匹配结果的话,这种歧义性会阻碍Spring自动装配属性、构造器参数或方法参数,并抛出NoUniqueBeanDefinitionException。当发生歧义性的时候,我们可以将可选bean中的某一个设定为所选(primary)的bean,或者使用限定符(qualifiler)来帮助Spring将可选的bean的范围缩小到只有一个bean
标识首选的bean:在bean类上,或者bean方法上添加@Promary
限定自动装配的bean:与@Autowired组合是用@Qualifiler注解
4.bean的作用域
在默认的情况下,Spring应用上下文中所有bean都是作为已单例(singleton)的形式创建的。也就是说,不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同一个实例
有时候,可能会发现,你所使用的类是易变的(mutable),它们会保持一些状态,因此重用是不安全的。在这种情况下,将class声明为单例的bean就不是什么好主意了,因为对象会被污染,稍后重用的时候会出现意想不到的问题
Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
单例(Singleton):在整个应用中,只创建bean的一个实例。
原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
会话(Session):在Web应用中,为每个会话创建一个bean实例。
请求(Rquest):在Web应用中,为每个请求创建一个bean实例。
单例是默认的作用域,但是正如之前所述,对于易变的类型,这并不合适。如果选择其他的作用域,要使用@Scope注解,它可以与@Commonent或@Bean一起使用
5.运行时值注入
当讨论依赖注入的时候,我们通常所讨论的是将一个bean引用注入到另一个bean的属性或构造器参数中。它通常来讲的是将一个对象与另一个对象进行关联。但是bean装配的另外一个方面是将一个值注入到bean的属性或者构造参数中
Spring提供了两种在运行时求值的方式:
- 属性占位符
- Spring表达式语言
在Spring中,处理外部值最简单方式就是声明属性源并通过Spring的Environment来检索属性
解析属性占位符:
使用Spring表达式语言进行装配: