Spring容器在处理实例化的过程一共经历了两个阶段。在第一个阶段,加载配置文件信息,装配BeanDefinition,并进行前置处理之后,当容器通过BeanFactory的getBean()
方法,请求某个对象实例的时候,会触发第二阶段,实现Bean的实例化操作。getBean()
方法可以在客户端对象中被显式调用也可以是被隐式调用的。
隐式调用的情况主要是有两种情况:
- BeanFactory在调用getBean()创建的对象依赖于另一个对象的场合。
- ApplicationContext的实例化过程与BeanFactory一致,不同于BeanFactory的是,在做完第一阶段的工作后,容器会调用全部的注册到容器中的getBean()方法。这点可以从
AbstractApplicationContext
的refresh()
查看
对于显性调用与隐性调用可以参考文章:
Spring xml文件详解第16点
容器在第一次被getBean()调用的过程中。会通过createBean()
的方式对对象进行实例化操作。过程如图所示:
Spring容器对管理的对象进行统一的生命周期管理。
对于
getBean()
的具体实现逻辑可以在AbstractBeanFactory
类中查看到具体的实现
对于createBean()
具体实现逻辑可以在其子类AbstractAutowireCapableBeanFactory
中查看
Bean的实例化与BeanWrapper
容器在内部实现的时候采用了策略模式,决定以何种方式初始化Bean。一般是两者方式来初始化Bean或者动态生成其子类:
- 反射方式(jdk动态代理)
- CGLIB动态字节码方式(CGLIB动态代理)
InstantiationStrategy
是实例化策略接口,在容器中提供了两种实现类:
SimpleInstantiaStrategy
CglibSubclassingInstantiationStrategy
SimpleInstantiationStrategy实现类可以实现简单的对象实例化过程。通过反射的方式实现实例化,但是不支持方法注入的方式将对象实例化。
CglibSubclassingInstantiationStrategy实现类继承了SimpleInstantiationStrategy类。不止可以以反射方式实例化对象还可以使用CGLIB动态字节码生成的功能,该策略可以动态生成类的子类。满足方法注入的实例化需求。可以满足方法注入的需求。
BeanWrapper代码:
Object bean=Class.forName("com.example.demo.beanwrapper.MyBean").newInstance();
Object a=Class.forName("com.example.demo.beanwrapper.MyBeanA").newInstance();
Object b=Class.forName("com.example.demo.beanwrapper.MyBeanB").newInstance();
BeanWrapper wrapper=new BeanWrapperImpl(bean);
wrapper.setPropertyValue("a",a);
wrapper.setPropertyValue("b",b);
使用JDK反射的方式操作对象Code对比操作BeanWrapper 而言,需要处理的异常多。
JDK反射代码:
Class beanClass=bean.getClass();
Field fa=beanClass.getField("a");
Field fb=beanClass.getField("b");
fa.set(bean,a);
fb.set(bean, b);
spring Aware接口
Spring会检查以Aware为结尾的接口定义。如果是的场合,将Aware接口定义的BeanName注入到当前对象实例。
有以下的几个Aware结尾的接口:
BeanFactory共有的
-
BeanNameAware
:将该实例对应的xml中的BeanName设置到当前对象实例 -
BeanClassLoaderAware
:将该实例对应的ClassLoader注入给当前对象。默认加载的是ClassUtils类的ClassLoader -
BeanFactoryAware
:将容器自身注入到实例化的对象中
例子:
配置文件
<bean id="beanNameAware" class="com.example.demo.aware.BeanNameAwareDemo"></bean>
类代码:
package com.example.demo.aware;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
public class BeanNameAwareDemo implements BeanNameAware,BeanClassLoaderAware,BeanFactoryAware{
private String beanName;
private BeanFactory factory;
private ClassLoader loader;
@Override
public void setBeanName(String name) {
// TODO Auto-generated method stub
this.beanName=name;
}
public void say() {
System.out.println(beanName+"被实例化了,它的beanFactory是:"+factory+",它的ClassLoader是:"+loader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
this.factory=beanFactory;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
// TODO Auto-generated method stub
this.loader=classLoader;
}
}
测试代码:
BeanNameAwareDemo demo=(BeanNameAwareDemo)factory.getBean("beanNameAware");
demo.say();
ApplicationContext独有的
-
ResourceLoaderAware
:ApplicatiionContext本身实现了ResourceLoaderAware接口,检测到该接口,会将ApplicatiionContext本身注入到对象实例中 -
ApplicationEventPublicherAware
:ApplicatiionContext本身实现了ApplicationEventPublisher接口,可以作为ApplicationEventPublisher来使用。检测到该接口,会将ApplicatiionContext本身注入到对象实例中 -
MessageSourceAware
:ApplicatiionContext本身实现了MessageSourceAware接口,用于支持国际化。检测到该接口,会将ApplicatiionContext本身注入到对象实例中 -
ApplicationAware
:检测到实现该接口的时候会将自身注入到bean对象中。
例子省略....
BeanPostProcessor处理实例化阶段对象
BeanPostProcessor
与BeanFactoryPostProcessor
有点相似,但是两者处理的内容是完全不一样的。
-
阶段不一样
BeanPostProcessor处理的是对象实例化阶段。 BeanFactoryPostProcessor处理的是容器启动阶段。
-
处理的内容不一样
BeanPostProcessor处理的是实例化的对象实例。 BeanFactoryPostProcessor处理的是BeanDefinition。
BeanPostProcessor需要实现两个方法:
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
postProcessBeforeInitialization
负责前置增强,在bean的实例化之前会执行,postProcessAfterInitialization
负责后置增强,在实例化之后进行。
上文介绍的Aware结尾的接口的自动注入操作就是通过BeanPostProcessor进行处理的。检测到对应的Aware接口到时候会调用postProcessBeforeInitialization()
检查设置相关的Aware依赖。
例子:
下列代码来自Spring源码中的ApplicationContextAwareProcessor
:
@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
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;
}
BeanPostProcessor还是Spring AOP实现的关键。AOP使用BeanPostProcessor生成代理对象。
创建一个自定义的BeanPostProcessor
配置文件
<bean id="beanPostProcessor" class="com.example.demo.beanpostprocessor.MyBean">
<property name="password">
<value>123456</value>
</property>
</bean>
bean code:
public class MyBean implements DecodAble{
private String password;
@Override
public String getPassword() {
// TODO Auto-generated method stub
return password;
}
@Override
public void setPassword(String something) {
// TODO Auto-generated method stub
Base64.Encoder en=Base64.getEncoder();
password=new String(en.encodeToString(something.getBytes()));
}
public void setValue(String someting) {
password=someting;
}
}
接口设置:
public interface DecodAble {
String getPassword();
void setPassword(String something);
}
BeanPostProcessor Code:
package com.example.demo.beanpostprocessor;
import java.util.Base64;
import java.util.Base64.Decoder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor{
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
if (bean instanceof DecodAble) {
String password=((MyBean)bean).getPassword();
System.out.println("原bean的password:"+password);
((MyBean)bean).setValue(decodeSomething(password));
}
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
private String decodeSomething(String something) {
Base64.Decoder decoder=Base64.getDecoder();
String result=new String(decoder.decode(something.getBytes()));
return result;
}
}
测试代码:
MyBeanPostProcessor beanPostProcessor=new MyBeanPostProcessor();
factory.addBeanPostProcessor(beanPostProcessor);
MyBean bean=(MyBean) factory.getBean("beanPostProcessor");
System.out.println("现在的password:"+bean.getPassword());
解析:
这是一个自定义的BeanPostProcessor,其功能为将注入的值Base64编码后传递给对象,在取出的时候通过BeanPostProcessor的前置处理将Base64解码为原字符串。添加了个接口,这个接口的作用只是作为一个判断的依据,不将未编码的bean也进行该解码操作。以至于后续取出的值与想要的值不一致。
其代码核心包括三个部分:
- 实现BeanPostProcessor
- 业务逻辑装配,其业务逻辑操作需要与BeanPostProcessor中的处理方法想对应。如编码方式要与解码方式相对应。
- 创建BeanPostProcessor的对象,同时将这个对象通过方法:
addBeanPostProcessor(beanPostProcessor)
注册到容器之中,之后的操作与政策的beanFactory操作对象的方式一致。
InitializingBean与init-method:
InitializingBean是容器 内部广泛使用的一个对象生命周期标识接口。
源码:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
使用场景是在容器实例化过程调用完BeanPostProcessor的前置处理之后。在前置处理结束后容器会检查bean是否实现了InitializingBean接口,是的场合会调用afterPropertiesSet()
进一步调整实例状态。使用InitializingBean
会导致Spring会有一种侵入式较高的感觉,Spring提供了init-method
,在xml文件中使用init-method
可以避开这种情景。
一般在使用第三方库的时候才会需要用到这种特性。比如第三方库提供了一个返回工作日的接口,该接口返回的工作日是不包括了公司的自定义的纪念日等日期的,在使用之前需要对其返回的日期进行过滤,去掉那些特殊的日期。这时候可以使用其init-method
属性,或者是让bean实现InitializingBean
接口,将过滤逻辑放在afterPropertiesSet()
中进行处理。
代码:
public class MyBean implements DecodAble,InitializingBean{
private String password;
//...省略
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("beanPostProcessor处理后的password:"+password);
}
}
DisposableBean与destory-method
在一切值处理结束之后,容器会检查Singleton类型的Bean实例。看是否实现了DisposableBean
接口:
public interface DisposableBean {
void destroy() throws Exception;
}
是的场合为该实例注入一个销毁的回调。容器在销毁这些对象的时候会执行该回调。
但是并不是说注入了该销毁的回调就一定会执行,只有在对象没被使用的时候才会执行该销毁逻辑,之后才会执行这个销毁的方法,这个动作一般是在容器关闭的时候之后执行,要在容器关闭前执行的话需要我们手动告知在什么时候执行销毁的方法。
对于ApplicationContext道理也是一样的。
beanFactory 测试:
DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
...省略
// bean.destroy();
factory.destroySingletons();
bean:
public class MyBean implements DecodAble,InitializingBean,DisposableBean{
....省略
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("bean执行了销毁的方法");
}
}
ApplicationContext测试:
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("simple.xml");
applicationContext.registerShutdownHook();
完整实例代码:
gihub