2013-01-12 129 views
2

我愛春天。但是當涉及在方法執行前後插入代碼時,我不得不承認,Spring/AOP錯了。使用Spring AOP創建攔截器

在我的拙見中,硬編碼方法名稱或類似於方法名稱的正則表達式應該是我們經歷過的最後一件事。在21世紀初,我們都從XML地獄中學到了這一難題。相比之下,EJB3的攔截器是基於註釋的,簡單的,易於閱讀的,它不僅能夠處理大多數(如果不是全部)我們的問題,還能夠處理大多數問題。如果我們可以在Spring中編寫一個EJB3風格的攔截器,這不是很好嗎?

我所知道的最好的解決辦法是在切入點表達式中使用@annotation,正如我在下面的代碼所做的:

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface AroundExecution { 

} 

@Component 
public class SimpleBean { 
    @AroundExecution 
    public void printGreetings(){ 
     logger.info("Hello World 2"); 
    } 
} 

@Aspect 
@Component 
public class SimpleAdvice { 
    @Around("@annotation(com.myinterceptors.springaop.AroundExecution)") 
    public Object adviceAround(ProceedingJoinPoint joinPoint) throws Throwable { 
     Object retVal = null; 
     try { 
      logger.info("Before executing"); 
      retVal = joinPoint.proceed(); 
      logger.info("After executing"); 
     } 
     catch(Throwable e) { 
      logger.error("Execution error"); 
      throw e; 
     } 
     return retVal; 
    } 
} 

這似乎是不可能消除的硬編碼的最後一點註釋類名稱。

如果我使用吉斯,我可以做這樣的事情:

public class SimpleModule extends AbstractModule { 
     protected void configure() { 
     AroundInterceptor interceptor = new AroundInterceptor(); 
     bindInterceptor(
      Matchers.any(), 
      Matchers.annotatedWith(BeforeExecution.class).or(Matchers.annotatedWith(AfterExecution.class)), 
      interceptor 
     ); 
     } 
    } 

public class AroundInterceptor implements MethodInterceptor { 

    private static final Logger logger = LoggerFactory.getLogger(AroundInterceptor.class); 

    public Object invoke(MethodInvocation invocation) throws Throwable { 

     try{ 
      if(invocation.getMethod().getAnnotation(BeforeExecution.class)!=null){ 
       invokeBefore(invocation); 
      } 
      return invocation.proceed(); 
     } 
     finally{ 
      if(invocation.getMethod().getAnnotation(AfterExecution.class)!=null){ 
       invokeAfter(invocation); 
      } 
     } 
    } 

    protected void invokeBefore(MethodInvocation invocation) throws Throwable { 
     logger.info("Intercepted before executing: "+invocation.getMethod().getName()); 
    } 


    protected void invokeAfter(MethodInvocation invocation) throws Throwable { 
     logger.info("Intercepted after executing: "+invocation.getThis().getClass().getName()+"."+ 
       invocation.getMethod().getName()); 
    } 
} 

不完全是最漂亮的,但它能夠完成任務。

裝飾圖案是一個選擇,但它增加了大量的開銷在維護:

JSR-299 CDI Decorators for Spring beans

這將會是容易得多,如果春天可以讓隆胸的切入點表達式支持類定義的參數例如@Around("@annotatedWith", MyAnnotation.class)

我想知道是否有人在Spring中實現過不需要在元數據或應用程序上下文中進行硬編碼的攔截器?

+0

又是怎樣的代碼不是硬編碼?您無法在運行中更改它,無論哪種方式都需要完整重新編譯。另外請注意,它不是Spring AOP,而是AspectJ(Spring AOP可以利用AspectJ或不依賴於你想要使用Spring AOP的方式)。我實際上不喜歡EJB(或實際上CDI攔截器,但那是恕我直言)。 –

+0

我寫道:「似乎不可能消除註釋類名稱的最後一點硬編碼」,它是對代碼的這一部分的引用:「@annotation(com.myinterceptors.springaop.AroundExecution)」。我們在字符串文字中「硬編碼」一個類名。如果類名改變但是這個字符串不是?這是一個個人品味的問題,是否可以依賴編譯器以外的其他方式來接受這種不匹配,但我個人更喜歡涉及編譯器的解決方案。 –

+0

正如我所說,這是因爲您選擇使用AspectJ方法。如果你不使用它,你可以做任何你想做的事情。如果您改爲使用普通的Spring AOP,則可以使用'AnnotationMatchingPointcut',如果您使用基於Java的配置對其進行配置,則可以提供您想要的內容。 –

回答

0

這是一個有點晚了,但是看這個代碼的官方Spring文檔中:

@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)") 
public void audit(Auditable auditable) { 
    AuditCode code = auditable.value(); 
    // ... 
} 

通知它在切入點引用的審計參數名稱。

如果你想使用@Around建議只是修改代碼:

@Around("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)") 
public void audit(ProceedingJoinPoint joinPoint, Auditable auditable) { 
    AuditCode code = auditable.value(); 
    // ... 
} 
+0

再一次,字符串文字必須與類名匹配。如果我把它拼錯爲「聽得見」,編譯器就不會有問題,但它肯定會在運行時失敗。再次,這是一個個人品味的問題,是否可以將它留給IDE或單元測試來挑出錯誤。我個人認爲像Java這樣的強類型編譯語言,我們應該努力尋找一種解決方案,將這樣的錯誤轉化爲編譯時錯誤。 –