Spring框架之AOP源码完全解析

Spring可以说是Java企业开发里最重要的技术。Spring两大核心IOC(Inversion of Control控制反转)和AOP(Aspect Oriented Programming面向切面编程)。IOC技术我们在上一篇文章“Spring框架之beans源码完全解析”中进行了分析,本文对Spring框架的AOP源码进行分析。

AOP面向切面编程是通过预编译方式和运行其动态代理,实现在不修改源代码的情况下给程序动态统一添加功能的一种技术,是OOP面向对象编程的有效补充。利用AOP技术将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不会影响业务逻辑的代码,实现了解耦,提高了代码的灵活性和可扩展性。

下面就Spring框架的AOP源码进行解析,主要从以下几个方面进行剖析:一是对Spring的几个关键概念进行阐述;二是对AOP源码文件清单进行梳理,一共9大类,204个java文件。三是Spring主要使用JDK动态代理来实现AOP,对JDK动态代理做了一个简单的介绍。四是结合源码对Spring AOP的实现原理进行了分析。

 

文章目录

一、Spring AOP几个关键概念

1、AOP联盟

2、AOP、Spring AOP、AspectJ的区别

3、Spring AOP 10个常见概念

4、Advice(通知/增强)

5、Advisor(顾问/增强器)

6、Pointcut(切点)

7、TargetSource(目标对象)

8、Interceptor(拦截器)

二、AOP源码文件清单

1、aopalliance包含的接口和类

2、AOP包含的接口和类

3、AOP/aspectj包含的接口和类

4、AOP/config包含的接口和类

5、AOP/framework包含的接口和类

6、AOP/interceptor包含的接口和类

7、AOP/scope包含的接口和类

8、AOP/support包含的接口和类

9、AOP/target包含的接口和类

三、JDK动态代理

(一)什么是代理

(二)Java的动态代理类

(三)动态代理的步骤

四、Spring AOP的实现原理

(一)标签的解析

(二)获取增强方法或者增强器

(三)根据获取的增强创建代理

(四)织入

一、Spring AOP几个关键概念

1、AOP联盟

AOP联盟规范了一套用于规范AOP实现的底层API,通过这些统一的底层API,可以使得各个AOP实现及工具产品之间实现相互移植。这些API主要以标准接口的形式提供,是AOP编程思想所要解决的横切交叉关注点问题各部件的最高抽象。Spring的AOP框架中也直接以这些API为基础构建。下面我们来看看当前AOP联盟发布的AOP相关的标准接口。

AOP联盟的API主要包括四个部分,第一个是aop包,定义了一个表示通知Advice的标识接口,各种各样的通知都继承或者实现了该接口。aop包中还包括了一个用于描述AOP系统框架错误运行时异常AspectException。

第二个部分是intercept包,也就是拦截器包,这个包中规范了AOP核心概念中的连接点及通知Advice的类型。

第三部分及第四部分是instrument及reflect包。这两个包中的API主要包括AOP框架或者产品为了实现把横切关注点的模块和核心应用模块组合集成,所需要使用的设施、技术及底层实现规范等。

aopalliance1.0.jar类结构如下图所示(未包括第三、四部分):

aopalliance有三个主要的业务实体:Joinpoint 、Advice、Interceptor。这三个接口构成了aopalliance功能结构。

1、Joinpoint:程序在执行过程中一个运行时Joinpoint,在这些点关联的静态位置通常会安装有一些Interceptor,当程序运行到这个运行时Joinpoint时,AOP框架会拦截运行时Joinpoint的执行,把运行时Joinpoint交给已经安装的Interceptor们进行处理。JoinPoint接口有三个方法:

proceed():该方法用于执行拦截器逻辑;

getThis():返回保持当前连接点的静态部分的对象;

getStaticPart():返回此连接点的静态部分(通常包含构造函数,成员变量,方法等信息)

2、Advice只是起到一个超类标记功能。Advice(通知)定义了AOP框架在某个Joinpoint(连接点)的通用处理逻辑。

3、Interceptor(拦截器)。Interceptor继承了Advice,可以看成是对Advice的特殊功能实现。Interceptor只是Advice处理逻辑中的一种类型或者方式,表示的仅仅是采用拦截处理机制实现了Advice这种功能。

Advice和Interceptor两个接口没有任何操作,都是标记接口。(标识接口就是空方法的接口。与其他接口的区别是:这个接口里面什么方法都没有,只是标记而已。例如serilizeabled就是这样一个接口,他只是告诉jvm,继承于这个接口的CLASS需要序列化处理,我们不用实现这个接口的方法。)

2、AOP、Spring AOP、AspectJ的区别

(1)AOP:Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

(2)AspectJ:AspectJ来自于 Eclipse 基金会,属于静态植入,它是通过修改代码来实现的,它的植入时机可以是:Compile-time weaving编译期织入;Post-compile weaving编译后织入,也就是已经生成了.class 文件,或已经达成 jar 包了;Load-time weaving指的是在加载类的时候进行织入。AspectJ 能干很多 Spring AOP 干不了的事情,它是 AOP 编程的完全解决方案。

(3)SpringAOP:它基于动态代理来实现AOP。如果使用接口的,用JDK提供的动态代理实现,如果没有接口,使用 CGLIB 实现。Spring AOP 致力于解决的是企业级开发中最普遍的 AOP 需求(方法织入),而不是力求成为一个像 AspectJ 一样的 AOP 编程完全解决方案。

AOP是一种概念,springAOP、AspectJ都是AOP的实现,Spring AOP有自己的语法,但是语法复杂,所以SpringAOP借助了AspectJ的注解,但是底层实现还是自己的。

3、Spring AOP 10个常见概念

(1)Joinpoint(连接点)

程序执行的某个特定位置,比如某个方法调用前、调用后,方法抛出异常后,对类成员的访问以及异常处理程序块的执行等。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。它自身还可以嵌套其他的Joinpoint。AOP中的Joinpoint可以有多种类型:构造方法调用,字段的设置和获取,方法的调用,方法的执行,异常的处理执行,类的初始化。Spring仅支持方法执行类型的Joinpoint。

(2)Pointcut(切点)

如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。所以切点表示一组Joinpoint,这些Jointpoint或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

(3)Advice(通知/增强)

通知是织入到目标类连接点上的一段程序代码。Spring提供的通知接口都是带方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。我们通过AOP将横切关注功能加到原有的业务逻辑上,这是对原有业务逻辑的一种增强,可以是前置、后置、返回后、抛出异常时等。其实Advice翻译成“增强”更合理,更能准确表达其本质。既然大部分文献都是称为通知,我们这里也称为通知。

(4)Introduction(引介)

引介是一种特殊的通知,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

(5)Interceptor(拦截器)

在Advice的基础上扩展定义,定义了通知的增强方式,也就是通过对Joinpoint(连接点)的拦截。一个通用的拦截器可以拦截发生在基础程序中的运行时事件。

(6)Aspect(切面)

切面是由Pointcut(切点)和Advice(通知)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。

(7)Advisor(顾问/增强器)

Advisor是切面的另一种实现,绑定通知跟切点。没有指定切点的通知是没有意义的,Advisor可以说就是一个绑定在指定切点上的通知。它能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。

(8)TargetSource(目标对象)

包含连接点的对象。也被称作被通知或被代理对象。

(9)Proxy(代理对象)

包含了原始对象的代码(是在合适的位置调用目标对象的方法)和增加后的代码(Advice通知的内容)的那个对象。

(10)Weaving(织入)

织入是将Advice通知添加到目标类具体连接点上的过程,AOP有三种织入方式:①编译期织入:需要特殊的Java编译期(例如AspectJ的ajc);②装载期织入:要求使用特殊的类加载器,在装载类的时候对类进行增强;③运行时织入:在运行时为目标类生成代理实现增强。

AspectJ采用了编译期织入和装载期织入的方式,Spring采用了动态代理的方式实现了运行时织入。

4、Advice(通知/增强)

Advice有以下几种常见的类型:

1、AspectJMethodBeforeAdvice:前置通知。AspectJ中 before 属性对应的通知(@Before标注的方法会被解析成该通知),在切面方法执行之前执行。

2、AspectJAfterReturningAdvice:后置通知。AspectJ中 afterReturning 属性对应的通知(@AfterReturning 标注的方法会被解析成该通知),在切面方法执行之后执行,如果有异常,则不执行。注意:该通知与AspectJMethodBeforeAdvice对应。

3、AspectJAroundAdvice:环绕通知。AspectJ中 around 属性对应的通知(@Around标注的方法会被解析成该通知),在切面方法执行前后执行。

4、AspectJAfterAdvice:返回通知。AspectJ中 after 属性对应的通知(@After 标注的方法会被解析成该通知),不论是否异常都会执行。

5、AspectJAfterThrowingAdvice:异常通知,AspectJ中 after 属性对应的通知(@AfterThrowing标注的方法会被解析成该通知),在连接点抛出异常后执行。

5、Advisor(顾问/增强器)

advisor:顾问(切面的另一种实现),封装了spring aop中的切点和通知。通知(advice)中包含了增强的横切代码,切点(pointcut)包含了连接点的描述信息。

1、StaticMethodMatcherPointcut:静态方法切面。定义了一个classFilter,通过重写getClassFilter()方法来指定切面规则。另外实现了StaticMethodMatcher接口,通过重写matches来指定方法匹配规则。

2、StaticMethodMatcherPointcutAdvisor:静态方法匹配切面顾问。扩展了切面排序方法。

3、NameMatchMethodPointcut:名称匹配切面。通过指定方法集合变量mappedNames,模糊匹配。

4、NameMatchMethodPointcutAdvisor:方法名称切面顾问。内部封装了NameMatchMethodPointcut,通过设置方法名称模糊匹配规则和通知来实现切面功能。

5、RegexpMethodPointcutAdvisor:正则表达式切面顾问。可设置多个正则表达式规则,通过内部封装的JdkRegexpMethodPointcut解析正则表达式。

6、DefaultPointcutAdvisor:默认切面顾问。比较灵活,可自由组合切面和通知。

7、InstantiationModelAwarePointcutAdvisorImpl:springboot自动装配的顾问类型。是最常用的一种顾问实现。在注解实现的切面中,所有@Aspect类,都会被解析成该对象。

advisorCreator:继承 spring ioc的扩展接口 beanPostProcessor,主要用来扫描获取advisor。

1、AbstractAutoProxyCreator:Spring 为Spring AOP 模块暴露的可扩展抽象类,也是 AOP 中最核心的抽象类。

2、BeanNameAutoProxyCreator:根据指定名称创建代理对象。通过设置 advisor,可以对指定的 beanName 进行代理。支持模糊匹配。

3、AbstractAdvisorAutoProxyCreator:功能比较强大,默认扫描所有Advisor的实现类。相对于根据Bean名称匹配,该类更加灵活。动态的匹配每一个类,判断是否可以被代理,并寻找合适的增强类,以及生成代理类。

4、DefaultAdvisorAutoProxyCreator:AbstractAdvisorAutoProxyCreator的默认实现类。可以单独使用,在框架中使用AOP,尽量不要手动创建此对象。

5、AspectJAwareAdvisorAutoProxyCreator:AspectJ的实现方式,也是Spring Aop中最常用的实现方式,如果用注解方式,则用其子类AnnotationAwareAspectJAutoProxyCreator。

6、AbstractAutoProxyCreator:Spring 为Spring AOP 模块暴露的可扩展抽象类,也是 AOP 中最核心的抽象类。

7、AnnotationAwareAspectJAutoProxyCreator:目前最常用的AOP使用方式。spring aop 开启注解方式之后,该类会扫描所有@Aspect()注释的类,生成对应的advisor。目前SpringBoot框架中默认支持的方式,自动配置。

6、Pointcut(切点)

1、AnnotationMatchingPointcut:注解匹配切点。根据类上或方法上是否存在指定的注解判断切点的匹配性,如果没有显示指定注解,则匹配所有。

2、DynamicMethodMatcherPointcut:动态方法匹配器切点。它本质上是一个方法匹配器,但同时具有了切点的功能。

3、ComposablePointcut:可组合的切点。这种切点可以与或逻辑,任意组合其他的Pointcut、ClassFilter和MethodMatcher。其本质是通过ClassFilters和MethodMatchers两个工具类进行Pointcut内部组件的组合。

4、JdkRegexpMethodPointcut: JDK正则表达式切点,即使用正则表达式描述方法的拦截规则和排除规则。

5、AspectJExpressionPointcut:AspectJ切点表达式切点。顾名思义,使用AspectJ的切点表达式描述筛选规则。表达式基本语法如下(非完整语法):execution(<方法修饰符>? <方法返回值类型> <包名>.<类名>.<方法名>(<参数类型>) [throws <异常类型>]?)

其中,‘*’代表0个或多个任意字符,包名中的..(两个点)代表当前包及其子包,参数列表中的..代表任意个参数。如:execution(public static * *..*.*(..) throws *),此表达式匹配所有方法。

7、TargetSource(目标对象)

TargetSource被用于获取当前MethodInvocation(方法调用)所需要的target(目标对象),这个target通过反射的方式被调用(如:method.invode(target,args))。换句话说,proxy(代理对象)代理的不是target,而是TargetSource。

为什么Spring AOP代理不直接代理target,而需要通过代理TargetSource(target的来源,其内部持有target),间接代理target呢?

通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:target pool(目标对象池)、hot swap(运行时目标对象热替换)等等。

TargetSource组件本身与Spring IoC容器无关,target的生命周期不一定是受spring容器管理的,我们以往的XML中的AOP配置,只是对受容器管理的bean而言的,我们当然可以手动创建一个target,同时使用Spring的AOP框架(而不使用IoC容器)

TargetSource包含4个简单实现和3大类实现。

4个简单实现包括:

(1)EmptyTargetSource:静态目标源,当不存在target目标对象,或者甚至连targetClass目标类都不存在(或未知)时,使用此类实例。

(2)HotSwappableTargetSource:动态目标源,支持热替换的目标源,支持spring应用运行时替换目标对象。

(3)JndiObjectTargetSource:spring对JNDI管理bean的支持,static属性可配置。

(4)SingletonTargetSource:静态目标源,单例目标源。Spring的AOP框架默认为受IoC容器管理的bean创建此目标源。换句话说,SingletonTargetSource、proxy与目标bean三者的声明周期均相同。如果bean被配置为prototype,则spring会在每次getBean时创建新的SingletonTargetSource实例。

