从XML配置角度理解Spring AOP

打印 上一主题 下一主题

主题 511|帖子 511|积分 1533

本文分享自华为云社区《Spring高手之路18——从XML配置角度理解Spring AOP》,作者: 砖业洋__。
1. Spring AOP与动态代理

1.1 Spring AOP和动态代理的关系

Spring AOP使用动态代理作为其主要机制来实现面向切面的编程。这种机制允许Spring在运行时动态地创建代理对象,这些代理对象包装了目标对象(即业务组件),以便在调用目标对象的方法前后插入额外的行为(如安全查抄、事件管理、日志记录等)。

  • JDK动态代理:当目标对象实现了一个或多个接口时,Spring AOP默认使用JDK的动态代理。JDK动态代理通过反射机制,为接口创建一个代理对象,这个代理对象会拦截对目标接口方法的所有调用。
  • CGLIB代理:如果目标对象没有实现任何接口,Spring AOP会退回到使用CGLIB库生成目标类的子类。CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它在运行时扩展了Java类,并在子类中覆盖了方法来实现方法拦截。
无论使用哪种代理方式,目的都是在不改变原有业务逻辑代码的基础上,通过切面定义的通知在方法执行的不同阶段插入附加行为。
1.2 AOP基本术语

切面(Aspect):切面是面向切面编程的核心,它是将横跨多个类的关注点(如日志记录、事件管理等)模块化的构造。一个切面可以包罗多种范例的通知(Advice)和一个或多个切点(Pointcut),用于定义在何处以及何时执行这些通知。
毗连点(Join Point):毗连点代表程序执行过程中的某个特定位置,Spring AOP限定这些位置为方法的调用。简而言之,毗连点就是能够插入切面通知的点。
通知(Advice):通知定义了切面在毗连点上要执行的动作。根据通知范例的不同,这些动作可以在方法调用之前、之后、返回结果后或抛出异常时执行。通知范例包括:

  • 前置通知(Before advice):在方法执行之前执行。
  • 后置通知(After advice):在方法执行后执行,无论其结果怎样。
  • 返回后通知(After-returning advice):在方法成功执行之后执行。
  • 异常后通知(After-throwing advice):在方法抛出异常后执行。
  • 环绕通知(Around advice):在方法执行之前和之后执行,提供对方法调用的全面控制。
切点(Pointcut):切点是一个表达式,切点表达式允许通过方法名称、访问修饰符等条件来匹配毗连点,决定了通知应该在哪些方法执行时触发。
目标对象(Target Object):被一个或多个切面所通知的对象。也被称为被代理对象。
AOP代理(AOP Proxy):AOP框架创建的对象,用于实现切面契约(由通知和切点定义)。在Spring AOP中,AOP代理可以是JDK动态代理或CGLIB代理。
引入(Introduction):引入允许向现有的类添加新的方法或属性。这是通过定义一个或多个附加接口(Introduction interfaces)实现的,AOP框架会为目标对象创建一个代理,该代理实现这些接口。
如果照旧觉得抽象,我们再举一个影戏制作的例子来类比
切面(Aspect)
想象一下,有人正在拍摄一部影戏,而影戏中的殊效(比如爆炸和特别光效)就像是应用程序中需要处理的横切关注点(比如日志记录或事件管理)。这些殊效会在影戏的很多不同场景中出现,而不仅仅范围于某一个特定场景。在AOP中,这些“殊效”就是切面,它们可以被应用到程序的多个部分,而不需要改变实际的场景(或代码)。
毗连点(Join Point)
继承使用影戏的比喻,每个场景中的特定时刻,比如一个爆炸发生的瞬间,可以看作是一个毗连点。在编程中,这通常对应于方法的调用。
通知(Advice)
通知就像是导演对殊效团队的具体指令,比如“在这个场景开始之前参加一个爆炸结果”或“场景结束后显示烟雾渐散的结果”。这些指令告诉殊效团队在影戏的哪个具体时刻应该添加特定的结果。在AOP中,这些“指令”就是通知,指定了切面(殊效)应该在毗连点(特定的代码执行时刻)之前、之后或周围执行。
切点(Pointcut)
如果说通知是导演对殊效团队的指令,那么切点就是指令中包罗的具体条件,比如“所有夜晚的外景戏”。切点定义了哪些毗连点(比如哪些具体的方法调用)应该吸收通知(殊效指令)。
目标对象(Target Object)
目标对象就是那些需要添加殊效的场景。在我们的编程比喻中,它们是那些被切面逻辑影响的对象(比如需要日志记录的类)。
AOP代理(AOP Proxy)
AOP代理就像是殊效团队提供的一个虚拟的、可控制殊效的场景副本。这个副本在观众看来与原场景无异,但实际上它能在导演需要的时刻自动添加殊效。在编程中,代理是一个被AOP框架自动创建的对象,它包装了目标对象,确保了通知(殊效指令)在精确的时间被执行。
引入(Introduction)
引入就好比是在影戏中参加一个全新的脚色或者场景,这在原本的脚本中并不存在。在AOP中,引入允许我们向现有的类添加新的方法或属性,这就像是在不改变原始脚本的情况下扩展影戏的内容。
2. 通过XML配置实现Spring AOP

