一、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

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. //被增强的目标对象
  4. public class BeanQ {
  5.  
  6. public void do1(String task, int time) {
  7. System.out.println("-------------do1 do " + task + " time:" + time);
  8. }
  9.  
  10. public String service1(String name) {
  11. System.out.println("-------------servce1 do " + name);
  12. return name;
  13. }
  14.  
  15. public String service2(String name) {
  16. System.out.println("-------------servce2 do " + name);
  17. if (!"s1".equals(name)) {
  18. throw new IllegalArgumentException("参数 name != s1, name=" + name);
  19. }
  20.  
  21. return name + " hello!";
  22. }
  23. }

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

前置增强:

MyBeforeAdvice

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. import java.lang.reflect.Method;
  4.  
  5. import org.springframework.aop.MethodBeforeAdvice;
  6.  
  7. //前置增强
  8. public class MyBeforeAdvice implements MethodBeforeAdvice {
  9.  
  10. @Override
  11. public void before(Method method, Object[] args, Object target) throws Throwable {
  12. System.out.println("------ MyBeforeAdvice before 增强 " + target + " " + method);
  13. }
  14.  
  15. }

环绕增强:

MyArroundAdvice

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. import org.aopalliance.intercept.MethodInterceptor;
  4. import org.aopalliance.intercept.MethodInvocation;
  5.  
  6. //环绕增强
  7. public class MyArroundAdvice implements MethodInterceptor {
  8.  
  9. @Override
  10. public Object invoke(MethodInvocation invocation) throws Throwable {
  11. System.out.println("--------- 环绕 -前增强");
  12. Object ret = invocation.proceed();
  13. System.out.println("--------- 环绕 -后增强");
  14. return ret;
  15. }
  16.  
  17. }

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

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context.xsd
  10. http://www.springframework.org/schema/aop
  11. http://www.springframework.org/schema/aop/spring-aop.xsd">
  12.  
  13. <!-- 传统方式的aop begin -->
  14. <!--被增强的目标对象 -->
  15. <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
  16.  
  17. <!--配置advice -->
  18. <bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />
  19. <bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />
  20.  
  21. <!--配置pointcut -->
  22. <aop:config >
  23.  
  24. <!--全局切入点,任何一个aop-config都可以使用 -->
  25. <aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />
  26. <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />
  27.  
  28. <aop:advisor advice-ref="yyArroundAdvice"
  29. pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>
  30. </aop:config>
  31. <!-- 传统方式的aop end -->
  32.  
  33. </beans>

配置文件里面的注意点:

<aop:config> 的属性了解

  1. proxy-target-class="false"使用jdk的动态代理 默认配置
  2. proxy-target-class="true"使用cglib的动态代理

掌握 spring Aop 的 API:

Advice:

Advisor的代码:

  1. * Copyright 2002-2017 the original author or authors.
  2.  
  3. package org.springframework.aop;
  4.  
  5. import org.aopalliance.aop.Advice;
  6.  
  7. /**
  8. * Base interface holding AOP <b>advice</b> (action to take at a joinpoint)
  9. * and a filter determining the applicability of the advice (such as
  10. * a pointcut). <i>This interface is not for use by Spring users, but to
  11. * allow for commonality in support for different types of advice.</i>
  12. *
  13. * <p>Spring AOP is based around <b>around advice</b> delivered via method
  14. * <b>interception</b>, compliant with the AOP Alliance interception API.
  15. * The Advisor interface allows support for different types of advice,
  16. * such as <b>before</b> and <b>after</b> advice, which need not be
  17. * implemented using interception.
  18. *
  19. * @author Rod Johnson
  20. * @author Juergen Hoeller
  21. */
  22. public interface Advisor {
  23.  
  24. /**
  25. * Common placeholder for an empty {@code Advice} to be returned from
  26. * {@link #getAdvice()} if no proper advice has been configured (yet).
  27. * @since 5.0
  28. */
  29. Advice EMPTY_ADVICE = new Advice() {};
  30.  
  31. /**
  32. * Return the advice part of this aspect. An advice may be an
  33. * interceptor, a before advice, a throws advice, etc.
  34. * @return the advice that should apply if the pointcut matches
  35. * @see org.aopalliance.intercept.MethodInterceptor
  36. * @see BeforeAdvice
  37. * @see ThrowsAdvice
  38. * @see AfterReturningAdvice
  39. */
  40. Advice getAdvice();
  41.  
  42. /**
  43. * Return whether this advice is associated with a particular instance
  44. * (for example, creating a mixin) or shared with all instances of
  45. * the advised class obtained from the same Spring bean factory.
  46. * <p><b>Note that this method is not currently used by the framework.</b>
  47. * Typical Advisor implementations always return {@code true}.
  48. * Use singleton/prototype bean definitions or appropriate programmatic
  49. * proxy creation to ensure that Advisors have the correct lifecycle model.
  50. * @return whether this advice is associated with a particular target instance
  51. */
  52. boolean isPerInstance();
  53.  
  54. }

 

