在日常中,经常会碰到一些环境相关的术语,也有说是上下文的,Maven中通过profile抽象出了环境配置,使开发人员可以通过不同的profile来定义不同的build上下文,Spring中也有环境一说,通过接口Enviroment来抽象,其随着Spring IoC容器的初始化而建立起来,本文将探究一番Spring运行时环境的相关细节。
-
接口定义
Spring中将容器环境抽象为了Environment接口:
可以看到Environment定义了有关获取profile的方法,并且继承了PropertyResolver接口,该接口定义了从具体的属性源中解析属性的功能:
可见Spring环境主要由两个组件组成:配置(profile)和属性(property)。profile只是一个名称字符串,可对Bean容器中的Bean进行逻辑分组,即在定义Bean时,可以指定该Bean归入到某些profile中。Property则是大多应用所常见的,Spring中这些属性可以来自properties文件,JVM系统属性,系统环境变量,JNDI,Servlet上下文参数,Properties对象等。
-
Environment实现
先看下Environment继承树,比较简单直观:
-
ConfigurableEnvironment
ConfigurableEnvironment主要提供对Spring容器环境的一些配置功能,并暴露了重要的getPropertySources()方法,用户可以对环境中的属性源进行更新操作等:
-
AbstractEnvironment
AbstractEnvironment作为Environment的基础实现,可以设置激活的profile和默认激活的profile,具体子类主要需要提供默认的属性源对象,通过customizePropertySources()来定制属性源,而客户端应该通过getPropertySources()来定制属性源:
-
StandardEnvironment
StandardEnvironment比较简单,主要定制了系统属性和系统环境这两个属性源:
-
StandardServletEnvironment
StandardServletEnvironment从其名称,是针对Web容器的一个环境对象:
-
Environment初始化
从Spring IoC容器实现中可以知道Environment的初始化发生在Bean加载之前,并在解析配置文件占位符时被实例化:
-
Environment使用
-
直接使用Environment
日常开发中,若想使用Environment,可以通过依赖注入或Spring中普遍的Aware回调方式:
-
profile使用
之前介绍IoC容器初始化时,在DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions()中加载XML配置文件元素时,会根据当前容器激活的环境进行加载:
从前文可知,用户可以通过spring.profile.active属性激活当前容器环境,这将便于开发人员在不同的profile环境中配置不同的bean:
-
属性源
上文将了Spring容器环境两个组件中的profile组件,下面将介绍其另一个组件property(属性)。
-
定制属性源
通常开发中,需要定制自己的一些属性源,如一些properties文件等,可以通过PropertySourcesPlaceholderConfigurer配置自定义的properties属性文件:
PropertyPlaceholderConfigurer本质是一个BeanFactoryPostProcessor,在Spring IoC容器实现中说到过,BeanFactoryPostProcessor会在所有Bean加载完之后且Bean实例化之前被调用,即在AbstractApplicationContext.invokeBeanFactoryPostProcessors()中调用:
通过上述BeanFactoryPostProcessor的处理,BeanDefinition中的属性的值若包含占位符,将被替换为属性源中对应的属性值,当然这只是解析了bean配置中的属性,如:
而对于通过注解方式定义的属性,将会AutowiredAnnotationBeanPostProcessor注解处理器中的postProcessPropertyValues方法中注入,如:
除了通过PropertyPlaceholderConfigurer注入属性,还可以通过Spring提供的util标签达到该目的:
与PropertyPlaceholderConfigurer不同,上面这种util:properties方式会在容器中对应注册一个名称为app的PropertiesFactoryBean,最终在注入mode字段时会通过SPEL表达式来解析。
以上则是Spring运行时环境相关的一些细节,其通过profile机制使得开发人员可以在Spring容器中隔离不同的配置,和通过属性源定制,能在程序中注入各种配置的属性,甚至通过定制PropertyPlaceholderConfigurer,可以实现类似中心化配置的功能。