AOP编程详细解析

xiaoxiao2021-02-28  58

1.什么是AOP

   AOP全称为Aspect Oriented Programming的缩写,也意为:面向切面编程,通过预编译手段和运行期动态代理实现程序功能的统一维护技术。采用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时也提高了开发效率。AOP采取横向抽取机制,取代了纵向继承体系重复的代码。

1.1AOP应用场景

   AOP典型的应用场景有:事务管理,性能监控、安全监测、日志等。

1.2AOP实现原理

   AOP底层采用代理的机制进行实现的。代理的方式有两种:jdk动态代理、cglib代理(字节码增强)

1.3AOP相关的术语

    1.target:目标类。需要被代理的类,也是需要通过代理增强的类。

    2.JoinPoint:连接点。连接点就是指需要增强类(target)可能拦截到的方法。如同target中。

    3.PointCut:切入点。已经被增强的连接点(在众多连接点中已经被增强的方法,就叫切入点。切入点是连接点的子集)。

    4.advice:通知(增强)。增强的代码(在切入点中增强所需的代码就是通知,例如:事务管理就是在切入点方法中前后开启事务和提交事务)。

    5.Weaving:织入。把增强代码(advice)应用到目标类(target)来创建新的代理对象(proxy)的过程就叫做织入。

    6.proxy:代理类。

    7.Aspect:切面。是切入点(PointCut)和通知(advice)的结合。

2.手动方式实现AOP编程

2.1采用JDK动态代理实现手动方式

   采用jdk动态代理需要的条件是:需要代理的类是采用接口+实现类的方式。(因为代理是采用目标类所实现的接口来生成目标类的代理)。

 第一步:准备目标类(target):

    接口:

public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); }

   实现类:

public class UserServiceImpl implements UserService{ @Override public void addUser() { System.out.println("addUser"); } @Override public void updateUser() { System.out.println("updateUser"); } @Override public void deleteUser() { System.out.println("deleteUser"); } }

  第二步:准备切面类(也是增强代码所在的类advice)

       切面类代码:

public class MyAspect { public void before() { System.out.println("前方法"); } public void after() { System.out.println("后方法 "); }; }

  第三步:创建生成代理类工厂实现对目标类的代理

     创建代理类通过Proxy类中的静态方法newProxyInstance(类加载器,代理类实现的接口,InvacationHandler处理类)

public class MyBeanFactory { //应为这个类是生成代理类,所以就通过这个方法产生代理类 public static UserService createService() { //首先需要目标类 UserService userService = new UserServiceImpl(); //切面类 MyAspect myAspect = new MyAspect(); /* 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面 * Proxy.newProxyInstance * 参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。 * 一般情况:当前类.class.getClassLoader(); * 目标类实例.getClass().get... * 参数2:Class[] interfaces 代理类需要实现的所有接口 * 方式1:目标类实例.getClass().getInterfaces() ;注意:只能获得自己接口,不能获得父元素接口 * 方式2:new Class[]{UserService.class} * 例如:jdbc 驱动 --> DriverManager 获得接口 Connection * 参数3:InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部 * 提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke * 参数31:Object proxy :代理对象 * 参数32:Method method : 代理对象当前执行的方法的描述对象(反射) * 执行方法名:method.getName() * 执行方法:method.invoke(对象,实际参数) * 参数33:Object[] args :方法实际参数 * */ UserService obj = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader() , userService.getClass().getInterfaces() , new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //执行前 myAspect.before(); //执行目标类的方法 Object obj = method.invoke(userService, args); //执行后 myAspect.after(); return obj; } } ); return obj; } }
2.2采用cglib(字节码增强)方式实现手动方式

   使用cglib字节码增强方式实现代理,需要导入相关的jar包(cglib.jar和asm.jar),但是一般Spring的核心包总已经提供了对cglib的支持。

   使用cglib字节码生成代理类不需要实现目标类的接口,而是通过目标类直接生成代理类(cglib生成代理类是采用继承的方式  实现代理)

   第一步:准备目标类(target)

     目标类代码:

public class UserServiceImpl{ public void addUser() { System.out.println("addUser"); } public void updateUser() { System.out.println("updateUser"); } public void deleteUser() { System.out.println("deleteUser"); } }

    第二步:准备切面类(advice)

       切面类代码:

public class MyAspect { public void before() { System.out.println("前方法"); } public void after() { System.out.println("后方法 "); }; }