Pointcut:

Pointcut的子类:

Advisor的子类:

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

PointcutAdvisor的代码:

  1. * Copyright 2002-2012 the original author or authors.
  2.  
  3. package org.springframework.aop;
  4.  
  5. /**
  6. * Superinterface for all Advisors that are driven by a pointcut.
  7. * This covers nearly all advisors except introduction advisors,
  8. * for which method-level matching doesn't apply.
  9. *
  10. * @author Rod Johnson
  11. */
  12. public interface PointcutAdvisor extends Advisor {
  13.  
  14. /**
  15. * Get the Pointcut that drives this advisor.
  16. */
  17. Pointcut getPointcut();
  18.  
  19. }

PointcutAdvisor的子类:

测试类:

AopMain

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.support.GenericXmlApplicationContext;
  5.  
  6. public class AopMain {
  7. public static void main(String[] args) {
  8. ApplicationContext context = new GenericXmlApplicationContext(
  9. "classpath:com/study/leesmall/spring/sample/aop/application.xml");
  10.  
  11. BeanQ bq = context.getBean(BeanQ.class);
  12. bq.do1("task1", 20);
  13. System.out.println();
  14.  
  15. bq.service1("service1");
  16.  
  17. System.out.println();
  18. bq.service2("ssss");
  19. }
  20. }

