SpringAOP

SpringAOP

什么是AOP

AOP即面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。

在不改变原有的逻辑的基础上,增加一些额外的功能。

aop是对OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

AOP的相关概念

横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

Aspect(切面):通常是一个类,里面可以定义切入点和通知

JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

Advice(通知):AOP在特定的切入点上执行的增强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕)

Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

weave(织入):将切面应用到目标对象并导致代理对象创建的过程

introduction(引入):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

AOP代理(AOP Proxy):AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO

AOP通知类型介绍

1、Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可

2、AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值

3、AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名

来访问目标方法中所抛出的异常对象

4、After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式

5、Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint

手动实现AOP

JDK动态代理的方式

jdk动态代理对“装饰者”设计模式的简化。使用前提:必须有接口

需要的类

1、目标类:接口+实现

2、切面类:用于存通知

3、代理类:由工厂生成

目标类
public interface UserService {
    public void addUser();
    public void updateUser();
    public void deleteUser();
}
public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("noauto.jdk.add");
    }

    @Override
    public void updateUser() {
        System.out.println("noauto.jdk.update");
    }

    @Override
    public void deleteUser() {
        System.out.println("noauto.jdk.delete");
    }
}
切面类
public class MyAspect {

    public void before(){
        System.out.println("前置通知");
    }

    public void after(){
        System.out.println("后置通知");
    }

}
代理类
public class MyBenFactory {
    public static UserService createUserService(){
        //切面类
        MyAspect myAspect = new MyAspect();
        //目标类
        UserServiceImpl userService = new UserServiceImpl();
        /**
         * 代理类
         *  Proxy.newProxyInstance(  )
         *      ClassLoader loader:类加载器,动态代理类运行时创建,任何类都需要类加载器加载到内存
         *             一般情况:当前类.class.getClassLoader();
         *                      目标类实例.getClass().getClassLoader();
         *      Class<?>[] interfaces 代理类需要实现的所有接口
         *              方式一:目标类实例.getClass().getInterFaces();
         *              方式二:new Class[]{UserService.class}
         *      InvocationHandler h:处理类,接口,必须进行实现,一般采用匿名内部类
         *              提供了一个invoke方法:代理类的每一个方法执行时,都将调用一次invoke
         *                  1:proxy:代理对象
         *                  2:method:代理对象当前执行的方法的描述对象(反射)
         *                      执行方法名:method.getName()
         *                      执行方法:method.invoke(对象,参数)
         *                  3:args:方法的实际参数
         */
        UserService proxUserService
                = (UserService)Proxy.newProxyInstance(
                MyBenFactory.class.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 proxUserService;
    }
}
测试类
public class TestNoAutoJDK {

