相关背景及资源:html
曹工说Spring Boot源码(1)-- Bean Definition究竟是什么,附spring思惟导图分享java
曹工说Spring Boot源码(2)-- Bean Definition究竟是什么,我们对着接口,逐个方法讲解git
曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,咱们来试一下spring
曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean definition的?express
曹工说Spring Boot源码(5)-- 怎么从properties文件读取beanjson
曹工说Spring Boot源码(6)-- Spring怎么从xml文件里解析bean的缓存
曹工说Spring Boot源码(7)-- Spring解析xml文件,到底从中获得了什么(上)app
曹工说Spring Boot源码(8)-- Spring解析xml文件,到底从中获得了什么(util命名空间)spring-boot
曹工说Spring Boot源码(9)-- Spring解析xml文件,到底从中获得了什么(context命名空间上)工具
曹工说Spring Boot源码(10)-- Spring解析xml文件,到底从中获得了什么(context:annotation-config 解析)
曹工说Spring Boot源码(11)-- context:component-scan,你真的会用吗(此次来讲说它的奇技淫巧)
曹工说Spring Boot源码(12)-- Spring解析xml文件,到底从中获得了什么(context:component-scan完整解析)
曹工说Spring Boot源码(13)-- AspectJ的运行时织入(Load-Time-Weaving),基本内容是讲清楚了(附源码)
曹工说Spring Boot源码(14)-- AspectJ的Load-Time-Weaving的两种实现方式细细讲解,以及怎么和Spring Instrumentation集成
曹工说Spring Boot源码(15)-- Spring从xml文件里到底获得了什么(context:load-time-weaver 完整解析)
曹工说Spring Boot源码(16)-- Spring从xml文件里到底获得了什么(aop:config完整解析【上】)
曹工说Spring Boot源码(17)-- Spring从xml文件里到底获得了什么(aop:config完整解析【中】)
曹工说Spring Boot源码(18)-- Spring AOP源码分析三部曲,终于快讲完了 (aop:config完整解析【下】)
曹工说Spring Boot源码(19)-- Spring 带给咱们的工具利器,建立代理不用愁(ProxyFactory)
曹工说Spring Boot源码(20)-- 码网恢恢,疏而不漏,如何记录Spring RedisTemplate每次操做日志
曹工说Spring Boot源码(21)-- 为了让你们理解Spring Aop利器ProxyFactory,我已经拼了
曹工说Spring Boot源码(22)-- 你说我Spring Aop依赖AspectJ,我依赖它什么了
曹工说Spring Boot源码(23)-- ASM又立功了,Spring原来是这么递归获取注解的元注解的
曹工说Spring Boot源码(24)-- Spring注解扫描的瑞士军刀,asm技术实战(上)
曹工说Spring Boot源码(25)-- Spring注解扫描的瑞士军刀,ASM + Java Instrumentation,顺便提提Jar包破解
曹工说Spring Boot源码(26)-- 学习字节码也太难了,实在不能忍受了,写了个小小的字节码执行引擎
曹工说Spring Boot源码(27)-- Spring的component-scan,光是include-filter属性的各类配置方式,就够玩半天了
曹工说Spring Boot源码(28)-- Spring的component-scan机制,让你本身来进行简单实现,怎么办
工程结构图:
在获取单例bean的时候,会进入如下方法:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 1 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 2 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 3 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 4 singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
这里面涉及到了该类中的三个field。
/** 1级缓存 Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** 2级缓存 Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); /** 3级缓存 Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
接着说前面的代码。
1处,在最上层的缓存singletonObjects
中,获取单例bean,这里面拿到的bean,直接可使用;若是没取到,则进入2处
2处,在2级缓存earlySingletonObjects
中,查找bean;
3处,若是在2级缓存中,仍是没找到,则在3级缓存中查找对应的工厂对象,利用拿到的工厂对象(工厂对象中,有3个field,一个是beanName,一个是RootBeanDefinition,一个是已经建立好的,但尚未注入属性的bean),去获取包装后的bean,或者说,代理后的bean。
什么是已经建立好的,但没有注入属性的bean?
好比一个bean,有10个字段,你new了以后,对象已经有了,内存空间已经开辟了,堆里已经分配了该对象的空间了,只是此时的10个field仍是null。
说实话,若是简单写写的话,一级缓存都没问题。给你们看一个我之前写的渣渣ioc容器:
曹工说Tomcat4:利用 Digester 手撸一个轻量的 Spring IOC容器
@Data public class BeanDefinitionRegistry { /** * map:存储 bean的class-》bean实例 */ private Map<Class, Object> beanMapByClass = new ConcurrentHashMap<>(); /** * 根据bean 定义获取bean * 一、先查bean容器,查到则返回 * 二、生成bean,放进容器(此时,依赖还没注入,主要是解决循环依赖问题) * 三、注入依赖 * * @param beanDefiniton * @return */ private Object getBean(MyBeanDefiniton beanDefiniton) { Class<?> beanClazz = beanDefiniton.getBeanClazz(); Object bean = beanMapByClass.get(beanClazz); if (bean != null) { return bean; } // 0 bean = generateBeanInstance(beanClazz); // 1 先行暴露,解决循环依赖问题 beanMapByClass.put(beanClazz, bean); beanMapByName.put(beanDefiniton.getBeanName(), bean); // 2 查找依赖 List<Field> dependencysByField = beanDefiniton.getDependencysByField(); if (dependencysByField == null) { return bean; } // 3 for (Field field : dependencysByField) { try { autowireField(beanClazz, bean, field); } catch (Exception e) { throw new RuntimeException(beanClazz.getName() + " 建立失败",e); } } return bean; } }
你们看上面的代码,我只定义了一个field,就是一个map,存放bean的class-》bean。
/** * map:存储 bean的class-》bean实例 */ private Map<Class, Object> beanMapByClass = new ConcurrentHashMap<>();
上面这个代码,有啥问题没?spring为啥整整三级?
一级缓存的问题在于,就1个map,里面既有完整的已经ready的bean,也有不完整的,还没有设置field的bean。
若是这时候,有其余线程去这个map里获取bean来用怎么办?拿到的bean,不完整,怎么办呢?属性都是null,直接空指针了。
因此,咱们就要加一个map,这个map,用来存放那种不完整的bean。这里,仍是拿spring举例。咱们能够只用下面这两层:
/** 1级缓存 Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** 2级缓存 Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
由于spring代码里是三级缓存,因此咱们对源码作一点修改。
建立了bean以后,属性注入以前,将建立出来的不完整bean,放到earlySingletonObjects
这个代码,在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
,我这边只有4.0版本的spring源码工程,不过这套逻辑,算是spring核心逻辑,和5.x版本差异不大。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 1 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); ... boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 2 earlySingletonObjects.put(beanName,bean); registeredSingletonObjects.add(beanName); // 3 // addSingletonFactory(beanName, new ObjectFactory() { // public Object getObject() throws BeansException { // return getEarlyBeanReference(beanName, mbd, bean); // } // }); }
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
以前的代码是文章开头那样的,我这里修改成:
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); return singletonObject; } } return (singletonObject != NULL_OBJECT ? singletonObject : null);
这样,就是只用两级缓存了。
ioc循环依赖,一点问题都没有,彻底够用了。
我这边一个简单的例子,
public class Chick{ private Egg egg; public Egg getEgg() { return egg; } public void setEgg(Egg egg) { this.egg = egg; } }
public class Egg { private Chick chick; public Chick getChick() { return chick; } public void setChick(Chick chick) { this.chick = chick; }
<bean id="chick" class="foo.Chick" lazy-init="true"> <property name="egg" ref="egg"/> </bean> <bean id="egg" class="foo.Egg" lazy-init="true"> <property name="chick" ref="chick"/> </bean>
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( "context-namespace-test-aop.xml"); Egg egg = (Egg) ctx.getBean(Egg.class);
结论:
因此,一级缓存都能解决的问题,二级固然更没问题。
可是,若是我这里给上面的Egg类,加个切面(aop的逻辑,意思就是最终会生成Egg的一个动态代理对象),那还有问题没?
<aop:config> <aop:pointcut id="mypointcut" expression="execution(public * foo.Egg.*(..))"/> <aop:aspect id="myAspect" ref="performenceAspect"> <aop:after method="afterIncubate" pointcut-ref="mypointcut"/> </aop:aspect> </aop:config>
注意这里的切点:
execution(public * foo.Egg.*(..))
就是切Egg类的方法。
加了这个逻辑后,咱们继续运行,在 Egg egg = (Egg) ctx.getBean(Egg.class);
行,会抛出以下异常:
我涂掉了一部分,由于那是官方对这个异常的推论,由于咱们改了代码,因此推论不许确,所以干脆隐去。
这个异常是说:
兄弟啊,bean egg已经被注入到了其余bean:chick中。(由于咱们循环依赖了),可是,注入到chick中的,是Egg类型。可是,咱们这里最后对egg这个bean,进行了后置处理,生成了代理对象。那其余bean里,用原始的bean,是否是不太对啊?
因此,spring给咱们抛错了。
怎么理解呢? 以io流举例,咱们一开始都是用的原始字节流,而后给别人用的也是字节流,可是,最后,我感受不方便,我本身悄悄弄了个缓存字符流(类比代理对象),我是方便了,可是,别人用的,仍是原始的字节流啊。
你bean不是单例吗?不能这么玩吧?
因此,这就是二级缓存,不能解决的问题。
什么问题?aop情形下,注入到其余bean的,不是最终的代理对象。
要解决这个问题,必须在其余bean(chick),来查找咱们(以上面例子为例,咱们是egg)的时候,查找到最终形态的egg,即代理后的egg。
怎么作到这点呢?
加个三级缓存,里面不存具体的bean,里面存一个工厂对象。经过工厂对象,是能够拿到最终形态的代理后的egg。
ok,咱们将前面修改的代码还原:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 1 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 2 // Map<String, Object> earlySingletonObjects = this.getEarlySingletonObjects(); // earlySingletonObjects.put(beanName,bean); // // Set<String> registeredSingletonObjects = this.getRegisteredSingletonObjects(); // registeredSingletonObjects.add(beanName); // 3 addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); }
1处,建立bean,单纯new,不注入
2处,revert咱们的代码
3处,这里new了一个ObjectFactory,而后会存入到以下的第三级缓存。
/** 3级缓存 Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
注意,new一个匿名内部类(假设这个匿名类叫AA)的对象,其中用到的外部类的变量,都会在AA中隐式生成对应的field。
你们看上图,里面的3个字段,和下面代码1处中的,几个字段,是一一对应的。
addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { // 1 return getEarlyBeanReference(beanName, mbd, bean); } });
ok,如今,egg已经把本身存进去了,存在了第三级缓存,1级和2级都没有,那后续chick在使用getSingleton查找egg的时候,就会进入下面的逻辑了(就是文章开头的那段代码,下面已经把咱们的修改还原了):
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Object singletonObject = this.singletonObjects.get(beanName); // if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // synchronized (this.singletonObjects) { // singletonObject = this.earlySingletonObjects.get(beanName); // return singletonObject; // } // } // return (singletonObject != NULL_OBJECT ? singletonObject : null); Object singletonObject = this.singletonObjects.get(beanName); 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) { // 1 singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
上面就会进入1处,调用singletonFactory.getObject();
。
而前面咱们知道,这个factory的逻辑是:
addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { // 1 return getEarlyBeanReference(beanName, mbd, bean); } });
1处就是这个工厂方法的逻辑,这里面,简单说,就会去调用各个beanPostProcessor的getEarlyBeanReference方法。
其中,主要就是aop的主力beanPostProcessor,AbstractAutoProxyCreator#getEarlyBeanReference
其实现以下:
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.add(cacheKey); // 1 return wrapIfNecessary(bean, beanName, cacheKey); }
这里的1处,就会去对egg这个bean,建立代理,此时,返回的对象,就是个代理对象了,那,注入到chick的,天然也是代理后的egg了。
咱们上面说的那个getEarlyBeanReference
就在这个接口中。
这个接口继承了BeanPostProcessor
。
而建立代理对象,目前就是在以下两个方法中去建立:
public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
这两个方法,都是在实例化以后,建立代理。那咱们前面建立代理,是在依赖解析过程当中:
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor { ... Object getEarlyBeanReference(Object bean, String beanName) throws BeansException; }
因此,spring但愿咱们,在这几处,要返回一样的对象,即:既然你这几处都要返回代理对象,那就不能返回不同的代理对象。
文章用到的aop循环依赖的demo,本身写一个也能够,很简单:
http://www.javashuo.com/article/p-smkruniq-nt.html
若是有问题,欢迎指出;欢迎加群讨论;有帮助的话,请点个赞吧,谢谢