Spring提供了丰富的AOP支持,可以通过XML配置来定义切面、通知(advice)和切点(pointcuts)。这样可以在不修改源代码的情况下增长额外的行为(如日志、事件管理等)
实现步骤:

  • 添加Spring依赖:在项目的pom.xml中添加Spring框架和AOP相关的依赖。
  • 定义业务接口和实现类:创建业务逻辑接口及其实现,比如一个简单的服务类。
  • 定义切面类:创建一个切面类,用于定义前置、后置、环绕等通知。
  • 配置XML:在applicationContext.xml中配置切面和业务bean,以及AOP相关的标签。
2.1 添加Spring依赖

在pom.xml文件中,添加以下依赖
  1. <dependencies>
  2. <aop:config proxy-target-class="true">
  3. <aop:config proxy-target-class="true">
  4.    
  5. </aop:config>
  6. </aop:config><dependency>
  7. <aop:config proxy-target-class="true">
  8. <aop:config proxy-target-class="true">
  9.    
  10. </aop:config>
  11. </aop:config><aop:config proxy-target-class="true">
  12. <aop:config proxy-target-class="true">
  13.    
  14. </aop:config>
  15. </aop:config><groupId>org.springframework</groupId>
  16. <aop:config proxy-target-class="true">
  17. <aop:config proxy-target-class="true">
  18.    
  19. </aop:config>
  20. </aop:config><aop:config proxy-target-class="true">
  21. <aop:config proxy-target-class="true">
  22.    
  23. </aop:config>
  24. </aop:config><artifactId>spring-context</artifactId>
  25. <aop:config proxy-target-class="true">
  26. <aop:config proxy-target-class="true">
  27.    
  28. </aop:config>
  29. </aop:config><aop:config proxy-target-class="true">
  30. <aop:config proxy-target-class="true">
  31.    
  32. </aop:config>
  33. </aop:config><version>5.3.10</version>
  34. <aop:config proxy-target-class="true">
  35. <aop:config proxy-target-class="true">
  36.    
  37. </aop:config>
  38. </aop:config></dependency>
  39. <aop:config proxy-target-class="true">
  40. <aop:config proxy-target-class="true">
  41.    
  42. </aop:config>
  43. </aop:config><dependency>
  44. <aop:config proxy-target-class="true">
  45. <aop:config proxy-target-class="true">
  46.    
  47. </aop:config>
  48. </aop:config><aop:config proxy-target-class="true">
  49. <aop:config proxy-target-class="true">
  50.    
  51. </aop:config>
  52. </aop:config><groupId>org.springframework</groupId>
  53. <aop:config proxy-target-class="true">
  54. <aop:config proxy-target-class="true">
  55.    
  56. </aop:config>
  57. </aop:config><aop:config proxy-target-class="true">
  58. <aop:config proxy-target-class="true">
  59.    
  60. </aop:config>
  61. </aop:config><artifactId>spring-aop</artifactId>
  62. <aop:config proxy-target-class="true">
  63. <aop:config proxy-target-class="true">
  64.    
  65. </aop:config>
  66. </aop:config><aop:config proxy-target-class="true">
  67. <aop:config proxy-target-class="true">
  68.    
  69. </aop:config>
  70. </aop:config><version>5.3.10</version>
  71. <aop:config proxy-target-class="true">
  72. <aop:config proxy-target-class="true">
  73.    
  74. </aop:config>
  75. </aop:config></dependency>
  76. <aop:config proxy-target-class="true">
  77. <aop:config proxy-target-class="true">
  78.    
  79. </aop:config>
  80. </aop:config><dependency>
  81. <aop:config proxy-target-class="true">
  82. <aop:config proxy-target-class="true">
  83.    
  84. </aop:config>
  85. </aop:config><aop:config proxy-target-class="true">
  86. <aop:config proxy-target-class="true">
  87.    
  88. </aop:config>
  89. </aop:config><groupId>org.aspectj</groupId>
  90. <aop:config proxy-target-class="true">
  91. <aop:config proxy-target-class="true">
  92.    
  93. </aop:config>
  94. </aop:config><aop:config proxy-target-class="true">
  95. <aop:config proxy-target-class="true">
  96.    
  97. </aop:config>
  98. </aop:config><artifactId>aspectjweaver</artifactId>
  99. <aop:config proxy-target-class="true">
  100. <aop:config proxy-target-class="true">
  101.    
  102. </aop:config>
  103. </aop:config><aop:config proxy-target-class="true">
  104. <aop:config proxy-target-class="true">
  105.    
  106. </aop:config>
  107. </aop:config><version>1.9.6</version>
  108. <aop:config proxy-target-class="true">
  109. <aop:config proxy-target-class="true">
  110.    
  111. </aop:config>
  112. </aop:config></dependency>
  113. </dependencies>
复制代码
2.2 定义业务接口和实现类

起首,我们定义一个业务逻辑接口MyService和它的实现MyServiceImpl。
MyService.java:
  1. package com.example.demo.aop;
  2. public interface MyService {
  3. <aop:config proxy-target-class="true">
  4. <aop:config proxy-target-class="true">
  5.    
  6. </aop:config>
  7. </aop:config>String performAction(String input) throws Exception;
  8. }