3大类实现包括:

(1)AbstractBeanFactoryBasedTargetSource:此类目标源基于IoC容器实现,也就是说target目标对象可以通过beanName从容器中获取。此类又扩展出:① SimpleBeanTargetSource:简单实现,直接调用getBean从容器获取目标对象;② LazyInitTargetSource:延迟初始化目标源,子类可重写postProcessTargetObject方法后置处理目标对象;③AbstractPrototypeBasedTargetSource:原型bean目标源,此抽象类可确保beanName对应的bean的scope属性为prototype。其子类做了简单原型、池化原型、线程隔离原型这3种实现。

(2)AbstractRefreshableTargetSource:可刷新的目标源。此类实现可根据配置的刷新延迟时间,在每次获取目标对象时自动刷新目标对象。

(3)AbstractLazyCreationTargetSource:此类实现在调用getTarget()获取时才创建目标对象

8、Interceptor(拦截器)

Interceptor(拦截器)定义了通知的增强方式,也就是通过对Joinpoint(连接点)的拦截。一个通用的拦截器可以拦截发生在基础程序中的运行时事件。这些事件被连接点具体化。运行时连接点可以是一次方法调用、字段访问、异常产生等等。Interceptor接口也在强调概念而非功能,也是一个标记接口。 由Interceptor扩展出的ConstructorInterceptor和MethodInterceptor两个子接口,才具体定义了拦截方式。它们一个用于拦截构造方法,一个用于拦截普通方法。 但是,spring框架并没有支持AOP联盟对构造方法的拦截,因为spring框架本身,通过BeanPostProcessor的定义,对bean的生命周期扩展已经很充分了。

MethodInterceptor只定义了增强方式,我们可以通过实现此接口,自定义具体的增强内容。当然,spring框架也提供了3种预定义的增强内容:BeforeAdvice(前置通知)、AfterAdvice(后置通知)和DynamicIntroductionAdvice(动态引介通知)。BeforeAdvice和AfterAdvice更确切地说是定义了增强内容的执行时机(方法调用之前还是之后);而DynamicIntroductionAdvice比较特殊,它可以编辑目标类要实现的接口列表。最后,spring预定义的通知还是要通过对应的适配器,适配成MethodInterceptor接口类型的对象(如:MethodBeforeAdviceInterceptor负责适配MethodBeforeAdvice)。

几个常用拦截器:

(1)MethodBeforeAdviceInterceptor:MethodBeforeAdvice(前置通知,其父接口是BeforeAdvice)接口的适配器,用于支持spring预定义的前置通知,在目标方法调用前调用MethodBeforeAdvice.before()。

(2)AspectJAfterAdvice :AspectJ框架的后置通知实现,在目标方法执行结束后,return之前,调用配置指定的方法(注意:此方法调用被写在finally块中,无论如何都会得到执行)。

(3)AfterReturningAdviceInterceptor :AfterReturningAdvice接口的适配器,用于支持spring预定义的后置通知,在目标方法执行结束后,return之前,调用AfterReturningAdvice.afterReturning()执行(注意:如果目标方法抛出异常,则不会执行这个方法)。

(4)AspectJAfterThrowingAdvice :AspectJ框架的异常通知,当目标方法执行时产生异常的时候,指定配置指定的方法。

(5)AspectJAroundAdvice :AspectJ框架的环绕通知,直接执行配置指定的方法。

(6)ThrowsAdviceInterceptor :spring框架预定义的异常通知的适配器,此适配器接受任意类型的对象,但是要求对象所在类必须声明public的名称为afterThrowing,且参数个数为1个或4个,且最后一个参数为Throwable类型的方法。该适配器会保存该Throwable对象的实际类型到该方法之间的映射,当目标方法执行产生异常时,根据产生的异常类型找到对应的通知方法进行调用。

(7)DelegatingIntroductionInterceptor:通过构造方法传入指定的引介对象,每当调用的目标方法是引介接口定义的方法时,都会调用该引介对象的对应方法。

(8)DelegatePerTargetObjectIntroductionInterceptor:通过构造函数传入指定的引介接口和接口对应的实现类,该拦截器会为每个目标对象创建新的引介对象(通过调用实现类的默认无参构造)。当调用的方法是引介接口定义的方法时,则调用该新建的引介对象对应的方法。

二、AOP源码文件清单

本文进行梳理的AOP源码文件清单是基于版本号为5.2.4.BUILD-SNAPSHOT的Spring源码。一共9大类,204个java文件。

1、aopalliance

aopalliance/aop

1.1  Advice:定义了一个表示通知的标识接口,各种各样的通知都继承或实现了该接口。

1.2  AspectException:描述AOP系统框架错误的运行时异常。

aopalliance/intercept

1.3  Joinpoint:连接点。在拦截器中使用,封装了原方法调用的相关信息,如参数、原对象信息,以及直接调用原方法的proceed方法。

1.4  Invocation 运行时的方法调用,继承自joinpoint。

1.5  ConstructorInvocation:Invocation的子类,包含了获取构造器的方法。

1.6  MethodInvocation:Invocation的子类,返回被调用的方法信息。

1.7  Interceptor:拦截器,Advice的子接口,标记拦截器。拦截器是增强器的一种。

1.8  ConstructorInterceptor:构造器拦截器,Interceptor的子接口,拦截构造器并处理。

1.9  MethodInterceptor:方法拦截器,Interceptor的子接口,拦截方法并处理,核心类。

2、AOP

2.1  Pointcut:切点的一个顶层抽象。切点的主要作用是定义通知所要应用到的类跟方法。具体的哪些类、哪些方法由ClassFilter和MethodMatcher匹配,只有满足切入点的条件时才插入advice。

2.2  TruePointcut:Pointcut(切点)接口的一个最简单的实现类,匹配到所有。

2.3  AfterAdvice:无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice。

2.4  AfterReturningAdvice:后置通知(After Returning Advice)后置通知相比较于前置通知,主要有以下几点不同:后置通知可以访问目标方法的返回值,但是不能修改,后置通知是在方法执行完成后执行。

2.5  BeforeAdvice:在 join point 前被执行的 advice。虽然 before advice 是在 join point 前被执行,但是它并不能够阻止 join point 的执行,除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)。

2.6  MethodBeforeAdvice:前置通知(Before Advice)跟环绕通知不同的是,这个接口中定义的方法的返回值是void,所以前置通知是无法修改方法的返回值的。如果在前置通知中发生了异常,那么会直接终止目标方法的执行以及打断整个拦截器链的执行。

2.7  ThrowsAdvice:异常通知(Throws Advice),其中没有定义任何方法,它更像一个标记接口。我们在定义异常通知时需要实现这个接口,同时方法的签名也有要求:1、方法名称必须是afterThrowing。2、方法的参数个数必须是1个或者4个。

2.8  Advisor:顾问,封装了spring aop中的切点和通知。 就是我们常用的@Aspect 注解标记的类。

2.9  PointcutAdvisor:代表具有切点的切面,它包含Advice和Pointcut两个类,这样就可以通过类、方法名以及方法方位等信息灵活地定义切面的连接点,提供更具适用性的切面。其有6种实现类:

DefaultPointcutAdvisor:最常用的切面类型,它可以通过任意Pointcut和Advice定义一个切面,唯一不支持的是引介的切面类型,一般可以通过扩展该类实现自定义的切面;

NameMatchMethodPointcutAdvisor:通过该类可以定义,按方法名定义切点的切面;

RegexpMethodPointcutAdvisor:按正则表达式匹配方法名进行切点定义的切面;

StaticMethodMatcherPointcutAdvisor:静态方法匹配器切点定义的切面;

AspecJExpressionPointcutAdvisor:Aspecj切点表达式定义切点的切面;

AspecJPointcutAdvisor:使用AspecJ语法定义切点的切面。

2.10  MethodMatcher:用来匹配方法。MethodMatcher中一共有三个核心方法:

matches(Method method, @Nullable Class<?> targetClass),这个方法用来判断当前定义的切点跟目标类中的指定方法是否匹配,它可以在创建代理的时候就被调用,从而决定是否需要进行代理,这样就可以避免每次方法执行的时候再去做判断。

isRuntime(),如果这个方法返回true的话,意味着每次执行方法时还需要做一次匹配。

matches(Method method, @Nullable Class<?> targetClass, Object... args),当之前的isRuntime方法返回true时,会调用这个方法再次进行一次判断,返回false的话,意味着不对这个方法应用通知。

接口的实现类有很多,如StaticMethodMatcher(只支持静态匹配,两个参数的matchs)、AspectJExpressionPointcut(AOP重要组件)、TrueMethodMatcher(总是匹配)、AnnotationMethodMatcher(注解匹配)。

2.11  TrueMethodMatcher:MethodMatcher接口的一个最简单的实现类,匹配到所有的方法。

2.12  ClassFilter:主要作用是在类级别上对通知的应用进行一次过滤,如果它的match方法对任意的类都返回true的话,说明在类级别上我们不需要过滤,这种情况下,通知的应用,就完全依赖MethodMatcher的匹配结果。ClassFilter有4中简单方式的实现:

(1)TypePatternClassFilter:基于AspectJ的类型匹配实现;

(2)AnnotationClassFilter:通过检查目标类是否存在指定的注解,决定是否匹配;

(3)RootClassFilter:通过判断目标类是否是指定类型(或其子类型),决定是否匹配;

(4)TrueClassFilter:这是最简单实现,matches方法总会返回true。此类设计使用了单例模式,且其对象引用直接被在ClassFilter接口中声明成了常量。

2.13  TrueClassFilter:ClassFilter接口的一个最简单的实现类,匹配到所有的类。

2.14  IntroductionInfo:描述一个引介需要的基本信息。引介(Introduction)是指在不更改源代码的情况,给一个现有类增加属性、方法,以及让现有类实现其它接口或指定其它父类等,从而改变类的静态结构。是另一种类型的增强,和通知是并列的两种不同的增强。

2.15  IntroductionAdvisor:advisor顾问,封装了spring aop中的切点和通知。这个接口是用来处理一个或者多个引入的顾问的父接口。

2.16  DynamicIntroductionAdvice:引介通知(Introduction Advice)。

2.17  IntroductionAwareMethodMatcher:继承自MethodMatcher,在进行方法匹配时考虑了引入。

2.18  IntroductionInterceptor:AOP联盟中的MethodInterceptor(方法拦截器,拦截方法并处理)的子接口。

2.19  TargetSource:TargetSource(目标源)是被代理的target(目标对象)实例的来源。

2.20  TargetClassAware:在代理中用来公开目标类的最小接口,主要被代理对象和代理工厂所实现.。所有的Aop代理对象或者代理工厂(proxy factory)都要实现的接口,该接口用于暴露出被代理目标对象类型。

2.21  RawTargetAccess:AOP代理的标记接口,用来返回原始的目标对象。

2.22  SpringProxy:标记接口,Spring使用JDK动态代理或者CGLIB的方式来生成代理对象,其中每个代理对象都会实现SpringProxy接口。用来判定是否是Spring产生的代理对象。

2.23  ProxyMethodInvocation:MethodInvocation接口的拓展,能够返回一个代理对象,而当前的Method Invocation是通过该代理对象办到的。

2.24  AopInvocationException:因为错误的配置或者始料不及的运行时出现的问题,导致AOP运行时方法调用失败时抛出的异常。

3、AOP/aspectj 

3.1  AbstractAspectJAdvice:AOP基类,用来包装AspectJ切面(AspectJ aspect)或AspectJ注解(AspectJ-annotated)的通知方法。

3.2  AspectJAfterAdvice:返回通知,AspectJ中 after 属性对应的通知(@After 标注的方法会被解析成该通知),不论是否异常都会执行。

3.3  AspectJAfterReturningAdvice:后置通知,AspectJ中 afterReturning 属性对应的通知(@AfterReturning 标注的方法会被解析成该通知),在切面方法执行之后执行,如果有异常,则不执行。注意:该通知与AspectJMethodBeforeAdvice对应。

3.4  AspectJMethodBeforeAdvice:前置通知,AspectJ中 before 属性对应的通知(@Before标注的方法会被解析成该通知),在切面方法执行之前执行。

3.5  AspectJAfterThrowingAdvice:异常通知,AspectJ中 after 属性对应的通知(@AfterThrowing标注的方法会被解析成该通知),在连接点抛出异常后执行。

3.6  AspectJAroundAdvice:环绕通知,AspectJ中 around 属性对应的通知(@Around标注的方法会被解析成该通知),在切面方法执行前后执行。

3.7  AspectJPointcutAdvisor:继承自接口PointcutAdvisor,该类用来调整AbstractAspectJAdvice 使其适应PointcutAdvisor接口 。

3.8  AspectJExpressionPointcutAdvisor:用来处理对应 AspectJ 的 advice 和切点的,有advice的设置和获取、切点表达式的一些处理、设置切点的Bean工厂,获取该切点等方法。该类创建了一个 AspectJExpressionPointcut,它们之间的关系是一对一的组合关系。

3.9  DeclareParentsAdvisor:Spring AOP提供的@Before、@After、@AfterReturning、@AfterThrowing、@Around只对类的现有方法进行增强处理。如果需要对现有类增加新的方法,有两种方法可实现:(1)扩展现有类:实现简单,但如果对多个现有类进行扩展时,需增加多个类。(2)使用@DeclareParents注解实现:实现复杂,可使用通配符匹配。

3.10  InstantiationModelAwarePointcutAdvisor:由spring aop顾问实现的接口,封装了可能具有延迟初始化策略的AspectJ 切面。

3.11  AspectJExpressionPointcut:AspectJ表达式切点(通过解析XML配置文件中的<aop:pointcut>元素生成的就是此类型的bean)。它是一种切点,但与一般的切点不同,一般的切点需要持有单独的ClassFilter和MethodMatcher。但是AspectJ表达式切点本身就兼具了这两个组件的功能。因为切点表达式,就是用来描述要代理的目标类和目标方法的。

3.12  MethodInvocationProceedingJoinPoint:AspectJ的ProceedingJoinPoint接口的一个实现类,包装了MethodInvocation。Proceedingjoinpoint接口 继承自 JoinPoint,是在JoinPoint的基础上暴露出 proceed 这个方法。环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的,暴露出这个方法,就能支持 aop:around 这种切面。

