【问题标题】:Custom annotation for method in controller that extends an abstract controller扩展抽象控制器的控制器中方法的自定义注释
【发布时间】:2018-03-13 15:16:59
【问题描述】:

我需要做的是使用拦截器非常简单,但我真的希望有一个基于注释的更优雅的解决方案。问题是我的“解决方案”并没有真正起作用,我不知道为什么。也许这甚至是不可能的。

我的基本堆栈是: 弹簧靴 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。

我有几个扩展抽象控制器的控制器。这个抽象控制器必须准备一个连接,然后每个控制器都使用自己的特定实现放在 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() 方法上。这里肯定有问题。我不知道是什么...

【问题讨论】:

  • 你能确认你的acquire()方法真的是public吗?因为 Spring Aspects 仅适用于公共方法。
  • 是的,它是公开的

标签: java spring spring-boot aop spring-boot-starter


【解决方案1】:

问题在于您建议的代码的调用。你看,你定义了以下代码:

@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 代理,那么你就不能直接在建议的类中直接执行方法调用之类的事情,你需要确保每次都通过代理。

【讨论】:

  • 非常感谢埃德温!绝对必须更多地了解 Spring 所做的内在和隐藏的技巧,“代理”就是其中之一。
猜你喜欢
  • 2014-08-20
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-17
  • 2012-02-27
  • 1970-01-01
  • 2011-09-21
相关资源
最近更新 更多