@20190906
1、什么是AOP?AOP的各个构成部分的概念?
AOP是面向切面的编程思想:将业务逻辑模块中,需要进性处理的功能点,从逻辑上的角度将其作为一个整体进性管理;这个整体,就可以看做是有这些不同模块中的逻辑点构成的一个面;这个面就是切面;
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
连接点(Joinpoint):进性切面业务处理时的时机点,例如方法执行前、方法执行后、抛出异常等时机点;
增强(Advice):添加特定连接点上的一段业务逻辑处理代码;也就是在连接点上执行的业务处理过程;
就是在特定连接点上触发的一组动作;
切入点(PointCut):被增强的连接点就是切入点; 添加了增强功能的连接点;
织入(Weaving):将增强添加到目标对象的具体的连接点的过程。也就是将增强和连接点结合的过程就是织入;
切面(Aspect):切面由切入点和增强构成。切面就是切入点的集合;AOP就是通过切面来管理连接点和增强的;
目标对象(Target):需要添加增强的目标类。(被代理的目标类)
代理(Proxy):目标对象通过AOP生成的代理对象;代理和目标对象之间是代理和被代理的关系;
引入(Introduction):是一种特殊的增强;可以为目标对象添加一些属性和方法;一般用作动态的给目标对象附加一些行为能力;
总结:
把"增强"使用"织入"方式结合在"连接点"上构成了"切入点",“切入点"汇集成"切面” ;
AOP就是使用动态代理等方式对切面的管理机制;
2、AOP的实现方式?【拦截器思想、动态代理】
AOP是目前Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础。它是一种面向切面编程的思想。关于AOP的基础知识,相信多数童鞋都已经了如指掌,我们就略过这部分,来讲解下AOP的核心功能的底层实现机制:如何用动态代理来实现切面拦截。
AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。
那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。
AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
一、jdk动态代理实现AOP拦截(代码中的关键地方都添加了注释)
1、为目标类(target)定义统一的接口类Service,这个是jdk动态代理必须的前提。
2、目标类AService,我们的实验目标就是在AService中add和update方法的前后实现拦截,加入自定义切面逻辑advise
3、实现动态代理类MyInvocationHandler,实现InvocationHandler接口,并且实现接口中的invoke方法。仔细看invoke方法,就是在该方法中加入切面逻辑的。目标类方法的执行是由mehod.invoke(target,args)这条语句完成。
4、测试类,其中增强的目标对象是由Proxy.newProxyInstance(aService.getClass().getClassLoader(), aService.getClass().getInterfaces(), handler);来生成的。
自此,jdk动态代理来实现AOP拦截机制的代码已经实现,下面我们看一下拦截的结果,程序输出结果如下:
before-----------------------------
AService add>>>>>>>>>>>>>>>>>>
after------------------------------
before-----------------------------
AService update>>>>>>>>>>>>>>>
after------------------------------
可以看到,在目标类AService的add和update方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。
二、cglib动态代理实现AOP拦截(代码中的关键地方都添加了注释)
1、目标类,cglib不需要定义目标类的统一接口
2、实现动态代理类CglibProxy,需要实现MethodInterceptor接口,实现intercept方法。该代理中在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args);
3、获取增强的目标类的工厂Factory,其中增强的方法类对象是有Enhancer来实现的,代码如下所示:
4、测试类
自此,cglib动态代理实现的AOP拦截机制已经基本实现,下面我们来看一下拦截的效果如何,程序执行结果如下:
before-------------
add ------------
after--------------
可以看到,在目标类Base的add方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。
此外,需要说明一下的是,cglib动态代理用到了第三方类库,需要在项目中引入两个jar包:cglib.jar和asm.jar。稍后会在csdn资源中上传这两个jar包。
jar包和相关代码请见http://download.csdn.net/detail/dreamrealised/6427885
总之,AOP的核心机制和基本功能已经能够通过动态代理来实现了,至于AOP中,如何从配置文档中得到目标类target、advisor的bean,如何判断拦截器类型等问题,就借助于Spring中另一个核心IOC来解决了,后续会有IOC的核心实现机制讲解。
3、Spring容器初始化的流程?
一、Spring 容器高层视图
Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配号Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。
二、内部工作机制
该图描述了Spring容器从加载配置文件到创建出一个完整Bean的作业流程:
1、ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示这个配置文件的资源;
2、BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
3、容器扫描BeanDefinitionRegistry中的BeanDefinition,使用Java的反射机制自动识别出Bean工厂后处理后器(实现BeanFactoryPostProcessor接口)的Bean,然后调用这些Bean工厂后处理器对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要完成以下两项工作:
1)对使用到占位符的元素标签进行解析,得到最终的配置值,这意味对一些半成品式的BeanDefinition对象进行加工处理并得到成品的BeanDefinition对象;
2)对BeanDefinitionRegistry中的BeanDefinition进行扫描,通过Java反射机制找出所有属性编辑器的Bean(实现java.beans.PropertyEditor接口的Bean),并自动将它们注册到Spring容器的属性编辑器注册表中(PropertyEditorRegistry);
4.Spring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition,并调用InstantiationStrategy着手进行Bean实例化的工作;
5.在实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装,BeanWrapper提供了很多以Java反射机制操作Bean的方法,它将结合该Bean的BeanDefinition以及容器中属性编辑器,完成Bean属性的设置工作;
6.利用容器中注册的Bean后处理器(实现BeanPostProcessor接口的Bean)对已经完成属性设置工作的Bean进行后续加工,直接装配出一个准备就绪的Bean。
Spring容器确实堪称一部设计精密的机器,其内部拥有众多的组件和装置。Spring的高明之处在于,它使用众多接口描绘出了所有装置的蓝图,构建好Spring的骨架,继而通过继承体系层层推演,不断丰富,最终让Spring成为有血有肉的完整的框架。所以查看Spring框架的源码时,有两条清晰可见的脉络:
1)接口层描述了容器的重要组件及组件间的协作关系;
2)继承体系逐步实现组件的各项功能。
接口层清晰地勾勒出Spring框架的高层功能,框架脉络呼之欲出。有了接口层抽象的描述后,不但Spring自己可以提供具体的实现,任何第三方组织也可以提供不同实现, 可以说Spring完善的接口层使框架的扩展性得到了很好的保证。纵向继承体系的逐步扩展,分步骤地实现框架的功能,这种实现方案保证了框架功能不会堆积在某些类的身上,造成过重的代码逻辑负载,框架的复杂度被完美地分解开了。
Spring组件按其所承担的角色可以划分为两类:
1)物料组件:Resource、BeanDefinition、PropertyEditor以及最终的Bean等,它们是加工流程中被加工、被消费的组件,就像流水线上被加工的物料;
2)加工设备组件:ResourceLoader、BeanDefinitionReader、BeanFactoryPostProcessor、InstantiationStrategy以及BeanWrapper等组件像是流水线上不同环节的加工设备,对物料组件进行加工处理。
4、Spring bean的加载过程?以及生命周期?
对于加载bean的功能,Spring的调用方式是 MyBean bean = bf.getBean(“myBean”);而我们从这一方法进入寻找bean的加载过程。发现过程如下图所示,共9个步骤
1.转换beanName:传入的参数beanName可能是别名、FactoryBean。FactoryBean解析去掉修饰符,也就是如果是name="&aa",会解析为name=“aa”;
2.尝试从缓存中加载单例bean:单例在Spring的同一个容器只会创建一次,后续加载从缓存中获取;因为在创建单例bean的时候可能存在依赖注入,而Spring为了避免循环依赖,创建bean的原则是不等bean创建完成,就会将创建bean的beanFactory提前曝光在缓存中,一旦下一个bean需要依赖上一个bean的时候,就直接使用beanFactory。
3.实例化bean:如果从缓存中获取到bean的原始状态,则需要对bean实例化。
4.循环依赖检查:检查当前bean是否存在循环依赖,即bean A 引用bean B,而bean B又引用bean A,则抛出异常
5.检测parentBeanFactory:如果parentBeanFactory不为空,且当前获取不到bean的配置信息,则会从parentBeanFactory中递归寻找。
6.将存储xml配置信息的gernericBeanDefinition转换为RootBeanDefintion:因为所有bean的后续处理都是针对RootBeanDefintion,所以这里需要转换,如果存在parentBean,则将parentBean的配置信息也合并到bean中。
7.寻找依赖:在初始化某一个bean的时候首先会初始化这个bean所对应的依赖。
8.根据不同的scope创建bean:scope包括singleton、request、prototype。默认singleton。
9.类型转换:根据输入的参数转换bean类型。
实例化bean对象(通过构造方法或者工厂方法)
设置对象属性(setter等)(依赖注入)
如果Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。(和下面的一条均属于检查Aware接口)
如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身
将Bean实例传递给Bean的前置处理器的postProcessBeforeInitialization(Object bean, String beanname)方法
调用Bean的初始化方法
将Bean实例传递给Bean的后置处理器的postProcessAfterInitialization(Object bean, String beanname)方法
使用Bean
容器关闭之前,调用Bean的销毁方法