前言
在后端开发时, 为了让代码解耦, 写出更加优雅的代码,通常都会用到代理,在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;
}
网友评论