我们一起了解 Spring 中的 AOP !

时间:2022-04-25 23:22:31

我们一起了解 Spring 中的 AOP !

我们一起了解 Spring 中的 AOP !

我们一起了解 Spring 中的 AOP !

  • 1. Spring AOP简介
  • 2. 动态代理
  • jdk动态代理
  • CGLIB代理
  • 3. 基于代理类的AOP实现
  • Spring的通知类型
  • ProxyFactoryBean
  • 4. AspectJ开发
  • 基于XML的声明式AspectJ
  • 基于注解的声明式AspectJ(常用)

“GitHub:https://github.com/nateshao/ssm/tree/master/103-spring-aop

1. Spring AOP简介

什么是AOP?

AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。

在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。

为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。

类与切面的关系

我们一起了解 Spring 中的 AOP !

AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多的关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。

我们一起了解 Spring 中的 AOP !

Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。

Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

2. 动态代理

jdk动态代理

“JDK动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。

UserDao.java

  1. public interface UserDao { 
  2.     public void addUser(); 
  3.     public void deleteUser(); 

UserDaoImpl.java

  1. package com.nateshao.aop; 
  2.  
  3. import org.springframework.stereotype.Repository; 
  4.  
  5. /** 
  6.  * @date Created by 邵桐杰 on 2021/10/14 17:59 
  7.  * @微信公众号 程序员千羽 
  8.  * @个人网站 www.nateshao.cn 
  9.  * @博客 https://nateshao.gitee.io 
  10.  * @GitHub https://github.com/nateshao 
  11.  * @Gitee https://gitee.com/nateshao 
  12.  * Description: 
  13.  */ 
  14. @Repository("userDao"
  15. public class UserDaoImpl implements UserDao{ 
  16.     @Override 
  17.     public void addUser() { 
  18.         System.out.println("添加用户"); 
  19.     } 
  20.  
  21.     @Override 
  22.     public void deleteUser() { 
  23.         System.out.println("删除用户"); 
  24.     } 

JdkProxy.java

  1. package com.nateshao.aop; 
  2.  
  3. import com.nateshao.aspect.MyAspect; 
  4. import java.lang.reflect.InvocationHandler; 
  5. import java.lang.reflect.Method; 
  6. import java.lang.reflect.Proxy; 
  7.  
  8. /** 
  9.  * @date Created by 邵桐杰 on 2021/10/14 18:01 
  10.  * @微信公众号 程序员千羽 
  11.  * @个人网站 www.nateshao.cn 
  12.  * @博客 https://nateshao.gitee.io 
  13.  * @GitHub https://github.com/nateshao 
  14.  * @Gitee https://gitee.com/nateshao 
  15.  * Description: JDK代理类 
  16.  */ 
  17. public class JdkProxy implements InvocationHandler { 
  18.     // 声明目标类接口 
  19.     private UserDao userDao; 
  20.     // 创建代理方法 
  21.     public  Object createProxy(UserDao userDao) { 
  22.         this.userDao = userDao; 
  23.         // 1.类加载器 
  24.         ClassLoader classLoader = JdkProxy.class.getClassLoader(); 
  25.         // 2.被代理对象实现的所有接口 
  26.         Class[] clazz = userDao.getClass().getInterfaces(); 
  27.         // 3.使用代理类,进行增强,返回的是代理后的对象 
  28.         return  Proxy.newProxyInstance(classLoader,clazz,this); 
  29.     } 
  30.  
  31.     /** 
  32.      * 所有动态代理类的方法调用,都会交由invoke()方法去处理 
  33.      * @param proxy 被代理后的对象 
  34.      * @param method 将要被执行的方法信息(反射) 
  35.      * @param args 执行方法时需要的参数 
  36.      * @return 
  37.      * @throws Throwable 
  38.      */ 
  39.     @Override 
  40.     public Object invoke(Object proxy, Method method, Object[] args) 
  41.             throws Throwable { 
  42.         // 声明切面 
  43.         MyAspect myAspect = new MyAspect(); 
  44.         // 前增强 
  45.         myAspect.check_Permissions(); 
  46.         // 在目标类上调用方法,并传入参数 
  47.         Object obj = method.invoke(userDao, args); 
  48.         // 后增强 
  49.         myAspect.log(); 
  50.         return obj; 
  51.     } 

CGLIB代理

通过前面的学习可知,JDK的动态代理用起来非常简单,但它是有局限性的,使用动态代理的对象必须实现一个或多个接口。

如果想代理没有实现接口的类,那么可以使用CGLIB代理。

“CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。

UserDao.java

  1. public class UserDao { 
  2.  
  3.     public void addUser(){ 
  4.         System.out.println("添加用户"); 
  5.     } 
  6.  
  7.     public void deleteUser(){ 
  8.         System.out.println("添加用户"); 
  9.     } 

CglibProxy.java

  1. package com.nateshao.cglib; 
  2.  
  3. import com.nateshao.aspect.MyAspect; 
  4. import org.springframework.cglib.proxy.Enhancer; 
  5. import org.springframework.cglib.proxy.MethodInterceptor; 
  6. import org.springframework.cglib.proxy.MethodProxy; 
  7. import java.lang.reflect.Method; 
  8.  
  9. /** 
  10.  * @date Created by 邵桐杰 on 2021/10/14 18:18 
  11.  * @微信公众号 程序员千羽 
  12.  * @个人网站 www.nateshao.cn 
  13.  * @博客 https://nateshao.gitee.io 
  14.  * @GitHub https://github.com/nateshao 
  15.  * @Gitee https://gitee.com/nateshao 
  16.  * Description: 
  17.  */ 
  18. // 代理类 
  19. public class CglibProxy implements MethodInterceptor { 
  20.     // 代理方法 
  21.     public  Object createProxy(Object target) { 
  22.         // 创建一个动态类对象 
  23.         Enhancer enhancer = new Enhancer(); 
  24.         // 确定需要增强的类,设置其父类 
  25.         enhancer.setSuperclass(target.getClass()); 
  26.         // 添加回调函数 
  27.         enhancer.setCallback(this); 
  28.         // 返回创建的代理类 
  29.         return enhancer.create(); 
  30.     } 
  31.  
  32.     /** 
  33.      * @param proxy CGlib根据指定父类生成的代理对象 
  34.      * @param method 拦截的方法 
  35.      * @param args 拦截方法的参数数组 
  36.      * @param methodProxy 方法的代理对象,用于执行父类的方法 
  37.      * @return 
  38.      * @throws Throwable 
  39.      */ 
  40.     @Override 
  41.     public Object intercept(Object proxy, Method method, Object[] args, 
  42.                             MethodProxy methodProxy) throws Throwable { 
  43.         // 创建切面类对象 
  44.         MyAspect myAspect = new MyAspect(); 
  45.         // 前增强 
  46.         myAspect.check_Permissions(); 
  47.         // 目标方法执行 
  48.         Object obj = methodProxy.invokeSuper(proxy, args); 
  49.         // 后增强 
  50.         myAspect.log(); 
  51.         return obj; 
  52.     } 

CglibTest.java

  1. package com.nateshao.cglib; 
  2.  
  3. /** 
  4.  * @date Created by 邵桐杰 on 2021/10/14 18:25 
  5.  * @微信公众号 程序员千羽 
  6.  * @个人网站 www.nateshao.cn 
  7.  * @博客 https://nateshao.gitee.io 
  8.  * @GitHub https://github.com/nateshao 
  9.  * @Gitee https://gitee.com/nateshao 
  10.  * Description: 
  11.  */ 
  12. public class CglibTest { 
  13.     public static void main(String[] args) { 
  14.         // 创建代理对象 
  15.         CglibProxy cglibProxy = new CglibProxy(); 
  16.         // 创建目标对象 
  17.         UserDao userDao = new UserDao(); 
  18.         // 获取增强后的目标对象 
  19.         UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao); 
  20.         // 执行方法 
  21.         userDao1.addUser(); 
  22.         userDao1.deleteUser(); 
  23.     } 

我们一起了解 Spring 中的 AOP !

3. 基于代理类的AOP实现

Spring的通知类型

Spring按照通知在目标类方法的连接点位置,可以分为5种类型,具体如下:

  • org.springframework.aop.MethodBeforeAdvice(前置通知)

在目标方法执行前实施增强,可以应用于权限管理等功能。

  • org.springframework.aop.AfterReturningAdvice(后置通知)

在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。

  • org.aopalliance.intercept.MethodInterceptor(环绕通知)

在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。

  • org.springframework.aop.ThrowsAdvice(异常抛出通知)

在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。

  • org.springframework.aop.IntroductionInterceptor(引介通知)

在目标类中添加一些新的方法和属性,可以应用于修改老版本程序。

ProxyFactoryBean

“ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。

ProxyFactoryBean类中的常用可配置属性如下:

我们一起了解 Spring 中的 AOP !

代码实现

MyAspect.java

  1. package com.nateshao.factorybean; 
  2.  
  3. import org.aopalliance.intercept.MethodInterceptor; 
  4. import org.aopalliance.intercept.MethodInvocation; 
  5.  
  6. /** 
  7.  * @date Created by 邵桐杰 on 2021/10/14 18:36 
  8.  * @微信公众号 程序员千羽 
  9.  * @个人网站 www.nateshao.cn 
  10.  * @博客 https://nateshao.gitee.io 
  11.  * @GitHub https://github.com/nateshao 
  12.  * @Gitee https://gitee.com/nateshao 
  13.  * Description:  切面类 
  14.  */ 
  15. public class MyAspect implements MethodInterceptor { 
  16.  
  17.     @Override 
  18.     public Object invoke(MethodInvocation mi) throws Throwable { 
  19.         check_Permissions(); 
  20.         // 执行目标方法 
  21.         Object obj = mi.proceed(); 
  22.         log(); 
  23.         return obj; 
  24.     } 
  25.     public void check_Permissions(){ 
  26.         System.out.println("模拟检查权限..."); 
  27.     } 
  28.     public void log(){ 
  29.         System.out.println("模拟记录日志..."); 
  30.     } 

applicationContext.xml

  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.        xsi:schemaLocation="http://www.springframework.org/schema/beans 
  5.  http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> 
  6.     <!-- 1 目标类 --> 
  7.     <bean id="userDao" class="com.nateshao.jdk.UserDaoImpl" /> 
  8.     <!-- 2 切面类 --> 
  9.     <bean id="myAspect" class="com.nateshao.factorybean.MyAspect" /> 
  10.     <!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 --> 
  11.     <bean id="userDaoProxy" 
  12.           class="org.springframework.aop.framework.ProxyFactoryBean"
  13.         <!-- 3.1 指定代理实现的接口--> 
  14.         <property name="proxyInterfaces" 
  15.                   value="com.nateshao.jdk.UserDao" /> 
  16.         <!-- 3.2 指定目标对象 --> 
  17.         <property name="target" ref="userDao" /> 
  18.         <!-- 3.3 指定切面,织入环绕通知 --> 
  19.         <property name="interceptorNames" value="myAspect" /> 
  20.         <!-- 3.4 指定代理方式,true:使用cglib,false(默认):使用jdk动态代理 --> 
  21.         <property name="proxyTargetClass" value="true" /> 
  22.     </bean> 
  23. </beans> 

 

 

 

ProxyFactoryBeanTest.java

  1. package com.nateshao.factorybean; 
  2.  
  3. import com.nateshao.jdk.UserDao; 
  4. import org.springframework.context.ApplicationContext; 
  5. import org.springframework.context.support.ClassPathXmlApplicationContext; 
  6.  
  7. /** 
  8.  * @date Created by 邵桐杰 on 2021/10/14 18:41 
  9.  * @微信公众号 程序员千羽 
  10.  * @个人网站 www.nateshao.cn 
  11.  * @博客 https://nateshao.gitee.io 
  12.  * @GitHub https://github.com/nateshao 
  13.  * @Gitee https://gitee.com/nateshao 
  14.  * Description: 测试类 
  15.  */ 
  16. public class ProxyFactoryBeanTest { 
  17.     public static void main(String args[]) { 
  18.         String xmlPath = "applicationContext.xml"
  19.         ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); 
  20.         // 从Spring容器获得内容 
  21.         UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy"); 
  22.         // 执行方法 
  23.         userDao.addUser(); 
  24.         userDao.deleteUser(); 
  25.     } 

4. AspectJ开发

“概述:AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。Spring 2.0以后,Spring AOP引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而Spring自身的AOP API也尽量与AspectJ保持一致。新版本的Spring框架,也建议使用AspectJ来开发AOP。使用AspectJ实现AOP有两种方式:一种是基于XML的声明式AspectJ,另一种是基于注解的声明式AspectJ。

基于XML的声明式AspectJ

“基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在< aop:config >元素内。

< aop:config >元素及其子元素如下:

我们一起了解 Spring 中的 AOP !

小提示:图中灰色部分标注的元素即为常用的配置元素

XML文件中常用元素的配置方式如下:

  1. <bean id="myAspect" class="com.nateshao.aspectj.xml.MyAspect" /> 
  2. <aop:config> 
  3. <aop:aspect  id="aspect"  ref="myAspect"
  4.     <aop:pointcut expression="execution(* com.nateshao.jdk.*.*(..))“ id="myPointCut" /> 
  5.                   <aop:before method="myBefore" pointcut-ref="myPointCut" /> 
  6.                   <aop:after-returning method="myAfterReturning“ pointcut-ref="myPointCut"                            returning="returnVal" /> 
  7.     <aop:around method="myAround" pointcut-ref="myPointCut" /> 
  8.     <aop:after-throwing method="myAfterThrowing“ pointcut-ref="myPointCut" throwing="e" /> 
  9.                   <aop:after method="myAfter" pointcut-ref="myPointCut" /> 
  10.     </aop:aspect> 
  11. </aop:config> 

配置切面

“在Spring的配置文件中,配置切面使用的是< aop:aspect >元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean。

配置< aop:aspect >元素时,通常会指定id和ref两个属性。

id:用于定义该切面的唯一标识名称。 ref:用于引用普通的Spring Bean

配置切入点

“当< aop:pointcut>元素作为< aop:config>元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当< aop:pointcut>元素作为< aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。

在定义< aop:pointcut>元素时,通常会指定id和expression两个属性。

id:用于指定切入点的唯-标识名称。. expressione:用于指定切入点关联的切入点表达式

切入点表达式

  • execution(* com.nateshao.jdk. * . * (..)) 是定义的切入点表达式,该切入点表达式的意思是匹配com.nateshao.jdk包中任意类的任意方法的执行。
  • execution(* com.nateshao.jdk..(..)) :表达式的主体
  • execution(* :* 表示所有返回类型
  • com.nateshao.jdk:需要拦截的包名字
  • execution(* com.nateshao.jdk. * :* 代表所有类
  • execution(* com.nateshao.jdk. * . * :方法名,使用* 代表所有方法
  • execution(* com.nateshao.jdk..(..)) :. . 表示任意参数

配置通知

“使用< aop:aspect>的子元素可以配置5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:

我们一起了解 Spring 中的 AOP !

MyAspect.java

  1. package com.nateshao.aspectj.xml; 
  2.  
  3. import org.aspectj.lang.JoinPoint; 
  4. import org.aspectj.lang.ProceedingJoinPoint; 
  5. /** 
  6.  * @date Created by 邵桐杰 on 2021/10/14 19:56 
  7.  * @微信公众号 程序员千羽 
  8.  * @个人网站 www.nateshao.cn 
  9.  * @博客 https://nateshao.gitee.io 
  10.  * @GitHub https://github.com/nateshao 
  11.  * @Gitee https://gitee.com/nateshao 
  12.  * Description: 切面类,在此类中编写通知 
  13.  */ 
  14. public class MyAspect { 
  15.     // 前置通知 
  16.     public void myBefore(JoinPoint joinPoint) { 
  17.         System.out.print("前置通知 :模拟执行权限检查...,"); 
  18.         System.out.print("目标类是:"+joinPoint.getTarget() ); 
  19.         System.out.println(",被织入增强处理的目标方法为:" 
  20.                 +joinPoint.getSignature().getName()); 
  21.     } 
  22.     // 后置通知 
  23.     public void myAfterReturning(JoinPoint joinPoint) { 
  24.         System.out.print("后置通知:模拟记录日志...," ); 
  25.         System.out.println("被织入增强处理的目标方法为:" 
  26.                 + joinPoint.getSignature().getName()); 
  27.     } 
  28.     /** 
  29.      * 环绕通知 
  30.      * ProceedingJoinPoint 是JoinPoint子接口,表示可以执行目标方法 
  31.      * 1.必须是Object类型的返回值 
  32.      * 2.必须接收一个参数,类型为ProceedingJoinPoint 
  33.      * 3.必须throws Throwable 
  34.      */ 
  35.     public Object myAround(ProceedingJoinPoint proceedingJoinPoint) 
  36.             throws Throwable { 
  37.         // 开始 
  38.         System.out.println("环绕开始:执行目标方法之前,模拟开启事务..."); 
  39.         // 执行当前目标方法 
  40.         Object obj = proceedingJoinPoint.proceed(); 
  41.         // 结束 
  42.         System.out.println("环绕结束:执行目标方法之后,模拟关闭事务..."); 
  43.         return obj; 
  44.     } 
  45.     // 异常通知 
  46.     public void myAfterThrowing(JoinPoint joinPoint, Throwable e) { 
  47.         System.out.println("异常通知:" + "出错了" + e.getMessage()); 
  48.     } 
  49.     // 最终通知 
  50.     public void myAfter() { 
  51.         System.out.println("最终通知:模拟方法结束后的释放资源..."); 
  52.     } 

config.xml

  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.        xsi:schemaLocation="http://www.springframework.org/schema/beans 
  5.    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> 
  6.     <!-- 1 目标类 --> 
  7.     <bean id="userDao" class="com.nateshao.jdk.UserDaoImpl" /> 
  8.     <!-- 2 切面类 --> 
  9.     <bean id="myAspect" class="com.nateshao.factorybean.MyAspect" /> 
  10.     <!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 --> 
  11.     <bean id="userDaoProxy" 
  12.           class="org.springframework.aop.framework.ProxyFactoryBean"
  13.         <!-- 3.1 指定代理实现的接口--> 
  14.         <property name="proxyInterfaces" 
  15.                   value="com.nateshao.jdk.UserDao" /> 
  16.         <!-- 3.2 指定目标对象 --> 
  17.         <property name="target" ref="userDao" /> 
  18.         <!-- 3.3 指定切面,织入环绕通知 --> 
  19.         <property name="interceptorNames" value="myAspect" /> 
  20.         <!-- 3.4 指定代理方式,true:使用cglib,false(默认):使用jdk动态代理 --> 
  21.         <property name="proxyTargetClass" value="true" /> 
  22.     </bean> 
  23. </beans> 

 

 

 

TestXmlAspectj.java

  1. package com.nateshao.aspectj.xml; 
  2.  
  3. import com.nateshao.jdk.UserDao; 
  4. import org.springframework.context.ApplicationContext; 
  5. import org.springframework.context.support.ClassPathXmlApplicationContext; 
  6.  
  7. /** 
  8.  * @date Created by 邵桐杰 on 2021/10/14 19:58 
  9.  * @微信公众号 程序员千羽 
  10.  * @个人网站 www.nateshao.cn 
  11.  * @博客 https://nateshao.gitee.io 
  12.  * @GitHub https://github.com/nateshao 
  13.  * @Gitee https://gitee.com/nateshao 
  14.  * Description: 
  15.  */ 
  16. public class TestXmlAspectj { 
  17.     public static void main(String args[]) { 
  18.         String xmlPath = 
  19.                 "config.xml"
  20.         ApplicationContext applicationContext = 
  21.                 new ClassPathXmlApplicationContext(xmlPath); 
  22.         // 1 从spring容器获得内容 
  23.         UserDao userDao = (UserDao) applicationContext.getBean("userDao"); 
  24.         // 2 执行方法 
  25.         userDao.addUser(); 
  26.     } 

基于注解的声明式AspectJ(常用)

AspectJ框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。AspectJ的注解及其描述如下所示:

我们一起了解 Spring 中的 AOP !

MyAspect.java

  1. package com.nateshao.aspectj.annotation; 
  2.  
  3. import org.aspectj.lang.JoinPoint; 
  4. import org.aspectj.lang.ProceedingJoinPoint; 
  5. import org.aspectj.lang.annotation.*; 
  6. import org.springframework.stereotype.Component; 
  7.  
  8. /** 
  9.  * @date Created by 邵桐杰 on 2021/10/14 20:06 
  10.  * @微信公众号 程序员千羽 
  11.  * @个人网站 www.nateshao.cn 
  12.  * @博客 https://nateshao.gitee.io 
  13.  * @GitHub https://github.com/nateshao 
  14.  * @Gitee https://gitee.com/nateshao 
  15.  * Description: 切面类,在此类中编写通知 
  16.  */ 
  17. @Aspect 
  18. @Component 
  19. public class MyAspect { 
  20.     // 定义切入点表达式 
  21.     @Pointcut("execution(* com.nateshao.jdk.*.*(..))"
  22.     // 使用一个返回值为void、方法体为空的方法来命名切入点 
  23.     private void myPointCut(){} 
  24.     // 前置通知 
  25.     @Before("myPointCut()"
  26.     public void myBefore(JoinPoint joinPoint) { 
  27.         System.out.print("前置通知 :模拟执行权限检查...,"); 
  28.         System.out.print("目标类是:"+joinPoint.getTarget() ); 
  29.         System.out.println(",被织入增强处理的目标方法为:" 
  30.                 +joinPoint.getSignature().getName()); 
  31.     } 
  32.     // 后置通知 
  33.     @AfterReturning(value="myPointCut()"
  34.     public void myAfterReturning(JoinPoint joinPoint) { 
  35.         System.out.print("后置通知:模拟记录日志...," ); 
  36.         System.out.println("被织入增强处理的目标方法为:" 
  37.                 + joinPoint.getSignature().getName()); 
  38.     } 
  39.     // 环绕通知 
  40.     @Around("myPointCut()"
  41.     public Object myAround(ProceedingJoinPoint proceedingJoinPoint) 
  42.             throws Throwable { 
  43.         // 开始 
  44.         System.out.println("环绕开始:执行目标方法之前,模拟开启事务..."); 
  45.         // 执行当前目标方法 
  46.         Object obj = proceedingJoinPoint.proceed(); 
  47.         // 结束 
  48.         System.out.println("环绕结束:执行目标方法之后,模拟关闭事务..."); 
  49.         return obj; 
  50.     } 
  51.     // 异常通知 
  52.     @AfterThrowing(value="myPointCut()",throwing="e"
  53.     public void myAfterThrowing(JoinPoint joinPoint, Throwable e) { 
  54.         System.out.println("异常通知:" + "出错了" + e.getMessage()); 
  55.     } 
  56.     // 最终通知 
  57.     @After("myPointCut()"
  58.     public void myAfter() { 
  59.         System.out.println("最终通知:模拟方法结束后的释放资源..."); 
  60.     } 

annotation.xml

  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:aop="http://www.springframework.org/schema/aop" 
  5.        xmlns:context="http://www.springframework.org/schema/context" 
  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans 
  7.   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 
  8.   http://www.springframework.org/schema/aop 
  9.   http://www.springframework.org/schema/aop/spring-aop-4.3.xsd 
  10.   http://www.springframework.org/schema/context 
  11.   http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 
  12.     <!-- 指定需要扫描的包,使注解生效 --> 
  13.     <context:component-scan base-package="com.nateshao" /> 
  14.     <!-- 启动基于注解的声明式AspectJ支持 --> 
  15.     <aop:aspectj-autoproxy /> 
  16. </beans> 

TestAnnotationAspectj.java

  1. package com.nateshao.aspectj.annotation; 
  2.  
  3. import com.nateshao.jdk.UserDao; 
  4. import org.springframework.context.ApplicationContext; 
  5. import org.springframework.context.support.ClassPathXmlApplicationContext; 
  6. /** 
  7.  * @date Created by 邵桐杰 on 2021/10/14 20:09 
  8.  * @微信公众号 程序员千羽 
  9.  * @个人网站 www.nateshao.cn 
  10.  * @博客 https://nateshao.gitee.io 
  11.  * @GitHub https://github.com/nateshao 
  12.  * @Gitee https://gitee.com/nateshao 
  13.  * Description: 
  14.  */ 
  15. public class TestAnnotationAspectj { 
  16.     public static void main(String args[]) { 
  17.         String xmlPath = "annotation.xml"
  18.         ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); 
  19.         // 1 从spring容器获得内容 
  20.         UserDao userDao = (UserDao) applicationContext.getBean("userDao"); 
  21.         // 2 执行方法 
  22.         userDao.addUser(); 
  23.     } 

我们一起了解 Spring 中的 AOP !

总结

这篇文章主要讲解了Spring框架中AOP的相关知识。

  • 首先对AOP进行了简单的介绍,
  • 然后讲解了Spring中的两种动态代理,
  • 接下来讲解了Spring中基于代理类的AOP实现,
  • 最后讲解了如何使用AspectJ框架来进行AOP开发。

通过本章的学习,我们可以了解AOP的概念和作用,理解AOP中的相关常用术语,熟悉Spring中两种动态代理方式的区别,并能够掌握基于代理类和AspectJ框架的AOP开发方式。

原文链接:https://mp.weixin.qq.com/s/qg0ISx44-9vfIWNzWqz4QQ