    @Test
    public void demo01(){
        UserService userService = MyBenFactory.createUserService();
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }

}
结果:
前置通知
noauto.jdk.add
后置通知
前置通知
noauto.jdk.update
后置通知
前置通知
noauto.jdk.delete
后置通知

CGLIB动态代理的方式

CGLIB:字节码增强,没有接口,只有实现类。

运行原理:在运行时创建目标类的子类,从而对目标类进行增强

导入jar包:spring的核心包已经导入了cglib、asm(cglib的依赖包)

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>

需要的类

目标类
public class UserServiceImpl  {
    public void addUser() {
        System.out.println("noauto.cglib.add");
    }
    public void updateUser() {
        System.out.println("noauto.cglib.update");
    }
    public void deleteUser() {
        System.out.println("noauto.cglib.delete");
    }
}

切面类
public class MyAspect {
    public void before(){
        System.out.println("前置通知cglib");
    }
    public void after(){
        System.out.println("后置通知cglib");
    }
}
代理类

image-20201106185323140

上面是Callback的一些子类

public class MyBenFactory {
    public static UserServiceImpl createUserService(){
        //切面类
        MyAspect myAspect = new MyAspect();
        //目标类
        UserServiceImpl userService = new UserServiceImpl();
        /**
         * 代理类  采用cglib,底层创建目标类的子类
         *
         */
        //1、核心类
        Enhancer enhancer = new Enhancer();
        //2、确定父类
        enhancer.setSuperclass( userService.getClass() );
        //3、设置回调函数,MethodInterceptor接口等效于invocationHandler接口
        enhancer.setCallback( new MethodInterceptor() {
            /**
             * intercept等效于 invoke,前三个参数与jdk动态代理的invoke的参数一样
             * @param o
             * @param method
             * @param objects
             * @param methodProxy	方法的代理
             				使用方式:methodProxy.invokeSuper(userService,objects)。执行当前类(代理类)的父类(父类)中的方法
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                myAspect.before();
                //执行目标类的方法
                Object obj = method.invoke( userService, objects );
                myAspect.after();
                return obj;
            }
        } );
        //4、创建代理
        UserServiceImpl proxyService = (UserServiceImpl)enhancer.create();

        return proxyService;
    }
}
测试类
public class TestNoAutoCGLIB {

    @Test
    public void demo01(){
        UserServiceImpl userService = MyBenFactory.createUserService();
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }

}
结果
前置通知cglib
noauto.cglib.add
后置通知cglib
前置通知cglib
noauto.cglib.update
后置通知cglib
前置通知cglib
noauto.cglib.delete
后置通知cglib

SpringAOP的实现

半自动的实现

前面都是手动创建代理对象,这里就让Spring创建代理对象,我们从spring容器中手动的获取代理对象

导入jar包:AOP、spring核心jar包

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>

目标类

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

    @Override
    public void addUser() {
        System.out.println("spring.beanfactory.add");
    }

    @Override
    public void updateUser() {
        System.out.println("spring.beanfactory.update");
    }

    @Override
    public void deleteUser() {
        System.out.println("spring.beanfactory.delete");
    }
}

切面类

/**
 * @author Administrator
 * @author 2020-11-06
 * @Description 切面类,要确定通知,以环绕通知为例
 **/

public class MyAspect implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object proceed = null;
       try {
           System.out.println("环绕前");
           //手动执行目标方法
           proceed = mi.proceed();
           System.out.println("环绕后");
       }catch (Exception e){
           System.out.println("环绕异常");
       }
        return proceed;
    }
}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--创建目标类-->
    <bean id="userServiceImpl" class="org.ybl.aop.auto.factory_bean.UserServiceImpl"/>
    <!--创建切面类-->
    <bean id="myAspect" class="org.ybl.aop.auto.factory_bean.MyAspect"/>

    <!--创建代理类-->
    <bean id="proxyUserService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces" value="org.ybl.aop.auto.factory_bean.UserService"/>
        <property name="target" ref="userServiceImpl"/>
        <property name="interceptorNames" value="myAspect"/>
        <!--为true就是强制使用cglib-->
        <property name="optimize" value="true"/>
    </bean>
</beans>

测试类

public class TestFactoryBean {

    @Test
    public void demo01(){
        ClassPathXmlApplicationContext cpac = new ClassPathXmlApplicationContext( "classpath:beans.xml" );

        UserService userService = (UserService)cpac.getBean( "proxyUserService" );
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }

}

结果

环绕前
spring.beanfactory.add
环绕后
环绕前
spring.beanfactory.update
环绕后
环绕前
spring.beanfactory.delete
环绕后

强制使用CGLIB的情况

image-20201106194757600

不强制使用CGLIB的情况

image-20201106194901191

总结:

​ 如果目标类实现了接口,就用jdk动态代理

​ 没有实现接口,就用CGLIB字节码增强

​ 如果申明optimize=true,不管有没有实现接口,都强制使用CGLIB

全自动的实现

目标类

public interface UserService {
    public void addUser();
    public void updateUser();
    public void deleteUser();
}
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("spring.AOP.add");
    }
    @Override
    public void updateUser() {
        System.out.println("spring.AOP.update");
    }
    @Override
    public void deleteUser() {
        System.out.println("spring.AOP.delete");
    }
}

切面类

public class MyAspect implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object proceed = null;
       try {
           System.out.println("环绕前");
           //手动执行目标方法
           proceed = mi.proceed();
           System.out.println("环绕后");
       }catch (Exception e){
           System.out.println("环绕异常");
       }
        return proceed;
    }
}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<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
                           https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--创建目标类-->
    <bean id="userServiceImpl" class="org.ybl.aop.auto.springaop.UserServiceImpl"/>
    <!--创建切面类-->
    <bean id="myAspect" class="org.ybl.aop.auto.springaop.MyAspect"/>
    <!--
        1、使用<aop:config>进行配置
            <aop:pointcut>切入点,从目标对象获得具体方法
            <aop:advisor>特殊的切面,只有一个通知和一个切入点
                advice-ref:通知引用
                pointcut-ref:切入点引用
         2、切入点表达式
            execution(* org.ybl.aop.auto.factory_bean.*.*(..))
                第一个*:表示任意返回值
                第二个*:表示org.ybl.aop.auto.factory_bean下面的任意类
                第三个*:表示这些类下面的任意方法
                ..:表示上面这些方法的多个参数(类型、个数都可以不一样)
    -->
    <aop:config>
        <aop:pointcut id="myPointCut" expression="execution(* org.ybl.aop.auto.springaop.*.*(..))"/>
        <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"/>
    </aop:config>
