文章目录
- 1 概述
- 1.1 容器启动阶段
- 1.2 实例化阶段
- 1.3 容器扩展机制
- 1.4 常用的BeanFactoryPostProcessor
- 1.4.1 PropertyPlaceholderConfigurer
- 1.4.2 PropertyOverrideConfigurer
- 1.4.3 CustomEditorConfigurer
- 1.5 bean的流程
- 1.5.1 BeanWrapper
- 1.5.2 Aware接口
- 1.5.3 BeanPostProcessor
- 1.5.4 BeanPostProcessor 自定义注解
- 1.5.4 InitializingBean和init-method
- 1.5.5 DisposableBean与destory-method
1 概述
spring 容器实现的功能,可以分为两个阶段,即容器启动阶段和bean实例化阶段。
1.1 容器启动阶段
首先会通过某种途径加载相关配置并进行解析和分析,并将分析后的信息编组为相应的BeanDefinition**,并把这些BeanDefinition注册到相应的BeanDefinitionRegistry。**
1.2 实例化阶段
在所有bean信息都通过BeanDefinition的方式注册BeanDefinitionRegistry中时,当请求通过容器的getBean方法明确了请求对象时(getbean调用)将会触发第二阶段。
首先容器会检查所请求的对象之前是否已经初始化。如果没有则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并注入依赖
1.3 容器扩展机制
spring 提供了 一种为 BeanFactoryPostProcessor容器扩展机制,该机制允许我们在实例化前,**对注册到BeanDefinition所保存的信息进行修改。**而对于ApplicationContext,其会自动识别配置文件中的BeanFactoryPostProcessor并应用.
xml 中的配置使用
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<!--获取配置文件位置-->
<list>
<value>conf/mysql.properties</value>
</list>
</property>
</bean>
1.4 常用的BeanFactoryPostProcessor
1.4.1 PropertyPlaceholderConfigurer
PropertyPlaceholderConfigurer 允许我们在xml配置文件中使用占位符,并将这些占位符所代表的资源单独配置到简单的properties文件中加载。
properties配置文件
jdbc.url=jdbc:mysql://127.0.0.1:3306/jdbc
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=yourPassword
xml配置文件
<!--加载PropertyPlaceholderConfigurer-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<!--获取配置文件位置-->
<list>
<value>conf/mysql.properties</value>
</list>
</property>
</bean>
<!--使用占位符进行替换-->
<bean id ="tm" class="org.example.Day01.TestMysql">
<property name="driver">
<value> ${jdbc.driver}</value>
</property>
<property name="password">
<value> ${jdbc.password}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
</bean>
当BeanFactory在第一阶段加载完成的所有配置信息时,BeanFactory中保存的属性信息还只是以占位符的形式存在如${jdbc.url},当PropertyPlaceholderConfigurer作为BeanFactoryPostProcessor应用时,它会使用properties配置文件中的配置信息来替换相应的BeanDefination中占位符所表示的属性值。
1.4.2 PropertyOverrideConfigurer
PropertyOverrideConfigurer的配置项,会覆盖掉原来xml中的bean定义的property信息.
properties中的格式beanName.propertyName=value
,其中的xml配置中bean定义的是以beanName(id的值)开头,后面对应着被覆盖的值。
<!--引入PropertyOverrideConfigurer-->
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="location">
<value>conf/test.properties</value>
</property>
</bean>
<!--进行加载-->
<bean id ="t2" class="org.example.Day01.Test02">
<property name="name" value="1"></property>
</bean>
properties 配置
t2.name=EFG@AB
最后加载的效果不是1 而是efg@ab
对配置文件进行加密的方法
这两者的父类propertyResourceConfigure提供了一个方法convertProperty(String,String)
的方法,可以进行覆盖并重写。因此我们可以在配置文件中加密,解析时进行解密。
public class Test01 extends PropertyOverrideConfigurer {
//重点在于实现此方法
@Override
public String convertProperty(String propertyName,String originalValue) {
if(isEncryptPropertyVal(propertyName)){
//进行解密
return MD5Test.convertMD5(originalValue);
}
return originalValue;
}
public boolean isEncryptPropertyVal(String value){
//判断是否满足解密条件
if(value.startsWith("t2")){
return true;
}else{
return false;
}
}
}
1.4.3 CustomEditorConfigurer
customeditorConfigurer是将后期使用到的数据注册到容器中,其中在xml中都是读取的字符串形式,最终的应用类型确实对象的各种类型,因此需要通过propertyEditor
进行转换。而CustomEditorConfigure是帮助我们传达类似的信息。重写propertyEditor如果单向的从string 到相应的类型转换只需实现setAsTest
,支持双向的则需要同时重写getAsTest
。
1.5 bean的流程
在借助BeanFactoryPostProcessor第一阶段执行后,后面就是第二阶段bean实例化的实现逻辑。
在第一阶段,容器仅仅拥有对象的beandefinition来保存实例化阶段将要用的必要信息。当通过getbean方法内部发现该bean定义前还没有被实例化之后,会通过createBean()
方法来进行具体的对象实例化
容器只要根据相应bean定义的BeanDefintion取得实例化信息,结合CglibSubclassingInstantiationStrategy以及不同的bean定义类型,就可以返回实例化完成的对象实例,但是所返回的是以Beanwrapper对构造完成对象实例进行包装,返回相应的beanwrapper实例。、
1.5.1 BeanWrapper
其作用是对某个bean进行包裹,然后对这个包裹的bean进行操作,进行设置或者获取bean的相应属性值。
beanwrapper继承了PropertyAccessor接口,可以统一的方式对对象属性进行访问,同时又继承了propertyEditorRegistry,当BeanWrapper转换类型,设置对象属性值时,就不会无从下手。
ect per;
Object dog;
per = Class.forName("org.example.Person").getDeclaredConstructor().newInstance();
dog = Class.forName("org.example.Dog").getDeclaredConstructor().newInstance();
BeanWrapper bw = new BeanWrapperImpl(per);
bw.setPropertyValue("dog",dog);
//断言错误会抛出异常
assertSame(dog,bw.getPropertyValue("dog"));
使用了beanwrapper就不需要在使用反射进行注入属性了。
1.5.2 Aware接口
当对象实例化完成,并且相关属性以及依赖设置完成后,spring容器会检测到当前对象实例是否实现了Aware命名结尾的接口定义,如果有则将这些Aware接口定义中规定的依赖注入到当前实例。
- BeannameAware
如果spring容器检测到当前对象实例实现了接口,会将该对象实例的bean定义对应的beanName设置到当前对象实例。 - BeanClassLoaderAware
如果容器检测到当前对象实例实现了该接口,会将对应加载当前bean的ClassLoader注入当前对象实例 - BeanFactoryAware
如果对象声明了beanFactoryAware,BeanFactory 会将自身设置到当前容器。
对于ApplicationContext容器还会检查一下的几种Aware
- ResourceLoaderAware
当容器检测到时,会将当前applicationContext 自身设置到对象实例 - ApplicationEventPublisherAware
ApplicaotionContext还实现了ApplicationEventPublisher接口,当其检测到此Aware时也会将自身注入到当前对象 - MessageSourceAware
ApplicationContext 同时也实现了国际化信息支持,所以当检测到时,也会将其自身注入 - ApplicationContextAware
会将其容器自身注入对象实例
1.5.3 BeanPostProcessor
** BeanPostProcessor存在于对象实例化阶段,而BeanFactoryPostProcessor则是存在于容器启动阶段**
BeanFactoryPostProcessor通常会处理所有符合条件的BeanDefinition,而BeanPostProcessor会处理容器内所有符合条件的实例化对象
public interface BeanPostProcessor {
//前置处理
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//后置处理
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
- postProcessBeforeInitialization()
在每一个bean对象的初始化方法调用之前回调 - postProcessAfterInitialization()
会在每个bean对象的初始化方法调用之后被回调。
常用于处理标记实现类或者为当前对象提供代理实现。
当ApplicationContext中的每个对象的实例化走到BeanPostPeocessor前置处理时,ApplicationContext容器会检测到之前注册到容器的ApplicationContextAwareProcessor这个BeanPostProcessor实现类然后调用其前置方法为其设置Aware相关依赖。
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
1.5.4 BeanPostProcessor 自定义注解
- 定义注解
- 在BeanPostProcessor中使用反射处理注解
- 将BeanPostProcessor 注入到容器中
定义注解
/**
* @author wfg
*/
//定义生命周期为运行期
@Retention(RetentionPolicy.RUNTIME)
//定义作用域 为成员变量
@Target({ElementType.FIELD})
@Documented
public @interface wfgValue {
String value() default "";
}
自定义BeanPostProcess
public class WfgBeanPostProcess implements BeanPostProcessor{
//在调用构造方法前执行
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for(Field f:fields){
wfgValue wv= f.getAnnotation(wfgValue.class);
if(wv==null){
continue;
}
f.setAccessible(true);
try {
f.set(bean,wv.value());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return bean;
}
//在调用构造方法后执行
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
将其注入到spring容器中
<bean id="wfgBpp" class="org.example.Day02.WfgBeanPostProcess"/>
1.5.4 InitializingBean和init-method
initializingBean是一个对象的生命周期接口。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
在调用后BeanPostProcessor前置处理后,会使用inITializingBean中的方法进一步调整对象的状态(对象执行前的初始化)
init-method是xml配置中提供的另外一种初始化方式,没有很强的侵略性。
1.5.5 DisposableBean与destory-method
当调用完成后,容器会检查singleton类型的bean实例,看其是否实现其接口或者对应的bean定义是否通过的destory-method属性指定了自定义的销毁方法,若果是则注册一个用于对象销毁的回调。但spring容器在关闭前不会自动调用这些方法,因此需要我们进行通知。
registerShutdownHook方法注册并触发对象销毁逻辑的调用。
xmlbeeans 配置
default-destroy-method="destory1"
手动调用
AbstractApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
Dog dog = (Dog) ac.getBean("dog");
//registerShutdownHook方法注册并处发对象销毁逻辑回调
ac.registerShutdownHook();
System.out.println(dog.getName());