美文网首页
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