复制代码
MyServiceImpl.java:
  1. package com.example.demo.aop;
  2. public class MyServiceImpl implements MyService {
  3. <aop:config proxy-target-class="true">
  4. <aop:config proxy-target-class="true">
  5.    
  6. </aop:config>
  7. </aop:config>@Override
  8. <aop:config proxy-target-class="true">
  9. <aop:config proxy-target-class="true">
  10.    
  11. </aop:config>
  12. </aop:config>public String performAction(String action) throws Exception {
  13. <aop:config proxy-target-class="true">
  14. <aop:config proxy-target-class="true">
  15.    
  16. </aop:config>
  17. </aop:config><aop:config proxy-target-class="true">
  18. <aop:config proxy-target-class="true">
  19.    
  20. </aop:config>
  21. </aop:config>System.out.println("Performing action in MyService: " + action);
  22. <aop:config proxy-target-class="true">
  23. <aop:config proxy-target-class="true">
  24.    
  25. </aop:config>
  26. </aop:config><aop:config proxy-target-class="true">
  27. <aop:config proxy-target-class="true">
  28.    
  29. </aop:config>
  30. </aop:config>if ("throw".equals(action)) {
  31. <aop:config proxy-target-class="true">
  32. <aop:config proxy-target-class="true">
  33.    
  34. </aop:config>
  35. </aop:config><aop:config proxy-target-class="true">
  36. <aop:config proxy-target-class="true">
  37.    
  38. </aop:config>
  39. </aop:config><aop:config proxy-target-class="true">
  40. <aop:config proxy-target-class="true">
  41.    
  42. </aop:config>
  43. </aop:config>throw new Exception("Exception from MyService");
  44. <aop:config proxy-target-class="true">
  45. <aop:config proxy-target-class="true">
  46.    
  47. </aop:config>
  48. </aop:config><aop:config proxy-target-class="true">
  49. <aop:config proxy-target-class="true">
  50.    
  51. </aop:config>
  52. </aop:config>}
  53. <aop:config proxy-target-class="true">
  54. <aop:config proxy-target-class="true">
  55.    
  56. </aop:config>
  57. </aop:config><aop:config proxy-target-class="true">
  58. <aop:config proxy-target-class="true">
  59.    
  60. </aop:config>
  61. </aop:config>return "Action performed: " + action;
  62. <aop:config proxy-target-class="true">
  63. <aop:config proxy-target-class="true">
  64.    
  65. </aop:config>
  66. </aop:config>}
  67. }
复制代码
2.3 定义切面类

