2017-10-02 9 views
1

요격기를 사용하면 꽤 간단하지만 주석을 기반으로 한보다 우아한 솔루션을 기대하고있었습니다. 문제는 내 "솔루션"이 실제로 작동하지 않으며 이유를 알 수 없다는 것입니다. 어쩌면 이것이 가능하지 않을 수도 있습니다.추상 컨트롤러를 확장하는 컨트롤러의 메소드에 대한 사용자 정의 주석

내 기본 스택은 다음과 같습니다 봄 부팅 1.4.1 :

  • 스프링 부팅 스타터 웹
  • 스프링 부팅 스타터 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() { 
    } 
} 

다양한 서비스 클래스에 내 주석을 넣으려고했는데 작동합니다. 하지만 아니 취득() 메서드입니다. 뭔가 잘못되었습니다. 나는 무엇을 알아낼 수 없다 ...

+0

'acquire()'메소드가 실제로'public'인지 확인할 수 있습니까? 스프링 측면은 공용 메서드에서만 작동하기 때문입니다. –

+0

예, 공개입니다 – Buzzo

답변

2

문제는 권고 된 코드의 호출에있다. 당신은 다음과 같은 코드가 정의한 참조 :

@GetMappein(value = "/checkout") 
public String getCheckout() { 
    //some code here .... 
    MyResult result = acquire(param); //Uh oh!!! 
    //some code again .... 
} 

그러나 문제는 조언 코드가 봄이 (당신이 볼 수 없을 컨트롤러의 경우와있는) 당신을 위해 생성하는 프록시에 정의되어 있다는 것입니다,하지만 호출 위의 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()는 줄 것이다 당신이 액세스 acquire(params)에 대한 조언이 실제로 정의 된 this 컨트롤러의 프록시. 그리고 예상대로 작동합니다.

스프링 프록시뿐만 아니라 진정한 AOP를 사용하는 것이 유일한 해결책이라는 것을 알고 있습니다. 그리고 진정한 AOP를 사용한다면 컴파일이나 로딩 시간에 코드를 작성하는 형태의 코드를 만들어야 할 것입니다. 그런 식으로 코드는 바보 같은 스프링 프록시뿐만 아니라 구체적인 클래스에 직접적으로 조언 될 것입니다. true AOP this.acquire()을 사용하는 것은 컴파일 시간 또는로드 시간에 계측을 통해 권고됩니다. 하지만 스프링 프록시 만 사용하는 경우 권장 클래스에서 직접 메소드 호출과 같은 작업을 직접 수행 할 수는 없으며 그렇게 할 때마다 프록시를 통과해야합니다.

+0

대단히 감사합니다! 확실히 Spring이하는 내부 및 숨겨진 트릭에 대해 더 자세히 알아야하며 "프록시"가 그 중 하나입니다. – Buzzo