3.13  AspectInstanceFactory:切面工厂,实现该接口的工厂用来生成AspectJ切面的一个实例。

3.14  SimpleAspectInstanceFactory:该接口主要用于从给定的类(或者 beanName 等)获取一个切面实例,这是AspectInstanceFactory接口的一个实现类。

3.15  SingletonAspectInstanceFactory:AspectInstanceFactory接口的一个实现类,用来支持单例的对象,对于每次调用getAspectInstance(),都返回同一个切面实例。

3.16  AspectJAopUtils:相比于AopUtils,AspectJAopUtils是专门针对于AspectJ advisors的工具类。

3.17  AspectJProxyUtils:它相对于AopProxyUtils,它只是专门处理AspectJ代理对象的工具类。

3.18  AspectJAdviceParameterNameDiscoverer:从切点表达式、返回值、抛出异常来推断一个AspectJ通知方法的参数名,如果不存在一个明确的推断,返回Null。

3.19  AspectJPrecedenceInformation:存储用来给通知、顾问根据AspectJ的排序规则进行排序用的相关信息。

3.20  AspectJWeaverMessageHandler:实现自AspectJ的IMessageHandler接口,与常规Spring消息一样,使用相同的日志系统来对AspectJ编织消息进行路由。

3.21  RuntimeTestWalker:这个类用来封装一些AspectJ内部的结果,在将来解压中再重新推送到AspectJ项目中。

3.22  TypePatternClassFilter:ClassFilter类过滤器。基于AspectJ的类型匹配实现。

AOP/aspectj/annotation

3.23  AspectJAdvisorFactory:该类主要用于对切面的校验,从切面中解析 Advisor, Advice 等。

3.24  AbstractAspectJAdvisorFactory:作为父类完成了 是否切面、切面校验、方法切面注解获取、切面注解封装、切面参数解析等一些列工作,将核心的解析 Advisors Advice 交给了 ReflectiveAspectJAdvisorFactory。

3.25  ReflectiveAspectJAdvisorFactory:获取 Advisors,从 MetadataAwareAspectInstanceFactory (包含有切面的元数据信息)中获取切面元数据信息,将方法切点解析为对应的 AspectJExpressionPointcut(通过切点表达式),然后封装成 InstantiationModelAwarePointcutAdvisorImpl 返回。

3.26  AspectJProxyFactory:创建AspectJ的AOP对象,用于Spring集成AspectJ的作用,此时,就不需要使用AspectJ特定的编译器了。

3.27  MetadataAwareAspectInstanceFactory:AspectInstanceFactory的子接口,返回与AspectJ annotated相关的AspectMetedata。本来AspectInstanceFactory包含该方法是最好的,但是AspectMetedata仅仅使用Java 5,所以我们需要单独分离出该子接口。

3.28  LazySingletonAspectInstanceFactoryDecorator:简单的装饰,使MetadataAwareAspectInstanceFactory实例化一次。

3.29  SingletonMetadataAwareAspectInstanceFactory:MetadataAwareAspectInstanceFactory的一个实现类,每次调用getAspectInstance()函数时,都会返回同一个实例。

3.30  BeanFactoryAspectInstanceFactory:该类就是AspectInstanceFactory接口的一个实现类,用来生成AspectJ切面的一个实例。需要注意的是如果使用原型模式,实例化多次,可能返回的不是你想要的。使用LazySingletonAspectInstanceFactoryDecorator包装该类,这样就可以确保每次都可以得到一个新的切面实例。

3.31  PrototypeAspectInstanceFactory:多例专用的工厂。

3.32  SimpleMetadataAwareAspectInstanceFactory:MetadataAwareAspectInstanceFactory的一个实现类,每次调用getAspectInstance()函数时,都会创建指定的切面类的实例。

3.33  AspectMetadata:AspectJ切面类的元数据,每个切面附加一个额外的Spring AOP切点。该类有属性:aspectName、aspectClass、ajType(transient修饰,表示该属性不需要序列,序列化对象的时候,这个属性就不会被序列化)、perClausePointcut。

3.34  InstantiationModelAwarePointcutAdvisorImpl:AspectJPointcutAdvisor的内部实现,注意对于每一个目标方法都会有一个该顾问的实例。

3.35  BeanFactoryAspectJAdvisorsBuilder: Spring AOP内部工具类,用来从bean容器也就是BeanFactory中获取所有使用了@AspectJ注解的bean,最终用于自动代理机制(auto-proxying)。

该工具内部使用了缓存机制,虽然公开的查找方法可能会被调用多次,但并不是每次都会真正查找,而是会利用缓存。最核心的逻辑在其方法buildAspectJAdvisors中,该方法查找容器中所有@AspectJ注解的bean,然后将其中每个advice方法包装成一个Spring Advisor。最终结果以一个List<Advisor>的形式返回给调用者。

3.36  AnnotationAwareAspectJAutoProxyCreator:目前最常用的AOP使用方式。spring aop 开启注解方式之后,该类会扫描所有@Aspect()注释的类,生成对应的advisor。目前SpringBoot框架中默认支持的方式,自动配置。

3.37  NotAnAtAspectException:AopConfigException类的拓展,当试图对一个类生成顾问,但是该类又不是AspectJ 注解类型的切面的时候抛出异常。

AOP/aspectj/autoproxy

3.38  AspectJAwareAdvisorAutoProxyCreator:AspectJ的实现方式,也是Spring Aop中最常用的实现方式,如果用注解方式,则用AnnotationAwareAspectJAutoProxyCreator(该类的一个子类)。

3.39  AspectJPrecedenceComparator:排序比较器。它优先比较两个增强器所属的切面大小,如果是同一个切面产生的两个增强器,他们的大小是相同的,则需要拿到它们的声明顺序即InstantiationModelAwarePointcutAdvisorImpl的declarationOrder属性。

4、AOP/config  

4.1  PointcutComponentDefinition:创建一个切点的信息。

4.2  AspectComponentDefinition:创建一个切面的信息,包括了嵌套的切点。

4.3  AdvisorComponentDefinition:创建一个顾问的信息,用来弥合通过<aop:advisor>配置的顾问的bean definition和架构中的部件定义。

4.4  AbstractInterceptorDrivenBeanDefinitionDecorator:BeanDefinitionDecorator(接口)装饰相关的自定义属性。该抽象类继承自BeanDefinitionDecorator,用于注册相应的Interceptor bean 定义。

4.5  ScopedProxyBeanDefinitionDecorator:<aop:scoped-proxy>标签是spring<bean>标签的装饰标签,AOP命名空间的三大标签之一,它的作用是对生命周期短的bean提供装饰,使其能被生命周期长的bean正确调用。该类负责对该标签进行解析。

4.6  PointcutEntry:实现了接口ParseState.Entry,代表一个切点的入口。

ParseState(在包org.springframework.beans.factory.parsing中)在解析进程中作为一个简单的基于栈结构的追踪逻辑位置类。该类中有一个内部标记接口Entry,为了进入ParseState,要实现该内部标记接口。

4.7  AdviceEntry:实现了接口ParseState.Entry,代表一个通知的入口。

4.8  AdvisorEntry:实现了接口ParseState.Entry,代表一个顾问的入口。

4.9  AspectEntry:实现了接口ParseState.Entry,代表一个切面的入口。

4.10  SpringConfiguredBeanDefinitionParser:实现了接口BeanDefinitionParser,专门用来解析<aop:spring-configured/>标签。

4.11  AspectJAutoProxyBeanDefinitionParser:是一个实现了BeanDefinitionParser接口的类,专门用于解析切面自动代理的Bean定义的解析工作,重点在其parse方法。

4.12  ConfigBeanDefinitionParser:用来解析<aop:config />标签,并将标签相应的BeanDefinition注册BeanFactory(DefaultListableBeanFactory)。

4.13  AopConfigUtils:这个是关于AOP配置的工具类。因为配置AOP的方式有多种(比如xml、注解等),此工具类就是针对不同配置,提供不同的工具方法的。它的好处是不管什么配置,最终走底层逻辑都归一了。

4.14  AopNamespaceHandler:AOP命名空间处理器,Spring为了开放性提供了NamespaceHandler机制,这样我们就可以根据需求自己来处理我们设置的标签元素。我们使用基于xml的spring配置时,可能需要配置如<aop:config />这样的标签,在配置这个标签之前,通常我们需要引入这个aop所在的命名空间。只有通过配置aop的命名空间才会找到AOP标签的处理器AopNamespaceHandler,在AOP的jar中的spring.handlers配置文件中配置了命名空间和命名空间处理器之间的关系。

4.15  AopNamespaceUtils:处理Spring AOP命名空间的工具类。

4.16  MethodLocatingFactoryBean:实现了FactoryBean接口,通过调用getObject()方法获取MethodLocatingFactory对象。主要作用是通过Bean的名称和方法名定位到这个Method,然后通过反射进行调用。

4.17  SimpleBeanFactoryAwareAspectInstanceFactory:继承自AspectInstanceFactory(切面实例工厂),该类在bean工厂中通过bean的名字可以定位到切面。

5、AOP/framework 

5.1  AopProxy:代表一个AopProxy代理对象,可以通过这个对象构造代理对象实例。

5.2  AopProxyUtils:对org.springframework.aop.support.AopUtils的一个补充。其中比较重要的方法: completeProxiedInterfaces(判断一个advised真正需要代理的目标接口列表 )和ultimateTargetClass(获取一个代理对象的最终对象类型)。

5.3  ProxyConfig:AOP配置类,是所有的AOP代理工厂的父类,它包含了创建一个AOP代理所需要的基础的通用的一些配置信息。它有五个属性:

proxyTargetClass 是否直接对目标类进行代理,而不是通过接口产生代理;

optimize 标记是否对代理进行优化;

opaque 标记是否需要阻止通过该配置创建的代理对象转换为Advised类型;

exposeProxy 标记代理对象是否应该被aop框架通过AopContext以ThreadLocal的形式暴露出去;

frozen 标记是否需要冻结代理对象,即在代理对象生成之后,是否允许对其进行修改。

5.4  ProxyProcessorSupport:提供为代理创建器提供了一些公共方法实现。

5.5  ProxyCreatorSupport:这个类的主要作用是为创建一个AOP代理对象提供一些功能支持,通过它的getAopProxyFactory能获取一个创建代理对象的工厂。

5.6  ProxyFactory:ProxyFactory继承自ProxyCreatorSupport,使用它来创建一个代理对象也是要先去设置相关的配置信息,然后再调用创建代理的方法。

5.7  ProxyFactoryBean:ProxyFactoryBean的功能:初始化通知器链,获取单例、原型的Aop代理对象。

5.8  AbstractSingletonProxyFactoryBean:FactoryBean的一个超类,主要用来生成singleton-scoped代理对象。

5.9  AopProxyFactory:AopProxy代理工厂类,用于生成代理对象AopProxy。

5.10  DefaultAopProxyFactory  AopProxyFactory:AopProxyFactory默认实现类,核心函数createAopProxy。

5.11  JdkDynamicAopProxy:生成jdk动态代理。可以看到这个类本身就是一个InvocationHandler,这意味着当调用代理对象中的方法时,最终会调用到JdkDynamicAopProxy的invoke方法。

5.12  CglibAopProxy:生成Cglib动态代理。

5.13  ObjenesisCglibAopProxy  :该类继承自CglibAopProxy,重写了createProxyClassAndInstance方法。

objenesis是一个小型Java类库用来实例化一个特定class的对象。Java已经支持使用class.newinstance()的类动态实例化,但是必须要有一个合适的构造函数。而很多场景下类不能够用这种方式去实例化,例如:构造函数需要参数(Constructors that require arguments)、有副作用的构造函数(Constructors that have side effects)、会抛出异常的构造函数(Constructors that throw exceptions)。

因此,常见的是在类库中看到类必须要有一个默认的构造函数的限制,Objenesis旨在通过绕过对象实例化的构造函数来克服这些限制。典型用途:(1)序列化,远程调用和持久化-对象需要被实例化并恢复到特定的状态,而不需要调用代码。(2)代理、 AOP 库和 mock 对象-类可以被子类继承而子类不用担心父类的构造器。(3)容器框架-对象可以以非标准的方式动态地实例化。

5.14  Advised:代表被Advice增强的对象,包括添加advisor的方法、添加advice等的方法。

5.15  AdvisedSupport:ProxyConfig类的子类,封装了AOP中通用的对增强(Advice)和通知器(Advisor)的相关操作,对于所有生成AOP代理对象都通用,而代理对象的生成由子类完成。

5.16  AdvisedSupportListener:监听器,注册在ProxyCreatorSupport对象(用来注册和触发监听器)中,捕获第一个代理创建时和代理创建后通知状态发生改变的事件。

5.17  AopInfrastructureBean:免被AOP代理的标记接口。是一个标记接口。若Bean实现了此接口,表明它是一个Spring AOP的基础类,那么这个类是不会被AOP给代理的,即使它能被切面切进去。

5.18  AbstractAdvisingBeanPostProcessor:继承自BeanPostProcessor,自身内置一个 Advisor,检查当前bean是否符合应用该 Advisor 的条件,符合的话将自己的 Advisor 包裹到当前bean(必要的时候为当前bean创建代理对象以便包裹自己的Advisor)。其中调用了函数AopUtils.canApply(this.advisor, targetClass),用来检查某个 Advisor 是否可应用到某个 bean 上的。

5.19  AopConfigException:AOP配置参数非法时抛出的异常。

5.20  AopContext:代表AOP的上下文。主要是提供我们访问上下文中当前AOP对象的快速方法。

5.21  AdvisorChainFactory:获取增强器链的工厂接口。提供方法返回所有增强器,以数组返回。

5.22  DefaultAdvisorChainFactory:这个工厂类负责生成拦截器链。借助DefaultAdvisorAdapterRegistry将Advisor集合转换成MethodInterceptor集合。

5.23  InterceptorAndDynamicMethodMatcher:框架的内部类,组合MethodInterceptor实例和MethodMatch作为增强器链中的一个元素。

5.24  ReflectiveMethodInvocation:核心类,激发拦截链工作实现。该类实现了aop联盟的MethodInvocation,间接实现了Invocation和Joinpoint。

AOP/framework/adapter

5.25  AdvisorAdapter:一个适配器接口,它定义了自己支持的Advice类型,并且能把一个Advisor适配成MethodInterceptor。

