我有一個使用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();
}
}
謝謝!
那麼,我猜CGLIB代理實現了一些接口。但我認爲這超出了我的控制範圍,對吧?我寫的原始類沒有實現任何接口,甚至沒有擴展任何類(當然除了Object)。 我不知道檢查建議是否可以幫助我,因爲這個類是Spring(由於@Transactional)建議的,而不是我寫的任何切入點。我會看看 來看看它是否可以幫助我。謝謝! –
你能顯示攔截器代碼嗎? – Bozho
我編輯了我的問題與攔截器的來源。任何線索? –