扩展抽象控制器的控制器中方法的自定义注释

时间:2017-10-02 14:20:46

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

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

我的基本堆栈是: spring boot 1.4.1:

  • 弹簧引导起动的Web
  • 弹簧引导起动的AOP
  • 弹簧引导起动-JDBC
  • 弹簧引导起动高速缓存

春豆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()方法。这里肯定是错的。我无法弄清楚是什么......

1 个答案:

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