   第三步:创建自定义工厂对目标类进行代理

      使用cglib创建代理采用的核心类是Enhancer,通过Enhancer类中create方法创建目标类的代理。但是在执行create方法之前需要确定代理目标类(也就是代理类的父类),确定父类是通过Enhancer类中的setSupperClass(父类的字节码)。也需要对Enhancer设置回调函数,也就是调用setCallback(Callback)。

  代码:

public class MyBeanFactory { //应为这个类是生成代理类,所以就通过这个方法产生代理类 public static UserServiceImpl createService() { //首先需要目标类 UserServiceImpl userService = new UserServiceImpl(); //切面类 MyAspect myAspect = new MyAspect(); //代理类: 采用cglib,底层创建目标类的子类 //核心类 Enhancer enhancer = new Enhancer(); //确定父类 enhancer.setSuperclass(userService.getClass()); /** * 设置回调函数:MethodInterceptor接口 等效于jdk中 InvacationHandler接口 * intercept()方法等效于jdk 的invoke()方法 * 参数1、参数2、参数3和invoke方法一样。 * 参数4调用代理类父类的目标方法 */ enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //执行前方法 myAspect.before(); //执行目标方法 //Object object = method.invoke(userService, args); //methodProxy 执行代理类父类的目标方法相当于执行userServiceImpl中的目标方法 //Object object = methodProxy.invokeSuper(proxy, args); Object object = methodProxy.invoke(userService, args); //执行后方法 myAspect.after(); return object; } }); //创建代理类 UserServiceImpl userServiceProxy = (UserServiceImpl) enhancer.create(); return userServiceProxy; } }

3.通过AOP联盟定义的规范和Spring提供的支持实现半自动方式

 3.1.AOP联盟定义的通知类型

     1.AOP联盟定义的通知类型在org.aopalliance.aop.Advice中。

     2.Spring按照通知(advice)在目标方法的前后分为五类:

        前置通知org.springframework.aop.MethodBeforeAdvice:在目标方法执行前进行增强。

        后置通知org.springframework.aop.AfterReturningAdvice:在目标方法执行后进行增强。

        环绕通知org.aopalliance.intercept.MethodInterceptor:在目标方法执行前后进行增强,但是必须手动执行目标方法。

        异常抛出通知org.springframework.aop.ThrowsAdvice:在方法执行过程中抛出异常后进行增强。

        引介通知org.springframework.aop.IntroductionInterceptor:在目标类中添加一些新的属性和方法时进行增强。

 3.2.需要导入的jar包

   需要导入aop(org.aopalliance.jar)规范和Spring对规范的实现(spring-aop.jar)。

 3.3.进行半自动方式AOP编程

     第一步:准备目标类(target)

        目标类所实现的接口:

public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); }

          目标类:

public class UserServiceImpl implements UserService{ @Override public void addUser() { System.out.println("addUser"); } @Override public void updateUser() { System.out.println("updateUser"); } @Override public void deleteUser() { System.out.println("deleteUser"); } }     第二步:准备切面类(advice)

       这里用我们经常用到的环绕通知进行演示。

       使用aop联盟方式进行aop编程,切面类需要按照aop联盟规范进行定义,使用环绕通知,advice类就需要实现接口MethodInterceptor。

     代码:

public class MyAspect implements MethodInterceptor{ @Override public Object invoke(MethodInvocation mi) throws Throwable { //执行前方法 System.out.println("前方法"); //手动执行目标方法 Object object = mi.proceed(); //执行后方法 System.out.println("后方法"); return object; }; }     第三步:在Spring配置文件中进行配置

      Spring配置文件配置,首先需要向配置文件中配置目标类和切面类bean,然后再通过ProxyFactoryBean来对目标类进行自动生成代理(也就是对目标类进行加强)。由于是通过Spring容器来自动对目标类进行代理,所以需要给ProxyFactoryBean注入目标类所实现的接口(通过接口可以通过jdk方式生成代理,如果没有配置接口,就会采用cglib方式进行代理。这种代理方式也是Spring的默认方式),也需要注入目标类(不管是jdk代理还是cglib代理都需要目标类),同时也需要注入切面类。如果不管是否有注入接口,都需要采用cglib方式进行代理,就给ProxyFactoryBean类中注入optimize,并且设置为true。