接下来,我们定义一个切面类MyAspect,这个类将包罗一个前置通知(advice),它在MyService的performAction方法执行之前执行。
MyAspect.java:
  1. package com.example.demo.aop;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. public class MyAspect {
  4. <aop:config proxy-target-class="true">
  5. <aop:config proxy-target-class="true">
  6.    
  7. </aop:config>
  8. </aop:config>// 前置通知
  9. <aop:config proxy-target-class="true">
  10. <aop:config proxy-target-class="true">
  11.    
  12. </aop:config>
  13. </aop:config>public void beforeAdvice() {
  14. <aop:config proxy-target-class="true">
  15. <aop:config proxy-target-class="true">
  16.    
  17. </aop:config>
  18. </aop:config><aop:config proxy-target-class="true">
  19. <aop:config proxy-target-class="true">
  20.    
  21. </aop:config>
  22. </aop:config>System.out.println("Before advice is running!");
  23. <aop:config proxy-target-class="true">
  24. <aop:config proxy-target-class="true">
  25.    
  26. </aop:config>
  27. </aop:config>}
  28. <aop:config proxy-target-class="true">
  29. <aop:config proxy-target-class="true">
  30.    
  31. </aop:config>
  32. </aop:config>// 后置通知
  33. <aop:config proxy-target-class="true">
  34. <aop:config proxy-target-class="true">
  35.    
  36. </aop:config>
  37. </aop:config>public void afterAdvice() {
  38. <aop:config proxy-target-class="true">
  39. <aop:config proxy-target-class="true">
  40.    
  41. </aop:config>
  42. </aop:config><aop:config proxy-target-class="true">
  43. <aop:config proxy-target-class="true">
  44.    
  45. </aop:config>
  46. </aop:config>System.out.println("After advice is running!");
  47. <aop:config proxy-target-class="true">
  48. <aop:config proxy-target-class="true">
  49.    
  50. </aop:config>
  51. </aop:config>}
  52. <aop:config proxy-target-class="true">
  53. <aop:config proxy-target-class="true">
  54.    
  55. </aop:config>
  56. </aop:config>// 返回后通知
  57. <aop:config proxy-target-class="true">
  58. <aop:config proxy-target-class="true">
  59.    
  60. </aop:config>
  61. </aop:config>public void afterReturningAdvice(Object retVal) {
  62. <aop:config proxy-target-class="true">
  63. <aop:config proxy-target-class="true">
  64.    
  65. </aop:config>
  66. </aop:config><aop:config proxy-target-class="true">
  67. <aop:config proxy-target-class="true">
  68.    
  69. </aop:config>
  70. </aop:config>System.out.println("After returning advice is running! Return value: " + retVal);
  71. <aop:config proxy-target-class="true">
  72. <aop:config proxy-target-class="true">
  73.    
  74. </aop:config>
  75. </aop:config>}
  76. <aop:config proxy-target-class="true">
  77. <aop:config proxy-target-class="true">
  78.    
  79. </aop:config>
  80. </aop:config>// 异常后通知
  81. <aop:config proxy-target-class="true">
  82. <aop:config proxy-target-class="true">
  83.    
  84. </aop:config>
  85. </aop:config>public void afterThrowingAdvice(Throwable ex) {
  86. <aop:config proxy-target-class="true">
  87. <aop:config proxy-target-class="true">
  88.    
  89. </aop:config>
  90. </aop:config><aop:config proxy-target-class="true">
  91. <aop:config proxy-target-class="true">
  92.    
  93. </aop:config>
  94. </aop:config>System.out.println("After throwing advice is running! Exception: " + ex.getMessage());
  95. <aop:config proxy-target-class="true">
  96. <aop:config proxy-target-class="true">
  97.    
  98. </aop:config>
  99. </aop:config>}
  100. <aop:config proxy-target-class="true">
  101. <aop:config proxy-target-class="true">
  102.    
  103. </aop:config>
  104. </aop:config>// 环绕通知
  105. <aop:config proxy-target-class="true">
  106. <aop:config proxy-target-class="true">
  107.    
  108. </aop:config>
  109. </aop:config>public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
  110. <aop:config proxy-target-class="true">
  111. <aop:config proxy-target-class="true">
  112.    
  113. </aop:config>
  114. </aop:config><aop:config proxy-target-class="true">
  115. <aop:config proxy-target-class="true">
  116.    
  117. </aop:config>
  118. </aop:config>System.out.println("Around advice: Before method execution");
  119. <aop:config proxy-target-class="true">
  120. <aop:config proxy-target-class="true">
  121.    
  122. </aop:config>
  123. </aop:config><aop:config proxy-target-class="true">
  124. <aop:config proxy-target-class="true">
  125.    
  126. </aop:config>
  127. </aop:config>Object result = null;
  128. <aop:config proxy-target-class="true">
  129. <aop:config proxy-target-class="true">
  130.    
  131. </aop:config>
  132. </aop:config><aop:config proxy-target-class="true">
  133. <aop:config proxy-target-class="true">
  134.    
  135. </aop:config>
  136. </aop:config>try {
  137. <aop:config proxy-target-class="true">
  138. <aop:config proxy-target-class="true">
  139.    
  140. </aop:config>
  141. </aop:config><aop:config proxy-target-class="true">
  142. <aop:config proxy-target-class="true">
  143.    
  144. </aop:config>
  145. </aop:config><aop:config proxy-target-class="true">
  146. <aop:config proxy-target-class="true">
  147.    
  148. </aop:config>
  149. </aop:config>result = joinPoint.proceed();
  150. <aop:config proxy-target-class="true">
  151. <aop:config proxy-target-class="true">
  152.    
  153. </aop:config>
  154. </aop:config><aop:config proxy-target-class="true">
  155. <aop:config proxy-target-class="true">
  156.    
  157. </aop:config>
  158. </aop:config>} finally {
  159. <aop:config proxy-target-class="true">
  160. <aop:config proxy-target-class="true">
  161.    
  162. </aop:config>
  163. </aop:config><aop:config proxy-target-class="true">
  164. <aop:config proxy-target-class="true">
  165.    
  166. </aop:config>
  167. </aop:config><aop:config proxy-target-class="true">
  168. <aop:config proxy-target-class="true">
  169.    
  170. </aop:config>
  171. </aop:config>System.out.println("Around advice: After method execution");
  172. <aop:config proxy-target-class="true">
  173. <aop:config proxy-target-class="true">
  174.    
  175. </aop:config>
  176. </aop:config><aop:config proxy-target-class="true">
  177. <aop:config proxy-target-class="true">
  178.    
  179. </aop:config>
  180. </aop:config>}
  181. <aop:config proxy-target-class="true">
  182. <aop:config proxy-target-class="true">
  183.    
  184. </aop:config>
  185. </aop:config><aop:config proxy-target-class="true">
  186. <aop:config proxy-target-class="true">
  187.    
  188. </aop:config>
  189. </aop:config>return result;
  190. <aop:config proxy-target-class="true">
  191. <aop:config proxy-target-class="true">
  192.    
  193. </aop:config>
  194. </aop:config>}
  195. }
复制代码
2.4 配置XML