测试结果:

  1. ------ MyBeforeAdvice before 增强 com.study.leesmall.spring.sample.aop.BeanQ@1d119efb public void com.study.leesmall.spring.sample.aop.BeanQ.do1(java.lang.String,int)
  2. -------------do1 do task1 time:20
  3.  
  4. --------- 环绕 -前增强
  5. -------------servce1 do service1
  6. --------- 环绕 -后增强
  7.  
  8. --------- 环绕 -前增强
  9. -------------servce2 do ssss
  10. Exception in thread "main" java.lang.IllegalArgumentException: 参数 name != s1, name=ssss
  11. at com.study.leesmall.spring.sample.aop.BeanQ.service2(BeanQ.java:18)
  12. at com.study.leesmall.spring.sample.aop.BeanQ$$FastClassBySpringCGLIB$$3d1515ac.invoke(<generated>)
  13. at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
  14. at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
  15. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
  16. at com.study.leesmall.spring.sample.aop.MyArroundAdvice.invoke(MyArroundAdvice.java:12)
  17. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
  18. at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
  19. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
  20. at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
  21. at com.study.leesmall.spring.sample.aop.BeanQ$$EnhancerBySpringCGLIB$$2fc5343.service2(<generated>)
  22. 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:

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. import org.aspectj.lang.JoinPoint;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5.  
  6. //定义包含 Advice 方法的 Bean 类
  7. public class AspectAdviceBean {
  8.  
  9. public void before1() {
  10. System.out.println("----------- AspectAdviceBean before1 增强 ");
  11. }
  12.  
  13. //JoinPoint看具体哪个方法被增强了,JoinPoint一定要放在第一个参数
  14. public void before2(JoinPoint jp) {
  15. System.out.println("----------- AspectAdviceBean before2 增强 for " + jp);
  16. }
  17.  
  18. //调用被增强的方法时传参数
  19. //<aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))
  20. //and args(tk,..)" arg-names=""/>
  21. //args(tk,..)有两个意思,第一个意思是被增强的方法的第一个参数的类型要和before3的参数tk的类型一样
  22. //第二个意思是被增强的方法的第一个参数tk要赋值给before3的参数tk
  23. //arg-names="" 当不能确定方法参数的顺序时可以用这个参数指定arg-names="param1,param2"
  24. public void before3(String tk) {
  25. System.out.println("----------- AspectAdviceBean before3 增强 参数tk= " + tk);
  26. }
  27.  
  28. //调用被增强的方法时传参数
  29. public void before4(String tk, int ti) {
  30. System.out.println("----------- AspectAdviceBean before4 增强 参数tk= " + tk + " ti=" + ti);
  31. }
  32.  
  33. //ProceedingJoinPoint正在处理的方法
  34. public Object arround1(ProceedingJoinPoint pjp) throws Throwable {
  35. System.out.println("----------- AspectAdviceBean arround1 环绕-前增强 for " + pjp);
  36. Object ret = pjp.proceed();
  37. System.out.println("----------- AspectAdviceBean arround1 环绕-后增强 for " + pjp);
  38. return ret;
  39. }
  40.  
  41. public Object arround2(ProceedingJoinPoint pjp, String name) throws Throwable {
  42. System.out.println("--------- AspectAdviceBean arround2 参数 name=" + name);
  43. System.out.println("----------- AspectAdviceBean arround2 环绕-前增强 for " + pjp);
  44. Object ret = pjp.proceed();
  45. System.out.println("----------- AspectAdviceBean arround2 环绕-后增强 for " + pjp);
  46. return ret;
  47. }
  48.  
  49. public void afterReturning(Object retValue) {
  50. System.out.println("----------- AspectAdviceBean afterReturning 增强 , 返回值为: " + retValue);
  51. }
  52.  
  53. public void afterThrowing(JoinPoint jp, Exception e) {
  54. System.out.println("----------- AspectAdviceBean afterThrowing 增强 for " + jp);
  55. System.out.println("----------- AspectAdviceBean afterThrowing 增强 异常 :" + e);
  56. }
  57.  
  58. public void after(JoinPoint jp) {
  59. System.out.println("----------- AspectAdviceBean after 增强 for " + jp);
  60. }
  61.  
  62. }

JoinPoint和ProceedingJoinPoint :

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

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context.xsd
  10. http://www.springframework.org/schema/aop
  11. http://www.springframework.org/schema/aop/spring-aop.xsd">
  12.  
  13. <!-- 传统方式的aop begin -->
  14. <!--被增强的目标对象 -->
  15. <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
  16.  
  17. <!--配置advice -->
  18. <bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />
  19. <bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />
  20.  
  21. <!--配置pointcut -->
  22. <aop:config >
  23. <aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />
  24. <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />
  25.  
  26. <aop:advisor advice-ref="yyArroundAdvice"
  27. pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>
  28. </aop:config>
  29. <!-- 传统方式的aop end -->
  30.  
  31. <!-- AspectJ的aop begin -->
  32. <!-- 配置了包含advice方法的Bean -->
  33. <bean id="aspectAdviceBean" class="com.study.leesmall.spring.sample.aop.AspectAdviceBean" />
  34.  
  35. </beans>

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

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context.xsd
  10. http://www.springframework.org/schema/aop
  11. http://www.springframework.org/schema/aop/spring-aop.xsd">
  12.  
  13. <!-- 传统方式的aop begin -->
  14. <!--被增强的目标对象 -->
  15. <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
  16.  
  17. <!--配置advice -->
  18. <bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />
  19. <bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />
  20.  
  21. <!--配置pointcut -->
  22. <aop:config >
  23. <aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />
  24. <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />
  25.  
  26. <aop:advisor advice-ref="yyArroundAdvice"
  27. pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>
  28. </aop:config>
  29. <!-- 传统方式的aop end -->
  30.  
  31. <!-- AspectJ的aop begin -->
  32. <!-- 配置了包含advice方法的Bean -->
  33. <bean id="aspectAdviceBean" class="com.study.leesmall.spring.sample.aop.AspectAdviceBean" />
  34.  
  35. <!--配置 Aspect (引用包含 advice 方法的 bean),在里面配置各种 Advice(method + pointcut) -->
  36. <aop:config>
  37. <aop:pointcut id="services" expression="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))" />
  38. <aop:aspect id="a1" ref="aspectAdviceBean" order="1">
  39. <aop:before method="before1" pointcut-ref="doMethods" />
  40. <aop:before method="before2" pointcut-ref="doMethods"/>
  41. <!--args(tk,..)有两个意思,第一个意思是被增强的方法的第一个参数的类型要和before3的参数tk的类型一样
  42. 第二个意思是被增强的方法的第一个参数tk要赋值给before3的参数tk;
  43. arg-names="" 当不能确定方法参数的顺序时可以用这个参数指定arg-names="param1,param2"
  44. -->
  45. <aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,..)"/>
  46. <aop:before method="before4" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,ti)"/>
  47. <aop:around method="arround1" pointcut-ref="services"/>
  48. <aop:around method="arround2" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..)) and args(name)"/>
  49. <aop:after-returning method="afterReturning" pointcut-ref="services" returning="retValue"/>
  50. <aop:after-throwing method="afterThrowing" pointcut-ref="services" throwing="e"/>
  51. <aop:after method="after" pointcut-ref="services"/>
  52. </aop:aspect>
  53. </aop:config>
  54. <!-- AspectJ的aop end -->
  55.  
  56. </beans>

