我要做的非常简单,使用拦截器,但我真的希望有一个基于注释的更优雅的解决方案。问题是我的“解决方案”并没有真正起作用,我不知道为什么。或许这根本就不可能。
我的基本堆栈是: Spring 启动1.4.1:
- spring-boot-starter-web
- spring-boot-starter-aop
- spring-boot-starter-jdbc
- spring-boot-starter-cache
spring-beans 4.3.4 以及各种其他实用程序和测试 jar。
我有几个扩展抽象 Controller 的 Controller 。这个抽象 Controller 必须准备一个连接,然后每个 Controller 使用放置在 acquire() 方法中的自己的特定实现。有时,一些 cron 作业会访问此端点。我们希望对某些 Controller /作业进行审核,但不一定是全部。 所以我正在考虑在应该进行审核的地方添加自定义注释。
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() 方法。这里肯定有问题。我不明白什么...
最佳答案
问题出在调用您建议的代码上。您会看到,您定义了以下代码:
@GetMappein(value = "/checkout")
public String getCheckout() {
//some code here ....
MyResult result = acquire(param); //Uh oh!!!
//some code again ....
}
但问题是,建议的代码是在 Spring 为您创建的代理中定义的(对于您永远看不到的 Controller ),但是您对 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
Controller 的代理,其中 acquire(params)
的建议实际上是定义的。这将按预期工作。
我知道唯一的解决方法是使用真正的 AOP 而不仅仅是 Spring 代理。如果您使用真正的 AOP,那么您将必须进行某种形式的代码编织,以便在编译或加载期间为代码提供建议。这样,代码将直接在具体类上建议,而不仅仅是在愚蠢的 Spring 代理上。建议通过编译时或加载时的检测来使用真正的 AOP this.acquire()
。但是,如果您只使用 Spring 代理,那么您无法直接在建议的类中执行诸如直接方法调用之类的操作,您需要确保每次执行此操作时都通过代理。
关于java - Controller 中扩展抽象 Controller 的方法的自定义注释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46527414/