</beans>

测试类

public class TestSpringAOP {
    @Test
    public void demo01(){
        ClassPathXmlApplicationContext cpac 
                = new ClassPathXmlApplicationContext( "classpath:beans.xml" );
        UserService userService = (UserService)cpac.getBean( "userServiceImpl" );
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}

总结:

​ 这种方式默认使用的是jdk动态代理,也可以通过<aop:config proxy-target-class="true">这种方式来设置使用CGLIB动态代理

AspectJ

介绍:

是一个基于Java语言的AOP框架。Spring2.0后新增了堆AspectJ切点表达式的支持。@AspectJ是AspectJ1.5新增功能给你,通过JDK5注解技术,允许直接在Bean类中定义切面,新版本Spring框架,建议使用AspectJ方式来开发AOP

切入点表达式

1、execution() 用于描述方法

​ 语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)

​ 修饰符,一般省略

​ public 公共方法

				*          任意

​ 返回值,不能省略

​ void 返回没有值

​ String 返回值字符串

				*           任意

包,[省略]

​ com.xxx.crm 固定包

​ com.xxx.crm.*.service crm包下面子包任意 (例如:com.itheima.crm.staff.service)

​ com.xxx.crm.. crm包下面的所有子包(含自己)

​ com.xxx.crm.*.service.. crm包下面任意子包,固定目录service,service目录任意包

类,[省略]

​ UserServiceImpl 指定类

​ *Impl 以Impl结尾

​ User* 以User开头

	    *         						任意

方法名,不能省略

​ addUser 固定方法

​ add* 以add开头

​ *Do 以Do结尾

			* 										任意

(参数)

​ () 无参

​ (int) 一个整型

​ (int ,int) 两个

​ (..) 参数任意

throws ,可省略,一般不写。

例子:

execution(* com.xxx.crm.*service..*.*(...))

AspectJ通知类型

一共6种,了解5种

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

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

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

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

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

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

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

	必须手动执行目标方法

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

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

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

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

基于xml的配置

准备

1、目标类:接口+实现

2、切面类:编写多个通知,采用aspectj,通知名称任意(方法名任意)

3、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("spring.aspectj.add");
    }
    @Override
    public void updateUser() {
        System.out.println("spring.aspectj.update");
    }
    @Override
    public void deleteUser() {
        System.out.println("spring.aspectj.delete");
    }
}

切面类

public class MyAspect {
    public void myBefore(JoinPoint joinPoint) {
        System.out.println( "前置通知 : " + joinPoint.getSignature().getName() );
    }
    public void myAfterReturning(JoinPoint joinPoint, Object ret) {
        System.out.println( "后置通知 : " + joinPoint.getSignature().getName() + " , 返回值:" + ret );
        System.out.println("==========");
    }
    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(JoinPoint joinPoint) {
        System.out.println( "最终通知" );
    }
}

beans.xml

<!-- 1 创建目标类 -->
<bean id="userServiceImpl" class="org.ybl.aop.auto.aspect.UserServiceImpl"></bean>
<!-- 2 创建切面类(通知) -->
<bean id="myAspect" class="org.ybl.aop.auto.aspect.MyAspect"></bean>
<!--3 aop编程
        <aop:aspect> 将切面类 声明“切面”,从而获得通知(方法)
            ref 切面类引用
        <aop:pointcut> 声明一个切入点,所有的通知都可以使用。 expression 切入点表达式 id 名称,用于其它通知引用
    -->