spring aop框架对BeforeAdvice、AfterAdvice、ThrowsAdvice三种通知类型的支持实际上是借助适配器模式来实现的,这样的好处是使得框架允许用户向框架中加入自己想要支持的任何一种通知类型。

5.26  AdvisorAdapterRegistry:顾问适配器注册的接口,这是一个SPI接口,不会被任何Spring用户所实现。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。

5.27  DefaultAdvisorAdapterRegistry:它用来完成各种通知的适配和注册过程。将Advice包装成Advisor(DefaultPointCutAdvisor),借助AdvisorAdapter,将Advisor包装成MethodInterceptor。

5.28  GlobalAdvisorAdapterRegistry:负责拦截器的适配和注册过程。

5.29  AdvisorAdapterRegistrationManager:继承自BeanPostProcessor后置处理器,利用AdvisorAdapterRegistry在Bean工厂中注册AdvisorAdapter(通知适配器)。

5.30  AfterReturningAdviceAdapter:后置通知适配器。

在 Spring 的 Aop 中,适配器模式应用的非常广泛。Spring 使用 Advice(通知)来增强被代理类的功能,Advice 的类型主要有 BeforeAdvice、AfterReturningAdvice、ThrowsAdvice。每种 Advice 都有对应的拦截器,即 MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。各种不同类型的 Interceptor,通过适配器统一对外提供接口。最终调用不同的 advice来实现被代理类的增强。

5.31  AfterReturningAdviceInterceptor:后置通知拦截器。

5.32  MethodBeforeAdviceAdapter:前置通知适配器。

5.33  MethodBeforeAdviceInterceptor:前置通知拦截器。

5.34  ThrowsAdviceAdapter:异常通知适配器。

5.35  ThrowsAdviceInterceptor:异常通知拦截器。

5.36  UnknownAdviceTypeException:当尝试使用一个不支持的顾问或者通知时抛出的异常。

AOP/framework/autoproxy

5.37  AbstractAdvisorAutoProxyCreator:默认扫描所有Advisor的实现类,相对于根据Bean名称匹配,该类更加灵活。动态的匹配每一个类,判断是否可以被代理,并寻找合适的增强类,以及生成代理类。

5.38  AbstractAutoProxyCreator:Spring 为Spring AOP 模块暴露的可扩展抽象类,也是 AOP 中最核心的抽象类。自动代理机制的实现其实很简单,就是通过Bean的后置处理器,在创建Bean的最后一步对Bean进行代理,并将代理对象放入到容器中。实现自动代理的核心类就是AbstractAutoProxyCreator。

它的三个具体的实现类来进行分析,分别是BeanNameAutoProxyCreator、DefaultAdvisorAutoProxyCreator、AnnotationAwareAspectJAutoProxyCreator。

5.39  BeanNameAutoProxyCreator:根据指定名称创建代理对象。通过设置 advisor,可以对指定的 beanName 进行代理。支持模糊匹配。

5.40  DefaultAdvisorAutoProxyCreator:AbstractAdvisorAutoProxyCreator的默认实现类。可以单独使用,在框架中使用AOP,尽量不要手动创建此对象。

5.41  InfrastructureAdvisorAutoProxyCreator       :自动代理创建器,所有的创建器都是AbstractAutoProxyCreator抽象类的子类。该类是Spring给自己内部使用的一个自动代理创建器。它主要是读取Advisor类,并对符合的bean进行二次代理。

5.42  AutoProxyUtils:为自动代理组件准备的工具类。主要用于框架内部使用(AbstractAutoProxyCreator)。

5.43  ProxyCreationContext:当前代理创建的上下文,主要给自动代理创建器使用。比如AbstractAdvisorAutoProxyCreator。

5.44  AbstractBeanFactoryAwareAdvisingPostProcessor:该抽象类定义了这样一类BeanPostProcessor:拥有一个Advisor,对每个bean进行后置处理,如果该bean符合包裹自己所拥有的Advisor的条件,则将该Advisor包裹该bean。

这里将bean和Advisor包裹该bean的又分两种情况:目标bean是Advised,此时直接使用Advised接口定义的方法添加Advisor到目标bean。目标bean不是Advised,此时为目标对象创建代理对象,并将Advisor添加到目标bean的代理对象上。

以上主要逻辑基本实现在其父类AbstractAdvisingBeanPostProcessor 中,而该类主要是在此基础上实现了BeanFactoryAware接口。并覆盖实现父类的方法prepareProxyFactory,isEligible。

5.45  BeanFactoryAdvisorRetrievalHelper:这个类很重要,是一个Spring AOP内部工具类,用来从bean容器(BeanFactory)中获取所有Spring的Advisor bean(这里的 Spring Advisor bean指的是实现了接口org.springframework.aop.Advisor的bean)。是真正去容器中找出所有的Advisor的类。该工具内部使用了缓存机制,虽然公开的查找方法可能会被调用多次,但并不是每次都会真正查找,而是会利用缓存。

5.46  TargetSourceCreator:创建专用的target source。

AOP/framework/autoproxy/target

5.47  AbstractBeanFactoryBasedTargetSourceCreator:TargetSourceCreator的一个超类,对于一个原型bean需要创建多个实例时使用。使用一个内部的bean工厂去管理这些target实例。

5.48  LazyInitTargetSourceCreator:创建的代理对象并没有初始化,直到第一次调用时才进行初始化。

5.49  QuickTargetSourceCreator:根据beanName的不同前缀创建三种常用的TargetSource类型(bean必须为多例)。(1)CommonsPoolTargetSource:池化TargetSource,每次执行方法时从池中取代理对象,执行完方法再返回池中。(2)ThreadLocalTargetSource:线程级的TargetSource。(3)PrototypeTargetSource:多例TargetSource,每次执行方法创建新的代理对象,执行完销毁该对象。

6、AOP/interceptor 

6.1  AbstractTraceInterceptor:MethodInterceptor的实现类,主要用于日志记录。默认情况下,写入日志中的消息是记录拦截器类而不是被拦截的类。当把bean属性开关useDynamicLogger设置为true时,拦截器类和被拦截的类日志都会被记录下来。该抽象类的实现子类必须实现方法invokeUnderTrace。

6.2  SimpleTraceInterceptor:继承自AbstractTraceInterceptor, 该拦截器可以引入拦截器链中,用来显示被拦截的方法调用的详细跟踪信息,包括方法进入和退出的信息。 如果想要更高级的需求,可以考虑使用自定义的跟踪拦截器CustomizableTraceInterceptor。

6.3  CustomizableTraceInterceptor:方法级示踪器。 MethodInterceptor的实现类,使用占位符,来进行自定义的方法层级的示踪器。跟踪在方法入口处消息是否写入,在方法退出时判断方法调用是否成功。如果调用出现异常,那么一个异常消息记录下来。这些跟踪消息和占位符是高度定制化的,你可以将一些运行时信息写入日志系统中。

6.4  DebugInterceptor:这个拦截器可以引入链中用来将拦截的调用的一些详细信息显示到日志记录器中。在进行调试时,可以将方法入口和出口处的一些调用细节详细的记录下来,包括调用的参数和调用的次数。

6.5  ExposeInvocationInterceptor:暴露当前MethodInvocation的拦截器。作为线程本地对象, 我们偶尔需要这样做, 比如切入点时需要知道完整的调用上下文。除非确实有必要,否则不要使用此拦截器。 目标对象应该通常不了解Spring AOP,因为这会产生对Spring API的依赖。目标对象应尽可能是普通的POJO。如果要使用这个拦截器,要将这个拦截器放在拦截链中的开头。

6.6  AbstractMonitoringInterceptor:监控拦截器的基类,比如性能监控器。

6.7  PerformanceMonitorInterceptor:性能监控的拦截器,这个拦截器对于被拦截的方法不会有任何附加的作用。其在实际的性能测量时,使用了简洁的耗时统计小工具org.springframework.util.StopWatch。

6.8  JamonPerformanceMonitorInterceptor:性能监控的拦截器,使用了JAMon库对被拦截的方法和输出的状态信息进行性能的监测。另外它也可以对被拦截的方法抛出的异常进行跟踪计数,这些堆栈跟踪足迹可以在JAMopn的web应用中显示出来。

Jamon的全名是:Java Application Monitor。它是一个小巧的,免费的,高性能的,线程安全的性能监测工具。它可以用来测定系统的性能瓶颈,也可以用来监视用户和应用程序之间的交互情况。 Jamon主要是用来检测jee的应用程序。

6.9  ConcurrencyThrottleInterceptor:继承自ConcurrencyThrottleSupport,spring控制并发数的工具类。在ConcurrencyThrottleSupport类中,简单的通过synchronized和wati and notify达到控制线程数量的效果,从而实现限流的策略。

该拦截器中的invoke()方法中,在执行目标方法的前后分别执行beforeAccess()和 afterAccess()方法。在beforeAccess方法中通过内部计数器concurrencyCount来对比设置的阀值concurrencyLimit,如果超过设置值,则阻塞;若没有超过设置值,则concurrencyCount自加。在afterAccess方法中自减concurrencyCount。

6.10  AsyncExecutionAspectSupport:异步任务执行切面的一个基类。核心方法determineAsyncExecutor(),返回一个执行异步任务的线程池AsyncTaskExecutor。

6.11  AsyncExecutionInterceptor:异步任务选择执行器。其核心方法是invoke,主要流程;(1)获取拦截的方法。(2)根据被拦截的方法来选取执行异步任务的执行器。(3)构建任务(添加异常的处理方式)。(4)执行构建的任务并返回任务执行结果。

6.12  AsyncUncaughtExceptionHandler:异步任务执行抛出的异常catch不到时通过此接口进行处理。一个异步任务通常会返回一个java.util.concurrent.Future实例,以访问潜在的异常,当这个异步任务不提供这个返回值时,这个句柄就负责处理此类捕获不到的异常。

6.13  SimpleAsyncUncaughtExceptionHandler:AsyncUncaughtExceptionHandler接口的一个默认实现,用来简单记录这个异常。

6.14  ExposeBeanNameAdvisors:当Spring IOC容器创建自动代理bean时可以使用,使得创建顾问更加便捷,将bean name绑定到当前的调用。通常在spring自动代理中使用,在代理创建时,bean的名称就是已知的。

7、AOP/scope   

7.1  ScopedObject:用于作用域对象的AOP引介接口。ScopedProxyFactoryBean创建的对象可以转换到这个接口,能够得到原始的目标对象,从它的目标scopt中剥离出该对象。Spring的Bean是有scope属性的,表示bean的生存周期。scope的值有prototype、singleton、session、request。

7.2  DefaultScopedObject:ScopedObject接口的默认实现。

7.3  ScopedProxyFactoryBean:便捷的代理工厂bean,用于作用域对象。被这个工厂bean创建的代理是单例的,线程安全,可以会被注入到共享的对象中。

7.4  ScopedProxyUtils:创建作用域代理的一些功能性类。主要被ScopedProxyBeanDefinitionDecorator 和 ClassPathBeanDefinitionScanner使用。

8、AOP/support

8.1  AbstractExpressionPointcut:表达式切点类型的抽象超类,提供定位和表达式两个属性。

8.2  ExpressionPointcut:表达式切点类型,通过表达式匹配,用于支持AspectJ的表达式。

8.3  NameMatchMethodPointcut:名称匹配切面,通过指定方法集合变量mappedNames,模糊匹配。

8.4  DynamicMethodMatcherPointcut    :动态方法匹配器切点。它本质上是一个方法匹配器,但同时具有了切点的功能。

8.5  StaticMethodMatcherPointcut:静态方法切面,抽象类。定义了一个classFilter,通过重写getClassFilter()方法来指定切面规则。另外实现了StaticMethodMatcher接口,通过重写matches来指定方法匹配规则。子类:NameMatchMethodPointcut(简单字符串匹配方法签名)和 AbstractRegexpMethodPointcut(正则表达式匹配方法签名)。

8.6  AbstractRegexpMethodPointcut:正则表达式匹配方法签名。

8.7  JdkRegexpMethodPointcut:JDK正则表达式切点,即使用正则表达式描述方法的拦截规则和排除规则。

8.8  ControlFlowPointcut:流程切点。

8.9  ComposablePointcut:复合切点。这种切点可以与或逻辑,任意组合其他的Pointcut、ClassFilter和MethodMatcher。其本质是通过ClassFilters和MethodMatchers两个工具类进行Pointcut内部组件的组合。

8.10  AbstractPointcutAdvisor:PointcutAdvisor接口的抽象基类,其子类可以返回指定的切点/通知,或者是一个可自由配置的切点/通知。

8.11  AbstractGenericPointcutAdvisor:一般的、通用的PointcutAdvisor。

8.12  DefaultPointcutAdvisor:默认切面顾问,比较灵活。可自由组合切面和通知。

8.13  NameMatchMethodPointcutAdvisor:方法名称切面顾问,内部封装了NameMatchMethodPointcut,通过设置方法名称模糊匹配规则和通知来实现切面功能。

8.14  StaticMethodMatcherPointcutAdvisor:静态方法匹配切面顾问,扩展了切面排序方法。

8.15  RegexpMethodPointcutAdvisor:正则表达式切面顾问,可设置多个正则表达式规则,通过内部封装的JdkRegexpMethodPointcut解析正则表达式。

8.16  DefaultIntroductionAdvisor:默认的引介通知器,它是一种通知器,但同时兼具了类过滤器的功能,且matches总返回true。它的作用是给所有bean追加指定接口。

8.17  AbstractBeanFactoryPointcutAdvisor:基于Bean工厂的切点增强器,允许任何通知配置成指向在bean工厂中的通知bean。通过指定通知bean的名字而不是通知对象本身,在初始化时可以降低耦合度,可以切点真正匹配的时候再去初始化通知对象。

8.18  DefaultBeanFactoryPointcutAdvisor:AbstractBeanFactoryPointcutAdvisor抽象类的实现。基于BeanFactory的PointAdvisor,

8.19  AopUtils:该工具类是Spring非常重要的一个工具类。一个外部工具类,我们平时若想要对AOP做一些判断、处理,可使用此工具类。

8.20  Pointcuts:提供了一些静态方法,在操作切点时有用,包括matches、SetterPointcut、GetterPointcut。