      配置文件如下:

<!-- 1 创建目标类 --> <bean id="userServiceId" class="com.test.c_proxy_by_FactoryBean.UserServiceImpl"></bean> <!-- 2创建切面类 --> <bean id="myAspect" class="com.test.c_proxy_by_FactoryBean.MyAspect"></bean> <!-- 3 创建代理类 * 使用工厂bean FactoryBean ,底层调用 getObject() 返回特殊bean * ProxyFactoryBean 用于创建代理工厂bean,生成特殊代理对象 interfaces : 确定接口们 通过<array>可以设置多个值 只有一个值时,value="" target : 确定目标类 interceptorNames : 通知 切面类的名称,类型String[],如果设置一个值 value="" optimize :强制使用cglib <property name="optimize" value="true"></property> 底层机制 如果目标类有接口,采用jdk动态代理 如果没有接口,采用cglib 字节码增强 如果声明 optimize = true ,无论是否有接口,都采用cglib --> <bean id="proxyUserService" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理类所需要实现的接口,通过接口去实现目标类的代理 --> <property name="interfaces" value="com.test.c_proxy_by_FactoryBean.UserService"></property> <!-- 目标类:通过Spring创建代理类需要指定代理类代理的目标类 --> <property name="target" ref="userServiceId"></property> <!-- 通知切面类名称 --> <property name="interceptorNames" value="myAspect"></property> </bean>

      这种方式也是通过Spring提供的一个特殊的Bean来自动对目标类进行代理或者增强。所以我们使用的时候是直接使用的代理类,而不是使用目标类。

4.Spring AOP编程:全自动

  第一步:准备目标类(target)

     目标类接口:

public interface UserService { public void addUser(); public String updateUser(); public void deleteUser(); }

    目标类:

public class UserServiceImpl implements UserService{ @Override public void addUser() { System.out.println("addUser"); //int a = 1/0; } @Override public String updateUser() { System.out.println("updateUser"); return "超哥"; } @Override public void deleteUser() { System.out.println("deleteUser"); } }

  第二步:准备切面类(advice)

  这里也是用环绕通知演示:

public class MyAspect implements MethodInterceptor{ @Override public Object invoke(MethodInvocation mi) throws Throwable { //执行前方法 System.out.println("前方法"); //手动执行目标方法 Object object = mi.proceed(); //执行后方法 System.out.println("后方法"); return object; }; }   第三步:在Spring配置文件中进行配置

    在配置前需要给配置文件中加入aop的头信息。

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    然后进行后面的配置:也是先配置目标类和切面类bean

<!-- 1 创建目标类 --> <bean id="userServiceId" class="com.test.d_proxy_by_SpringAOP.UserServiceImpl"></bean> <!-- 2创建切面类(通知) --> <bean id="myAspect" class="com.test.d_proxy_by_SpringAOP.MyAspect"></bean>

   最后进行aop编程:

<!-- 3 aop编程 3.1 导入命名空间 3.2 使用 <aop:config>进行配置 proxy-target-class="true" 声明时使用cglib代理 <aop:pointcut> 切入点 ,从目标对象获得具体方法 <aop:advisor> 特殊的切面,只有一个通知 和 一个切入点 advice-ref 通知引用 pointcut-ref 切入点引用 3.3 切入点表达式 execution(* com.itheima.c_spring_aop.*.*(..)) 选择方法 返回值任意 包 类名任意 方法名任意 参数任意 --> <aop:config proxy-target-class="false"> <aop:pointcut expression="execution(* com.test.d_proxy_by_SpringAOP.*.*(..))" id="myPointCut"/> <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"/> </aop:config>

这样在使用过程中就直接使用目标类就可以了,因为已经对目标类进行了加强。(其实采用这种方式进行增强原理是:在执行目标方法前通过了后处理bean(BeanPostProcessor)调用来对目标类进行代理,并返回。这样在执行目标类中的方法,而不是执行的是目标类中的方法,而是执行的是代理类中的方法)。

5.使用AOP框架AspectJ来进行AOP编程

  5.1.AspectJ介绍

    AspectJ是一个基于Java语言的AOP框架。

   Spring在2.0的时候提供了对AspectJ的切入点(PointCut)表达式的支持(切入点表达式就是从target(或者是说JoinPoint)中去选出需要增强的方法)

  5.2.切入点表达式