被增强的目标对象:

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. //被增强的目标对象
  4. public class BeanQ {
  5.  
  6. public void do1(String task, int time) {
  7. System.out.println("-------------do1 do " + task + " time:" + time);
  8. }
  9.  
  10. public String service1(String name) {
  11. System.out.println("-------------servce1 do " + name);
  12. return name;
  13. }
  14.  
  15. public String service2(String name) {
  16. System.out.println("-------------servce2 do " + name);
  17. /**if (!"s1".equals(name)) {
  18. throw new IllegalArgumentException("参数 name != s1, name=" + name);
  19. }**/
  20.  
  21. return name + " hello!";
  22. }
  23. }

测试类:

AopMain

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.support.GenericXmlApplicationContext;
  5.  
  6. public class AopMain {
  7. public static void main(String[] args) {
  8. ApplicationContext context = new GenericXmlApplicationContext(
  9. "classpath:com/study/leesmall/spring/sample/aop/application.xml");
  10.  
  11. BeanQ bq = context.getBean(BeanQ.class);
  12. bq.do1("task1", 20);
  13. System.out.println();
  14.  
  15. bq.service1("service1");
  16.  
  17. System.out.println();
  18. bq.service2("ssss");
  19. }
  20. }

测试结果:

  1. ----------- AspectAdviceBean before1 增强
  2. ----------- AspectAdviceBean before2 增强 for execution(void com.study.mike.spring.sample.aop.BeanQ.do1(leesmall,int))
  3. ----------- AspectAdviceBean before3 增强 参数tk= task1
  4. ----------- AspectAdviceBean before4 增强 参数tk= task1 ti=20
  5. ------ MyBeforeAdvice before 增强 com.study.mike.spring.sample.aop.BeanQ@3a0baae5 public void com.study.mike.spring.sample.aop.BeanQ.do1(java.lang.leesmall,int)
  6. -------------do1 do task1 time:20
  7.  
  8. ----------- AspectAdviceBean arround1 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
  9. --------- AspectAdviceBean arround2 参数 name=service1
  10. ----------- AspectAdviceBean arround2 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
  11. --------- 环绕 -前增强
  12. -------------servce1 do service1
  13. --------- 环绕 -后增强
  14. ----------- AspectAdviceBean arround2 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
  15. ----------- AspectAdviceBean arround1 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
  16. ----------- AspectAdviceBean afterReturning 增强 , 返回值为: service1
  17. ----------- AspectAdviceBean after 增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
  18.  
  19. ----------- AspectAdviceBean arround1 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
  20. --------- AspectAdviceBean arround2 参数 name=ssss
  21. ----------- AspectAdviceBean arround2 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
  22. --------- 环绕 -前增强
  23. -------------servce2 do ssss
  24. --------- 环绕 -后增强
  25. ----------- AspectAdviceBean arround2 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
  26. ----------- AspectAdviceBean arround1 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
  27. ----------- AspectAdviceBean afterReturning 增强 , 返回值为: ssss hello!
  28. ----------- AspectAdviceBean after 增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))