末了,我们需要在Spring的配置文件applicationContext.xml中配置上述bean以及AOP的相关内容。
applicationContext.xml:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. <aop:config proxy-target-class="true">
  4. <aop:config proxy-target-class="true">
  5.    
  6. </aop:config>
  7. </aop:config>   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  8. <aop:config proxy-target-class="true">
  9. <aop:config proxy-target-class="true">
  10.    
  11. </aop:config>
  12. </aop:config>   xmlns:aop="http://www.springframework.org/schema/aop"
  13. <aop:config proxy-target-class="true">
  14. <aop:config proxy-target-class="true">
  15.    
  16. </aop:config>
  17. </aop:config>   xsi:schemaLocation="http://www.springframework.org/schema/beans
  18. <aop:config proxy-target-class="true">
  19. <aop:config proxy-target-class="true">
  20.    
  21. </aop:config>
  22. </aop:config><aop:config proxy-target-class="true">
  23. <aop:config proxy-target-class="true">
  24.    
  25. </aop:config>
  26. </aop:config><aop:config proxy-target-class="true">
  27. <aop:config proxy-target-class="true">
  28.    
  29. </aop:config>
  30. </aop:config><aop:config proxy-target-class="true">
  31. <aop:config proxy-target-class="true">
  32.    
  33. </aop:config>
  34. </aop:config><aop:config proxy-target-class="true">
  35. <aop:config proxy-target-class="true">
  36.    
  37. </aop:config>
  38. </aop:config><aop:config proxy-target-class="true">
  39. <aop:config proxy-target-class="true">
  40.    
  41. </aop:config>
  42. </aop:config>   http://www.springframework.org/schema/beans/spring-beans.xsd
  43. <aop:config proxy-target-class="true">
  44. <aop:config proxy-target-class="true">
  45.    
  46. </aop:config>
  47. </aop:config><aop:config proxy-target-class="true">
  48. <aop:config proxy-target-class="true">
  49.    
  50. </aop:config>
  51. </aop:config><aop:config proxy-target-class="true">
  52. <aop:config proxy-target-class="true">
  53.    
  54. </aop:config>
  55. </aop:config><aop:config proxy-target-class="true">
  56. <aop:config proxy-target-class="true">
  57.    
  58. </aop:config>
  59. </aop:config><aop:config proxy-target-class="true">
  60. <aop:config proxy-target-class="true">
  61.    
  62. </aop:config>
  63. </aop:config><aop:config proxy-target-class="true">
  64. <aop:config proxy-target-class="true">
  65.    
  66. </aop:config>
  67. </aop:config>   http://www.springframework.org/schema/aop
  68. <aop:config proxy-target-class="true">
  69. <aop:config proxy-target-class="true">
  70.    
  71. </aop:config>
  72. </aop:config><aop:config proxy-target-class="true">
  73. <aop:config proxy-target-class="true">
  74.    
  75. </aop:config>
  76. </aop:config><aop:config proxy-target-class="true">
  77. <aop:config proxy-target-class="true">
  78.    
  79. </aop:config>
  80. </aop:config><aop:config proxy-target-class="true">
  81. <aop:config proxy-target-class="true">
  82.    
  83. </aop:config>
  84. </aop:config><aop:config proxy-target-class="true">
  85. <aop:config proxy-target-class="true">
  86.    
  87. </aop:config>
  88. </aop:config><aop:config proxy-target-class="true">
  89. <aop:config proxy-target-class="true">
  90.    
  91. </aop:config>
  92. </aop:config>   http://www.springframework.org/schema/aop/spring-aop.xsd">
  93. <aop:config proxy-target-class="true">
  94. <aop:config proxy-target-class="true">
  95.    
  96. </aop:config>
  97. </aop:config>
  98. <aop:config proxy-target-class="true">
  99. <aop:config proxy-target-class="true">
  100.    
  101. </aop:config>
  102. </aop:config><bean id="myService" class="com.example.demo.aop.MyServiceImpl"/>
  103. <aop:config proxy-target-class="true">
  104. <aop:config proxy-target-class="true">
  105.    
  106. </aop:config>
  107. </aop:config><bean id="myAspect" class="com.example.demo.aop.MyAspect"/>
  108. <aop:config proxy-target-class="true">
  109. <aop:config proxy-target-class="true">
  110.    
  111. </aop:config>
  112. </aop:config>
  113. <aop:config proxy-target-class="true">
  114. <aop:config proxy-target-class="true">
  115.    
  116. </aop:config>
  117. </aop:config><aop:config>
  118. <aop:config proxy-target-class="true">
  119. <aop:config proxy-target-class="true">
  120.    
  121. </aop:config>
  122. </aop:config><aop:config proxy-target-class="true">
  123. <aop:config proxy-target-class="true">
  124.    
  125. </aop:config>
  126. </aop:config>
  127. <aop:config proxy-target-class="true">
  128. <aop:config proxy-target-class="true">
  129.    
  130. </aop:config>
  131. </aop:config><aop:config proxy-target-class="true">
  132. <aop:config proxy-target-class="true">
  133.    
  134. </aop:config>
  135. </aop:config><aop:aspect id="myAspectRef" ref="myAspect">
  136. <aop:config proxy-target-class="true">
  137. <aop:config proxy-target-class="true">
  138.    
  139. </aop:config>
  140. </aop:config><aop:config proxy-target-class="true">
  141. <aop:config proxy-target-class="true">
  142.    
  143. </aop:config>
  144. </aop:config><aop:config proxy-target-class="true">
  145. <aop:config proxy-target-class="true">
  146.    
  147. </aop:config>
  148. </aop:config>
  149. <aop:config proxy-target-class="true">
  150. <aop:config proxy-target-class="true">
  151.    
  152. </aop:config>
  153. </aop:config><aop:config proxy-target-class="true">
  154. <aop:config proxy-target-class="true">
  155.    
  156. </aop:config>
  157. </aop:config><aop:config proxy-target-class="true">
  158. <aop:config proxy-target-class="true">
  159.    
  160. </aop:config>
  161. </aop:config><aop:pointcut id="serviceOperation" expression="execution(* com.example.demo.aop.MyService.performAction(..))"/>
  162. <aop:config proxy-target-class="true">
  163. <aop:config proxy-target-class="true">
  164.    
  165. </aop:config>
  166. </aop:config><aop:config proxy-target-class="true">
  167. <aop:config proxy-target-class="true">
  168.    
  169. </aop:config>
  170. </aop:config><aop:config proxy-target-class="true">
  171. <aop:config proxy-target-class="true">
  172.    
  173. </aop:config>
  174. </aop:config>
  175. <aop:config proxy-target-class="true">
  176. <aop:config proxy-target-class="true">
  177.    
  178. </aop:config>
  179. </aop:config><aop:config proxy-target-class="true">
  180. <aop:config proxy-target-class="true">
  181.    
  182. </aop:config>
  183. </aop:config><aop:config proxy-target-class="true">
  184. <aop:config proxy-target-class="true">
  185.    
  186. </aop:config>
  187. </aop:config><aop:before method="beforeAdvice" pointcut-ref="serviceOperation"/>
  188. <aop:config proxy-target-class="true">
  189. <aop:config proxy-target-class="true">
  190.    
  191. </aop:config>
  192. </aop:config><aop:config proxy-target-class="true">
  193. <aop:config proxy-target-class="true">
  194.    
  195. </aop:config>
  196. </aop:config><aop:config proxy-target-class="true">
  197. <aop:config proxy-target-class="true">
  198.    
  199. </aop:config>
  200. </aop:config>
  201. <aop:config proxy-target-class="true">
  202. <aop:config proxy-target-class="true">
  203.    
  204. </aop:config>
  205. </aop:config><aop:config proxy-target-class="true">
  206. <aop:config proxy-target-class="true">
  207.    
  208. </aop:config>
  209. </aop:config><aop:config proxy-target-class="true">
  210. <aop:config proxy-target-class="true">
  211.    
  212. </aop:config>
  213. </aop:config><aop:after method="afterAdvice" pointcut-ref="serviceOperation"/>
  214. <aop:config proxy-target-class="true">
  215. <aop:config proxy-target-class="true">
  216.    
  217. </aop:config>
  218. </aop:config><aop:config proxy-target-class="true">
  219. <aop:config proxy-target-class="true">
  220.    
  221. </aop:config>
  222. </aop:config><aop:config proxy-target-class="true">
  223. <aop:config proxy-target-class="true">
  224.    
  225. </aop:config>
  226. </aop:config>
  227. <aop:config proxy-target-class="true">
  228. <aop:config proxy-target-class="true">
  229.    
  230. </aop:config>
  231. </aop:config><aop:config proxy-target-class="true">
  232. <aop:config proxy-target-class="true">
  233.    
  234. </aop:config>
  235. </aop:config><aop:config proxy-target-class="true">
  236. <aop:config proxy-target-class="true">
  237.    
  238. </aop:config>
  239. </aop:config><aop:after-returning method="afterReturningAdvice" pointcut-ref="serviceOperation" returning="retVal"/>
  240. <aop:config proxy-target-class="true">
  241. <aop:config proxy-target-class="true">
  242.    
  243. </aop:config>
  244. </aop:config><aop:config proxy-target-class="true">
  245. <aop:config proxy-target-class="true">
  246.    
  247. </aop:config>
  248. </aop:config><aop:config proxy-target-class="true">
  249. <aop:config proxy-target-class="true">
  250.    
  251. </aop:config>
  252. </aop:config>
  253. <aop:config proxy-target-class="true">
  254. <aop:config proxy-target-class="true">
  255.    
  256. </aop:config>
  257. </aop:config><aop:config proxy-target-class="true">
  258. <aop:config proxy-target-class="true">
  259.    
  260. </aop:config>
  261. </aop:config><aop:config proxy-target-class="true">
  262. <aop:config proxy-target-class="true">
  263.    
  264. </aop:config>
  265. </aop:config><aop:after-throwing method="afterThrowingAdvice" pointcut-ref="serviceOperation" throwing="ex"/>
  266. <aop:config proxy-target-class="true">
  267. <aop:config proxy-target-class="true">
  268.    
  269. </aop:config>
  270. </aop:config><aop:config proxy-target-class="true">
  271. <aop:config proxy-target-class="true">
  272.    
  273. </aop:config>
  274. </aop:config><aop:config proxy-target-class="true">
  275. <aop:config proxy-target-class="true">
  276.    
  277. </aop:config>
  278. </aop:config>
  279. <aop:config proxy-target-class="true">
  280. <aop:config proxy-target-class="true">
  281.    
  282. </aop:config>
  283. </aop:config><aop:config proxy-target-class="true">
  284. <aop:config proxy-target-class="true">
  285.    
  286. </aop:config>
  287. </aop:config><aop:config proxy-target-class="true">
  288. <aop:config proxy-target-class="true">
  289.    
  290. </aop:config>
  291. </aop:config><aop:around method="aroundAdvice" pointcut-ref="serviceOperation"/>
  292. <aop:config proxy-target-class="true">
  293. <aop:config proxy-target-class="true">
  294.    
  295. </aop:config>
  296. </aop:config><aop:config proxy-target-class="true">
  297. <aop:config proxy-target-class="true">
  298.    
  299. </aop:config>
  300. </aop:config></aop:aspect>
  301. <aop:config proxy-target-class="true">
  302. <aop:config proxy-target-class="true">
  303.    
  304. </aop:config>
  305. </aop:config></aop:config>
  306. </beans>
