框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

本文内容纲要:

- 一、AOP的核心概念回顾

- 二、Spring中AOP的用法

- 1. 传统Advisor方式

- 2. Aspect语法方式

- Aspect的advice是基于方法的。

- 3. AspectJ注解方式

- 三、Spring AOP 源码学习

- 1、spring aop的工作流程是怎样?以传统的Advisor配置为例进行思考

- 2. 源码阅读思路

- 1、先看配置解析,看标签解析过程都做了什么、完成了什么。

- 2、 看织入的过程

- 3 、看方法被调用时的增强过程

- 4、源码对应的类图

一、AOP的核心概念回顾

https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aop

我们先来看一下下面的这张图

Image

说明:

程序运行时会调用很多方法,调用的很多方法就叫做Join points(连接点,可以被选择来进行增强的方法点),在方法的前或者后选择一个地方来切入,切入的的地方就叫做Pointcut(切入点,选择增强的方法),然后把要增强的功能(Advice)加入到切入点所在的位置。Advice和Pointcut组成一个切面(Aspect)

AOP的几个概念:

Image

Advice、Pointcut、Weaving的特点:

Advice(功能增强):

1)用户性:由用户提供增强功能的逻辑代码

2)变化的:不同的增强需求,会有不同的逻辑

3)可选时机:可选择在方法前、后、异常时进行功能增强

4)多重的:同一个切入点上可以有多重增强

Pointcut(切入点):

1)用户性:由用户来指定

2)变化的:用户可灵活指定

3)多点性:用户可以选择在多个点上进行功能增强

Weaving(织入):

1)无侵入性,因为不改变原类的代码

2)我们在框架中实现

二、Spring中AOP的用法

1. 传统Advisor方式

掌握用法:

1)编程提供Advice,实现对应的Advice接口

2)配置Advisor(advice+pointcut)

Advice接口:

Image

示例代码:

被增强的目标对象:

BeanQ

package com.study.leesmall.spring.sample.aop;

//被增强的目标对象

public class BeanQ {

public void do1(String task, int time) {

System.out.println("-------------do1 do " + task + " time:" + time);

}

public String service1(String name) {

System.out.println("-------------servce1 do " + name);

return name;

}

public String service2(String name) {

System.out.println("-------------servce2 do " + name);

if (!"s1".equals(name)) {

throw new IllegalArgumentException("参数 name != s1, name=" + name);

}

return name + " hello!";

}

}

编程提供Advice,实现对应的Advice接口:

前置增强:

MyBeforeAdvice

package com.study.leesmall.spring.sample.aop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

//前置增强

public class MyBeforeAdvice implements MethodBeforeAdvice {

@Override

public void before(Method method, Object[] args, Object target) throws Throwable {

System.out.println("------ MyBeforeAdvice before 增强 " + target + " " + method);

}

}

环绕增强:

MyArroundAdvice

package com.study.leesmall.spring.sample.aop;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

//环绕增强

public class MyArroundAdvice implements MethodInterceptor {

@Override

public Object invoke(MethodInvocation invocation) throws Throwable {

System.out.println("--------- 环绕 -前增强");

Object ret = invocation.proceed();

System.out.println("--------- 环绕 -后增强");

return ret;

}

}

在/spring-source-study/src/main/java/com/study/leesmall/spring/sample/aop/application.xml里面配置Advisor(advice+pointcut):

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 传统方式的aop begin -->

<!--被增强的目标对象 -->

<bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />

<!--配置advice -->

<bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />

<bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />

<!--配置pointcut -->

<aop:config >

<!--全局切入点,任何一个aop-config都可以使用 -->

<aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />

<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />

<aop:advisor advice-ref="yyArroundAdvice"

pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>

</aop:config>

<!-- 传统方式的aop end -->

</beans>

配置文件里面的注意点:

aop:config 的属性了解

Image

proxy-target-class="false"使用jdk的动态代理 默认配置

proxy-target-class="true"使用cglib的动态代理

Image

Image

掌握 spring Aop 的 API:

Advice:

Image

Advisor的代码:

* Copyright 2002-2017 the original author or authors.

package org.springframework.aop;

import org.aopalliance.aop.Advice;

/**

* Base interface holding AOP <b>advice</b> (action to take at a joinpoint)

* and a filter determining the applicability of the advice (such as

* a pointcut). <i>This interface is not for use by Spring users, but to

* allow for commonality in support for different types of advice.</i>

*

* <p>Spring AOP is based around <b>around advice</b> delivered via method

* <b>interception</b>, compliant with the AOP Alliance interception API.

* The Advisor interface allows support for different types of advice,

* such as <b>before</b> and <b>after</b> advice, which need not be

* implemented using interception.

*

* @author Rod Johnson

* @author Juergen Hoeller

*/

public interface Advisor {

/**

* Common placeholder for an empty {@code Advice} to be returned from

* {@link #getAdvice()} if no proper advice has been configured (yet).

* @since 5.0

*/

Advice EMPTY_ADVICE = new Advice() {};

/**

* Return the advice part of this aspect. An advice may be an

* interceptor, a before advice, a throws advice, etc.

* @return the advice that should apply if the pointcut matches

* @see org.aopalliance.intercept.MethodInterceptor

* @see BeforeAdvice

* @see ThrowsAdvice

* @see AfterReturningAdvice

*/

Advice getAdvice();

/**

* Return whether this advice is associated with a particular instance

* (for example, creating a mixin) or shared with all instances of

* the advised class obtained from the same Spring bean factory.

* <p><b>Note that this method is not currently used by the framework.</b>

* Typical Advisor implementations always return {@code true}.

* Use singleton/prototype bean definitions or appropriate programmatic

* proxy creation to ensure that Advisors have the correct lifecycle model.

* @return whether this advice is associated with a particular target instance

*/

boolean isPerInstance();

}

Pointcut:

Image

Pointcut的子类:

Image

Advisor的子类:

Image

PointcutAdvisor扩展了Advisor以后就会有一个切面Aspect=Advice+Pointcut

PointcutAdvisor的代码:

* Copyright 2002-2012 the original author or authors.

package org.springframework.aop;

/**

* Superinterface for all Advisors that are driven by a pointcut.

* This covers nearly all advisors except introduction advisors,

* for which method-level matching doesn't apply.

*

* @author Rod Johnson

*/

public interface PointcutAdvisor extends Advisor {

/**

* Get the Pointcut that drives this advisor.

*/

Pointcut getPointcut();

}

PointcutAdvisor的子类:

Image

测试类:

AopMain

package com.study.leesmall.spring.sample.aop;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.GenericXmlApplicationContext;

public class AopMain {

public static void main(String[] args) {

ApplicationContext context = new GenericXmlApplicationContext(

"classpath:com/study/leesmall/spring/sample/aop/application.xml");

BeanQ bq = context.getBean(BeanQ.class);

bq.do1("task1", 20);

System.out.println();

bq.service1("service1");

System.out.println();

bq.service2("ssss");

}

}

测试结果:

------ MyBeforeAdvice before 增强 com.study.leesmall.spring.sample.aop.BeanQ@1d119efb public void com.study.leesmall.spring.sample.aop.BeanQ.do1(java.lang.String,int)

-------------do1 do task1 time:20

--------- 环绕 -前增强

-------------servce1 do service1

--------- 环绕 -后增强

--------- 环绕 -前增强

-------------servce2 do ssss

Exception in thread "main" java.lang.IllegalArgumentException: 参数 name != s1, name=ssss

at com.study.leesmall.spring.sample.aop.BeanQ.service2(BeanQ.java:18)

at com.study.leesmall.spring.sample.aop.BeanQ$$FastClassBySpringCGLIB$$3d1515ac.invoke(<generated>)

at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)

at com.study.leesmall.spring.sample.aop.MyArroundAdvice.invoke(MyArroundAdvice.java:12)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)

at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)

at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)

at com.study.leesmall.spring.sample.aop.BeanQ$$EnhancerBySpringCGLIB$$2fc5343.service2(<generated>)

at com.study.leesmall.spring.sample.aop.AopMain.main(AopMain.java:18)

2. Aspect语法方式

Aspect的advice是基于方法的。

掌握用法:

1)定义包含Advice方法的Bean类

2)配置Bean定义

3)配置Aspect(引用包含advice方法的bean),在里面配置各种Advice(method+pointcut)

定义包含Advice方法的Bean类:

AspectAdviceBean:

package com.study.leesmall.spring.sample.aop;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

//定义包含 Advice 方法的 Bean 类

public class AspectAdviceBean {

public void before1() {

System.out.println("----------- AspectAdviceBean before1 增强 ");

}

//JoinPoint看具体哪个方法被增强了,JoinPoint一定要放在第一个参数

public void before2(JoinPoint jp) {

System.out.println("----------- AspectAdviceBean before2 增强 for " + jp);

}

//调用被增强的方法时传参数

//<aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))

//and args(tk,..)" arg-names=""/>

//args(tk,..)有两个意思,第一个意思是被增强的方法的第一个参数的类型要和before3的参数tk的类型一样

//第二个意思是被增强的方法的第一个参数tk要赋值给before3的参数tk

//arg-names="" 当不能确定方法参数的顺序时可以用这个参数指定arg-names="param1,param2"

public void before3(String tk) {

System.out.println("----------- AspectAdviceBean before3 增强 参数tk= " + tk);

}

//调用被增强的方法时传参数

public void before4(String tk, int ti) {

System.out.println("----------- AspectAdviceBean before4 增强 参数tk= " + tk + " ti=" + ti);

}

//ProceedingJoinPoint正在处理的方法

public Object arround1(ProceedingJoinPoint pjp) throws Throwable {

System.out.println("----------- AspectAdviceBean arround1 环绕-前增强 for " + pjp);

Object ret = pjp.proceed();

System.out.println("----------- AspectAdviceBean arround1 环绕-后增强 for " + pjp);

return ret;

}

public Object arround2(ProceedingJoinPoint pjp, String name) throws Throwable {

System.out.println("--------- AspectAdviceBean arround2 参数 name=" + name);

System.out.println("----------- AspectAdviceBean arround2 环绕-前增强 for " + pjp);

Object ret = pjp.proceed();

System.out.println("----------- AspectAdviceBean arround2 环绕-后增强 for " + pjp);

return ret;

}

public void afterReturning(Object retValue) {

System.out.println("----------- AspectAdviceBean afterReturning 增强 , 返回值为: " + retValue);

}

public void afterThrowing(JoinPoint jp, Exception e) {

System.out.println("----------- AspectAdviceBean afterThrowing 增强 for " + jp);

System.out.println("----------- AspectAdviceBean afterThrowing 增强 异常 :" + e);

}

public void after(JoinPoint jp) {

System.out.println("----------- AspectAdviceBean after 增强 for " + jp);

}

}

JoinPoint和ProceedingJoinPoint :

Image

在/spring-source-study/src/main/java/com/study/leesmall/spring/sample/aop/application.xml里面配置Bean定义

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 传统方式的aop begin -->

<!--被增强的目标对象 -->

<bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />

<!--配置advice -->

<bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />

<bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />

<!--配置pointcut -->

<aop:config >

<aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />

<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />

<aop:advisor advice-ref="yyArroundAdvice"

pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>

</aop:config>

<!-- 传统方式的aop end -->

<!-- AspectJ的aop begin -->

<!-- 配置了包含advice方法的Bean -->

<bean id="aspectAdviceBean" class="com.study.leesmall.spring.sample.aop.AspectAdviceBean" />

</beans>

3)配置Aspect(引用包含advice方法的bean),在里面配置各种Advice(method+pointcut)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 传统方式的aop begin -->

<!--被增强的目标对象 -->

<bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />

<!--配置advice -->

<bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />

<bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />

<!--配置pointcut -->

<aop:config >

<aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />

<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />

<aop:advisor advice-ref="yyArroundAdvice"

pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>

</aop:config>

<!-- 传统方式的aop end -->

<!-- AspectJ的aop begin -->

<!-- 配置了包含advice方法的Bean -->

<bean id="aspectAdviceBean" class="com.study.leesmall.spring.sample.aop.AspectAdviceBean" />

<!--配置 Aspect (引用包含 advice 方法的 bean),在里面配置各种 Advice(method + pointcut) -->

<aop:config>

<aop:pointcut id="services" expression="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))" />

<aop:aspect id="a1" ref="aspectAdviceBean" order="1">

<aop:before method="before1" pointcut-ref="doMethods" />

<aop:before method="before2" pointcut-ref="doMethods"/>

<!--args(tk,..)有两个意思,第一个意思是被增强的方法的第一个参数的类型要和before3的参数tk的类型一样

第二个意思是被增强的方法的第一个参数tk要赋值给before3的参数tk;

arg-names="" 当不能确定方法参数的顺序时可以用这个参数指定arg-names="param1,param2"

-->

<aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,..)"/>

<aop:before method="before4" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,ti)"/>

<aop:around method="arround1" pointcut-ref="services"/>

<aop:around method="arround2" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..)) and args(name)"/>

<aop:after-returning method="afterReturning" pointcut-ref="services" returning="retValue"/>

<aop:after-throwing method="afterThrowing" pointcut-ref="services" throwing="e"/>

<aop:after method="after" pointcut-ref="services"/>

</aop:aspect>

</aop:config>

<!-- AspectJ的aop end -->

</beans>

被增强的目标对象:

package com.study.leesmall.spring.sample.aop;

//被增强的目标对象

public class BeanQ {

public void do1(String task, int time) {

System.out.println("-------------do1 do " + task + " time:" + time);

}

public String service1(String name) {

System.out.println("-------------servce1 do " + name);

return name;

}

public String service2(String name) {

System.out.println("-------------servce2 do " + name);

/**if (!"s1".equals(name)) {

throw new IllegalArgumentException("参数 name != s1, name=" + name);

}**/

return name + " hello!";

}

}

测试类:

AopMain

package com.study.leesmall.spring.sample.aop;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.GenericXmlApplicationContext;

public class AopMain {

public static void main(String[] args) {

ApplicationContext context = new GenericXmlApplicationContext(

"classpath:com/study/leesmall/spring/sample/aop/application.xml");

BeanQ bq = context.getBean(BeanQ.class);

bq.do1("task1", 20);

System.out.println();

bq.service1("service1");

System.out.println();

bq.service2("ssss");

}

}

测试结果:

----------- AspectAdviceBean before1 增强 

----------- AspectAdviceBean before2 增强 for execution(void com.study.mike.spring.sample.aop.BeanQ.do1(leesmall,int))

----------- AspectAdviceBean before3 增强 参数tk= task1

----------- AspectAdviceBean before4 增强 参数tk= task1 ti=20

------ MyBeforeAdvice before 增强 com.study.mike.spring.sample.aop.BeanQ@3a0baae5 public void com.study.mike.spring.sample.aop.BeanQ.do1(java.lang.leesmall,int)

-------------do1 do task1 time:20

----------- AspectAdviceBean arround1 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))

--------- AspectAdviceBean arround2 参数 name=service1

----------- AspectAdviceBean arround2 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))

--------- 环绕 -前增强

-------------servce1 do service1

--------- 环绕 -后增强

----------- AspectAdviceBean arround2 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))

----------- AspectAdviceBean arround1 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))

----------- AspectAdviceBean afterReturning 增强 , 返回值为: service1

----------- AspectAdviceBean after 增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))

----------- AspectAdviceBean arround1 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))

--------- AspectAdviceBean arround2 参数 name=ssss

----------- AspectAdviceBean arround2 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))

--------- 环绕 -前增强

-------------servce2 do ssss

--------- 环绕 -后增强

----------- AspectAdviceBean arround2 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))

----------- AspectAdviceBean arround1 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))

----------- AspectAdviceBean afterReturning 增强 , 返回值为: ssss hello!

----------- AspectAdviceBean after 增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))

3. AspectJ注解方式

1)先在pom.xml文件里面引入AspectJ的依赖

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.9.1</version>

</dependency>

2)被增强的目标对象

package com.study.leesmall.spring.sample.aop;

//被增强的目标对象

public class BeanQ {

public void do1(String task, int time) {

System.out.println("-------------do1 do " + task + " time:" + time);

}

public String service1(String name) {

System.out.println("-------------servce1 do " + name);

return name;

}

public String service2(String name) {

System.out.println("-------------servce2 do " + name);

if (!"s1".equals(name)) {

throw new IllegalArgumentException("参数 name != s1, name=" + name);

}

return name + " hello!";

}

}

3)AspectJ注解方式实现AOP

AspectAdviceBeanUseAnnotation

package com.study.leesmall.spring.sample.aop;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.AfterThrowing;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

//AspectJ注解方式

@Aspect

public class AspectAdviceBeanUseAnnotation {

// 定义一个全局的Pointcut

@Pointcut("execution(* com.study.leesmall.spring.sample.aop.*.do*(..))")

public void doMethods() {

}

// 定义一个全局的Pointcut

@Pointcut("execution(* com.study.leesmall.spring.sample.aop.*.service*(..))")

public void services() {

}

// 定义一个Before Advice

@Before("doMethods() and args(tk,..)")

public void before3(String tk) {

System.out.println("----------- AspectAdviceBeanUseAnnotation before3 增强 参数tk= " + tk);

}

//环绕增强

@Around("services() and args(name,..)")

public Object around2(ProceedingJoinPoint pjp, String name) throws Throwable {

System.out.println("--------- AspectAdviceBeanUseAnnotation arround2 参数 name=" + name);

System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 环绕-前增强 for " + pjp);

Object ret = pjp.proceed();

System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 环绕-后增强 for " + pjp);

return ret;

}

@AfterReturning(pointcut = "services()", returning = "retValue")

public void afterReturning(Object retValue) {

System.out.println("----------- AspectAdviceBeanUseAnnotation afterReturning 增强 , 返回值为: " + retValue);

}

@AfterThrowing(pointcut = "services()", throwing = "e")

public void afterThrowing(JoinPoint jp, Exception e) {

System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 增强 for " + jp);

System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 增强 异常 :" + e);

}

@After("doMethods()")

public void after(JoinPoint jp) {

System.out.println("----------- AspectAdviceBeanUseAnnotation after 增强 for " + jp);

}

}

4)在/spring-source-study/src/main/java/com/study/leesmall/spring/sample/aop/application2.xml里面配置bean和开启AspectJ注解的支持

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--被增强的目标对象 -->

<bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />

<!--AspectJ注解方式实现的AOP -->

<bean id="aspectAdviceBeanUseAnnotation" class="com.study.leesmall.spring.sample.aop.AspectAdviceBeanUseAnnotation" />

<!--开启AspectJ注解的支持 -->

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

开启@Aspectj注解方式支持:

Xml中aop:aspectj-autoproxy</aop:aspectj-autoproxy>注意了解它的属性、及子元素

注解方式开启:

@Configuration

@EnableAspectJAutoProxy

public class AppConfig {

}

5)测试类

package com.study.leesmall.spring.sample.aop;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.GenericXmlApplicationContext;

public class AopMainUseAspectAnnotation {

public static void main(String[] args) {

ApplicationContext context = new GenericXmlApplicationContext(

"classpath:com/study/leesmall/spring/sample/aop/application2.xml");

BeanQ bq = context.getBean(BeanQ.class);

bq.do1("task1", 20);

System.out.println();

bq.service1("service1");

}

}

7)测试结果

----------- AspectAdviceBeanUseAnnotation before3  增强  参数tk= task1

-------------do1 do task1 time:20

----------- AspectAdviceBeanUseAnnotation after 增强 for execution(void com.study.leesmall.spring.sample.aop.BeanQ.do1(String,int))

--------- AspectAdviceBeanUseAnnotation arround2 参数 name=service1

----------- AspectAdviceBeanUseAnnotation arround2 环绕-前增强 for execution(String com.study.leesmall.spring.sample.aop.BeanQ.service1(String))

-------------servce1 do service1

----------- AspectAdviceBeanUseAnnotation arround2 环绕-后增强 for execution(String com.study.leesmall.spring.sample.aop.BeanQ.service1(String))

----------- AspectAdviceBeanUseAnnotation afterReturning 增强 , 返回值为: service1

三、Spring AOP 源码学习

1、spring aop的工作流程是怎样?以传统的Advisor配置为例进行思考

Image

2. 源码阅读思路

1、先看配置解析,看标签解析过程都做了什么、完成了什么。

入口:

E:\repository\org\springframework\spring-aop\5.1.3.RELEASE\spring-aop-5.1.3.RELEASE.jar/META-INF/spring.handlers

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

org.springframework.aop.config.AopNamespaceHandler:

Image

a)解析aop:config 及它的子元素

org.springframework.aop.config.ConfigBeanDefinitionParser:

Image

Image

1)注册了autoProxyCreator的Bean定义,它是一个ProxyConfig、BeanFactoryAwrare、BeanPostProcessor

下面来看一下配置自动代理的创建者的代码:

org.springframework.aop.config.ConfigBeanDefinitionParser.configureAutoProxyCreator(ParserContext, Element)

->

org.springframework.aop.config.AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(ParserContext, Element)

->

org.springframework.aop.config.AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry, Object)

->

org.springframework.aop.config.AopConfigUtils.registerOrEscalateApcAsRequired(Class<?>, BeanDefinitionRegistry, Object)

Image

Image

所以说自动代理创建者是一个ProxyConfig、BeanFactoryAwrare、BeanPostProcessor

->

org.springframework.aop.config.AopConfigUtils.findPriorityForClass(String)

Image

Image

说明:

org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator现在已经不使用了

org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator对应于AspectJ的xml方式

<aop:config>

<aop:pointcut id="services" expression="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))" />

<aop:aspect id="a1" ref="aspectAdviceBean" order="1">

<aop:before method="before1" pointcut-ref="doMethods" />

<aop:before method="before2" pointcut-ref="doMethods"/>

<!--args(tk,..)有两个意思,第一个意思是被增强的方法的第一个参数的类型要和before3的参数tk的类型一样

第二个意思是被增强的方法的第一个参数tk要赋值给before3的参数tk;

arg-names="" 当不能确定方法参数的顺序时可以用这个参数指定arg-names="param1,param2"

-->

<aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,..)" arg-names=""/>

<aop:before method="before4" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,ti)"/>

<aop:around method="arround1" pointcut-ref="services"/>

<aop:around method="arround2" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..)) and args(name)"/>

<aop:after-returning method="afterReturning" pointcut-ref="services" returning="retValue"/>

<aop:after-throwing method="afterThrowing" pointcut-ref="services" throwing="e"/>

<aop:after method="after" pointcut-ref="services"/>

</aop:aspect>

</aop:config>

查看org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator的继承体系:

父类:

Image

子类:

Image

查看org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator的继承体系,搞清楚它继承实现了什么,他的父类和子类有哪些

父类:

Image

子类:

Image

org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator对应于AspectJ的注解方式:

<bean id="aspectAdviceBeanUseAnnotation" class="com.study.leesamll.spring.sample.aop.AspectAdviceBeanUseAnnotation" />

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<aop:config proxy-target-class="false" expose-proxy="false">对应代码:

org.springframework.aop.config.AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(ParserContext, Element)

->

org.springframework.aop.config.AopNamespaceUtils.useClassProxyingIfNecessary(BeanDefinitionRegistry, Element)

Image

2)两种配置方式(advisor和aspectJ)解析之后都是向Bean工厂注册了Pointcut和Advisor的Bean定义

b)aop:aspectj-autoproxy开启AspectJ注解支持

1)注册了autoProxyCreator的Bean定义,它是一个ProxyConfig、BeanFactoryAwrare、BeanPostProcessor

org.springframework.aop.config.AopNamespaceHandler.init()

Image

org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser

org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser.parse(Element, ParserContext)

org.springframework.aop.config.AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext, Element)

Image

<!--开启AspectJ注解的支持  -->

<aop:aspectj-autoproxy>

<!-- 满足name里面的表达式(bean名称的表达式)的才进行切面的处理 -->

<aop:include name="bean名称的表达式"/>

</aop:aspectj-autoproxy>

Image

<aop:include name="bean名称的表达式"/>对应源码:

Image

c)AspectJ注解的切面在哪里读取加载的?

可能的地方:

BeanDefinitionRegistryPostProcessor x

BeanFactoryPostProcessor x

InstantiationAwareBeanPostProcessor Bean 实例创建前后

BeanPostProcessor

Xml方式的解析:

org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator

注解方式的解析:

org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator

两者的关系:

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator

org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()

Image

Image

BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors() 中完成了注解的读取、Advisor对象的创建及缓存。

org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()

Image

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory.getAdvisors(MetadataAwareAspectInstanceFactory)

Image

2、 看织入的过程

a) 在哪里做的织入?

在org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()里面打个断点拿到调用栈

入口:

com.study.leesmall.spring.sample.aop.AopMainUseAspectAnnotation

Image

b) 如何判断 Bean 要不要被创建代理?如何排除 advice Bean 的?

排除 advice Bean :

跳过advice bean 跳过带有@Aspect注解的自己,不能自己为自己创建代理,否则进入死循环,如AspectAdviceBeanUseAnnotation

org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(Class<?>, String)

Image

Image

再次进来,是在 PostProcessAfterInitialization()

Image

创建代理的方法:

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(Object, String, Object)

如何判断 Bean 要不要被创建代理:

Image

c)如何选择 jdk动态代理 还是cglib的动态代理

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(Class<?>, String, Object[], TargetSource)

org.springframework.aop.framework.ProxyFactory.getProxy(ClassLoader)

org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy()

org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(AdvisedSupport)

Image

d) 如何来创建代理的,涉及哪些类,如何协作的。

AutoProxyCreator

ProxyConfig ProcxyFactory

AopProxyFactory

AopProxy

3 、看方法被调用时的增强过程

a) 在代理中如何决定对当前方法合格的 advice 的?

  调用的 Advisor 中 Pointcut 进行匹配

b) 如何组织多个 advice 执行的?

  责任链模式

org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[])

org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()

4、源码对应的类图

Image

本文内容总结:一、AOP的核心概念回顾,二、Spring中AOP的用法,1. 传统Advisor方式,2. Aspect语法方式,Aspect的advice是基于方法的。,3. AspectJ注解方式,三、Spring AOP 源码学习,1、spring aop的工作流程是怎样?以传统的Advisor配置为例进行思考,2. 源码阅读思路,1、先看配置解析,看标签解析过程都做了什么、完成了什么。,2、 看织入的过程,3 、看方法被调用时的增强过程,4、源码对应的类图,

原文链接:https://www.cnblogs.com/leeSmall/p/10236553.html

以上是 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习) 的全部内容, 来源链接: utcz.com/z/296657.html

回到顶部