3. AspectJ注解方式

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

  1. <dependency>
  2. <groupId>org.aspectj</groupId>
  3. <artifactId>aspectjweaver</artifactId>
  4. <version>1.9.1</version>
  5. </dependency>

2)被增强的目标对象

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. //被增强的目标对象
  4. public class BeanQ {
  5.  
  6. public void do1(String task, int time) {
  7. System.out.println("-------------do1 do " + task + " time:" + time);
  8. }
  9.  
  10. public String service1(String name) {
  11. System.out.println("-------------servce1 do " + name);
  12. return name;
  13. }
  14.  
  15. public String service2(String name) {
  16. System.out.println("-------------servce2 do " + name);
  17. if (!"s1".equals(name)) {
  18. throw new IllegalArgumentException("参数 name != s1, name=" + name);
  19. }
  20.  
  21. return name + " hello!";
  22. }
  23. }

3)AspectJ注解方式实现AOP

AspectAdviceBeanUseAnnotation

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. import org.aspectj.lang.JoinPoint;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.annotation.After;
  6. import org.aspectj.lang.annotation.AfterReturning;
  7. import org.aspectj.lang.annotation.AfterThrowing;
  8. import org.aspectj.lang.annotation.Around;
  9. import org.aspectj.lang.annotation.Aspect;
  10. import org.aspectj.lang.annotation.Before;
  11. import org.aspectj.lang.annotation.Pointcut;
  12.  
  13. //AspectJ注解方式
  14. @Aspect
  15. public class AspectAdviceBeanUseAnnotation {
  16.  
  17. // 定义一个全局的Pointcut
  18. @Pointcut("execution(* com.study.leesmall.spring.sample.aop.*.do*(..))")
  19. public void doMethods() {
  20. }
  21.  
  22. // 定义一个全局的Pointcut
  23. @Pointcut("execution(* com.study.leesmall.spring.sample.aop.*.service*(..))")
  24. public void services() {
  25. }
  26.  
  27. // 定义一个Before Advice
  28. @Before("doMethods() and args(tk,..)")
  29. public void before3(String tk) {
  30. System.out.println("----------- AspectAdviceBeanUseAnnotation before3 增强 参数tk= " + tk);
  31. }
  32.  
  33. //环绕增强
  34. @Around("services() and args(name,..)")
  35. public Object around2(ProceedingJoinPoint pjp, String name) throws Throwable {
  36. System.out.println("--------- AspectAdviceBeanUseAnnotation arround2 参数 name=" + name);
  37. System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 环绕-前增强 for " + pjp);
  38. Object ret = pjp.proceed();
  39. System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 环绕-后增强 for " + pjp);
  40. return ret;
  41. }
  42.  
  43. @AfterReturning(pointcut = "services()", returning = "retValue")
  44. public void afterReturning(Object retValue) {
  45. System.out.println("----------- AspectAdviceBeanUseAnnotation afterReturning 增强 , 返回值为: " + retValue);
  46. }
  47.  
  48. @AfterThrowing(pointcut = "services()", throwing = "e")
  49. public void afterThrowing(JoinPoint jp, Exception e) {
  50. System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 增强 for " + jp);
  51. System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 增强 异常 :" + e);
  52. }
  53.  
  54. @After("doMethods()")
  55. public void after(JoinPoint jp) {
  56. System.out.println("----------- AspectAdviceBeanUseAnnotation after 增强 for " + jp);
  57. }
  58.  
  59. }

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

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context.xsd
  10. http://www.springframework.org/schema/aop
  11. http://www.springframework.org/schema/aop/spring-aop.xsd">
  12.  
  13. <!--被增强的目标对象 -->
  14. <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
  15.  
  16. <!--AspectJ注解方式实现的AOP -->
  17. <bean id="aspectAdviceBeanUseAnnotation" class="com.study.leesmall.spring.sample.aop.AspectAdviceBeanUseAnnotation" />
  18.  
  19. <!--开启AspectJ注解的支持 -->
  20. <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  21.  
  22. </beans>