复制代码
myService:这是业务逻辑的bean,指向MyServiceImpl类的实例。
myAspect:这是切面的bean,指向MyAspect类的实例。
:这是AOP配置的根元素,所有的AOP配置,包括切面定义、切点和通知方法等,都需要在此元素内部定义。
切面(Aspect):通过元素定义,它包罗了一系列通知(advice)和一个或多个切点(pointcut)。这个元素将切面类(包罗通知逻辑的类)与具体的操作(怎样、何时对目标对象进行加强)关联起来。
切点(Pointcut):通过元素定义,切点通过表达式来指定,当需要精确控制哪些方法执行时会触发通知时,就需要定义切点。切点表达式可以非常精确地指定方法,例如通过方法名称、参数范例、注解等。expression定义了切点的表达式,指明了切点的匹配规则。这里的表达式execution(* com.example.demo.aop.MyService.performAction(..))意味着切点匹配MyService接口中performAction方法的执行,切点用于指定在哪些毗连点(Join Point,例如方法调用)上应用通知。
关于解析表达式execution(* com.example.demo.aop.MyService.performAction(..))
execution:是最常用的切点函数,用于匹配方法执行的毗连点。
*:表示方法的返回范例是任意的。
com.example.demo.aop.MyService.performAction:指定了全路径的接口名和方法名。
(…):表示方法参数是任意的,无论方法有多少个参数都匹配。

  • 毗连点(Join Point):毗连点是指在程序执行过程中的某一点,比如方法的调用。 毗连点是通过切点(Pointcut)的表达式来辨认和匹配的,execution(* com.example.demo.aop.MyService.performAction(..))表达式定义了一个切点,它指定了一个明确的毗连点集合——即MyService接口的performAction方法的所有调用。这个例子中,MyService接口的performAction方法的调用就是潜伏的毗连点。每次performAction方法被调用时,就达到了一个毗连点。这个毗连点就是这里通知应用的时机。
  • 通知(Advice):这是AOP通过在特定时机执行的操作来加强方法的执行。method属性指明当切点匹配时应该执行的切面的方法名,pointcut-ref引用了上面定义的切点。比如这里的beforeAdvice是在目标方法performAction执行之前被调用的方法。这意味着每当MyService.performAction(..)方法被调用时,beforeAdvice方法将起首被执行。
