Mam aplikację uruchomioną ze Springem i używam AOP w niektórych miejscach. Ponieważ chcę używać adnotacji @Transactional na poziomie interfejsu, muszę zezwolić Springowi na tworzenie serwerów proxy JDK. Tak więc nie ustawiam wartości właściwości proxy-target-class-class na true. Z drugiej strony, nie chcę tworzyć interfejsu dla każdej klasy, którą chcę doradzić: jeśli interfejs po prostu nie ma sensu, chcę mieć tylko implementację, a Spring powinien utworzyć proxy CGLIB.Mieszanie proxy JDK i CGLIB na wiosnę
Wszystko działało idealnie, tak jak to opisałem. Ale chciałem mieć inne adnotacje (stworzone przeze mnie) wchodzące w interfejsy i będące "odziedziczone" przez klasy implementacji (tak jak @ Transactional). Okazuje się, że nie mogę tego zrobić z wbudowaną obsługą AOP na wiosnę (przynajmniej nie mogłem tego po wykonaniu niektórych badań zrobić. Adnotacja w interfejsie nie jest widoczna w klasie implementacji, i stąd ta klasa nie zostanie powiadomiona).
Więc postanowiłem realizować własne punktu przekroju i przechwytywania, pozwalając inne adnotacje metoda, aby przejść na interfejsach. Zasadniczo mój lookcut szuka adnotacji w metodzie i, dopóki nie zostanie znaleziony, w tej samej metodzie (tej samej nazwie i typach parametrów) interfejsów, które implementuje klasa lub jej nadklasy.
Problem polega na tym, że kiedy deklaruję bean bean DefaultAdvisorAutoProxyCreator, który poprawnie zastosuje ten punkt/przechwytujący, zachowanie klas doradzających bez interfejsów jest zepsute. Najwyraźniej coś poszło nie tak, a Spring próbuje serwerować moje klasy dwukrotnie, raz z CGLIB, a potem z JDK.
To jest mój plik konfiguracyjny:
<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>
To jest klasa chcę być proxy, bez żadnych interfejsów:
@Component
public class ServiceExecutorImpl
{
@Transactional
public Object execute(...)
{
...
}
}
Kiedy próbuję autowire to w jakiś inny fasoli , jak:
public class BaseService {
@Autowired
private ServiceExecutorImpl serviceExecutorImpl;
...
}
Otrzymuję następujący wyjątek:
java.lang.IllegalArgumentException: Can not set mypackage.ServiceExecutorImpl field mypackage.BaseService.serviceExecutor to $Proxy26
To są niektóre linie wyjścia wiosny:
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]]
mogę dostarczyć pełną moc, jeśli ktoś uważa, że to pomoże. Nie mam pojęcia, dlaczego Spring próbuje "podwójnie proxy" mojej klasy i dlaczego tak się dzieje, gdy deklaruję bean bean DefaultAdvisorAutoProxyCreator.
Od pewnego czasu borykam się z tym problemem, a wszelka pomoc lub pomysły będą bardzo cenne.
EDIT:
To jest mój kod źródłowy przechwytujących, zgodnie z wnioskiem. Zasadniczo rejestruje on wykonanie metody (tylko metody z adnotacją @Trace zostają przechwycone). Jeśli metoda jest opatrzona adnotacją @Trace (fałsz), rejestrowanie jest zawieszane do czasu powrotu metody.
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();
}
}
Dzięki!
Cóż, domyślam się, że proxy CGLIB implementują niektóre interfejsy. Ale myślę, że to jest poza moją kontrolą, prawda? Oryginalna klasa, którą napisałem, nie implementuje żadnego interfejsu, ani nawet nie rozszerza żadnej klasy (poza Object, oczywiście). Nie wiem, czy sprawdzenie Doradztwa mogłoby mi pomóc, ponieważ ta klasa jest doradzana przez Spring (z powodu @Transactional), a nie przez żaden punkt, który napisałem. Zajrzę do , aby sprawdzić, czy to mi pomoże. Dzięki! –
możesz pokazać kod przechwytywania? – Bozho
Edytowałem moje pytanie ze źródłem dla przechwytywacza. Jakaś wskazówka? –