开启@Aspectj注解方式支持:
Xml中<aop:aspectj-autoproxy></aop:aspectj-autoproxy>注意了解它的属性、及子元素

注解方式开启:

  1. @Configuration
  2. @EnableAspectJAutoProxy
  3. public class AppConfig {
  4. }

5)测试类

  1. package com.study.leesmall.spring.sample.aop;
  2.  
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.support.GenericXmlApplicationContext;
  5.  
  6. public class AopMainUseAspectAnnotation {
  7. public static void main(String[] args) {
  8. ApplicationContext context = new GenericXmlApplicationContext(
  9. "classpath:com/study/leesmall/spring/sample/aop/application2.xml");
  10.  
  11. BeanQ bq = context.getBean(BeanQ.class);
  12. bq.do1("task1", 20);
  13. System.out.println();
  14.  
  15. bq.service1("service1");
  16.  
  17. }
  18. }

7)测试结果

  1. ----------- AspectAdviceBeanUseAnnotation before3 增强 参数tk= task1
  2. -------------do1 do task1 time:20
  3. ----------- AspectAdviceBeanUseAnnotation after 增强 for execution(void com.study.leesmall.spring.sample.aop.BeanQ.do1(String,int))
  4.  
  5. --------- AspectAdviceBeanUseAnnotation arround2 参数 name=service1
  6. ----------- AspectAdviceBeanUseAnnotation arround2 环绕-前增强 for execution(String com.study.leesmall.spring.sample.aop.BeanQ.service1(String))
  7. -------------servce1 do service1
  8. ----------- AspectAdviceBeanUseAnnotation arround2 环绕-后增强 for execution(String com.study.leesmall.spring.sample.aop.BeanQ.service1(String))
  9. ----------- 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

  1. 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方式

  1. <aop:config>
  2. <aop:pointcut id="services" expression="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))" />
  3. <aop:aspect id="a1" ref="aspectAdviceBean" order="1">
  4. <aop:before method="before1" pointcut-ref="doMethods" />
  5. <aop:before method="before2" pointcut-ref="doMethods"/>
  6. <!--args(tk,..)有两个意思,第一个意思是被增强的方法的第一个参数的类型要和before3的参数tk的类型一样
  7. 第二个意思是被增强的方法的第一个参数tk要赋值给before3的参数tk;
  8. arg-names="" 当不能确定方法参数的顺序时可以用这个参数指定arg-names="param1,param2"
  9. -->
  10. <aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,..)" arg-names=""/>
  11. <aop:before method="before4" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,ti)"/>
  12. <aop:around method="arround1" pointcut-ref="services"/>
  13. <aop:around method="arround2" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..)) and args(name)"/>
  14. <aop:after-returning method="afterReturning" pointcut-ref="services" returning="retValue"/>
  15. <aop:after-throwing method="afterThrowing" pointcut-ref="services" throwing="e"/>
  16. <aop:after method="after" pointcut-ref="services"/>
  17. </aop:aspect>
  18. </aop:config>

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

父类:

子类:

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

父类:

子类:

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

  1. <bean id="aspectAdviceBeanUseAnnotation" class="com.study.leesamll.spring.sample.aop.AspectAdviceBeanUseAnnotation" />
  2.  
  3. <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)

  1. <!--开启AspectJ注解的支持 -->
  2. <aop:aspectj-autoproxy>
  3. <!-- 满足name里面的表达式(bean名称的表达式)的才进行切面的处理 -->
  4. <aop:include name="bean名称的表达式"/>
  5. </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 源码学习)的更多相关文章

  1. Spring的俩大核心概念:IOC、AOP

    1.Spring 有两个核心部分: IOC 和 Aop (1)IOC:控制反转,把创建对象过程交给 Spring 进行管理   (2)Aop:面向切面,不修改源代码进行功能增强 2.Spring 特点 ...

  2. spring源码系列(十): 读取xml入口类 ClassPathXmlApplicationContext 分析

    环境准备: 使用spring5.1.6版本 1 xml配置文件 <?xml version="1.0" encoding="UTF-8"?> < ...

  3. 框架源码系列十二:Mybatis源码之手写Mybatis

    一.需求分析 1.Mybatis是什么? 一个半自动化的orm框架(Object Relation Mapping). 2.Mybatis完成什么工作? 在面向对象编程中,我们操作的都是对象,Myba ...

  4. Docker系列(一)Docker概述,核心概念讲解,安装部署

    部分内容参考链接: Docker实战总结(非常全面,建议收藏) 一. Docker概述 Docker是一个开源的应用容器引擎(基于Go语言开发),让开发者可以打包他们的应用以及依赖包到一个可移植的容器 ...

  5. 【转】Spring学习---Spring IoC容器的核心原理

    [原文] Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国. IoC和DI的基本概念 IoC(控制反转,英文含义:Inverse of Control)是Spr ...

  6. 框架源码系列三:手写Spring AOP(AOP分析、AOP概念学习、切面实现、织入实现)

    一.AOP分析 问题1:AOP是什么? Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类方法进行功能增强. 问题2:我们需要做什么? 在我们的框架中 ...

  7. AOP执行增强-Spring 源码系列(5)

    AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...

  8. spring源码系列8:AOP源码解析之代理的创建

    回顾 首先回顾: JDK动态代理与CGLIB动态代理 Spring中的InstantiationAwareBeanPostProcessor和BeanPostProcessor的区别 我们得知 JDK ...

  9. 框架源码系列六:Spring源码学习之Spring IOC源码学习

    Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的  1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...

随机推荐

  1. Android软件设计规范---命名规则/代码包设计规则等

    如果你将源码作为产品发布,就需要确认它是否被很好地打包并且清晰无误,一如你已构建的其他任何产品. 作为软件设计师,代码即是产品:不仅需要实现功能,还需有“优美.大方”的外表. 标识符命名法,标识符命名 ...

  2. Windows环境下Composer的安装教程

    1.先下载Composer-Setup.exe,下载地址:下载Composer .会自动搜索php.exe的安装路径,如果没有,就手动找到php路径下的php.exe. 2.在PHP目录下,打开php ...

  3. React Native升级目标SDK

    React Native升级目标SDK 打开在 android/app/的build.gradle 找到 android { } 区块 改变以下属性 compileSdkVersion 26 buil ...

  4. [P2850][USACO06DEC]虫洞Wormholes (最短路)

    死活调不出来 后来是发现这题建边的原因…… 吐血.jpg 所谓的虫洞传说也就是负边了 然后这里打的spfa和原来的不一样 感觉hzwer大佬的spfa好强啊…… 也更易写一点 贴代码 #include ...

  5. Ruby语法基础(一)

    Ruby语法基础(一) Ruby是一种开源的面向对象程序设计的服务器端脚本语言,最初由松本行弘(Matz)设计开发,追求『快乐和生产力』,程序员友好型,被称为『human-oriented langu ...

  6. nullptr/NULL

    NULL vs nullptr 在过去,我们如果要表示一个指针为空,我们条件反射肯定会这么写: int *p = NULL; 然而啊,有没有想过这是有问题的,比如下面的这段代码: #include & ...

  7. modelform的操作以及验证

    1,model的两个功能 1,数据库操作 2,验证只有一个clean方法作为钩子来操作,方法比较少 2,form(专门用来做验证的) 根据form里面写的类,类里面的字段,这些字段里有内置的的正则表达 ...

  8. [原创]RedisDesktopManager工具使用介绍

    [原创]RedisDesktopManager工具使用介绍 1 RedisDesktopManager简介 一款能够跨平台使用的开源性redis可视化工具.redis desktop manager主 ...

  9. 360se打开慢,lsass 过高 , cpu温度上升

    rd /s /q "%AppData%\Roaming\Microsoft\Protect" rem C:\Users\Administrator\AppData\Roaming\ ...

  10. log4j2自定义Appender(输出到文件/RPC服务中)

    1.背景 虽然log4j很强大,可以将日志输出到文件.DB.ES等.但是有时候确难免完全适合自己,此时我们就需要自定义Appender,使日志输出到指定的位置上. 本文,将通过两个例子说明自定义APP ...