总结为一句话:Spring AOP通过在切面中定义规则(切点)来指定何时(毗连点)以及怎样(通知)加强特定方法,实现代码的模块化和关注点分离,无需修改原有业务逻辑。
通过这种方式,Spring AOP 允许定义在特定方法执行前、执行后、环绕执行等时机插入自定义逻辑,而无需修改原有业务逻辑代码。这是实现关注点分离的一种强大机制,特别是对于跨越应用程序多个部分的横切关注点(如日志、事件管理等)。
注意,如果设置为
  1. <aop:config proxy-target-class="true">
  2. <aop:config proxy-target-class="true">
  3.    
  4. </aop:config>
  5. </aop:config>
复制代码
设置proxy-target-会使Spring AOP优先使用CGLIB代理,即使目标对象实现了接口。默认情况下,不需要设置proxy-target-class属性,或者将其设置为false,则是使用JDK动态代理。
主程序:
DemoApplication.java:
  1. package com.example.demo;import com.example.demo.aop.MyService;import org.springframework.context.support.ClassPathXmlApplicationContext;public class DemoApplication {<aop:config proxy-target-class="true">
  2. <aop:config proxy-target-class="true">
  3.    
  4. </aop:config>
  5. </aop:config>public static void main(String[] args) {<aop:config proxy-target-class="true">
  6. <aop:config proxy-target-class="true">
  7.    
  8. </aop:config>
  9. </aop:config><aop:config proxy-target-class="true">
  10. <aop:config proxy-target-class="true">
  11.    
  12. </aop:config>
  13. </aop:config>ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");<aop:config proxy-target-class="true">
  14. <aop:config proxy-target-class="true">
  15.    
  16. </aop:config>
  17. </aop:config><aop:config proxy-target-class="true">
  18. <aop:config proxy-target-class="true">
  19.    
  20. </aop:config>
  21. </aop:config>MyService myService = (MyService) context.getBean("myService");<aop:config proxy-target-class="true">
  22. <aop:config proxy-target-class="true">
  23.    
  24. </aop:config>
  25. </aop:config><aop:config proxy-target-class="true">
  26. <aop:config proxy-target-class="true">
  27.    
  28. </aop:config>
  29. </aop:config>try {<aop:config proxy-target-class="true">
  30. <aop:config proxy-target-class="true">
  31.    
  32. </aop:config>
  33. </aop:config><aop:config proxy-target-class="true">
  34. <aop:config proxy-target-class="true">
  35.    
  36. </aop:config>
  37. </aop:config><aop:config proxy-target-class="true">
  38. <aop:config proxy-target-class="true">
  39.    
  40. </aop:config>
  41. </aop:config>System.out.println(myService.performAction("normal"));<aop:config proxy-target-class="true">
  42. <aop:config proxy-target-class="true">
  43.    
  44. </aop:config>
  45. </aop:config><aop:config proxy-target-class="true">
  46. <aop:config proxy-target-class="true">
  47.    
  48. </aop:config>
  49. </aop:config>} catch (Exception e) {<aop:config proxy-target-class="true">
  50. <aop:config proxy-target-class="true">
  51.    
  52. </aop:config>
  53. </aop:config><aop:config proxy-target-class="true">
  54. <aop:config proxy-target-class="true">
  55.    
  56. </aop:config>
  57. </aop:config><aop:config proxy-target-class="true">
  58. <aop:config proxy-target-class="true">
  59.    
  60. </aop:config>
  61. </aop:config>e.printStackTrace();<aop:config proxy-target-class="true">
  62. <aop:config proxy-target-class="true">
  63.    
  64. </aop:config>
  65. </aop:config><aop:config proxy-target-class="true">
  66. <aop:config proxy-target-class="true">
  67.    
  68. </aop:config>
  69. </aop:config>}<aop:config proxy-target-class="true">
  70. <aop:config proxy-target-class="true">
  71.    
  72. </aop:config>
  73. </aop:config><aop:config proxy-target-class="true">
  74. <aop:config proxy-target-class="true">
  75.    
  76. </aop:config>
  77. </aop:config>System.out.println("=======================");<aop:config proxy-target-class="true">
  78. <aop:config proxy-target-class="true">
  79.    
  80. </aop:config>
  81. </aop:config><aop:config proxy-target-class="true">
  82. <aop:config proxy-target-class="true">
  83.    
  84. </aop:config>
  85. </aop:config>try {<aop:config proxy-target-class="true">
  86. <aop:config proxy-target-class="true">
  87.    
  88. </aop:config>
  89. </aop:config><aop:config proxy-target-class="true">
  90. <aop:config proxy-target-class="true">
  91.    
  92. </aop:config>
  93. </aop:config><aop:config proxy-target-class="true">
  94. <aop:config proxy-target-class="true">
  95.    
  96. </aop:config>
  97. </aop:config>System.out.println(myService.performAction("throw"));<aop:config proxy-target-class="true">
  98. <aop:config proxy-target-class="true">
  99.    
  100. </aop:config>
  101. </aop:config><aop:config proxy-target-class="true">
  102. <aop:config proxy-target-class="true">
  103.    
  104. </aop:config>
  105. </aop:config>} catch (Exception e) {<aop:config proxy-target-class="true">
  106. <aop:config proxy-target-class="true">
  107.    
  108. </aop:config>
  109. </aop:config><aop:config proxy-target-class="true">
  110. <aop:config proxy-target-class="true">
  111.    
  112. </aop:config>
  113. </aop:config><aop:config proxy-target-class="true">
  114. <aop:config proxy-target-class="true">
  115.    
  116. </aop:config>
  117. </aop:config>System.out.println("Exception caught in main: " + e.getMessage());<aop:config proxy-target-class="true">
  118. <aop:config proxy-target-class="true">
  119.    
  120. </aop:config>
  121. </aop:config><aop:config proxy-target-class="true">
  122. <aop:config proxy-target-class="true">
  123.    
  124. </aop:config>
  125. </aop:config>}<aop:config proxy-target-class="true">
  126. <aop:config proxy-target-class="true">
  127.    
  128. </aop:config>
  129. </aop:config><aop:config proxy-target-class="true">
  130. <aop:config proxy-target-class="true">
  131.    
  132. </aop:config>
  133. </aop:config>context.close();<aop:config proxy-target-class="true">
  134. <aop:config proxy-target-class="true">
  135.    
  136. </aop:config>
  137. </aop:config>}}
