首先从Spring的理解开始。
Spring是一个框架,在整个开发流程中,所有的框架都依赖于Spring。同时Spring起到了一个容器的作用,用来承载整体的bean对象,并且对bean的一个从生产到销毁的生命周期进行管理。在使用Spring的时候,我们可以使用配置文件或者注释的方式,通过一个BeanDefinitionReader接口的形式(因为配置文件的格式有多种,例如xml,注解,JSON等)来对bean的定义信息进行读取,并且生成BeanDefinition的实例化对象。我们可以通过BeanFactoryPostProcesser来对BeanDefinition进行增强处理,如替换XML文件中的占位符或者添加注解的额外处理功能。之后通过BeanDefiniton Map 将BeanDefinition对象映射到BeanFactory上,通过反射的方式来完成Bean的实例化过程。在完成了Bean的实例化后,需要对Bean进行初始化。首先通过populateBean 对Bean对象进行属性填充(循环依赖发生的位置),然后执行Aware方法(作用:使自定义的对象能够方便获取容器对象,如Environment,ApplicationContext),进行beforeBeanPostProcesser增强,调用init-method,进行AfterBeanPostProcesser增强。这些步骤走完算是完成了Bean的初始化,之后我们就可以获取到完整的Bean对象了。
为什么要执行BeanPostProcesser?
对实例化完成的Bean对象进行修改其实就是AOP的过程。因为实例化完成后,java文件变成了.class的字节码文件,被加载到JVM上了。这时候我们想对文件进行修改,就不能用原生的.class文件了,需要重新生成.class文件再被JVM加载。这时候ASM提供了这样一个解决方案:能够在程序的运行期间,根据需求动态的创建字节码文件,生成的字节码文件可以写在内存中,也可以溢写到磁盘上,但是最终要有一个class文件。
匹配方式:根据expression表达式(方法名,方法参数,方法的返回值,全限定路径名)来匹配哪些方法需要被改变
切面:对方法进行代码插入的位置叫做切面。它包括:Before(在方法的前面插入),After,Around(在方法的前面或后面插入),afterThrowing(在异常处插入,catch语句块内),afterReturning(在方法的返回值处插入)
advice通知:插入的语句块,即要插入的方法逻辑
由于五个advice通知都需要被执行,于是引入了责任链模式。当通知想要被执行的时候,我们需要进行方法执行顺序的打断时,因此,通知是要实现MethodIntercepter接口的
AOP的核心是动态代理,动态代理有两种:jdk和cglib。
jdk动态代理:首先有一个目标类A实现了接口A0,A有自己的方法。我们需要创建一个包装类B继承invocationHandler,提供一个方法将目标对象A set进去,同时改写invoke方法(在调用invoke方法前可以对A进行增强----也就是加新的方法)。包装类B中还需要一个方法createProxyObj(A的构造器,A的构造方法,包装类B对象)去返回一个代理类对象C。
A0 a =new A();//创建目标类A的对象a
B b = new B();//创建包装类B的对象b
b.set(a);//将目标对象a传到对象b里
Object c= b.createProxyObj();//创建代理类对象 c
A0 a0 = (A0) c;//将代理类对象c强转为目标类A的接口类型即可使用
循环依赖
三级缓存:singletonFactory,earlySingletonFactory,ObjectFactory
Bean的create流程:Spring进行扫描,通过反射生成BeanDefinition对象,放入BeanDefinition map中,遍历map,进行验证(是否是单例的,是否懒加载,是否抽象),推断构造方法,生成实例化对象。
A完成了实例化,将自己提前暴露到一级缓存singletonFactory中,进行属性填充,发现依赖B,B走上述流程,完成实例化,将B暴露到一级缓存singletonFactory中,准备进行属性填充,发现依赖A,发现A已经在创建中,则把A移入二级缓存earlySingletonFactory,并且删除一级缓存中的A,找到了A后,B属性填充完毕,将B暴露到三级缓存ObjectFactory中,这时候A可以通过getBean方法获取到B对象,于是A的属性填充完毕,将A暴露到三级缓存ObjectFactory中。
循环依赖只支持单例而不是原型:因为原型只有在用到bean的时候才会走生命周期流程,不存在一个已经实例化好的A对象。
循环依赖只支持非构造方法:如果在A构造方法的参数中需要传入B,B的构造方法中需要传入A,那么会形成类似死锁的问题。