8.21  ClassFilters:针对ClassFilter,还有一个工具类——ClassFilters。ClassFilters内部定义了两个私有的静态内部类:IntersectionClassFilter和UnionClassFilter,分别支持以与的逻辑和或的逻辑组合多个ClassFilter。此工具类同时对外提供了组合ClassFilter的API。

8.22  RootClassFilter:通过判断目标类是否是指定类型(或其子类型),决定是否匹配。

8.23  MethodMatchers:同ClassFilters一样,是一个工具类。

8.24  DynamicMethodMatcher:动态方法匹配器的一个抽象超类,主要用于关注运行期间的参数。

8.25  StaticMethodMatcher:静态方法匹配的一个抽象超类,它并不关系运行时期的参数。

8.26  IntroductionInfoSupport:描述一个引介需要的基本信息接口的增强功能,其实现的子类可以方便的从一个指定的对象所实现的所有接口提取出来,将不应该加入的接口排除掉,同时也可以查询所有引入的接口。

8.27  DelegatePerTargetObjectIntroductionInterceptor:引介可以看成一种特殊的Advice,在没有改变原先类的定义的情况下为其增加新的方法。这中通知是spring-aop自己定义的,所以没有类似 @Before 这种配置可以直接注入advice到spring容器中,故此需要自己实现 DynamicIntroductionAdvice 这个接口来进行相应逻辑处理,又由于spring没有对 DynamicIntroductionAdvice 的关于MethodInterceptor的适配器,故此我们需要实现的其实是 IntroductionInterceptor 这个接口。DelegatingIntroductionInterceptor 和DelegatePerTargetObjectIntroductionInterceptor 就是接口IntroductionInterceptor的两个实现类。

8.28  DelegatingIntroductionInterceptor:接口IntroductionInterceptor的一个实现类,这个类就是一个委托引入拦截器,具体解释同上。

AOP/support/annotation

8.29  AnnotationClassFilter:通过检查目标类是否存在指定的注解,决定是否匹配

8.30  AnnotationMatchingPointcut:注解匹配切点,JDK5以后,通过注解方式声明切点。根据类上或方法上是否存在指定的注解判断切点的匹配性,如果没有显示指定注解,则匹配所有。

8.31  AnnotationMethodMatcher:注解匹配。寻找指定的Java 5的注解进行简单的方法匹配。

9、AOP/target   

9.1  AbstractBeanFactoryBasedTargetSource:此类目标源基于IoC容器实现,也就是说target目标对象可以通过beanName从容器中获取。此类又扩展出:(1)SimpleBeanTargetSource:简单实现,直接调用getBean从容器获取目标对象;(2)LazyInitTargetSource:延迟初始化目标源,子类可重写postProcessTargetObject方法后置处理目标对象;(3)AbstractPrototypeBasedTargetSource:原型bean目标源,此抽象类可确保beanName对应的bean的scope属性为prototype。其子类做了简单原型、池化原型、线程隔离原型这3种实现。

9.2  SimpleBeanTargetSource:AbstractBeanFactoryBasedTargetSource简单实现,直接调用getBean从容器获取目标对象。

9.3  EmptyTargetSource:静态目标源,当不存在target目标对象,或者甚至连targetClass目标类都不存在(或未知)时,使用此类实例。

9.4  HotSwappableTargetSource:动态目标源,支持热替换的目标源,支持spring应用运行时替换目标对象。

9.5  AbstractLazyCreationTargetSource:此类实现在调用getTarget()获取时才创建目标对象。

9.6  LazyInitTargetSource:延迟初始化目标源,子类可重写postProcessTargetObject方法后置处理目标对象。

9.7  SingletonTargetSource:该接口代表一个目标对象,在aop调用目标对象的时候,使用该接口返回真实的对象。Singleton表示每次调用返回同一个实例。

9.8  AbstractPrototypeBasedTargetSource:原型bean目标源,此抽象类可确保beanName对应的bean的scope属性为prototype。其子类做了简单原型、池化原型、线程隔离原型这3种实现。

9.9  PrototypeTargetSource:该接口代表一个目标对象,在aop调用目标对象的时候,使用该接口返回真实的对象。Prototype表示每次调用返回每次调用返回一个新的实例。

9.10  ThreadLocalTargetSource:将获取的bean存储在ThreadLocal ,它在releaseTarget的操作的时候不会从ThreadLocal中释放对应的target。

9.11  ThreadLocalTargetSourceStats:为ThreadLocal目标源做一些统计工作。

9.12  AbstractPoolingTargetSource:这个抽象类实现了PoolConfig,表示是池类的targetSource 。

9.13  PoolingConfig:池类目标对象的配置接口。比如返回池子的最大容量,统计池子中活跃的对象数目,统计池子中空闲的对象数目。

9.14  CommonsPool2TargetSource:利用apache的ObjectPool来存储对应的target对象,它实现了PooledObjectFactory,这个池中的对象还是从beanFactory中获取。

AOP/target/dynamic

9.15  Refreshable:会被动态目标对象实现的接口,支持通过重新载入,轮询进行刷新。

9.16  AbstractRefreshableTargetSource:可刷新的目标源。此类实现可根据配置的刷新延迟时间,在每次获取目标对象时自动刷新目标对象。

9.17  BeanFactoryRefreshableTargetSource:可刷新的目标源。其target bean是从bean工厂中获取的,每次刷新周期到来时都会自动进行刷新。其子类也可以重写方法requiresRefresh(),把一些不必要的刷新给取消掉。

三、JDK动态代理

Spring默认采取的动态代理机制实现AOP, 简单来说就是在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。动态代理技术包括Java动态代理和CGLIB动态代理,前者基于接口实现,后者基于类实现。Spring默认采取的Java动态代理机制实现AOP。在介绍Spring AOP原理之前先介绍Java动态代理。

(一)什么是代理

编程中有个思想,不要随意的去修改别人已经写好的代码或者方法。但是如果我想在原有实现的基础上增加额外的功能呢,即对目标对象的功能进行拓展。比如在执行某个方法的时候记录下日志,这个时候就可以使用代理。

代理是一种常见的设计模式,其目的就是为目标对象提供一个代理对象以控制对目标对象的访问,即通过代理对象访问目标对象。其中代理对象是目标对象的拓展,并且会使用到目标对象。

代理模式又分为静态代理和动态代理。静态代理简单来说就是代理类和目标类(委托类)实现同一个接口,然后代理类中引入对目标类对象的引用。目的:这样可以实现一些其他功能,但是不会让目标类变得膨胀。缺点:这样必须为目标对象创建一个实现了相同接口的代理对象,并且代理对象中的方法也要和目标对象保持一致。一旦目标对象改动了,代理对象也要变更相应的代码。这样就出现了大量的重复代码,增加了代码的维护复杂度。而且这种方法代理对象只能服务于一种类型的目标对象,如果要服务多个类型的对象,则需要为每一种对象都进行代理。举例:比如我想在调用具体实现类前后打印日志等信息,在不修改已有代码的情况下,我们只需要增加一个代理类,在代理类中增加打印日志的功能,然后去调用目标类,这样就可以避免修改目标类。在创建代理对象时,通过构造器塞入一个目标对象,然后在代理对象的方法内部调用目标对象同名方法,并在调用前后打印日志。也就是说,代理对象=目标对象+增强代码,有了代理对象之后,就不用原对象了。但是如果想让多个目标类都添加打印日志功能,那么就需要增加多个代理类,代理类中各个方法都要增加打印日志功能,这就不堪负重了。

动态代理:上面我们知道静态代理每个代理类只能为一个接口服务,这样程序中就会出现很多代理类,动态代理可以通过一个代理类完成全部的代理功能。这个代理类是在运行时候动态生成的,是通过反射机制动态创建(反射机制是指在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意属性和方法。)这样不需要为每一个接口写一个代理类,避免一个类对应一个代理的问题,大大提高了系统的灵活性,减少重复,降低了代码维护的复杂性和成本。Java动态代理机制以巧妙的方式实践了代理模式的设计理念。

Java动态代理:Java的动态代理是基于接口的,代理类和目标类实现相同的接口,这样他们行为保持了一致性,在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效的对委托类的对象的直接访问,可以很好的隐藏和保护委托对象,同时为实施不同的策略预留空间。

(二)Java的动态代理类

Java动态代理机制中有两个重要的接口和类:接口InvocationHandler()和类Proxy(),位于java.lang.reflect包中,这是实现动态代理的核心。

(1)接口InvocationHandler:动态代理类的调用处理程序

InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,该接口中仅定义了一个方法:

public Object invoke(Object proxy, Method method, Object[] args)

其中:第一个参数proxy表示执行这个方法的代理对象,method表示目标对象实际需要执行的方法,args表示目标对象实际要执行的方法所需要的参数。

每一个proxy代理实例都要有一个关联的调用处理程序,该调用处理程序都必须实现InvocationHandler接口。所以在实际编程中,需要先定义一个实现了InvocationHandler接口的调用处理器对象,然后将它作为创建代理类实例的参数(见Proxy类的newProxyInstance()方法)。

(2)Proxy:动态代理类

Proxy类就是用来创建一个代理对象的类,最常用的方法是static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)。这个方法的作用就是创建一个代理对象,其中三个参数为:

1、ClassLoader loader:指定当前目标对象使用类加载器,对于不同来源(系统库或网络等)的类需要不同的类加载器来加载,这是Java安全模型的一部分。

2、Class[] interfaces:一个interface对象数组,目标对象实现的接口的类型。表示我们要给代理对象提供什么样的接口。如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。

3、InvocationHandler h:一个InvocationHandler调用处理程序对象,它必须是实现了InvocationHandler接口的对象,作用就是定义代理对象中需要执行的具体操作。当执行目标对象的方法时,会关联到一个InvocationHandler对象上,从而触发调用处理程序的方法,会把当前执行目标对象的方法作为参数传入,并最终调用h中的invoke()方法。

其实动态代理类可以看做是这样一种类:它是在运行时生成的类,在生成它时你必须提供一组接口给它,然后该类就宣称它实现了这些接口。你当然可以把该类的实例当做这里接口中的任何一个来用。当然,这个动态代理类其实就是一个代理,它不会替你做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。在实际使用代理类的时候,我们必须实现InvocationHandler接口。这样目标对象、需要控制的接口和控制方式都可以动态改变,从而实现灵活的动态代理关系。

(三)动态代理的步骤

1、创建目标类(委托类)的接口

这里定义了两个接口,interface IBusiness1 和interface IBusiness2,各包含一个方法。

interface IBusiness1:

1 package com.dynamicproxy;
2
3 public interface IBusiness1 {
4 public void doSomeThing1() ;
5 }

interface IBusiness2:

1 package com.dynamicproxy;
2
3 public interface IBusiness2 {
4 public void doSomeThing2() ;
5 }

2、创建目标类(委托类)

创建一个目标类Business,实现这两个接口。

 1 package com.dynamicproxy;
2
3 public class Business implements IBusiness1, IBusiness2 {
4 @Override
5 public void doSomeThing1() { System.out.println("执行业务逻辑1"); }
6
7 @Override
8 public void doSomeThing2() {
9 System.out.println("执行业务逻辑2");
10 }
11 }

3、定义一个代理类的调用处理程序。该程序必须实现接口InvocationHandler,且必须实现接口的invoke方法。

InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被转发到调用处理程序的invoke方法。

 1 package com.dynamicproxy;
2
3 import java.lang.reflect.Method;
4 import java.lang.reflect.InvocationHandler;
5
6 public class LogInvocationHandler implements InvocationHandler{
7 //目标对象
8 private Object target;
9
10 //构造函数,给目标对象赋值
11 LogInvocationHandler(Object target) {
12 this.target = target;
13 }
14
15 @Override
16 /**
17 * proxy:代理类
18 * method:被代理的方法
19 * args:该方法的参数数组
20 */
21 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
22 //在真实的对象执行之前添加自己的操作
23 System.out.println("日志: 方法" + method.getName() + "即将执行");
24 //执行原有逻辑
25 Object rev = method.invoke(target, args);
26 //在真实的对象执行之后添加自己的操作
27 System.out.println("日志: 方法" + method.getName() + "执行完毕");
28
29 return rev;
30 }
31 }

4、通过Proxy的静态方法newProxyInstance()创建一个代理对象。

Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。这个方法的作用就是创建一个代理类对象。该方法有三个参数,三个参数具体解释见上述。

 1 package com.dynamicproxy;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Proxy;
5
6 public class Test {
7 public static void main(String[] args) {
8 //要代理的目标对象
9 Business myBusiness = new Business();
10 //代理类要实现的接口列表
11 Class[] proxyInterface = myBusiness.getClass().getInterfaces();
12
13 //代理对象的调用处理程序。我们将要代理的目标对象传入代理对象的调用处理的构造函数中,代理对象的调用处理程序最终会调用目标对象的方法
14 LogInvocationHandler handler = new LogInvocationHandler(myBusiness);
15
16 /**
17 * 通过Proxy类的newProxyInstance方法创建代理对象
18 * 第一个参数:handler.getClass().getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
19 * 第二个参数:proxyInterface,这里为代理类提供的接口是目标对象实现的接口,这样代理对象就能像目标对象一样调用接口中的所有方法
20 * 第三个参数:handler,我们将代理对象关联到上面的InvocationHandler对象上
21 */
22 IBusiness1 proxyBusiness = (IBusiness1) Proxy.newProxyInstance(handler.getClass().getClassLoader(), proxyInterface, handler);
23
24 //使用代理类的实例来调用方法。
25 proxyBusiness.doSomeThing1();
26 ((IBusiness2) proxyBusiness).doSomeThing2();
27 }
28 }

5、通过代理对象调用委托类对象的方法。

其实Proxy类只是一个连接桥,把代理(InvocationHandler)与被代理类关联起来,真正处理事情的是InvocaHandler。InvocationHandler接口中的invoke方法在代理类中是动态实现的,当我们通过动态代理调用一个方法的时候,这个方法的调用会被转发到到调用处理程序的invoke方法中。

1 //使用代理类的实例来调用方法。
2 proxyBusiness.doSomeThing1();
3 ((IBusiness2) proxyBusiness).doSomeThing2();

运行结果:

动态代理的优势就是可以很方便的对目标类的函数进行统一的处理,而不用修改每个目标类的方法。所有被代理执行的方法在调用执行的时候,其方法只是作为参数传入InvocationHandler中的invoke方法,实际是在invoke方法中处理的,这样通过在invoke方法中我们可以对所有被代理的方法进行相同的增强操作。

