目录
  • AOP的使用背景和好处
  • 一、几种使用姿势
    • 1、@AfterReturning和@Before
    • 2、@Around
    • 3、@Around可以篡改返回值,篡改入参
  • 二、使用AOP常见的问题和bug
    • 1、切不进去
    • 2、ctrl+鼠标左键不能自动跳到被切方法
  • 总结

    本文主要举几个工作中典型AOP的实操案例,还有经常出现的问题(切不进去,ctrl+左键跳不到被切方法中)等等。

    本文对于AOP的实现原理概不讨论,百度一搜有的是。

    AOP的使用背景和好处

    比如A模块是公司的核心模块,这块代码未经允许不得轻易篡改。

    但是你又有新的需求,需要在公司的核心模块的某个方法上进行增强(比如在执行核心方法的之前打印自定义日志,或者修改该核心方法的入参和返回值等等)

    这样你就可以在不修改核心模块源码的情况下,对源代码的方法进行增强,扩展原来方法的一些功能。

    这样既能保证源代码不被破坏,又可以扩展源代码现有的功能。

    一、几种使用姿势

    1、@AfterReturning和@Before

    @AfterReturning是后置方法,在目标方法执行后执行,@Before是前置方法,在目标方法执行前执行

    它们一般配合JoinPoint来使用(不能配合ProceedingJoinPoint,会报错)。

    直接看例子:

    被切的方法:

    @Service
    public class OriFuncImpl implements OriFunc{
    
        @Override
        public String ori(String str){
            System.out.println("执行了原方法");
            return str;
        }
    }

    使用@AfterReturning 和 @Before:

    @Aspect
    @Component
    public class AopFunc {
        @Pointcut("execution(* com.daji.aop_test.OriFuncImpl.ori(..))")
        public void test() {
        }
    
        @Before("test()")
        public void before(JoinPoint joinPoint) {
            Object[] args = joinPoint.getArgs();    //获取方法入参
            System.out.println("原方法的入参是:"+args[0]);
            System.out.println("原方法执行前会先执行我!!");
        }
    
    
        @AfterReturning("test()")
        public void after(JoinPoint joinPoint) {
            Object[] args = joinPoint.getArgs();    //获取方法入参
            System.out.println("原方法执行后会执行我!!");
        }
    
    }
    

    如果遇到异常,则不执行。

    • 当连接点方法成功执行后,返回通知方法才会执行,如果连接点方法出现异常,则返回通知方法不执行。
    • 返回通知方法在目标方法执行成功后才会执行,所以,返回通知方法可以拿到目标方法(连接点方法)执行后的结果。

    @AfterReturning获取被切方法返回值,篡改返回参数:

    在注解中增加returning参数即可: returning = “methodResult”

    @Pointcut("execution(* com.daji.aop_test.AopTestController.test1(..))")
        public void publish() {
        }
    
        @AfterReturning(value = "publish()",returning = "methodResult")
        public Object afterReturningPublish(JoinPoint joinPoint, Object methodResult) {
            //获取方法返回值
            String returnJson = JSONObject.toJSONString(methodResult);
            Object[] args = joinPoint.getArgs();
            System.out.println("原方法执行后会执行我!!");
            //这个返回值可以被我们篡改。
            return methodResult;
        }
    

    其实这个返回值也不是能任意篡改的:

    • 答案来了:可以改变返回值,但是分情况,

    不能改变:

    • 第一种情况:如果返回的对象,改变了对象的引用地址,这种情况,是不能改变返回对象中的值的
    • 第二种情况:如果返回的对象是一个基本数据类型,或者是String的值,是不能改变返回值的,尤其是String这种final类型的。

    可以改变:

    • 直接使用传入的object对象,改变其中的值,是可以的。

    2、@Around

    @Around是环绕通知,既可以控制入参,还可以控制原方法的执行和返回值

    常常配合ProceedingJoinPoint来使用。

    直接看例子:

    被切的方法:

    @Service
    public class OriFuncImpl implements OriFunc{
    
        @Override
        public String ori(String str){
            System.out.println("执行了原方法");
            return str;
        }
    }
    
    

    使用@Around:

    @Aspect
    @Component
    public class AopFunc {
        @Pointcut("execution(* com.daji.aop_test.OriFuncImpl.ori(..))")
        public void modifyReturn() {
        }
    
        @Around("modifyReturn()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            Object[] args = joinPoint.getArgs();
            Object result = joinPoint.proceed(args);
            return result;
        }
    
    }
    
    

    3、@Around可以篡改返回值,篡改入参

    需要ProceedingJoinPoint的配合

    注意一定要将@Around修饰的方法用Object修饰其返回值,并且返回原方法执行的结果,如下图所示:

    Spring AOP之@Around,@AfterReturning使用、切不进去的解决方案

    篡改入参一样的道理,只需要篡改下图中的 args数组,然后让其传入proceed中,即可完成篡改入参。

    如下图所示:

    Spring AOP之@Around,@AfterReturning使用、切不进去的解决方案

    所以,这个@Around比较万能,尤其是配合ProceedingJoinPoint的使用。使AOP能做的事情更多了。

    引申一下JoinPoint 和 ProceedingJoinPoint的关系:

    • ProceedingJoinPoint 只能在@Around中使用
    • JoinPoint也可以获取入参(getArgs()),它可以用于@Before 和 @AfterReturning
    • Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。它们之间的关系如下图:

    Spring AOP之@Around,@AfterReturning使用、切不进去的解决方案

    4、@Around如果不执行proceed(),那么原方法将不会执行

    二、使用AOP常见的问题和bug

    1、切不进去

    检查是否有如下注解:

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.9.4</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.2</version>
    </dependency>
    

    检查完毕后检查切面类,看看有没有以下注解:

    Spring AOP之@Around,@AfterReturning使用、切不进去的解决方案

    2、ctrl+鼠标左键不能自动跳到被切方法

    正常情况如图:

    Spring AOP之@Around,@AfterReturning使用、切不进去的解决方案

    如果你存在上述问题,检查你有没有安装下列插件:

    Spring AOP之@Around,@AfterReturning使用、切不进去的解决方案

    如果你是idea社区版,那么默认是没有的,你要么自己下,要么换成正式版。

    总结

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。