      通过execution()来描述方法,也就是从target中(或者说是JoinPoint中)选出需要增强的方法。至于如何选出就是在execution()中写入选择的表达式。

execution()语法介绍:execution(需要筛选方法的修饰符 筛选方法的返回值  包.类.方法名(方法参数) throws 异常)。

    修饰符:一般可以省略。

           public   公共方法(private protect.....)

           *           任意修饰符

     返回值:返回值不能进行省略。

           void       返回值为空

           String     返回值为字符串(其它省略)

           *             任意返回值

      包:可以省略,但是一般不省略。

           com.test.a         固定包

           com.test.a.*.service   a包下的任意子包下的service包

           com.test.a..        a包下的所有子包

      类:可以省略,但是一般不省略。

           UserServiceImpl    指定类

           *Impl                      以Impl结尾的类

           User*                     以User开头的类

           *                            任意类

      方法名:不能省略。

            addUser               固定方法

            add*                     以add开头的方法

            *Delete                 以Delete结尾的方法

            *                            任意方法 

      参数:不能省略。

            ()                      无参数

            (int)                  一个int参数

            (int,int)            两个int参数

             (..)                   参数任意

       throws,可以省略。一般我们也不写。

  5.3AspectJ通知类型

   

     before:前置通知(应用:各种校验)

     在方法执行前执行,如果通知抛出异常,阻止方法运行

     afterReturning:后置通知(应用:常规数据处理)

     方法正常返回后执行,如果方法中抛出异常,通知无法执行

     必须在方法执行后才执行,所以可以获得方法的返回值。

     around:环绕通知(应用:十分强大,可以做任何事情)

     方法执行前后分别执行,可以阻止方法的执行

     必须手动执行目标方法

     afterThrowing:抛出异常通知(应用:包装异常信息)

     方法抛出异常后执行,如果方法没有抛出异常,无法执行

     after:最终通知(应用:清理现场)

     方法执行完毕后执行,无论方法中是否出现异常

5.3.导入jar包

   导入的jar主要有四个:aop联盟(aopallicance.jar),Spring对AOP联盟的支持(Spring-aop.jar),AspectJ(aspectJ.weaver.jar),Spring对AspectJ的支持(Spring-aspectJ.jar)

5.4.使用AspectJ方式进行AOP编程
     第一步:准备目标类

         目标类所实现的接口:

public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); }

         目标类:

public class UserServiceImpl implements UserService{ @Override public void addUser() { System.out.println("addUser"); } @Override public void updateUser() { System.out.println("updateUser"); } @Override public void deleteUser() { System.out.println("deleteUser"); } }     第二步:准备切面类

      切面类不需要向AOP联盟那样必须实现他的规范,而是采用AspectJ提供通知名任意(方法名任意)

     代码:

public class MyAspect { //JoinPoint 用于描述连接点(目标方法),可以获得目标方法名等。 public void myBefore(JoinPoint joinPoint) { System.out.println("前置通知" + joinPoint.getSignature().getModifiers()); } //后置通知 // 通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){ // 参数1:连接点描述 // 参数2:类型Object,参数名 returning="ret" 配置的 public void myAfterReturning(JoinPoint joinPoint,Object ret) { System.out.println("后置通知"+joinPoint.getSignature().getName()+",---->"+ret); } //环绕通知 public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ //执行前方法 System.out.println("前"); //需要手动执行目标方法 Object obj =joinPoint.proceed(); //执行后方法 System.out.println("后"); return obj; } //异常通知 public void myAfterThrowing(JoinPoint joinPoint,Throwable e) { System.out.println("抛出异常通知:"+e.getMessage()); } //最终通知 public void myAfter() { System.out.println("最终通知"); } }   第三步:Spring配置文件进行配置

    

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 1 创建目标类 --> <bean id="userServiceId" class="com.test.e_proxy_by_AspectJXML.UserServiceImpl"></bean> <!-- 2创建切面类(通知) --> <bean id="myAspect" class="com.test.e_proxy_by_AspectJXML.MyAspect"></bean> <!-- 3aop编程 <aop:aspect>将切面类声明成“切面”,从而获得通知(方法) ref 切面类引用 <aop:pointcut>声明一个切入点,所有通知都可以使用。 expression 切入点表达式 id 名称用于其他通知引用 --> <aop:config proxy-target-class="false"> <aop:pointcut expression="execution(* com.test.e_proxy_by_AspectJXML.UserServiceImpl.*(..))" id="myPointCut"/> <aop:aspect ref="myAspect"> <!-- 3.1前置通知: <aop:before method="" pointcut="" pointcut-ref=""/> method:通知,及方法名。 pointcut:切入点表达式,此表达式只能当前通知使用。 pointcut-ref:切入点引用,可以与其他通知共享切入点。 <aop:before method="myBefore" pointcut-ref="myPointCut"/> --> <!-- 3.2后置通知:目标方法后执行,可以获得返回值。 <aop:after-returning method="" pointcut-ref="" returning=""/> returning:第二个参数的名称。 <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret"/> --> <!-- 3.3 环绕通知 <aop:around method="" pointcut-ref=""/> 通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ 返回值类型:Object 方法名:任意 参数:org.aspectj.lang.ProceedingJoinPoint 抛出异常 执行目标方法:Object obj = joinPoint.proceed(); 例如: <aop:around method="myAround" pointcut-ref="myPointCut"/> --> <!-- 3.4 抛出异常 <aop:after-throwing method="" pointcut-ref="" throwing=""/> throwing :通知方法的第二个参数名称 通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ 参数1:连接点描述对象 参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置 例如: <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> --> <!-- 3.5 最终通知 --> <aop:after method="myAfter" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config> </beans>

6.使用AspectJ注解方式

第一步:修改配置文件