复制代码
运行结果:
通过结合动态代理技术和这些AOP概念,Spring AOP能够以非侵入式的方式为应用程序提供横切关注点的支持,这样开发者就可以将这些关注点模块化,并保持业务逻辑组件的聚焦和简洁。
如果对动态代理感爱好可以再调试看看,这里是JDK动态代理是由于public class MyServiceImpl implements MyService 实现了接口,调试如下:
简单说一下这里能看到的关键类和接口
ProxyFactory: 这是Spring AOP用来创建代理对象的工厂类。它可以根据目标对象是否实现接口来决定使用JDK动态代理照旧CGLIB代理。
AopProxy: 这个接口定义了获代替理对象的方法。它有两个主要实现:JdkDynamicAopProxy(用于JDK动态代理)和CglibAopProxy(用于CGLIB代理)。
JdkDynamicAopProxy: 实现了AopProxy接口,使用JDK动态代理技术创建代理。它实现了InvocationHandler接口,拦截对代理对象的所有方法调用。
CglibAopProxy: 同样实现了AopProxy接口,但使用CGLIB库来创建代理对象。对于没有实现接口的类,Spring会选择这种方式来创建代理。
如果大家想深入相识Spring AOP的源码,可以直接查察JdkDynamicAopProxy和CglibAopProxy这两个类的实现。这里不是本篇重点,简单提一下:
比如在JdkDynamicAopProxy中看到动态代理的实现:

  • JdkDynamicAopProxy类实现了InvocationHandler接口,这是JDK动态代理的核心。在其invoke方法中,会有逻辑判定是否需要对调用进行拦截,并在调用前后应用相应的通知。
  • 创建代理的过程主要是在ProxyFactory通过调用createAopProxy()方法时完成的,这个方法会根据配置返回JdkDynamicAopProxy或CglibAopProxy的实例。
  • 代理的使用:客户端代码通过ProxyFactory获代替理对象,并通过这个代理对象调用目标方法。代理对象在内部使用JdkDynamicAopProxy或CglibAopProxy来拦截这些调用,并根据AOP配置执行通知。通过ProxyFactory获代替理对象的过程,通常在Spring的配置和使用中是隐式完成的,特别是在使用Spring容器管理AOP时。这一过程不需要开发者直接调用ProxyFactory类。当Spring配置中定义了一个bean,并对其应用了切面,Spring容器会自动处理代理的创建和应用通知的过程。这是通过Spring的后处理器和AOP定名空间的支持实现的,开发者通常只需声明式地配置切面和通知即可。
如果想看到CGLIB代理,这里有2种方法
第1种方法是去掉MyServiceImpl实现的MyService接口,然后把主程序和expression表达式对应的地方改成MyServiceImpl。
第2种方法就是Spring配置文件中显式设置标签的proxy-target-属性来实现这一点。如下:
  1. <aop:config proxy-target-class="true">
  2. <aop:config proxy-target-class="true">
  3.    
  4. </aop:config>
  5. </aop:config>
复制代码
调试如下:
欢迎一键三连~
有问题请留言,大家一起探讨学习
 
点击关注,第一时间相识华为云新鲜技术~
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张国伟

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表