Spring5(六)——AspectJ(xml)

2021年09月15日 阅读数:7
这篇文章主要向大家介绍Spring5(六)——AspectJ(xml),主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

1、AspectJ

一、介绍

  AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,也能够说 AspectJ 是一个基于 Java 语言的 AOP 框架。一般咱们在使用 Spring AOP 的时候,都会导入 AspectJ 的相关 jar 包。spring

二、案例(xml)

  定义目标对象(被代理的对象)(与上一章相同)
  编写一个切面类(通知)express

 1 // 建立切面类(包含各类通知)
 2 public class MyAspect {
 3 
 4     // JoinPoint 能获取目标方法的一些基本信息
 5     public void myBefore(JoinPoint joinPoint) {
 6         System.out.println("前置通知:方法加强myBefore()" + " , -->" + joinPoint.getSignature().getName());
 7     }
 8     
 9     // object:目标方法的返回值
10     public void myAfterReturning(JoinPoint joinPoint, Object object) {
11         System.out.println("后置通知:方法加强myAfterReturning()" + " , -->" + joinPoint.getSignature().getName() + " , -->" + object);
12     }
13 
14     public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
15         System.out.println("============环绕前==============");
16         Object obj = joinPoint.proceed(); // 手动执行目标方法
17         System.out.println("============环绕后==============");
18         return obj;
19     }
20 
21     public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
22         System.out.println("抛出异常通知:" + e.getMessage());
23     }
24 
25     public void myAfter() {
26         System.out.println("最终通知:方法加强myAfter()");
27     }
28 
29 }

  编写配置文件 application.xmlapp

 1 <!-- 配置目标对象 -->
 2 <bean id="teacher" class="com.lx.spring.common.Teacher"/>
 3 <!-- 配置切面对象(通知) -->
 4 <bean id="myAspect" class="com.lx.spring.day3.MyAspect"/>
 5 
 6 <aop:config>
 7     <!-- 切入点表达式,指明了在哪里引入通知 -->
 8     <aop:pointcut id="myPointcut" expression="execution(* com.lx.spring.common.ITeacher.*(..))"/>
 9 
10     <!-- 方法加强,指明了引入一个什么样的通知 -->
11     <aop:aspect ref="myAspect">
12         <aop:before method="myBefore" pointcut-ref="myPointcut"/>
13         <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut" returning="object"/>
14         <aop:around method="myAround" pointcut-ref="myPointcut"/>
15         <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="e"/>
16         <aop:after method="myAfter" pointcut-ref="myPointcut"/>
17     </aop:aspect>
18 </aop:config>
 1 // 测试类
 2 public class Main {
 3     public static void main(String[] args) {
 4         ApplicationContext app = new ClassPathXmlApplicationContext("app3.xml");
 5         ITeacher iTeacher = app.getBean(ITeacher.class);
 6 
 7         iTeacher.add(11, 24);
 8     }
 9 }
10 
11 // 结果一:未配置环绕通知时.注意:此时 最终通知 在 后置通知后面
12 前置通知:方法加强myBefore() , -->add
13 执行目标方法:老师正在作加法,结果为:35
14 后置通知:方法加强myAfterReturning() , -->add , -->35
15 最终通知:方法加强myAfter()
16 
17 
18 // 结果二:配置环绕通知时.注意:此时 最终通知 在 后置通知前面
19 前置通知:方法加强myBefore() , -->add
20 ============环绕前==============
21 执行目标方法:老师正在作加法,结果为:35
22 最终通知:方法加强myAfter()
23 ============环绕后==============
24 后置通知:方法加强myAfterReturning() , -->add , -->35

  说明:这里有不少细节须要补充一下。深入理解通知,重点思想在于:①在哪里(切点,或者说方法)引入?②引入一个什么样的通知?针对这两个问题,则不难理解AOP。
  ①切入点表达式:指明了在哪里(切点,在哪一个方法)引入一个通知(即对目标方法的加强),也就是在哪些方法进行加强。execution 是 AspectJ 框架定义的一个切入点函数,其语法形式以下:框架