<aop:config>
    <aop:aspect ref="myAspect">
        <aop:pointcut expression="execution(* org.ybl.aop.auto.aspect.UserServiceImpl.*(..))" id="myPointCut"/>
        <!-- 3.1 前置通知 <aop:before method="" pointcut="" pointcut-ref=""/>
                method : 通知,及方法名
                pointcut :切入点表达式,此表达式只能当前通知使用。
                pointcut-ref : 切入点引用,可以与其他通知共享切入点。
                通知方法格式:public void myBefore(JoinPoint joinPoint){
                    参数1:org.aspectj.lang.JoinPoint 用于描述连接点(目标方法),获得目标方法名等
                    例如:
                         -->
        <aop:before method="myBefore" pointcut-ref="myPointCut"/>
        <!-- 3.2后置通知 ,目标方法后执行,获得返回值 <aop:after-returning method="" pointcut-ref="" returning=""/>
                returning 通知方法第二个参数的名称
                    通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){
                        参数1:连接点描述 参数2:类型Object,参数名 returning="ret" 配置的
                    例如: <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
            -->
        <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>

测试类

public class TestAspectjXML {
    @Test
    public void demo01(){
        ClassPathXmlApplicationContext cpac 
            			= new ClassPathXmlApplicationContext( "classpath:beans-aspectj.xml" );
        UserService userService = (UserService)cpac.getBean( "userServiceImpl" );
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}

结果

前置通知 : addUser
环绕前
spring.aspectj.add
最终通知
环绕后
后置通知 : addUser , 返回值:null
==========
前置通知 : updateUser
环绕前
spring.aspectj.update
最终通知
环绕后
后置通知 : updateUser , 返回值:null
==========
前置通知 : deleteUser
环绕前
spring.aspectj.delete
最终通知
环绕后
后置通知 : deleteUser , 返回值:null
==========

基于注解的方式

目标类

public interface UserService {
    public void addUser();
    public void updateUser();
    public void deleteUser();
}
@Service("userServiceImpl")
public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("spring.anno.add");
    }

    @Override
    public void updateUser() {
        System.out.println("spring.anno.update");

    }

    @Override
    public void deleteUser() {
        System.out.println("spring.anno.delete");
        int i = 1/0;
    }
}

切面类

@Component
@Aspect
public class MyAspect {
    /**
     * 公共的切入点表达式
     */
    @Pointcut("execution(* org.ybl.aop.auto.aspect.anno.UserServiceImpl.*(..))")
    public void myPontcut() {

    }

    @Before(value = "myPontcut()")
    public void myBefore(JoinPoint joinPoint) {
        System.out.println( "前置通知 : " + joinPoint.getSignature().getName() );
    }

    @AfterReturning(value = "myPontcut()", returning = "ret")
    public void myAfterReturning(JoinPoint joinPoint, Object ret) {
        System.out.println( "后置通知 : " + joinPoint.getSignature().getName() + " , 返回值:" + ret );
        System.out.println( "==========" );
    }

    @Around(value = "myPontcut()")
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println( "环绕前" );
        //手动执行目标方法
        Object obj = joinPoint.proceed();
        System.out.println( "环绕后" );
        return obj;
    }

    @AfterThrowing(value = "myPontcut()", throwing = "e")
    public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
        System.out.println( "抛出异常通知 : " + e.getMessage() );
    }

    @After(value = "myPontcut()")
    public void myAfter(JoinPoint joinPoint) {
        System.out.println( "最终通知" );
    }
}

beans.xml

<!--扫描注解类-->
<context:component-scan base-package="org.ybl.aop.auto.aspect.anno"/>
<!--确定aop的注解要生效-->
<aop:aspectj-autoproxy/>

测试类

public class TestAspectjAnno {
    @Test
    public void demo01(){
        ClassPathXmlApplicationContext cpac 
                    = new ClassPathXmlApplicationContext( "classpath:beans-aspectjanno.xml" );
        UserService userService = (UserService)cpac.getBean( "userServiceImpl" );
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}

结果

环绕前
前置通知 : addUser
spring.anno.add
后置通知 : addUser , 返回值:null
==========
最终通知
环绕后
环绕前
前置通知 : updateUser
spring.anno.update
后置通知 : updateUser , 返回值:null
==========
最终通知
环绕后
环绕前
前置通知 : deleteUser
spring.anno.delete
抛出异常通知 : / by zero
最终通知

java.lang.ArithmeticException: / by zero
posted @ 2021-10-26 22:16  程序员清风  阅读(34)  评论(0编辑  收藏  举报