2011-10-03 42 views
16

我有一個使用Spring運行的應用程序,我在某些地方使用AOP。由於我想在接口級使用@Transactional註解,所以我必須允許Spring創建JDK代理。所以,我沒有將proxy-target-class屬性設置爲true。另一方面,我不想爲我想要的每一個類創建一個接口:如果接口沒有意義,我只想實現,Spring應該創建一個CGLIB代理。在Spring中混合使用JDK和CGLIB代理

正如我所描述的,一切都很完美。但是我想要一些其他的註解(由我創建)進入接口並被實現類「繼承」(就像@Transactional一樣)。事實證明,我不能在Spring中使用AOP的內置支持來完成這項工作(至少在我經過一番研究之後,我不知道該怎麼做),接口中的註釋在實現類中是不可見的,因此該班級不會被建議)。

所以我決定實現自己切入點攔截,允許其他方法的註釋去的接口。基本上,我的切入點在該方法上查找註釋,並且直到找不到該類或其超類實現的接口的相同方法(相同名稱和參數類型)。

問題是:當我聲明一個DefaultAdvisorAutoProxyCreator bean時,會正確應用這個切入點/攔截器,建議沒有接口的類的行爲被破壞。顯然有些事情出錯了,Spring試圖用我的兩次代理類,一次用CGLIB,然後用JDK。

這是我的配置文件:

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" 
    xsi:schemaLocation=" 
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> 

    <!-- Activates various annotations to be detected in bean classes: Spring's 
     @Required and @Autowired, as well as JSR 250's @Resource. --> 
    <context:annotation-config /> 

    <context:component-scan base-package="mypackage" /> 

    <!-- Instruct Spring to perform declarative transaction management automatically 
     on annotated classes. --> 
    <tx:annotation-driven transaction-manager="transactionManager" /> 

    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" /> 

    <bean id="logger.advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> 
     <constructor-arg> 
      <bean class="mypackage.MethodAnnotationPointcut"> 
       <constructor-arg value="mypackage.Trace" /> 
      </bean> 
     </constructor-arg> 
     <constructor-arg> 
      <bean class="mypackage.TraceInterceptor" /> 
     </constructor-arg> 
    </bean> 
</beans> 

這是I類要被代理,無接口:

@Component 
public class ServiceExecutorImpl 
{ 
    @Transactional 
    public Object execute(...) 
    { 
     ... 
    } 
} 

當我嘗試自動裝配它在其他一些豆,如:

public class BaseService { 
    @Autowired 
    private ServiceExecutorImpl serviceExecutorImpl; 

    ... 
} 

我收到以下異常:

java.lang.IllegalArgumentException: Can not set mypackage.ServiceExecutorImpl field mypackage.BaseService.serviceExecutor to $Proxy26 

這是Spring輸出的一些行:

13:51:12,672 [main] DEBUG [org.springframework.aop.framework.Cglib2AopProxy] - Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [[email protected]] 
... 
13:51:12,782 [main] DEBUG [org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'serviceExecutorImpl' with 0 common interceptors and 1 specific interceptors 
13:51:12,783 [main] DEBUG [org.springframework.aop.framework.JdkDynamicAopProxy] - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [[email protected]] 

如果有人認爲這將有助於我可以提供完整的輸出。我不知道爲什麼Spring試圖「雙重代理」我的類,以及爲什麼當我聲明DefaultAdvisorAutoProxyCreator bean時會發生這種情況。

我一直在爲此奮鬥一段時間,任何幫助或想法都將非常感激。

編輯:

這是我的攔截器源代碼,根據要求。它基本上記錄了方法的執行情況(只有用@Trace註釋的方法纔會被攔截)。如果該方法使用@Trace(false)進行註釋,則記錄將暫停,直到該方法返回。

public class TraceInterceptor 
    implements 
     MethodInterceptor 
{ 

    @Override 
    public Object invoke(
     MethodInvocation invocation) 
     throws Throwable 
    { 
     if(ThreadExecutionContext.getCurrentContext().isLogSuspended()) { 
      return invocation.proceed(); 
     } 

     Method method = AopUtils.getMostSpecificMethod(invocation.getMethod(), 
      invocation.getThis().getClass()); 
     Trace traceAnnotation = method.getAnnotation(Trace.class); 

     if(traceAnnotation != null && traceAnnotation.value() == false) { 
      ThreadExecutionContext.getCurrentContext().suspendLogging(); 
      Object result = invocation.proceed(); 
      ThreadExecutionContext.getCurrentContext().resumeLogging(); 
      return result; 
     } 

     ThreadExecutionContext.startNestedLevel(); 
     SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss.SSS"); 
     Logger.log("Timestamp: " + dateFormat.format(new Date())); 

     String toString = invocation.getThis().toString(); 
     Logger.log("Class: " + toString.substring(0, toString.lastIndexOf('@'))); 

     Logger.log("Method: " + getMethodName(method)); 
     Logger.log("Parameters: "); 
     for(Object arg : invocation.getArguments()) { 
      Logger.log(arg); 
     } 

     long before = System.currentTimeMillis(); 
     try { 
      Object result = invocation.proceed(); 
      Logger.log("Return: "); 
      Logger.log(result); 
      return result; 
     } finally { 
      long after = System.currentTimeMillis(); 
      Logger.log("Total execution time (ms): " + (after - before)); 
      ThreadExecutionContext.endNestedLevel(); 
     } 
    } 

    // Just formats a method name, with parameter and return types 
    private String getMethodName(
     Method method) 
    { 
     StringBuffer methodName = new StringBuffer(method.getReturnType().getSimpleName() + " " 
      + method.getName() + "("); 
     Class<?>[] parameterTypes = method.getParameterTypes(); 

     if(parameterTypes.length == 0) { 
      methodName.append(")"); 
     } else { 
      int index; 
      for(index = 0; index < (parameterTypes.length - 1); index++) { 
       methodName.append(parameterTypes[ index ].getSimpleName() + ", "); 
      } 
      methodName.append(parameterTypes[ index ].getSimpleName() + ")"); 
     } 
     return methodName.toString(); 
    } 
} 

謝謝!

回答

3

某些東西在這裏不匹配 - 如果它是$ProxyXX,則表示存在接口。確保沒有界面。其他一些注意事項:

  • 在你的切入點

    ,你可以檢查目標對象是已經使用(x instanceof Advised)代理,那麼你就可以轉換爲Advised

  • 可以使用<aop:scoped-proxy />定義代理策略per- bean

+0

那麼,我猜CGLIB代理實現了一些接口。但我認爲這超出了我的控制範圍,對吧?我寫的原始類沒有實現任何接口,甚至沒有擴展任何類(當然除了Object)。 我不知道檢查建議是否可以幫助我,因爲這個類是Spring(由於@Transactional)建議的,而不是我寫的任何切入點。我會看看來看看它是否可以幫助我。謝謝! –

+0

你能顯示攔截器代碼嗎? – Bozho

+0

我編輯了我的問題與攔截器的來源。任何線索? –

13

我找到了一個使用Bozho建議的'scoped-proxy'的解決方案。

由於我使用的幾乎只有註解,我ServiceExecutor類現在看起來是這樣的:

@Component 
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 
public class ServiceExecutor 
{ 
    @Transactional 
    public Object execute(...) 
    { 
     ... 
    } 
} 

到現在爲止一切作用似乎是工作的罰款。我不知道爲什麼我必須明確地告訴Spring,這個類應該使用CGLIB代理,因爲它沒有實現任何接口。也許這是一個錯誤,我不知道。

非常感謝Bozho。

相關問題