在使用Spring的过程中,我们需要书写的第一行代码就为:
ApplicationContext context =new ClassPathXmlApplicationContext("SpringDao.xml");
在学习IOC的时候我们就知道:Spring提供的IOC容器实现的两个接口为BeanFactory接口和ApplicationContext接口。
(面试题)ApplicationContext接口是BeanFactory的子接口并且多了以下功能:
BeanFactory 接口是 Spring 的“心脏”。它就是 Spring IoC 容器的真面目。Spring 使用该接口来实例化、配置和管理 Bean。它最重要的方法就是 getBean() ,作用是返回特定的名称的Bean,该方法是IOC容器获取bean对象和引发依赖注入的起点。以下是源码:
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
#该接口会加载存储在配置源中的Bean定义,并使用org.springframework.beans包来配置Bean。
#但是,实现可以根据需要直接在Java代码中直接返回它创建的Java对象。
#定义的存储方式没有任何限制:LDAP,RDBMS,XML,属性文件等。鼓励实现以支持Bean之间的引用(依赖注入)
public interface BeanFactory {
// 用于返回FactoryBean对象用的前缀
String FACTORY_BEAN_PREFIX = "&";
// 返回指定名称的Bean实例。如果找不到该bean,会在父工厂中寻找
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 获取bean的提供者(对象工厂)
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
// 这个bean工厂是否包含给定名称的bean
boolean containsBean(String name);
// 是否为单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// 是否为原型
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
// 指定名字的bean是否和指定的类型匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
// 获取指定名字的bean的类型
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
// 获取指定名字的bean的所有别名
String[] getAliases(String name);
}
该String类型的变量是用于获取FactoryBean对象。
首先我们需要来了解什么是FactoryBean。FactoryBean属于工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑:
FactoryBean
:FactoryBean 在IOC容器的基础上给 Bean 的实现加上了一个简单工厂模式和装饰模式。任何一个实现了该接口的 bean 对象都为FactoryBean
,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean
。举例说明:首先创建 MyFacrotyBeanTest 类实现FactoryBean
接口,重写getObject()和getObjectType()方法。内有一个其他bean对象:
public class MyFacrotyBeanTest implements FactoryBean {
//其他bean对象的引用
Student studentByFactoryBean;
//对Student这个bean的定制逻辑
//在实际开发中,我们是通过Class.forName(interfaceName)来获得bean对象,再通过代理模式增强。
//将增强的bean的返回
public Object getObject() throws Exception {
return studentByFactoryBean;
}
public Class<?> getObjectType() {
return null;
}
}
将 MyFacrotyBeanTest 加入Spring的容器后,然后通过xml(或者注解)配置该类的属性。通过以下代码返回的是MyFacrotyBeanTest类增强的Student类bean对象(也就是getObject()方法的返回值):
ApplicationContext context =new ClassPathXmlApplicationContext("SpringDao.xml");
//会返回增强的bean对象,也就是 getObject()方法返回的对象
Object bean = context.getBean("MyFacrotyBeanTest");
如果要获取MyFacrotyBeanTest
对象就需要在 id 前加上 & :
Object bean = context.getBean("&MyFacrotyBeanTest");
以下是FactoryBean的源码:
package org.springframework.beans.factory;
public interface FactoryBean<T> {
//返回由FactoryBean创建的Bean实例
//如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中
T getObject() throws Exception;
//返回FactoryBean创建的Bean类型。
Class<?> getObjectType();
//返回由FactoryBean创建的Bean实例的作用域是singleton还是prototype
boolean isSingleton();
}
(面试题)BeanFactory和FactoryBean的区别:
该方法为重要方法,在以后单独讲解。
Spring官方说明,绝大多数情况下建议使用ApplicationContext。前文说过ApplicationContext对BeanFactory进行了扩展。
利用MessageSource进行国际化
BeanFactory是不支持国际化功能的,因为BeanFactory没有扩展Spring中MessageResource接口。相反,由于ApplicationContext扩展了MessageResource接口,因而具有消息处理的能力(i18N)。
事件机制(Event)
ApplicationContext的事件机制主要通过ApplicationEvent和ApplicationListener这两个接口来提供的,当ApplicationContext中发布一个事件的时,所有扩展了ApplicationListener的Bean都将会接受到这个事件,并进行相应的处理。 Spring提供了部分内置事件,主要有以下几种:
用户也可根据自己需要来扩展spriong中的事物,扩展的事件都要实现ApplicationEvent接口。
底层资源的访问
ApplicationContext扩展了ResourceLoader(资源加载器)接口,从而可以用来加载多个Resource,而BeanFactory是没有扩展ResourceLoader。
对Web应用的支持
与BeanFactory通常以编程的方式被创建不同的是,ApplicationContext能以声明的方式创建,如使用ContextLoader。当然你也可以使用ApplicationContext的实现之一来以编程的方式创建ApplicationContext实例 。
ApplicationContext常用实现类 | 作用 |
---|---|
AnnotationConfigApplicationContext | 从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式。 |
ClassPathXmlApplicationContext | 从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式。 |
FileSystemXmlApplicationContext | 从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件。 |
AnnotationConfigWebApplicationContext | 专门为web应用准备的,适用于注解方式。 |
XmlWebApplicationContext | 从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。 |
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。注意:循环依赖不是循环调用。循环调用是方法之间的环调用,并且循环调用是无法解决的,除非有结束条件,否则就是死循环最终导致内存溢出错误。
Spring解决循环依赖的类是DefaultSingletonBeanRegistry,用于注册,获得,管理singleton单例对象,它实现了SingletonBeanRegistry接口。该类中就有解决循环依赖的三级缓存:
//一级缓存。用于保存BeanName和创建Bean实例之间的关系。用于存放完全初始化好了的Bean对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存。用于保存BeanName和创建Bean实例之间的关系。与singletonObjects不同之处在于:
//当一个单例Bean对象放里面后,那么当Bean还在创建过程中,就可以通过getBean()方法获取到了.
//也就是说,它是存放尚未填充属性的原始Bean对象,用于检测循环依赖的引用.
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存。用于保存BeanName和创建Bean的工厂之间的关系。
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
还有其他两个比较重要的集合:
//用于保存正在创建的Bean的标识符
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
//用于保存已经创建完毕的Bean的标识符
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
关于Spring bean的创建,其本质上还是一个对象的创建,既然是对象,一定要明白一个完整的对象包含两部分:当前对象实例化和对象属性的实例化。
在Spring中,对象的实例化是通过反射实现的,而对象的属性则是在对象实例化之后通过一定的方式设置的。而循环依赖就发生在对象的属性设置的时候。
在Spring的循环依赖中一共有三种情况:构造器注入循环依赖、field属性注入(setter方法注入)循环依赖、prototype field属性注入循环依赖。
构造器注入循环依赖(无法解决)
@Service
public class A {
public A(B b) {
}
}
@Service
public class B {
public B(A a) {
}
}
对于构造器注入循环依赖,Spring本来想以普通框架的身份与我们相处,但是换来的却是BeanCurrentlyInCreationException
异常,因此Spring直接摊牌了:我无法解决构造器注入循环依赖。
原因: Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是已经实例化,但还没设置属性的状态。而构造器是用于完成实例化的,所以构造器的循环依赖无法解决。
setter方法循环依赖(可解决)
对于Setter方法的依赖注入,Spring会提前暴露刚完成构造器注入但未完成属性设置的Bean对象来完成的,并且只能解决单例作用域的Bean的循环依赖。通过暴露一个单例工厂方法,从而使其他Bean能引用到该Bean。
prototype field属性注入循环依赖(无法解决)
无法解决。只是由于Spring中对于prototype
作用域的Bean对象不进行缓存,自然也无法提前暴露一个Bean对象供其他Bean对象引用。
原因: 多实例Bean是每次创建都会调用doGetBean方法,根本没有使用一二三级缓存,肯定不能解决循环依赖。
涉及到循环依赖处理的的方法为getSingleton的两个重载方法和doCreateBean()方法。为了分析方便我们首先建立两个类,让他们首先构成 set 注入的循环依赖:
public class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
当通过A a=(A) context.getBean("A")
来获取目标对象的时候,实际上是调用AbstractBeanFactory
类的getBean()
方法,内部又实际调用了doGetBean()
方法。
在doGetBean()
方法内部调用了以下的getSingleton()
方法。首先我们需要知道单例Bean对象只在Spring容器中加载一次,后续如果要使用该对象直接从容器中获取即可。该方法是尝试从缓存中获取对象,而我们的容器才刚创建自然其中没有A对象
,对于A对象
来说该方法就只是走个过场,会直接返回Null
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//首先从singletonObjects集合中查看是否有A对象
//singletonObjects也就是我们的一级缓存
Object singletonObject = this.singletonObjects.get(beanName);
//如果没有获取到,并且singletonsCurrentlyInCreation集合中包含A对象
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//从二级缓存中查询,获取Bean的早期引用。
//所谓早期引用是指:实例化完成但是未赋值完成的Bean。
singletonObject = this.earlySingletonObjects.get(beanName);
//二级缓存中不存在,并且allowEarlyReference=true
//allowEarlyReference:是否允许早期引用。
if (singletonObject == null && allowEarlyReference) {
//从三级缓存中查询,获取Bean的早期引用。
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//如果从三级缓存中查询到了
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//添加至二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
注意isSingletonCurrentlyInCreation(beanName)
是判断singletonsCurrentlyInCreation
集合中是否包含A对象。前文我们说过该集合的作用是:用于保存正在创建的Bean的标识符。由于此时只是从缓存中查询并没有创建对象,自然返回false。
上诉方法执行完毕过后返回null,在缓存中没有找到想要的bean对象后,自然我们需要从头开始bean的加载了,但是我们需要对该bean对象进行类型检测,因为只有在单例情况下才会尝试解决循环依赖:
//如果是Prototype就抛出错误
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
检测完毕后,Spring使用getSingleton()的重载方法
来进行bean的创建,但它先会记录加载状态,也就是记录A对象正在创建,记录完毕后才开始正在创建:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
//全局变量需要同步
synchronized (this.singletonObjects) {
//首先检测Bean对象是否已经加载过,因为其实Singleton模式其实就是复用以创建bean,此处是必要的!
Object singletonObject = this.singletonObjects.get(beanName);
//singletonObject为空才能继续执行
if (singletonObject == null) {
//省略代码
//记录加载状态,也就是将当前正要创建的bean记录在singletonsCurrentlyInCreation缓存中
//与前文isSingletonCurrentlyInCreation(beanName)对应!
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//初始化Bean,真正的创建对象的代码
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
//省略代码
}
catch (BeanCreationException ex) {
//省略代码
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
//当bean加载结束后,A对象就不再是正在创建状态了。
//因此从singletonsCurrentlyInCreation缓存中移除。
afterSingletonCreation(beanName);
}
if (newSingleton) {
//加入singletonObjects缓存,也就是一级缓存。
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
singletonObject = singletonFactory.getObject()
该段代码其实是调用AbstractBeanFactory
类中的doGetBean()
方法中的一段代码:
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
重要的是createBean(beanName, mbd, args)
它有如下功能:
Object bean = resolveBeforeInstantiation(beanName, mbdToUse)
。此行代码是我们实现AOP功能的关键。并且BeanFactoryPostProcessor
与BeanPostProcessor
在此处出现了!!doCreateBean()
方法来真正创建A对象!doCreateBean()
方法是创建Bean对象的核心方法,该方法里面包括了解析Bean对象的class属性、构造方法,依赖注入等等。此处我们只分析与循环依赖相关的代码。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
//省略代码
if (instanceWrapper == null) {
//创建原始对象,创建出来的对象没有填充属性
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//省略代码
//重要代码。解决循环依赖
//如果是单例bean&&允许循环依赖&&当前bean正在创建,则将创建Bean的工厂对象加入三级缓存
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// addSingletonFactory()加入三级缓存
// getEarlyBeanReference():有循环依赖的时候,AOP将在此处将Advice动态织入Bean中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
//依赖注入相关。引发循环依赖。
//因为我们的A对象中含有B对象的属性,这下子B对象就要此处之前的流程了
populateBean(beanName, mbd, instanceWrapper);
// 初始化方法调用
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
//省略代码
}
//下面的if语块其实是与AOP相关
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
//省略代码
}
}
}
try {
// 注册销毁逻辑
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
}
addSingletonFactory(beanName, () ......)
:将A对象加入三级缓存,再次强调放入三级缓存中的Bean对象依旧只是实例化还未填充属性的对象!
-> getEarlyBeanReference(beanName, mbd, bean)
:通过该方法提前暴露出去的一个对象,还不是一个完整的Bean。还有个作用就是:当开启了AOP并且有循环依赖的时候,AOP将在此处将Advice动态织入Bean中。 并且在创建A对象的过程中是不会
调用该方法,而是在创建B对象的过程中被调用。
populateBean(beanName, mbd, instanceWrapper)
:该方法起到填充属性的。但是由于A对象中含有B对象属性,因此在调用该方法的时候发现B对象属性,因此,B对象也要走前面得流程!。
当B对象走到了populateBean()
这个方法时,又发现了B对象中含有A对象属性(该属性我们暂且叫它为B-A),那么B-A又要开始走前面的流程,但是别忘了第1点getSingleton()
方法中:
//由于A对象正常创建,所以isSingletonCurrentlyInCreation(beanName)不再返回false而是true!!!
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//会直接到三级缓存中获取A对象的早期引用
//从而完成B对象的创建
//此处实际就调用了getEarlyBeanReference(beanName, mbd, bean)方法
singletonObject = singletonFactory.getObject();
//获得以后,二级缓存添加该对象
this.earlySingletonObjects.put(beanName, singletonObject);
//三级缓存中清除该对象的工厂对象
this.singletonFactories.remove(beanName);
}
}
}
}
由于A对象已经存放在三级缓存中了,通过以上逻辑,此时A对象(也就是B-A)会从三级缓存中移动到二级缓存中,特别注意singletonObject = singletonFactory.getObject()
,这段代码实际调用的是:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
//if语块都是为了AOP做判断用
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
//如果有AOP,则进入该方法。
//实际调用AbstractAutoProxyCreator类的getEarlyBeanReference()方法
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
//如果没有AOP,就直接到此处,将对象返回
return exposedObject;
}
这段代码的作用在于,如果A开启了AOP功能,那么将A对象包装成A的代理对象并放进二级缓存中。
当B对象会从二级缓存中拿到A对象后完成赋值,B对象成功后加入一级缓存。此时需要注意,如果A开启了AOP功能,那么说明B对象中的A对象属性一定注入的是A的代理对象,换句话说A已经被代理了。Spring有多处机会可以返回一个proxy对象,但是,最终只要在其中一处处理了,其他处根本不再继续处理。因此回到doCreateBean()
方法的if (earlySingletonExposure)
判断语块:
//当B已经加入一级缓存,A的属性填充完毕,此处调用初始化方法开始初始化A时
// initializeBean()`方法中不再生成代理对象,左边的exposedObject为原对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
//省略代码
//earlySingletonExposure=true
if (earlySingletonExposure) {
//第二个参数为false,不会从三级缓存中取值,而是从二级缓存中取值
//此处二级缓存中的B-A对象是A的代理对象,因此earlySingletonReference= A的代理对象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
//用代理对象替换掉原对象
exposedObject = earlySingletonReference;
}
//省略代码
}
}
}
一般来说在Spring中都是在initializeBean()
方法中完成AOP代理,但是在此处由于循环依赖的原因,在 getEarlyBeanReference()
方法中已经提前生成了代理对象(也就是咱们的B-A),所以在initializeBean()
方法中不再生成代理对象。我们在上述if语块中用代理对象替换掉原对象,保证最终A对象就是代理对象。
也就是说,Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。
总结如下:
getSingleton(beanName)
方法尝试从缓存中查找是不是有该实例。getSingleton(String beanName, ObjectFactory> singletonFactory)
方法来进行bean的加载,在此方法中会对A对象进行标记,标记它为正在加载。doGetBean()
,因为它涉及到对象的创建和populateBean()
方法对属性赋值。对A对象属性赋值之前,会把创建A对象的工厂对象放进三级缓存。populateBean()
方法又发现了B对象中含有A对象属性。此时又将进行该属性B-A的创建,但是由于第2步我们已经对A对象进行了标记,因此会直接去三级缓存中利用工厂方法来获得A对象的早期引用,完成B对象的属性赋值并放进一级缓存中参考资料:同事踩进Spring循环依赖的坑出不来,被我diss了。里面有个动态图,非常清晰明了!
通过对于循环依赖流程的分析,我们发现了一个问题:解决循环依赖的关键就是创建一个对象的早期引用并且缓存起来,在创建另外一个对象的时候注入进去。既然这样,似乎二级缓存就够用了,那为何还需要三级缓存?
实际上,对于普通的循环依赖,三级缓存一点用处都没有!直接上二级缓存就完事儿了。但是我们忽略了Spring的另外一个强大功能-AOP,三级缓存实际上跟Spring中的AOP相关。从源码层面详解Spring循环依赖。
首先回到doCreateBean()
方法中:
package org.springframework.beans.factory.support;
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//省略代码
if (instanceWrapper == null) {
//创建原始对象,创建出来的对象没有填充属性
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//如果是单例bean&&允许循环依赖&&当前bean正在创建,则将创建Bean的工厂对象加入三级缓存
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// getEarlyBeanReference():我们熟知的AOP就是在此处将Advice动态织入Bean中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//因为我们的A对象中含有B对象的属性,这下子B对象就要此处之前的流程了
populateBean(beanName, mbd, instanceWrapper);
// 初始化方法调用
exposedObject = initializeBean(beanName, exposedObject, mbd);
//省略代码
}
进入getEarlyBeanReference()
方法中,前文已经介绍过了,该方法作用在于:如果没有开启AOP功能,则直接将实例化阶段创建的对象返回;如果开启了AOP功能则返回代理对象! 前文提到了只有在创建B对象的过程中才会被调用,这里再明确一下:是在注入B对象中的B-A属性时才会调用该方法,也就是总结中的第5点>>去三级缓存中获取A对象的早期引用的时候。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
//if语块都是为了AOP做判断用
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
//如果有AOP,则进入该方法。
//实际调用AbstractAutoProxyCreator类的getEarlyBeanReference()方法
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
//如果没有AOP,就直接到此处,将对象返回
return exposedObject;
}
如果在开启AOP的情况下,那么就是调用到AbstractAutoProxyCreator
的getEarlyBeanReference
方法,对应的源码如下:
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
现在我们知道了开启AOP后何时返回一个代理对象了。下面我们回到问题本身,为什么非得需要三级缓存而不是二级缓存?首先我们先明确两个地方:
addSingletonFactory()
方法在填充属性方法populateBean()
和初始化方法initializeBean()
之前。B对象中有A对象的属性构成了循环依赖。但是我们要清楚Spring是不知道的,因为它需要到填充属性方法populateBean()
处才能知道是否有循环依赖,为了保证B属性注入的是A对象的代理对象 ,Spring会在A对象初始化(甚至注入属性)之前就为其创建代理对象。也就是说在没有三级缓存的情况下,为了保险,就算没有循环依赖Spring都会在初始化之前为每个对象创建代理对象:
显然在初始化之前就给对象创建代理对象是不符合Spring的设计原则的。因此就来了三级缓存,当执行到填充属性方法populateBean()
处时:
getEarlyBeanReference()
方法,依然按部就班在A和B初始化后再创建代理对象;getEarlyBeanReference()
方法创建A的代理对象 。通过上面的解析,我们可以知道 Spring 需要三级缓存的目的是为了延迟代理对象的创建,使 Bean 的创建符合 Spring 的设计原则。
关于第二大点-三级缓存的作用资料来自于:面试必杀技,讲一讲Spring中的循环依赖。
通过依赖注入来自动解决类对象之间的引用关系,即由Spring来创建bean对象,并且在Spring的IOC容器内部自动查找或者创建该bean对象所依赖的其他bean对象,从而保证整个bean对象的属性值的完整性。
在学习Spring的过程中我们已经接触到了以下几种依赖注入的方式:
Set函数的依赖注入,可以注入基本类型和其他Bean对象。可以通过配置autowire
指明ByName或者ByType。如:
<bean id="userServiceImpl" class="cn.com.bochy.service.impl.UserServiceImpl" autowire="byName/byType">
构造函数的依赖注入,可以注入基本类型和其他Bean对象
利用@Autowired进行依赖注入,可以注入其他Bean对象
而利用@Autowired进行依赖注入又称自动装配,它有6种模式:
在进入学习之前我们要明确一件事情,我们常用的ApplicationContext
容器有个显著的特点就是:当容器初始化时就会初始化所有非懒加载的Bean对象。IOC容器初始化最重要的方法就是refresh()
方法,该方法内部又会调用finishBeanFactoryInitialization()
方法,该方法就是起到了实例化所有非懒加载的单例Bean对象的作用:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory{
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// 此处省略多行与本次无关代码
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
这个方法就是一个傀儡方法,真正起作用的是preInstantiateSingletons()
方法,代码解释来自于Spring 源码分析之 bean 依赖注入原理(注入属性):
public void preInstantiateSingletons() throws BeansException {
// 所有beanDefinition集合
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 触发所有非懒加载单例bean的初始化
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 判断是否是懒加载单例bean,如果是单例的并且不是懒加载的则在Spring 容器
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 判断是否是FactoryBean
if (isFactoryBean(beanName)) {
// 对FactoryBean的处理
}else {
// 如果是普通bean则进行初始化依赖注入,此 getBean(beanName)接下来触发的逻辑跟
// context.getBean("beanName") 所触发的逻辑是一样的
getBean(beanName);
}
}
}
}
又看到了熟悉的getBean()
方法,它的逻辑与context.getBean()
方法一样的。该方法在循环依赖中我们已经初步的介绍了,也知道了属性注入最重要的方法就是:
populateBean(beanName, mbd, instanceWrapper);
该方法中出现了两个重要方法autowireByName()
和autowireByType()
,这两个方法分别代表按照名称注入和按照类型注入。
下面我们分代码块进行分析,第一个代码块比较简单,就是查看是否有属性,没有属性就直接return:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
//没有可填充的属性就直接return
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
//省略代码
}
第二个代码块是判断是否自定义了InstantiationAwareBeanPostProcessor
接口的实现类,给该实现类一次机会在注入属性前来改变Bean对象。首先它会遍历所有实现了BeanPostProcessor
接口的实现类,再判断是否是InstantiationAwareBeanPostProcessor
接口的实现类,然后调用对应的postProcessAfterInstantiation()
方法,如果该方法返回false直接return结束。
这段代码也解释了:为什么在InstantiationAwareBeanPostProcessor
接口中postProcessAfterInstantiation()
方法返回false后,postProcessPropertyValues()
方法不会执行。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
//省略代码
//是否自定义了InstantiationAwareBeanPostProcessor接口的实现类
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//遍历所有BeanPostProcessor接口实现类
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
}
}
//省略代码
第三个代码是与自动装配有关。处理autowire的注入,这里的autowire指的是在配置文件中通过autowire="byName"
或者autowire="byType
"等属性配置的bean。通过autowire
的值来判断调用autowireByName()
还是autowireByType()
方法。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
//省略第二个代码块
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// 按照名称注入
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// 按照类型注入
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
}
//省略代码
第四个代码块很重要,这里也是InstantiationAwareBeanPostProcessor
的生命周期回调,特别是ibp.postProcessPropertyValues()
方法,这个方法涉及到了@Autowired注解是如何注入属性的 。此方法会在@Autowired注解实现原理章节中单独分析。
而applyPropertyValues()
方法是用来处理xml配置文件中Property属性注入,我们需要单独分析。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
//省略代码
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// 这里会处理对注解形式的注入 重点!!!!
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
//省略代码
if (pvs != null) {
//处理Property属性注入,也是真正注入属性的地方
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
下面我们进入applyPropertyValues(beanName, mbd, bw, pvs)
方法进行分析,该方法是真正注入属性的地方,beanName
代表Bean对象的名字,mbd
为合并的 BeanDefinition,bw
为包装目标对象的BeanWrapper,pvs
为新的属性值。
该方法内部一共做了三件事情:
resolveValueIfNecessary()
方法进行类型转换。由于我们在xml文件中获取的值都是String类型的值,需要将其转换为Bean对象的真正属性。在该方法内部有着大量的if-else
判断,对于属性的类型进行判断,然后将值转换为该属性类型;setPropertyValues()
方法将转换好的值注入进Bean的属性。原理就是利用反射调用set方法对属性进行赋值。protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
//如果新的属性值为空直接return
if (pvs.isEmpty()) {
return;
}
//省略代码
// 判断是否已转换,已经转换了则return
if (mpvs.isConverted()) {
// Shortcut: use the pre-converted values as-is.
try {
bw.setPropertyValues(mpvs);
return;
}
}
original = mpvs.getPropertyValueList();
}
else {
original = Arrays.asList(pvs.getPropertyValues());
}
//创建用于解析BeanDefinition的解析器
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// Create a deep copy, resolving any references for values.
List<PropertyValue> deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
// 属性名 如(name,orderService)
String propertyName = pv.getName();
// 未转换前的值,
Object originalValue = pv.getValue();
// 转换后的值,进行转换处理
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
//省略代码
// 转换完成
if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
}
// 这里就是进行属性注入的地方。
try {
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
参考资料:Spring 源码分析之 bean 依赖注入原理(注入属性)。
Spring 容器可以管理 singleton作用域 Bean的生命周期,在此作用域下,Spring能够精确地知道该Bean 何时被创建,何时初始化完成,以及何时被销毁。
而对于prototype作用域的Bean,Spring只负责创建,当容器创建了Bean的实例后,Bean的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。每次客户端请求prototype作用域的Bean时,Spring容器都会创建一个新的实例,并且不会管那些被配置成prototype作用域的Bean的生命周期。
了解Spring生命周期的意义就在于,可以利用Bean在其存活期间的指定时刻完成一些相关操作。这种时刻可能有很多,但一般情况下,会在Bean被初始化后和被销毁前执行一些相关操作。Spring 容器中 Bean
的生命周期流程 下图所示:
下图是 Spring
的生命周期,它是包括了Bean的生命周期,图片来自:Spring Bean的生命周期分析。
在进行学习之前,我们先建一个Demo,一个Teacher类:
public class Teacher {
private String tname;
private int tage;
private String tsex;
private Student tStudent;
public Teacher(){
System.out.println("teacher类--空构造方法");
}
//省略set、get、toString方法
}
Student类:
public class Student {
private String sname;
private int sage;
//省略set、get、toString方法
}
对应的xml文件配置类:
<bean id="teacher" class="com.itachi.Dao.Teacher">
<property name="tage" value="16">property>
<property name="tname" value="李大嘴 ">property>
<property name="tsex" value="男">property>
<property name="tStudent" ref="student">property>
bean>
<bean id="student" class="com.itachi.Dao.Student">
<property name="sname" value="白展堂">property>
<property name="sage" value="16">property>
bean>
BeanFactoryPostProcessor:在Bean Factory容器初始化完成之后,该接口可以在容器实例化任何bean之前读取bean的定义,并且修改、覆盖或者添加Bean属性。该接口只有一个postProcessBeanFactory方法,
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
该方法中只有一个ConfigurableListableBeanFactory
接口类型的参数,该接口提供Bean definition的解析,注册功能和对单例预加载(解决循环依赖问题):
package org.springframework.beans.factory.config;
public interface ConfigurableListableBeanFactory
extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
//忽略自动装配的依赖类型
void ignoreDependencyType(Class<?> type);
//忽略自动装配的接口
//这个通常被使用由application contexts去注册依赖,可以以多种方式实现。
//例如BeanFactory通过BeanFactoryAware,ApplicationContext 通过ApplicationContextAware。
//默认情况下,仅BeanFactoryAware接口是被忽略,需要忽略其他接口,调用此方法。
void ignoreDependencyInterface(Class<?> ifc);
//注册一个可分解的依赖
void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue);
//确认这个被指定的bean是否是一个autowire候选,将被注入到其他声明匹配类型的依赖的bean中。
boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)
throws NoSuchBeanDefinitionException;
//根据指定的bean name返回被注册的bean定义,允许访问其属性值和构造函数参数值(可以在bean工厂后期处理期间被修改)。
//这个被返回的bean definition对象不应该是副本而是原始在工厂被注册的。这意味着如果需要它可以被转换为更具体的实现类型。
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
//返回由这个bean factory管理的所有bean name统一视图。
Iterator<String> getBeanNamesIterator();
void clearMetadataCache();
//冻结全部bean定义。标明注册的bean定义不在修改或后置处理
void freezeConfiguration();
//返回该工厂的bean definnitions是否被冻结。
boolean isConfigurationFrozen();
//确保所有非懒加载的单例bean被实例化,包括factoryBeans。
//用于解决循环依赖问题
void preInstantiateSingletons() throws BeansException;
}
其中getBeanDefinition
方法是我们常用的一个,通过返回的BeanDefinition
对象来获取属性并进行修改,下面我们来测试一下,首先先创建自定义BeanFactoryPostProcessor类:
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public MyBeanFactoryPostProcessor(){
super();
System.out.println("BeanFactoryPostProcessor实现类--构造方法");
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//获取BeanDefinition对象
BeanDefinition beanDefinition=beanFactory.getBeanDefinition("teacher");
//对属性值进行更改
beanDefinition.getPropertyValues().add("tname", "BeanFactoryPostProcessor-李大嘴");
}
}
内容比较简单:就是通过获取到的BeanDefinition对象来改变配置文件中Bean对象的属性值!我们将teacher
类中的tname
属性值改为了" BeanFactoryPostProcessor-李大嘴 “(xml文件中配置的是” 李大嘴 ")。
此处涉及到的beanDefinition的方法不做探讨,以后深入研究。创建该类后,还需要配置到xml文件中:
<bean id="beanFactoryPostProcessor" class="com.itachi.config.MyBeanFactoryPostProcessor">
bean>
下面我们来进行测试:
@Test
public void HAHA(){
ApplicationContext context =new ClassPathXmlApplicationContext("Singleton.xml");
Teacher teacher= (Teacher) context.getBean("teacher");
System.out.println(teacher);
}
ApplicationContext
接口在启动的时候就把所有的Bean全部实例化了。BeanFactoryPostProcessor
接口实现类构造时机在Bean对象之前!此处我们跳过了BeanPostProcessor
接口和InstantiationAwareBeanPostProcessorAdapter
类。其实这三个东西是继承实现关系:
对于该接口的官方介绍为:
它是BeanPostProcessor
的子接口,它可以作用于Bean对象实例化前后,通常用于替换bean默认创建方式。注意:此接口是专用接口,主要供框架内部使用,尽可能使用它的实现类InstantiationAwareBeanPostProcessorAdapter
,或者实现BeanPostProcessor
接口,以免对该接口进行扩展。
此处特别强调实例化的概念,所谓实例化是指:调用Bean的构造函数,将生成的单例Bean放入单例池中,但是此时的Bean还没有填充属性! 在我们熟悉的doCreateBean()
中,实例化的代码在此处:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//省略代码
//实例化。内部会对Bean对象的构造器方法进行分析
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//省略代码
}
JDK8之后由于default
关键字,InstantiationAwareBeanPostProcessor
接口不再需要实现BeanPostProcessor
接口的两个方法了。它内部提供了4个方法(最后一个方法不去探究,因为我不会!)该类位于org.springframework.beans.factory.config
包下。
package org.springframework.beans.factory.config;
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
//自身方法,是最先执行的方法,它在目标对象实例化之前调用。
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
//在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。
//因为它的返回值是决定要不要调用postProcessProperties方法的其中一个因素
//如果该方法返回false,并且不需要check,那么postProcessProperties就会被忽略不执行,并且Bean对象不会填充属性!
//默认返回true,postProcessPropertyValues就会被执行
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
//对属性值进行修改,如果postProcessAfterInstantiation方法返回false,该方法可能不会被调用。
//可以在该方法内对属性值进行修改
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
return null;
}
}
下面我们来写个简答的例子来验证InstantiationAwareBeanPostProcessor
接口方法的执行顺序。但是采用的是继承InstantiationAwareBeanPostProcessorAdapter
的方式来验证。在验证之前给Student
类加了一个空构造函数:
public Student(){
System.out.println("Student类--空构造方法");
}
然后是自定义的继承InstantiationAwareBeanPostProcessorAdapter
的子类:
public class MyIABeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter{
public MyIABeanPostProcessor(){
super();
System.out.println("MyIABeanPostProcessor类构造器!!");
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out
.println("MyIABeanPostProcessor调用postProcessBeforeInstantiation方法-构造方法之前执行");
return super.postProcessBeforeInstantiation(beanClass, beanName);
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out
.println("MyIABeanPostProcessor调用postProcessAfterInitialization方法-构造方法之后执行,若返回false,Bean对象不会填充属性");
return true;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out
.println("MyIABeanPostProcessor调用postProcessPropertyValues方法");
return super.postProcessProperties(pvs, bean, beanName);
}
}
配置在xml文件中:
<bean class="com.itachi.config.MyIABeanPostProcessor">bean>
测试结果如下,由于Teacher类中含有Student类的属性,所以执行了两次:
该方法在目标对象实例化之前调用。该方法有以下特性:
postProcessAfterInitialization
方法会调用,其它方法不再调用;否则按照正常的流程走。以下例子为采用Cglib的代理模式来生成关于teahcer
类的代理对象,用来替换原本对象。查看替换后的方法执行情况。
需要定义一个Enhancer
类的局部变量,用它来生成代理对象;通过它的setSuperclass()
方法来指定teahcer
类为父类,因为Cglib是基于子类的。最后使用它的setCallback()
实现代理逻辑:
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("MyIABeanPostProcessor调用postProcessBeforeInstantiation方法-构造方法之前执行");
if(beanClass== Teacher.class) {
//创建Enhancer类局部变量
Enhancer enhancer = new Enhancer();
//指定Teacher类为父类
enhancer.setSuperclass(beanClass);
//代理逻辑
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//无特殊逻辑
Object object = methodProxy.invokeSuper(obj, objects);
return object;
}
});
//生成Teacher类的代理对象
Teacher tByClib=(Teacher)enhancer.create();
//打印一下代理对象
System.out.println(tByClib.getClass());
return tByClib;
}
return null;
}
测试结果如下:
可以看见当返回了代理对象后,跳过了postProcessAfterInstantiation()
方法和postProcessProperties
方法(自定义初始化方法也会跳过)。
为什么会跳过?在之前学习循环依赖的时候说过createBean(beanName, mbd, args)
方法中调用了一个重要方法,它是实现AOP功能的关键:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
//省略代码
try {
//根据bean判断如果不为空null就直接返回了,而不执行doCreateBean()方法了
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
//省略代码
}
进入resolveBeforeInstantiation(beanName, mbdToUse)
方法查看:
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// 判断是否有注册过InstantiationAwareBeanPostProcessor类型的bean
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
// 直接执行BeanPostProcessor接口的postProcessAfterInitialization方法
//该方法之前的方法不再执行
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
applyBeanPostProcessorsAfterInitialization
方法的作用是获取到所有实现了BeanPostProcessor
接口的实现类,并调用对应的postProcessAfterInitialization
方法,如果该方法返回不为null,则这个bean就直接返回给ioc容器。
该方法允许:在Bean对象属性注入之前对已配置好的属性值进行修改。该方法能执行的前提是:postProcessAfterInstantiation()
方法返回true。
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out.println("MyIABeanPostProcessor调用postProcessPropertyValues方法");
if(bean instanceof Teacher){
//获取name属性
PropertyValue value = pvs.getPropertyValue("tname");
System.out.println("修改前的属性值为:"+value.getValue());
//对name属性的值进行修改
value.setConvertedValue("李大嘴-InstantiationAwareBeanPostProcessor");
}
return super.postProcessProperties(pvs, bean, beanName);
}
测试结果:
该接口我们也叫后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法(init-method)
的前后添加我们自己的逻辑:
public interface BeanPostProcessor {
//在Bean对象实例化和属性注入后,该方法执行
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//在int-method方法执行后,该方法执行。
//int-method方法的执行时机在Bean对象实例化和属性注入之后
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
前文学习的InstantiationAwareBeanPostProcessor
接口是它的子接口,只是两个接口内部方法的作用时机不一样。在测试之前,我们首先对实体Teacher类添加一个init-method
初始化方法和destory
销毁方法:
public void init(){
System.out.println("teacher类--init-method初始化方法");
}
public void destroy(){
System.out.println("teacher类--destroy销毁方法");
}
并且在xml文件中对这两个方法进行定义:
<bean id="teacher" class="com.itachi.Dao.Teacher" init-method="init" destroy-method="destroy">bean>
下面我们定义MyBeanPostProcessor
类,验证方法的执行流程:
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("实例化》注入属性》postProcessBeforeInitialization"+"\t"+beanName);
return null;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("init-Method》postProcessAfterInitialization"+"\t"+beanName);
return null;
}
}
测试结果如下,可以看到会先对Student属性进行赋值然后在给Teacher的其他属性赋值,方法执行流程也正确。
当然我们可以在postProcessBeforeInitialization
方法中返回自定义或者代理对象,但是其实该接口一般用于自定义注解用,关于自定义注解,后续再学习!
当有多个BeanPostProcessor
接口实现类时,默认情况下Spring容器会根据后置处理器的定义顺序来依次调用。
我们也可以让实现类再多实现一个Ordered
接口,利用它的getOrder(
)方法来指定顺序,该方法返回一整数,默认值为 0,优先级最高,值越大优先级越低:
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
//省略代码
public int getOrder() {
return 10;
}
}
关于BeanPostProcessor
接口还有几个注意点:
BeanPostProcessor
接口的实现类A中含有其他Bean对象属性,那么该Bean对象属性不会执行A的重写方法。InitializingBean接口为bean提供了属性初始化后的处理方法,只有afterPropertiesSet
方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。
public interface InitializingBean {
//该方法在设置属性后才执行
void afterPropertiesSet() throws Exception;
}
该接口比较简单,此处就不再演示,关于该接口的总结如下: