框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一、AOP的核心概念回顾
https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aop
我们先来看一下下面的这张图
说明:
程序运行时会调用很多方法,调用的很多方法就叫做Join points(连接点,可以被选择来进行增强的方法点),在方法的前或者后选择一个地方来切入,切入的的地方就叫做Pointcut(切入点,选择增强的方法),然后把要增强的功能(Advice)加入到切入点所在的位置。Advice和Pointcut组成一个切面(Aspect)
AOP的几个概念:
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接口:
示例代码:
被增强的目标对象:
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> 的属性了解
proxy-target-class="false"使用jdk的动态代理 默认配置
proxy-target-class="true"使用cglib的动态代理
掌握 spring Aop 的 API:
Advice:
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:
Pointcut的子类:
Advisor的子类:
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的子类:
测试类:
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 :
在/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配置为例进行思考
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:
a)解析<aop:config> 及它的子元素
org.springframework.aop.config.ConfigBeanDefinitionParser:
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)
所以说自动代理创建者是一个ProxyConfig、BeanFactoryAwrare、BeanPostProcessor
->
org.springframework.aop.config.AopConfigUtils.findPriorityForClass(String)
说明:
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的继承体系:
父类:
子类:
查看org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator的继承体系,搞清楚它继承实现了什么,他的父类和子类有哪些
父类:
子类:
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)
2)两种配置方式(advisor和aspectJ)解析之后都是向Bean工厂注册了Pointcut和Advisor的Bean定义
b)<aop:aspectj-autoproxy>开启AspectJ注解支持
1)注册了autoProxyCreator的Bean定义,它是一个ProxyConfig、BeanFactoryAwrare、BeanPostProcessor
org.springframework.aop.config.AopNamespaceHandler.init()
org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser
org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser.parse(Element, ParserContext)
org.springframework.aop.config.AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext, Element)
<!--开启AspectJ注解的支持 -->
<aop:aspectj-autoproxy>
<!-- 满足name里面的表达式(bean名称的表达式)的才进行切面的处理 -->
<aop:include name="bean名称的表达式"/>
</aop:aspectj-autoproxy>
<aop:include name="bean名称的表达式"/>对应源码:
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()
BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors() 中完成了注解的读取、Advisor对象的创建及缓存。
org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()
org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory.getAdvisors(MetadataAwareAspectInstanceFactory)
2、 看织入的过程
a) 在哪里做的织入?
在org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()里面打个断点拿到调用栈
入口:
com.study.leesmall.spring.sample.aop.AopMainUseAspectAnnotation
b) 如何判断 Bean 要不要被创建代理?如何排除 advice Bean 的?
排除 advice Bean :
跳过advice bean 跳过带有@Aspect注解的自己,不能自己为自己创建代理,否则进入死循环,如AspectAdviceBeanUseAnnotation
org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(Class<?>, String)
再次进来,是在 PostProcessAfterInitialization()
创建代理的方法:
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(Object, String, Object)
如何判断 Bean 要不要被创建代理:
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)
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、源码对应的类图
框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)的更多相关文章
- Spring的俩大核心概念:IOC、AOP
1.Spring 有两个核心部分: IOC 和 Aop (1)IOC:控制反转,把创建对象过程交给 Spring 进行管理 (2)Aop:面向切面,不修改源代码进行功能增强 2.Spring 特点 ...
- spring源码系列(十): 读取xml入口类 ClassPathXmlApplicationContext 分析
环境准备: 使用spring5.1.6版本 1 xml配置文件 <?xml version="1.0" encoding="UTF-8"?> < ...
- 框架源码系列十二:Mybatis源码之手写Mybatis
一.需求分析 1.Mybatis是什么? 一个半自动化的orm框架(Object Relation Mapping). 2.Mybatis完成什么工作? 在面向对象编程中,我们操作的都是对象,Myba ...
- Docker系列(一)Docker概述,核心概念讲解,安装部署
部分内容参考链接: Docker实战总结(非常全面,建议收藏) 一. Docker概述 Docker是一个开源的应用容器引擎(基于Go语言开发),让开发者可以打包他们的应用以及依赖包到一个可移植的容器 ...
- 【转】Spring学习---Spring IoC容器的核心原理
[原文] Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国. IoC和DI的基本概念 IoC(控制反转,英文含义:Inverse of Control)是Spr ...
- 框架源码系列三:手写Spring AOP(AOP分析、AOP概念学习、切面实现、织入实现)
一.AOP分析 问题1:AOP是什么? Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类方法进行功能增强. 问题2:我们需要做什么? 在我们的框架中 ...
- AOP执行增强-Spring 源码系列(5)
AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...
- spring源码系列8:AOP源码解析之代理的创建
回顾 首先回顾: JDK动态代理与CGLIB动态代理 Spring中的InstantiationAwareBeanPostProcessor和BeanPostProcessor的区别 我们得知 JDK ...
- 框架源码系列六:Spring源码学习之Spring IOC源码学习
Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的 1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...
随机推荐
- redis副本集
1.数据库配置细节: a.拷贝多个redis.conf文件:b.开启daemonize yes; c.Pid文件名字; d.指定端口; e.Log文件名字; f:Dump.rdb名字 2.配置redi ...
- 给电脑C盘系统盘瘦身的四种方法
进入后XP时代,又有很多朋友选择了Win 7系统,很多朋友在使用时可能会发现一个问题.电脑刚被买回来时,我们往往都把C盘(系统盘)预留几十个G左右的空间,而且可能装的东西并不是太多,但是磁盘却显示,可 ...
- web中icon 图标问题
每个页面都会引入 icon 小图标,下面说下它的用法 一.icon使用 icon的引入方式,与css外部引入方式类似,需要在头部引入, 即: <link rel="shortcut i ...
- parcel 中小型项目打包工具
"0配置"打包器(bundler)Parcel Parcel官网(有中文文档) webpack 要有大量的配置,这样带来的成本就是复杂性--与此相对的,Parcel 带来了简洁性. ...
- db2 执行报错收集
1.对于执行中的报错,可以在db2命令行下运行命令 : db2=>? SQLxxx 查看对应的报错原因及解决方法. 2.错误SQL0206N SQLSTATE=42703 检测到一个未定义的列 ...
- Mac Mysql 修改初始化密码
第一步: 点击系统偏好设置->最下边点MySQL,在弹出页面中,关闭服务 第二步:进入终端输入:cd /usr/local/mysql/bin/回车后 登录管理员权限 sudo su回车后输入以 ...
- 安装istio v1.0 详细过程和步骤
创建 istio 目录 [root@centos-110 ~]# mkdir istio [root@centos-110 ~]# cd istio 方案一: # 去下面的地址下载压缩包 # ...
- Python中syncio和aiohttp
CPython 解释器本身就不是线程安全的,因此有全局解释器锁(GIL),一次只允许使用一个线程执行 Python 字节码.因此,一个 Python 进程通常不能同时使用多个 CPU 核心.然而,标准 ...
- dlib的编译和安装
之前写过python dlib依赖的安装,安装过程还算比较复杂,还需要安装boost.Python依赖等,但是如果纯粹的编译C++的dlib库,则要简单得多,基本上不需要其他外部的依赖,这里简单叙述一 ...
- Git远程仓库地址变更本地如何修改
以项目test为例: 老地址:http://192.168.1.1:9797/john/test.git 新地址:http://git.xxx.xxx/john/test.git 远程仓库名称: or ...