我需要做的是使用拦截器非常简单,但我真的希望基于注释提供更优雅的解决方案。问题是我的“解决方案”并没有真正奏效,我不知道为什么。也许这甚至不可能。
我的基本堆栈是: spring boot 1.4.1:
春豆4.3.4 和各种其他公用事业和测试罐。
我有几个扩展抽象控制器的控制器。此抽象控制器必须准备连接,然后每个控制器使用其自己的特定实现,置于 acquire()方法中。有时候一些cron工作正在达到这个终点。我们希望对某些控制器/作业进行审核,但不一定全部都是。 所以我考虑在审计应该添加自定义注释。
public abstract class ImportController {
@RequestMapping(value = "/checkout", method = RequestMethod.GET, produces = "application/json")
public String importEntities() {
//some code here ....
MyResult result = acquire(param);
//some code again ....
}
public abstract MyResult acquire(MyParam param)
}
需要审核的实施:
@RestController
@RequestMapping(value = "/cars")
public class CarsImportController extends ImportController {
@Override
@MyJobAudit // <--- this should add a pointcut used for Audit logging
public MyResult acquire(MyParam param) {
//cars specific code
}
}
不需要审核的实施
@RestController
@RequestMapping(value = "/tomatoes")
public class TomatoesImportController extends ImportController {
@Override
//no audit annotation
public MyResult acquire(MyParam param) {
//tomatoes specific code
}
}
我的JobAudit注释:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyJobAudit {
}
和方面类:
@Aspect
@Component
public class SystemAspectArchitecture {
@Pointcut("@annotation(MyJobAudit)")
public void auditableJob() {
}
}
我试着将我的注释放在各种服务类上,但它确实有用。但不是 acquire()方法。这里肯定是错的。我无法弄清楚是什么......
答案 0 :(得分:2)
问题在于调用您的建议代码。你看,你定义了以下代码:
@GetMappein(value = "/checkout")
public String getCheckout() {
//some code here ....
MyResult result = acquire(param); //Uh oh!!!
//some code again ....
}
但问题是建议的代码是在Spring为您创建的代理中定义的(如果是控制器,您从未见过),但是您在上面调用acquire(param)
方法并不是在Spring代理,但直接在你的具体类上,换句话说它相当于说this.acquire(param)
,但代码是this
的代理,而不仅仅是this
(你的代码)具体对象)。
解决问题的方法是获取对当前代理的访问权限。我解决了它如下。
首先,在您的应用中启用expose-proxy
。
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication, args);
}
}
然后,在具体类中,您打算调用应该建议的方法,您可以执行以下操作:
@RestController
public class ConcreteController {
@GetMapping("/checkout")
public String getSomething() {
Object proxy = AopContext.currentProxy();
return ((ConcreteController) proxy).acquire("Luke Skywalker");
}
@Auditable
public String acquire(Object param) {
return "Hello World, " + param;
}
}
AopContext.currentProxy()
将允许您访问this
控制器的代理,其中实际定义了acquire(params)
的建议。这将按预期工作。
据我所知,唯一的解决方法是使用真正的AOP而不仅仅是Spring代理。如果您使用真正的AOP,那么您将不得不进行某种形式的代码编织,这将在编译或加载时间期间建议代码。这样,代码将直接建议在具体类上,而不仅仅是在愚蠢的Spring代理上。在编译时或加载时通过检测建议使用真正的AOP this.acquire()
。但是如果你只使用Spring代理,那么你就不能直接在一个建议的类中进行直接方法调用,你需要确保每次执行时都要通过代理。