四、Spring AOP的实现原理

下面Spring AOP实现流程图:

(一)标签的解析

首先我们来看下AOP常用的两种实现方式,一种是采用声明的方式来实现(基于XML),一种是采用注解的方式来实现(基于AspectJ)。下面举例说明:

方式一:xml方式配置

第一步:创建被加强类

1 package cn.ytk.dao;
2 import org.springframework.stereotype.Repository;
3
4 @Repository
5 public class UserDao {
6 public void sys() {
7 System.out.println("userDao.....");
8 }
9 }

第二步:创建增强类

 1 package cn.ytk.strong;
2
3 import org.aspectj.lang.ProceedingJoinPoint;
4
5 public class Project {
6 public void before1() {
7 System.out.println("前置通知。。。。。。。。。");
8 }
9 //环绕通知
10 public void around(ProceedingJoinPoint point) throws Throwable {
11 System.out.println("环绕通知前。。。。。");
12 point.proceed();
13 System.out.println("环绕通知后。。。。。。");
14 }
15
16 public void after1() {
17 System.out.println("后置通知。。。。。。。。。");
18 }
19 }

第三步:配置切点和切面

 1 <!-- spring bean的配置 -->
2 <bean id="userDao" class="cn.ytk.dao.UserDao"></bean>
3 <bean id="userService" class="cn.ytk.service.UserService">
4 <property name="userDao" ref="userDao"></property>
5 </bean>
6 <bean id="project" class="cn.ytk.strong.Project"></bean>
7
8 <aop:config>
9 <aop:pointcut expression="execution(* cn.ytk.dao.UserDao.*(..))" id="pointcut1"/>
10 <aop:aspect ref="project">
11 <aop:before method="before1" pointcut-ref="pointcut1"/>
12 <aop:after-returning method="after1" pointcut-ref="pointcut1"/>
13 <aop:around method="around" pointcut-ref="pointcut1"/>
14 </aop:aspect>
15 </aop:config>

方式二:通过Spring AOP注解实现

第一步:配置spring文件,开启aop注解

1 <!-- 开启aop的注解     -->
2 <aop:aspectj-autoproxy/>
3 <context:component-scan base-package="cn.ytk.*"></context:component-scan>

第二步:编写增强类

 1 package cn.ytk.strong;
2
3 import org.aspectj.lang.ProceedingJoinPoint;
4 import org.aspectj.lang.annotation.AfterReturning;
5 import org.aspectj.lang.annotation.Aspect;
6 import org.aspectj.lang.annotation.Before;
7 import org.aspectj.lang.annotation.Pointcut;
8 import org.springframework.stereotype.Component;
9
10 @Component
11 @Aspect
12 public class Project2 {
13 //方式1:
14 @Before(value="execution(* cn.ytk.dao.UserDao.*(..))")
15 public void before() {
16 System.out.println("前置通知。。。。。。");
17 }
18
19 //方式2:先编写切点在将切点加到加强上。
20 @Pointcut("execution(* cn.ytk.dao.*.*(..))")
21 public void after() {}
22
23 @AfterReturning("after()")
24 public void after1() {
25 System.out.println("....后置通知....");
26 }
27 }

从示例中看出,XML方式中使用AOP技术的需要做以下几步:

1)开启AOP注解,<aop:aspectj-autoproxy/>

2)定义切面类,使用@Aspect注解  

3)在切面类上加设注解@Component

   4)在切面类中定义切点方法,使用@PointCut注解

   5)在切面类中定义通知方法,使用@Before、@After、@Around等注解

   6)在通知方法的注解中使用切点方法

声明了自定义的注解,一定会在程序的某个地方注册对应的解析器。<aop:aspectj-autoproxy/>注解使用AspectJAutoProxyBeanDefinitionParser解析器进行解析。这个对应关系在AopNamespaceHandler(package org.springframework.aop.config)类中指定。

1     public void init() {
2 // In 2.0 XSD as well as in 2.1 XSD.
3 registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
4 registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
5 registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
6
7 // Only in 2.0 XSD: moved to context namespace as of 2.1
8 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
9 }

接着来看下AspectJAutoProxyBeanDefinitionParser(package org.springframework.aop.config)解析器的代码。它是一个实现了BeanDefinitionParser接口的类,专门用于解析切面自动代理的Bean定义的解析工作,重点在其parse方法。

1     @Override
2 @Nullable
3 public BeanDefinition parse(Element element, ParserContext parserContext) {
4 //注册AnnotationAwareAspectJAutoProxyCreator
5 AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
6 //对注解中子类的处理
7 extendBeanDefinition(element, parserContext);
8 return null;
9 }

其中调用了AopNamespaceUtils类(处理Spring AOP命名空间的工具类,package org.springframework.aop.config)的registerAspectJAnnotationAutoProxyCreatorIfNecessary函数。我们进入此函数看一下代码逻辑:

 1     public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
2 ParserContext parserContext, Element sourceElement) {
3 //注册或者升级AutoProxyCreator定义beanName为
4 // org.Springframework.aop.config.internalAutoProxyCreator的BeanDefinition
5 BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
6 parserContext.getRegistry(), parserContext.extractSource(sourceElement));
7 //对于proxy-target-class以及expose-proxy属性的处理
8 useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
9 //注册组件并通知,便于监听器进一步处理,其中BeanDefinition的className
10 // 为AnnotationAwareAspectJAutoProxyCreator
11 registerComponentIfNecessary(beanDefinition, parserContext);
12 }
上述代码一共三行代码,每行代码完成一件事情,都是一个完整的逻辑。

第一行代码:BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(                      parserContext.getRegistry(), parserContext.extractSource(sourceElement));

注册或者升级AnnotatonAwareAspectJAutoProxyCreator。

我们进入AopConfigUtils类(package org.springframework.aop.config)的registerAspectJAnnotationAutoProxyCreatorIfNecessary函数看看:

1     @Nullable
2 public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
3 BeanDefinitionRegistry registry, @Nullable Object source) {
4
5 return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
6 }

上面这个函数又实际调用了本类中的registerOrEscalateApcAsRequired函数,我们继续查看该函数的实现逻辑:

 1     @Nullable
2 private static BeanDefinition registerOrEscalateApcAsRequired(
3 Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
4
5 Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
6
7 //如果已经存在了自动代理创建器且存在的自动代理创建器与现在的不一致,
8 // 那么需要根据优先级判断到底使用哪个
9 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
10 BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
11 if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
12 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
13 int requiredPriority = findPriorityForClass(cls);
14 if (currentPriority < requiredPriority) {
15 //改变bean最重要的就是改变bean所对应的className属性
16 apcDefinition.setBeanClassName(cls.getName());
17 }
18 }
19 //如果已经存在自动代理创建器且与将要创建的一致,那么无需再次创建
20 return null;
21 }
22
23 RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
24 beanDefinition.setSource(source);
25 beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
26 beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
27 registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
28 return beanDefinition;
29 }

对于Aop的实现,基本都是靠AnnotatonAwareAspectJAutoProxyCreator去完成的,它可以根据@Point注解定义的切点来自动代理相匹配的bean。但是为了配置简便,Spring使用了自定义的配置帮助我们自动注册AnnotatonAwareAspectJAutoProxyCreator。其注册过程就是在上述函数中实现的。上述代码实现了自动注册AnnotatonAwareAspectJAutoProxyCreator类的功能,同时存在优先级问题,如果已经存在了自动代理创建器,而且存在的与现在的不一致,那么需要根据优先级判断到底使用哪个。

第二行代码:useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);

处理proxy-target-class和expose-proxy属性,进入该函数:

 1     private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
2 if (sourceElement != null) {
3 //对应proxy-target-class属性的处理
4 boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
5 if (proxyTargetClass) {
6 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
7 }
8 //对应expose-proxy属性的处理
9 boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
10 if (exposeProxy) {
11 AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
12 }
13 }
14 }

proxy-target-class和expose-proxy属性解释:

(1)proxy-target-class属性

Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理。如果被代理的目标对象至少实现了一个接口,则使用JDK动态代理,所有该目标类型实现的接口都将被代理。如果该目标对象没有实现任何接口,则创建一个CGLIB代理。也可以强制使用CGLIB代理,强制使用CGLIB代理需要将<aop:config>的proxy-target-class属性设置为true:<aop:config proxy-target-class=”true”>…</aop:config>

当需要使用CGLIB代理和@AspectJ自动代理支持,可以按照下面的方式设置<aop:aspectj-autoproxy>的proxy-target-class属性:<aop:aspectj-autoproxy proxy-target-class=”true”>。

(2)expose-proxy属性

Spring AOP无法拦截内部方法调用,比如一个接口里面有两个方法:doSomething1()和doSomething2()。然后在方法1中调用了方法2:this.doSomething2()。此处this指向目标对象,因此调用this.doSomething2()将不会执行doSomething2的增强。(也就是切面只会对doSomething1方法进行增强,但是不会对doSomething2进行增强)。

解决方法:this. doSomething2 ()修改为 ((AService) AopContext.currentProxy()).doSomething2 ()。同时修改Spring AOP的配置:<aop:aspectj-autoproxy expose-proxy="true" />

第三行代码:registerComponentIfNecessary(beanDefinition, parserContext);

注册组件并通知,便于监听器进一步处理。

(二)获取增强方法或者增强器

上面通过自定义配置完成了对AnnotatonAwareAspectJAutoProxyCreator(package org.springframework.aop.aspectj.annotation中)的自动注册。

AnnotatonAwareAspectJAutoProxyCreator类:SpringBoot框架中默认支持的方式。spring aop 开启注解方式之后,该类会扫描所有@Aspect()注释的类,生成对应的advisor。

AnnotationAwareAspectJAutoProxyCreator的继承关系如下:

      
 AnnotationAwareAspectJAutoProxyCreator

      
     --AspectJAwareAdvisorAutoProxyCreator

      
         --AbstractAdvisorAutoProxyCreator

      
           
 --AbstractAutoProxyCreator

      
               
 -- ProxyProcessorSupport;

                           SmartInstantiationAwareBeanPostProcessor;

                           BeanFactoryAware;

        其中

        ProxyProcessorSupport

      
     --ProxyConfig;

             
Ordered;

             
BeanClassLoaderAware;

              AopInfrastructureBean;

SmartInstantiationAwareBeanPostProcessor

      
     --InstantiationAwareBeanPostProcessor

      
         --BeanPostProcessor

BeanFactoryAware

      
     --Aware

AnnotatonAwareAspectJAutoProxyCreator间接实现了BeanPostProcessor接口,所以当Spring加载这个Bean的时候会在实例化之前调用postProcessAfterInitialization这个方法。一直往父类寻找,在其父类AbstractAutoProxyCreator(package
org.springframework.aop.framework.autoproxy)实现了该方法。

 1     @Override
2 public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
3 if (bean != null) {
4 //根据给定的bean的class和name构建出key,格式:beanClassName_beanName
5 Object cacheKey = getCacheKey(bean.getClass(), beanName);
6 if (this.earlyProxyReferences.remove(cacheKey) != bean) {
7 //一个非常核心的方法:wrapIfNecessary(),如果它适合被代理,则需要封装指定的bean。
8 return wrapIfNecessary(bean, beanName, cacheKey);
9 }
10 }
11 return bean;
12 }

其有一个非常核心的方法:wrapIfNecessary()。继续进入该类中的wrapIfNecessary方法实现逻辑:

 1     /**
2 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
3 * @param bean the raw bean instance
4 * @param beanName the name of the bean
5 * @param cacheKey the cache key for metadata access
6 * @return a proxy wrapping the bean, or the raw bean instance as-is
7 */
8 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
9 //如果已经处理过
10 if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
11 return bean;
12 }
13 //这个bean无需增强
14 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
15 return bean;
16 }
17 //判断给定的bean是否是一个基础设施类,基础设施类不应代理,或者配置了指定bean不需要代理。
18 //所谓InfrastructureClass就是指Advice/PointCut/Advisor等接口的实现类。
19 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
20 this.advisedBeans.put(cacheKey, Boolean.FALSE);
21 return bean;
22 }
23
24 // 如果存在增强方法则创建代理
25 //获取这个bean的advice
26 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
27 //如果获取到了增强则需要针对增强创建代理
28 if (specificInterceptors != DO_NOT_PROXY) {
29 this.advisedBeans.put(cacheKey, Boolean.TRUE);
30 //创建代理
31 Object proxy = createProxy(
32 bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
33 this.proxyTypes.put(cacheKey, proxy.getClass());
34 return proxy;
35 }
36
37 this.advisedBeans.put(cacheKey, Boolean.FALSE);
38 return bean;
39 }

从上述代码中我们看到了代理创建的雏形。创建代理主要包含了两个步骤:1、获取增强方法或者增强器。2、根据获取的增强进行代理。

下面我们先来看下获取增强方法或者增强器:

AbstractAutoProxyCreator类的wrapIfNecessary方法中调用了getAdvicesAndAdvisorsForBean,AbstractAutoProxyCreator类只对该方法进行定义,真正实现在其子类AbstractAdvisorAutoProxyCreator(package org.springframework.aop.framework.autoproxy)中实现。

1     protected Object[] getAdvicesAndAdvisorsForBean(
2 Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
3
4 List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
5 if (advisors.isEmpty()) {
6 return DO_NOT_PROXY;
7 }
8 return advisors.toArray();
9 }

上面方法实现又调用了该类中的findEligibleAdvisors方法,进入其代码:

1     protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
2 List<Advisor> candidateAdvisors = findCandidateAdvisors();
3 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
4 extendAdvisors(eligibleAdvisors);
5 if (!eligibleAdvisors.isEmpty()) {
6 eligibleAdvisors = sortAdvisors(eligibleAdvisors);
7 }
8 return eligibleAdvisors;
9 }

里面有两个函数findCandidateAdvisors和findAdvisorsThatCanApply。这两个方法就是来获取所有的增强以及寻找增强中适用于bean的增强并应用。

(1) findCandidateAdvisors方法(获取所有的增强)
   我们首先进入findCandidateAdvisors看下:
1     protected List<Advisor> findCandidateAdvisors() {
2 Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
3 return this.advisorRetrievalHelper.findAdvisorBeans();
4 }

方法中调用了BeanFactoryAdvisorRetrievalHelper类(package org.springframework.aop.framework.autoproxy中)的findAdvisorBeans方法。