   使用注解的方式实现AOP编程,首先需要对Spring配置文件添加context头信息

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

  然后打开Spring的注解扫描:

<!-- 添加注解扫描 --> <context:component-scan base-package="com.test.f_proxy_by_AspectJAnnotation"></context:component-scan>

  接下来是让AspectJ注解生效:

<!-- 让Aspect注解生效 :添加aspectj自动代理--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

整个Spring配置文件代码如下:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 添加注解扫描 --> <context:component-scan base-package="com.test.f_proxy_by_AspectJAnnotation"></context:component-scan> <!-- 让Aspect注解生效 :添加aspectj自动代理--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans> 第二步:准备目标类(target)

   目标类所实现的接口:

public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); }

   目标类:

@Service("userServiceId") public class UserServiceImpl implements UserService{ @Override public void addUser() { System.out.println("addUser"); } @Override public void updateUser() { System.out.println("updateUser"); //int a = 1/0; } @Override public void deleteUser() { System.out.println("deleteUser"); } } 第三步:准备切面类并直接在切面类添加注解进行AOP编程

在切面类上添加@Aspect注解是指该类为切面类。

在切面类的方法中分别添加以下注解:

(在value中对AspectJ表达式进行引用:首先需要有可引用的pointcut。如何准备pointcut呢?

    在切面类中任意写一个无返回值无参数的方法,并在方法前面加上@Pointcut(里面写表达式)注解。

   后面引用就直接写该方法。

  )

 @Before(value="aspectJ表达式或者aspectJ表达式的引用"):该方法将作为增强代码(advice)织入(weaving)到目标类中(target)。  

代码如下:

@Component @Aspect public class MyAspect { //注解方式生成切入点引用 @Pointcut(value="execution(* com.test.f_proxy_by_AspectJAnnotation.UserServiceImpl.*(..))") private void myPointCut() { } //添加前置通知注解:value属性里面写切入点表达式或者切入点引用 //@Before("execution(* com.test.f_proxy_by_AspectJAnnotation.UserServiceImpl.*(..))") public void myBefore(JoinPoint joinPoint) { System.out.println("前置通知" + joinPoint.getSignature().getModifiers()); } //添加后置通知 //@AfterReturning(value="myPointCut()",returning="ret") public void myAfterReturning(JoinPoint joinPoint,Object ret) { System.out.println("后置通知"+joinPoint.getSignature().getName()+",---->"+ret); } //添加环绕通知 //@Around("myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ //执行前方法 System.out.println("前"); //需要手动执行目标方法 Object obj =joinPoint.proceed(); //执行后方法 System.out.println("后"); return obj; } //添加抛出异常通知 //@AfterThrowing(value="myPointCut()",throwing="e") public void myAfterThrowing(JoinPoint joinPoint,Throwable e) { System.out.println("抛出异常通知:"+e.getMessage()); } //添加最后通知 @After("execution(* com.test.f_proxy_by_AspectJAnnotation.UserServiceImpl.*(..))") public void myAfter() { System.out.println("最终通知"); } }

转载请注明原文地址: https://www.6miu.com/read-2624790.html

最新回复(0)