1 execution(modifiers-pattern? ref-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
2                 类修饰符           返回值           方法所在的包             方法名(参数)             方法抛出的异常

  那么不难理解,对知足如下规则的方法进行加强。也就是对这些方法引入一个通知。函数

1 <aop:pointcut id="myPointcut" expression="execution(* com.lx.spring.common.ITeacher.*(..))"/>
2                                         选择方法 任意返回值    此包下.此接口   任意方法名(任意参数)

  ②通知(方法加强):指明了对知足切点表达式的方法引入一个什么样的通知。测试

 1 <!-- 指明引入的切面(通知) -->
 2 <aop:aspect ref="myAspect">
 3     <!-- 对知足上面切入点表达式的方法配置一个前置通知 -->
 4     <!-- 即在目标方法前执行方法 myBefore -->
 5     <aop:before method="myBefore" pointcut-ref="myPointcut"/>
 6     
 7     <!-- 对知足上面切入点表达式的方法配置一个后置通知 -->
 8     <!-- 即在目标方法后执行方法 myAfterReturning -->
 9     <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut" returning="object"/>
10 </aop:aspect>

三、切入点表达式

  上一节中已经介绍过切入点表达式的相关语法,且理解不难。再补充几点,若是切入点表达式有多个不一样目录呢?能够经过 || 来表示或的关系。this

1 <!--表示匹配com.lx.aop包下的,以Service结尾或者以Facade结尾的类的任意方法。-->
2 <aop:pointcut id="myPointcut" expression="execution(* com.lx.aop.*Service.*(..)) || execution(* com.lx.aop.*Facade.*(..))"/>

  AOP 切入点表达式支持多种形式的定义规则:spa

 1 1、execution:匹配方法的执行(经常使用)
 2     execution(public *.*(..))
 3 2、within:匹配包或子包中的方法(了解)
 4     within(com.ys.aop..*)
 5 三、this:匹配实现接口的代理对象中的方法(了解)
 6     this(com.ys.aop.user.UserDAO)
 7 4、target:匹配实现接口的目标对象中的方法(了解)
 8     target(com.ys.aop.user.UserDAO)
 9 5、args:匹配参数格式符合标准的方法(了解)
10     args(int,int)
11 6、bean(id):对指定的bean全部的方法(了解)
12     bean('userServiceId')

四、通知类型

通知类型
接口
描述
前置通知
(before)
org.springframework.aop.aspectj.AspectJMethodBeforeAdvice
在目标方法前调用,若是经过抛出异常,阻止方法运行。
应用:各类校验。
后置通知
(afterReturning)
org.springframework.aop.aspectj.AspectJAfterReturningAdvice
在目标方法后调用,能够得到目标方法返回值,若目标方法抛出异常,通知没法执行。
应用:常规数据处理。
环绕通知
(around)
org.springframework.aop.aspectj.AspectJAroundAdvice
在目标方法先后调用,能够阻止方法的执行,必须手动执行目标方法。
应用:十分强大,能够作任何事情。
异常通知
(afterThrowing)
org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
目标方法抛出异常时调用,若目标方法没有抛出异常,没法执行。
应用:包装异常信息
最终通知
(after)
org.springframework.aop.aspectj.AspectJAfterAdvice
目标方法执行完毕后执行,不管方法中是否出现异常
应用:清理现场

  这里最重要的是around,环绕通知,它能够代替上面的任意通知。3d

  在程序中表示的意思以下:代理

 1 public class Main {
 2     public static void main(String[] args) {
 3         try {
 4             // 前置 before
 5             // 手动执行目标方法
 6             // 后置 afterReturning
 7         } catch (Exception e) {
 8             // 抛出异常通知 afterThrowing
 9         } finally {
10            // 最终 after
11         }
12     }
13 }

  源码:

五、小结

  使用 <aop:config>进行配置,proxy-target-class="true",声明时使用cglib代理;若是不声明,Spring 会自动选择cglib代理仍是JDK动态代理。
  SpringAOP 的具体加载步骤:
  ①当 spring 容器启动的时候,加载 spring 的配置文件。
  ②为配置文件中的全部 bean 建立对象。
  ③spring 容器会解析 aop:config 的配置,解析切入点表达式,用切入点表达式和归入 spring 容器中的 bean 作匹配,若是匹配成功,则会为该 bean 建立代理对象,代理对象的方法 = 目标方法 + 通知;若是匹配不成功,不会建立代理对象。
  ④在客户端利用 context.getBean() 获取对象时,若是该对象有代理对象,则返回代理对象;若是没有,则返回目标对象
  说明:若是目标类没有实现接口,则 spring 容器会采用 cglib 的方式产生代理对象,若是实现了接口,则会采用 jdk 的方式。