美文网首页
AspectJ拦截方法内调用的其他方法

AspectJ拦截方法内调用的其他方法

作者: 任未然 | 来源:发表于2021-11-24 17:33 被阅读0次

    前言

    在后端开发时, 为了让代码解耦, 写出更加优雅的代码,通常都会用到代理,在spring项目用的最多的就是AspectJ. 那现在说下问题, 有时候我们写了带多代理方法, 套来套去,发现有些方法拦截不生效,在最近有个同事问这个问题,这就引出了本文的编写

    问题案例

    注解

    @Target({ElementType.METHOD})  //作用的位置
    @Retention(RetentionPolicy.RUNTIME) //作用域
    public @interface TestAnno {
    }
    

    代理

    @Component
    @Aspect
    @Slf4j
    public class TestAop {
        @Around(value = "@annotation(com.wpr.annotation.TestAnno)")
        public Object cacheClear(ProceedingJoinPoint pjp) throws Throwable {
            Object proceed = null;
            log.info("-------方法执行开始-------");
            proceed = pjp.proceed();
            log.info("-------方法执行结束-------");
            return proceed;
        }
    }
    

    测试例

    @RestController
    @RequestMapping("/test/base")
    public class TestController {
        @TestAnno
        @PostMapping("test1")
        public AjaxResult test1(@RequestBody Base base){
            return AjaxResult.success(base);
        }
    
        @PostMapping("test2")
        public AjaxResult test2(@RequestBody Base base){
            test3(base.getName());
            return AjaxResult.success(base);
        }
    
        @TestAnno
        public String test3(String name){
            return name;
        }
    }
    

    问题

    调用test1方法是能实现拦截的, 但是调用test2其方法内的test3()拦截不生效

    问题分析

    • test1()test2()这两个方法的区别是: 对于当前对象来说,test1()是bean注入时首次调用的方法就有注解@TestAnno, 而test2()方法没有注解TestAnno,其方法内的test3()有注解@TestAnno
    • AspectJ原理是Spring注入Bean时已经完成代理操作,返回的是一个cglib代理对象,因此其调用的test1()方法自然而然可以对其进行增强,简单来说就是, 在一个对象首次调用的方法如果有代理,注入bean时就生成了代理对象注入,如果首次调用的方法没有代理,注入的就是普通对象

    解决

    知道了问题解决就简单了, 我们可以强制使用代理对象去执行方法,只需在启动类加上注解@EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass=true), 然后就可以用方法AopContext.currentProxy()获取代理对象了

    • @EnableAspectJAutoProxy
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {
    
        /**
         * 指示是否要创建基于子类 (CGLIB) 的代理,而不是基于标准 Java 接口的代理。 默认值为false 。
         */
        boolean proxyTargetClass() default false;
    
        /**
         * 指示代理应由 AOP 框架公开为ThreadLocal以通过org.springframework.aop.framework.AopContext类进行检索。 
         * 默认关闭,即不保证AopContext访问将起作用。
         */
        boolean exposeProxy() default false;
    
    }
    

    启动类

    @SpringBootApplication()
    @EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass=true)
    public class BaseApp {
        public static void main(String[] args) {
            SpringApplication.run(BaseApp.class, args);
        }
    }
    

    修正示例

        @PostMapping("test2")
        public AjaxResult test2(@RequestBody Base base){
            getHelloServiceImpl().test3(base.getName());
            return AjaxResult.success(base);
        }
    
        @TestAnno
        public String test3(String name){
            return name;
        }
    
        /**
         * 强制获取代理对象,必须开启exposeProxy配置,否则获取不到当前代理对象
         * @return
         */
        private TestController getHelloServiceImpl() {
            return AopContext.currentProxy() != null ? (TestController) AopContext.currentProxy() : this;
        }
    

    相关文章

      网友评论

          本文标题:AspectJ拦截方法内调用的其他方法

          本文链接:https://www.haomeiwen.com/subject/tfantrtx.html