BeanFactoryAdvisorRetrievalHelper这个类是一个Spring AOP内部工具类,用来从bean容器中获取所有Spring的Advisor bean(这里的 Spring Advisor bean指的是实现了接口org.springframework.aop.Advisor的bean)。是真正去容器中找出所有的Advisor的类。该工具内部使用了缓存机制,虽然公开的查找方法可能会被调用多次,但并不是每次都会真正查找,而是会利用缓存。

 1     public List<Advisor> findAdvisorBeans() {
2 //cachedAdvisorBeanNames是advisor名称的缓存
3 String[] advisorNames = this.cachedAdvisorBeanNames;
4 //如果cachedAdvisorBeanNames为空,则到容器中查找,并设置缓存,后续直接使用缓存即可
5 if (advisorNames == null) {
6 // Do not initialize FactoryBeans here: We need to leave all regular beans
7 // uninitialized to let the auto-proxy creator apply to them!
8 //从容器中查找Advisor类型的bean的名称
9 advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
10 this.beanFactory, Advisor.class, true, false);
11 this.cachedAdvisorBeanNames = advisorNames;
12 }
13 if (advisorNames.length == 0) {
14 return new ArrayList<>();
15 }
16
17 List<Advisor> advisors = new ArrayList<>();
18 //遍历advisorNames
19 for (String name : advisorNames) {
20 if (isEligibleBean(name)) {
21 //忽略郑州创建中的advisor bean
22 if (this.beanFactory.isCurrentlyInCreation(name)) {
23 if (logger.isTraceEnabled()) {
24 logger.trace("Skipping currently created advisor '" + name + "'");
25 }
26 }
27 else {
28 try {
29 //调用getBean方法从容器中获取名称为name的bean,并将bean添加到advisors中
30 advisors.add(this.beanFactory.getBean(name, Advisor.class));
31 }
32 catch (BeanCreationException ex) {
33 Throwable rootCause = ex.getMostSpecificCause();
34 if (rootCause instanceof BeanCurrentlyInCreationException) {
35 BeanCreationException bce = (BeanCreationException) rootCause;
36 String bceBeanName = bce.getBeanName();
37 if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
38 if (logger.isTraceEnabled()) {
39 logger.trace("Skipping advisor '" + name +
40 "' with dependency on currently created bean: " + ex.getMessage());
41 }
42 // Ignore: indicates a reference back to the bean we're trying to advise.
43 // We want to find advisors other than the currently created bean itself.
44 continue;
45 }
46 }
47 throw ex;
48 }
49 }
50 }
51 }
52 return advisors;
53 }
(2)findAdvisorsThatCanApply方法(寻找增强中适用于bean的增强并应用)
  我们首先进入findAdvisorsThatCanApply看下:
 1     protected List<Advisor> findAdvisorsThatCanApply(
2 List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
3
4 ProxyCreationContext.setCurrentProxiedBeanName(beanName);
5 try {
6 return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
7 }
8 finally {
9 ProxyCreationContext.setCurrentProxiedBeanName(null);
10 }
11 }
  方法调用了AopUtils类(package org.springframework.aop.support中)的findAdvisorsThatCanApply方法。
 1     public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
2 if (candidateAdvisors.isEmpty()) {
3 return candidateAdvisors;
4 }
5 List<Advisor> eligibleAdvisors = new ArrayList<>();
6 for (Advisor candidate : candidateAdvisors) {
7 //刷选IntroductionAdvisor引介类型的通知器
8 if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
9 eligibleAdvisors.add(candidate);
10 }
11 }
12 boolean hasIntroductions = !eligibleAdvisors.isEmpty();
13 for (Advisor candidate : candidateAdvisors) {
14 if (candidate instanceof IntroductionAdvisor) {
15 //引介增强已经处理
16 continue;
17 }
18 //刷选普通类型的通知器
19 if (canApply(candidate, clazz, hasIntroductions)) {
20 eligibleAdvisors.add(candidate);
21 }
22 }
23 return eligibleAdvisors;
24 }
该函数调用了该类中的canApply方法,该方法实现了重载,其代码如下:
 1     public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
2 Assert.notNull(pc, "Pointcut must not be null");
3 //使用 ClassFilter 匹配 class
4 if (!pc.getClassFilter().matches(targetClass)) {
5 return false;
6 }
7
8 MethodMatcher methodMatcher = pc.getMethodMatcher();
9 if (methodMatcher == MethodMatcher.TRUE) {
10 // No need to iterate the methods if we're matching any method anyway...
11 return true;
12 }
13
14 IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
15 if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
16 introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
17 }
18
19
20 /*
21 * 查找当前类及其父类(以及父类的父类等等)所实现的接口,由于接口中的方法是 public,
22 * 所以当前类可以继承其父类,和父类的父类中所有的接口方法
23 */
24 Set<Class<?>> classes = new LinkedHashSet<>();
25 if (!Proxy.isProxyClass(targetClass)) {
26 classes.add(ClassUtils.getUserClass(targetClass));
27 }
28 classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
29
30 for (Class<?> clazz : classes) {
31 // 获取当前类的方法列表,包括从父类中继承的方法
32 Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
33 for (Method method : methods) {
34 // 使用 methodMatcher 匹配方法,匹配成功即可立即返回
35 if (introductionAwareMethodMatcher != null ?
36 introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
37 methodMatcher.matches(method, targetClass)) {
38 return true;
39 }
40 }
41 }
42
43 return false;
44 }
 1     public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
2 if (advisor instanceof IntroductionAdvisor) {
3 /*
4 * 从通知器中获取类型过滤器 ClassFilter,并调用 matchers 方法进行匹配。
5 * ClassFilter 接口的实现类 AspectJExpressionPointcut 为例,该类的
6 * 匹配工作由 AspectJ 表达式解析器负责,具体匹配细节这个就没法分析了,我
7 * AspectJ 表达式的工作流程不是很熟
8 */
9 return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
10 }
11 else if (advisor instanceof PointcutAdvisor) {
12 PointcutAdvisor pca = (PointcutAdvisor) advisor;
13 // 对于普通类型的通知器,这里继续调用重载方法进行筛选
14 return canApply(pca.getPointcut(), targetClass, hasIntroductions);
15 }
16 else {
17 // It doesn't have a pointcut so we assume it applies.
18 return true;
19 }
20 }

以上是通知器筛选的过程,筛选的工作主要由 ClassFilter 和 MethodMatcher 完成。

所以获取增强器的主要流程:(1)获取所有bean名称;(2)遍历所有bean名称找出其中标记Aspect的bean;(3)解析并构造获取bean的所有增强器;(4)将解析到的增强器添加到缓存中;(5)过滤匹配出当前bean的增强器。

(三)根据获取的增强创建代理

我们再回到AbstractAutoProxyCreator类的wrapIfNecessary方法。从这个方法代码中我们看到了代理创建的雏形。创建代理主要包含了两个步骤:1、获取增强方法或者增强器。2、根据获取的增强进行代理。上面我们介绍了获取增强方法或者增强器,下面我们看下根据获取的增强进行代理。

wrapIfNecessary函数中创建代理用了调用了Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

我们进入AbstractAutoProxyCreator类中的createProxy函数:

 1     protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
2 @Nullable Object[] specificInterceptors, TargetSource targetSource) {
3
4 if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
5 AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
6 }
7
8 ProxyFactory proxyFactory = new ProxyFactory();
9 //获取当前类中的相关属性
10 proxyFactory.copyFrom(this);
11
12 if (!proxyFactory.isProxyTargetClass()) {
13 if (shouldProxyTargetClass(beanClass, beanName)) {
14 proxyFactory.setProxyTargetClass(true);
15 }
16 else {
17 evaluateProxyInterfaces(beanClass, proxyFactory);
18 }
19 }
20
21 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
22 //加入增强器
23 proxyFactory.addAdvisors(advisors);
24 //设置要代理的类
25 proxyFactory.setTargetSource(targetSource);
26 //定制代理
27 customizeProxyFactory(proxyFactory);
28
29 //用来控制代理工厂被配置后,是否还允许修改通知
30 //缺省值为false(即在被代理知乎,不允许修改代理的配置)
31 proxyFactory.setFrozen(this.freezeProxy);
32 if (advisorsPreFiltered()) {
33 proxyFactory.setPreFiltered(true);
34 }
35
36 //最终调用的是 proxyFactory.getProxy()方法, proxyFactory 有 JDK 和 CGLib 的
37 //继续进入aop/framework/ProxyFactory类中查看该函数
38 return proxyFactory.getProxy(getProxyClassLoader());
39 }

此函数主要是对ProxyFactory的初始化操作,进而对真正的代理创建做准备。这些初始化操作包括:

步骤1:获取当前类的属性。

步骤2:添加代理接口。

步骤3:拦截器封装转化为增强器

步骤4:封装Advisor并加入到ProxyFactory中。

步骤5:设置要代理的类。

步骤6:在Spring中还为子类提供了定制函数customizeProxyFactory,子类可以在此函数中对ProxyFactory进一步封装。

步骤7:设置是否需要冻结代理对象

步骤8:进行代理操作。

下面我们重点先来看下步骤3、4、8.

步骤3 :Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);

步骤4:proxyFactory.addAdvisors(advisors);

步骤8: return proxyFactory.getProxy(getProxyClassLoader())

(1)步骤3:将拦截器封装转化为增强器。

首先要将拦截器封装转化为增强器,实现函数为buildAdvisors方法,在该类AbstractAutoProxyCreator中,代码如下:

 1     protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
2 // 解析注册所有的InterceptorNames
3 Advisor[] commonInterceptors = resolveInterceptorNames();
4
5 List<Object> allInterceptors = new ArrayList<>();
6 if (specificInterceptors != null) {
7 //加入拦截器
8 allInterceptors.addAll(Arrays.asList(specificInterceptors));
9 if (commonInterceptors.length > 0) {
10 if (this.applyCommonInterceptorsFirst) {
11 allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
12 }
13 else {
14 allInterceptors.addAll(Arrays.asList(commonInterceptors));
15 }
16 }
17 }
18 if (logger.isTraceEnabled()) {
19 int nrOfCommonInterceptors = commonInterceptors.length;
20 int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
21 logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
22 " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
23 }
24
25 Advisor[] advisors = new Advisor[allInterceptors.size()];
26 for (int i = 0; i < allInterceptors.size(); i++) {
27 //将拦截器进行封装转化成Advisor
28 advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
29 }
30 return advisors;
31 }

上述方法调用了接口advisorAdapterRegistry的wrap方法,DefaultAdvisorAdapterRegistry类(package org.springframework.aop.framework.adapter)是接口advisorAdapterRegistry的默认实现,用来完成各种通知的适配和注册过程。将Advice包装成Advisor(DefaultPointCutAdvisor),借助AdvisorAdapter,将Advisor包装成MethodInterceptor。我们进入此类中的wrap方法:

 1     @Override
2 public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
3 //如果要封装的对象本身就是Advisor类型的,那么无需再做过多的处理
4 if (adviceObject instanceof Advisor) {
5 return (Advisor) adviceObject;
6 }
7 //因为此封装方法只对Advisor与Advice两种类型的数据有效,如果不是将不能封装
8 if (!(adviceObject instanceof Advice)) {
9 throw new UnknownAdviceTypeException(adviceObject);
10 }
11 Advice advice = (Advice) adviceObject;
12 if (advice instanceof MethodInterceptor) {
13 //如果是MethodInterceptor类型,则使用DefaultPointcutAdvisor封装
14 return new DefaultPointcutAdvisor(advice);
15 }
16 //如果存在Advisor的适配器,那么也同样需要进行封装
17 for (AdvisorAdapter adapter : this.adapters) {
18 // Check that it is supported.
19 if (adapter.supportsAdvice(advice)) {
20 return new DefaultPointcutAdvisor(advice);
21 }
22 }
23 throw new UnknownAdviceTypeException(advice);
24 }

(2)步骤4:将Advisor加入到ProxyFactory中。

将拦截器封装转化为增强器后,再通过ProxyFactory提供的addAdvisor方法将增强器置入创建工厂中。AbstractAutoProxyCreator.createProxy函数中直接调用了proxyFactory.addAdvisor (advisors)。proxyFactory继承自ProxyCreatorSupport,ProxyCreatorSupport继承自AdvisedSupport。addAdvisor这个方法实际在AdvisedSupport类(package org.springframework.aop.framework)中给出。

1     public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
2 if (advisor instanceof IntroductionAdvisor) {
3 validateIntroductionAdvisor((IntroductionAdvisor) advisor);
4 }
5 addAdvisorInternal(pos, advisor);
6 }
(3)步骤8:进行获取代理操作。

我们再回到AbstractAutoProxyCreator.createProxy方法。最后一句代码:

1     public Object getProxy() {
2 // 调用了ProxyCreatorSupport的createAopProxy()方法创建一个AopProxy对象
3 // 然后调用AopProxy对象的getProxy方法
4 return createAopProxy().getProxy();
5 }

进行解析最重要的一步就是代理类的创建和处理,Spring委托给了ProxyFactory去处理。

我们进入ProxyFactory类(package org.springframework.aop.framework)中的getProxy函数。

1     protected final synchronized AopProxy createAopProxy() {
2 if (!this.active) {
3 activate();
4 }
5 // 实际就是使用DefaultAopProxyFactory来创建一个代理对象
6 // 可以看到在调用createAopProxy方法时,传入的参数是this
7 // 这是因为ProxyCreatorSupport本身就保存了创建整个代理对象所需要的配置信息
8 return getAopProxyFactory().createAopProxy(this);
9 }

其中就一句话return createAopProxy().getProxy();。createAopProxy 方法没有在ProxyFactory类中定义,createAopProxy方法在其父类ProxyCreatorSupport(package org.springframework.aop.framework)中定义了,进入其代码:

1     protected final synchronized AopProxy createAopProxy() {
2 if (!this.active) {
3 activate();
4 }
5 // 实际就是使用DefaultAopProxyFactory来创建一个代理对象
6 // 可以看到在调用createAopProxy方法时,传入的参数是this
7 // 这是因为ProxyCreatorSupport本身就保存了创建整个代理对象所需要的配置信息
8 return getAopProxyFactory().createAopProxy(this);
9 }

核心就一句代码return getAopProxyFactory().createAopProxy(this)。实际用的是AopProxyFactory类的createAopProxy方法。AopProxyFactory只是一个接口,DefaultAopProxyFactory是AopProxyFactory默认实现类,核心函数createAopProxy。所以其实是调用了DefaultAopProxyFactory类的createAopProxy方法。

 1     @Override
2 // 就是通过AOP相关的配置信息来决定到底是使用cglib代理还是jdk代理
3 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
4 // 如果开启了优化,或者ProxyTargetClass设置为true
5 // 或者没有提供代理类需要实现的接口,那么使用cglib代理
6 // 在前面分析参数的时候已经说过了
7 // 默认情况下Optimize都为false,也不建议设置为true,因为会进行一些侵入性的优化
8 // 除非你对cglib的优化非常了解,否则不建议开启
9 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
10 Class<?> targetClass = config.getTargetClass();
11 if (targetClass == null) {
12 throw new AopConfigException("TargetSource cannot determine target class: " +
13 "Either an interface or a target is required for proxy creation.");
14 }
15 // 需要注意的是,如果需要代理的类本身就是一个接口
16 // 或者需要被代理的类本身就是一个通过jdk动态代理生成的类
17 // 那么不管如何设置都会使用jdk动态代理
18 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
19 return new JdkDynamicAopProxy(config);
20 }
21 return new ObjenesisCglibAopProxy(config);
22 }
23 // 否则都是jdk代理
24 else {
25 return new JdkDynamicAopProxy(config);
26 }
27 }
至此完成了代理的创建。

(四)织入

Spring主要使用JDK动态代理,所以先回顾性JDK动态代理的原理(详见上一节JDK动态代理)。

Java动态代理机制中有两个重要的接口和类:

(1)接口InvocationHandler:动态代理类的调用处理程序。

(2)Proxy:动态代理类,Proxy类就是用来创建一个代理对象的类,

Java动态代理的步骤:

1、创建目标类(委托类)的接口

2、创建目标类(委托类)

3、定义一个代理类的调用处理程序。该程序必须实现接口InvocationHandler,且必须实现接口的invoke方法。

4、通过Proxy的静态方法newProxyInstance()创建一个代理对象。

5、通过代理对象调用委托类对象的方法。

其实Proxy类只是一个连接桥,把代理(InvocationHandler)与被代理类关联起来,真正处理事情的是InvocaHandler。InvocationHandler接口中的invoke方法在代理类中是动态实现的,当我们通过动态代理调用一个方法的时候,这个方法的调用会被转发到到调用处理程序的invoke方法中。

Spring的AOP实现也是用了Proxy和InvocationHandler这两个东西。JDK代理中InvocationHandler的创建是最为核心的。在自定义的InvocationHandler中需要重写3个函数。(1)构造函数,将代理的对象传入。(2)invoke方法,此方法中实现了AOP增强的所有逻辑。(3)getProxy方法。

我们进入JdkDynamicAopProxy类(package org.springframework.aop.framework)。这个类就是用来生成jdk动态代理。可以看到这个类本身就是一个InvocationHandler,这意味着当调用代理对象中的方法时,最终会调用到JdkDynamicAopProxy的invoke方法。

重点看其getProxy函数和invoke函数。

 1     public Object getProxy(@Nullable ClassLoader classLoader) {
2 if (logger.isTraceEnabled()) {
3 logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
4 }
5 // 这里获取到代理类需要实现的所有的接口
6 Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
7 // 需要明确是否在接口定义了hashCode以及equals方法
8 // 如果接口中没有定义,那么在调用代理对象的equals方法的时候
9 // 如果两个对象相等,那么意味着它们的目标对象,通知以及实现的接口都相同
10 findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
11 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
12 }

JDKProxy的使用关键是创建自定义的InvocationHandler,InvocationHandler中包含了需要覆盖的getProxy,上述方法就是完成了这个操作。JdkDynamicAopProxy实现了接口AopProxy、InvocationHandler和Serializable。InvocationHandler除了getProxy还有invoke函数,JdkDynamicAopProxy类将其核心逻辑写在了这个方法中。

  1     /**
2 * Implementation of {@code InvocationHandler.invoke}.
3 * <p>Callers will see exactly the exception thrown by the target,
4 * unless a hook method throws an exception.
5 */
6 @Override
7 @Nullable
8 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
9 Object oldProxy = null;
10 boolean setProxyContext = false;
11
12 TargetSource targetSource = this.advised.targetSource;
13 Object target = null;
14
15 try {
16 // 首先处理的是hashCode跟equals方法,如果接口中没有定义这两个方法,那么会调用本类中定义的equals方法
17 // equals方法的处理。只有当两个类的目标对象,通知以及实现的接口都相等的情况下equals才会返回true
18 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
19 // The target does not implement the equals(Object) method itself.
20 return equals(args[0]);
21 }
22 //hash方法的处理
23 else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
24 // The target does not implement the hashCode() method itself.
25 return hashCode();
26 }
27 // 也就是说我们调用的是DecoratingProxy这个接口中的方法
28 // 这个接口中只定义了一个getDecoratedClass方法,用于获取到
29 // 最终的目标对象,在方法实现中会通过一个while循环来不断接近
30 // 最终的目标对象,直到得到的目标对象不是一个被代理的对象才会返回
31 else if (method.getDeclaringClass() == DecoratingProxy.class) {
32 // There is only getDecoratedClass() declared -> dispatch to proxy config.
33 return AopProxyUtils.ultimateTargetClass(this.advised);
34 }
35 // 说明调用的是Advised接口中的方法,这里只是单纯的进行反射调用
36 else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
37 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
38 // Service invocations on ProxyConfig with the proxy config...
39 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
40 }
41
42 Object retVal;
43
44 //有时候目标对象内部的自我调用将无法实施切面中的增强则需要通过此属性暴露代理
45 // 将代理类暴露到线程上下文中,调用AopContext.setCurrentProxy方法将其放入到一个threadLocal中
46 if (this.advised.exposeProxy) {
47 // Make invocation available if necessary.
48 oldProxy = AopContext.setCurrentProxy(proxy);
49 setProxyContext = true;
50 }
51
52 // 接下来就是真正的执行代理逻辑了
53 target = targetSource.getTarget();
54 Class<?> targetClass = (target != null ? target.getClass() : null);
55
56 // 获取当前方法的拦截器链
57 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
58
59 if (chain.isEmpty()) {
60 //如果没有发现任何拦截器链那么直接调用切点方法
61 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
62 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
63 }
64 // 否则开始执行整个链条
65 else {
66 //将拦截器封装在ReflectiveMethodInvocation以便于使用期proceed进行链接表用拦截器
67 MethodInvocation invocation =
68 new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
69 //执行拦截器链
70 retVal = invocation.proceed();
71 }
72
73 // Massage return value if necessary.
74 // 这里是处理一种特殊情况,就是当执行的方法返回值为this的情况
75 // 这种情况下,需要返回当前的代理对象而不是目标对象
76 Class<?> returnType = method.getReturnType();
77 if (retVal != null && retVal == target &&
78 returnType != Object.class && returnType.isInstance(proxy) &&
79 !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
80 // Special case: it returned "this" and the return type of the method
81 // is type-compatible. Note that we can't help if the target sets
82 // a reference to itself in another returned object.
83 retVal = proxy;
84 }
85 else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
86 throw new AopInvocationException(
87 "Null return value from advice does not match primitive return type for: " + method);
88 }
89 return retVal;
90 }
91 finally {
92 if (target != null && !targetSource.isStatic()) {
93 // Must have come from TargetSource.
94 targetSource.releaseTarget(target);
95 }
96 if (setProxyContext) {
97 // Restore old proxy.
98 AopContext.setCurrentProxy(oldProxy);
99 }
100 }
101 }

上面的函数最主要的工作是创建了一个拦截器链,并使用ReflectiveMethodInvocation类进行了链的封装,而在ReflectiveMethodInvocation类的proceed方法中实现了拦截器的逐一调用。我们继续看proceed方法是怎么实现前置增强在目标方法前调用,后置增强在目标方法后调用的逻辑。

ReflectiveMethodInvocation(package org.springframework.aop.framework包中)是一个核心类,激发拦截链工作实现。该类实现了aop联盟的MethodInvocation,间接实现了Invocation和Joinpoint。我们进入其proceed函数:

 1     @Override
2 @Nullable
3 public Object proceed() throws Throwable {
4 // We start with an index of -1 and increment early.
5 //执行完所有增强后执行切点方法
6 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
7 return invokeJoinpoint();
8 }
9
10 //获取下一个要执行的拦截器
11 Object interceptorOrInterceptionAdvice =
12 this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
13 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
14 // Evaluate dynamic method matcher here: static part will already have
15 // been evaluated and found to match.
16 //动态匹配
17 InterceptorAndDynamicMethodMatcher dm =
18 (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
19 Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
20 if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
21 return dm.interceptor.invoke(this);
22 }
23 else {
24 // Dynamic matching failed.
25 // Skip this interceptor and invoke the next in the chain.
26 //不匹配就不执行拦截器
27 return proceed();
28 }
29 }
30 else {
31 // It's an interceptor, so we just invoke it: The pointcut will have
32 // been evaluated statically before this object was constructed.
33 //普通拦截器,直接调用拦截器。将this作为参赛传入以保障当前实例中调用链的执行。
34 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
35 }
36 }

在proceed方法中,ReflectiveMethodInvocation的主要职责是维护了调用的计数器,记录着当前调用链接的位置,以便可以有序的进行下去。

本文参考了 郝佳《Spring源码深度解析》第七章AOP及博客园、CSDN部分文献。

博众家之所长,集群英之荟萃。遴选各IT领域精品雄文!

欢迎关注“IT架构精选”

Spring框架之AOP源码完全解析的更多相关文章

  1. Spring框架之jms源码完全解析

    Spring框架之jms源码完全解析 我们在前两篇文章中介绍了Spring两大核心IOC(Inversion of Control控制反转)和AOP(Aspect Oriented Programmi ...

  2. Spring框架之spring-webmvc源码完全解析

    Spring框架之spring-webmvc源码完全解析 Spring框架提供了构建Web应用程序的全功能MVC模块.Spring MVC分离了控制器.模型对象.分派器以及处理程序对象的角色,支持多种 ...

  3. Spring框架之jdbc源码完全解析

    Spring框架之jdbc源码完全解析 Spring JDBC抽象框架所带来的价值将在以下几个方面得以体现: 1.指定数据库连接参数 2.打开数据库连接 3.声明SQL语句 4.预编译并执行SQL语句 ...

  4. Spring框架之websocket源码完全解析

    Spring框架之websocket源码完全解析 Spring框架从4.0版开始支持WebSocket,先简单介绍WebSocket协议(详细介绍参见"WebSocket协议中文版" ...

  5. Spring框架之事务源码完全解析

    Spring框架之事务源码完全解析   事务的定义及特性: 事务是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务将逻辑相关的一组操作绑定在一 ...

  6. Spring框架之beans源码完全解析

    导读:Spring可以说是Java企业开发里最重要的技术.而Spring两大核心IOC(Inversion of Control控制反转)和AOP(Aspect Oriented Programmin ...

  7. 设计模式(二十一)——解释器模式(Spring 框架中SpelExpressionParser源码分析)

    1 四则运算问题 通过解释器模式来实现四则运算,如计算 a+b-c 的值,具体要求 1) 先输入表达式的形式,比如 a+b+c-d+e,  要求表达式的字母不能重复 2) 在分别输入 a ,b, c, ...

  8. Android 图片加载框架Glide4.0源码完全解析(二)

    写在之前 上一篇博文写的是Android 图片加载框架Glide4.0源码完全解析(一),主要分析了Glide4.0源码中的with方法和load方法,原本打算是一起发布的,但是由于into方法复杂性 ...

  9. Spring基础系列-AOP源码分析

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...

随机推荐

  1. 使用rabbitmq实现集群im聊天服务器消息的路由

    这个地址图文会更清晰:https://www.jianshu.com/p/537e87c64ac7 单机系统的时候,客户端和连接都有同一台服务器管理.   image.png 在本地维护一份userI ...

  2. 重磅解读:K8s Cluster Autoscaler模块及对应华为云插件Deep Dive

    摘要:本文将解密K8s Cluster Autoscaler模块的架构和代码的Deep Dive,及K8s Cluster Autoscaler 华为云插件. 背景信息 基于业务团队(Cloud BU ...

  3. php 导出excel 10万数据

    php导出excel 10万数据(此代码主要测试用) 在工作当中要对一些基本信息和其他信息导出 起初信息比较小无所谓.... 但当信息超出65535的时候 发现点问题了 超出了 而且 反应速度很慢 实 ...

  4. Android Google官方文档解析之——Device Compatibility

    Android is designed to run on many different types of devices, from phones to tablets and television ...

  5. Java—递归

    递归 1. 概述 方法中调用当前方法 2. 递归需要注意的事项 递归次数不能过多,否则内存溢出 3. 案例 3.1 获取指定目录下的文件名 public class AllDirPath { /* * ...

  6. Find Any File for Mac(文件搜索软件)v2.1.2b6

    Find Any File for Mac是应用在Mac上的一款文件搜索工具,Find Any File Mac可以通过名称.创建或修改日期,大小或类型和创建者代码(而不是内容)在本地磁盘上搜索文件. ...

  7. Linux程序开发中如何判断目录是否为根目录?

    问题引入 判断某个目录字符串是否是根目录,咋一听很简单,只要判断字符串是否是"/"即可,但是,很多情况下使用的路径是相对路径,那么如何判断相对路径是根目录呢? 思路分析 熟悉Lin ...

  8. ceph集群的安装和配置教程

    本篇主题: 1.怎样配置ssh免登陆访问 2.为什么搭建集群要关闭防火墙和selinux,如何关闭 3.从哪里获取ceph的安装包,怎样安装才是快速正确的 4.为什么要配置时间同步服务,怎样配置 5. ...

  9. Bad magic number ImportError in python

    是源码编译里面版本不对,删除掉源码pyc然后重新编译就可以了 find .-name '*.pyc'-delete python -m compileall . 更新历史 why when 创建 20 ...

  10. ESP8266 鼓捣记 - 从零制作一个温湿度计

    一.前言 经过上一篇文章 <ESP8266 鼓捣记 - 入门(环境搭建) >搭建好环境后,肯定不会满足于 Hello World ,想快速做一个实际有用的东西出来,我认为温湿度计就非常合适 ...