Bahar

2011-10-03 18 views
16

içinde JDK ve CGLIB proxy'lerinin karıştırılması Yay ile çalışan bir uygulamam var ve bazı yerlerde AOP kullanıyorum. @ Transactional notunu arayüz seviyesinde kullanmak istediğim için, Spring'in JDK proxy'leri oluşturmasına izin vermeliyim. Bu nedenle, proxy-target-class özelliğini true değerine ayarlamıyorum. Öte yandan, tavsiye ettiğim her sınıf için bir arayüz oluşturmak istemiyorum: eğer arayüz sadece bir anlam ifade etmiyorsa, sadece uygulama yapmak istiyorum ve Spring bir CGLIB proxy'si yaratmalıdır.Bahar

Açıkladığım gibi her şey mükemmel çalışıyordu. Ancak, arayüzler halinde girme ve uygulama sınıfları tarafından "miras alınan" gibi "miras alma" gibi bazı ek açıklamaların (benim tarafımdan yaratılmış) olmasını istedim. İlkbaharda AOP için yerleşik destek ile bunu yapamayacağımı ortaya koyuyor (en azından bazı araştırmalardan sonra nasıl yapılacağını anlayamadım. Arayüzdeki ek açıklama uygulama sınıfında görünmüyor ve dolayısıyla bu sınıf tavsiye edilmez).

yüzden diğer yöntem ek açıklamaları arayüzleri gitmek için izin kendi pointcut ve önleme uygulamaya karar verdik. Temel olarak, pointcut, metot üzerindeki açıklamalara ve sınıf veya üst sınıfların uyguladığı arayüzlerin aynı metotta (aynı isim ve parametre tipleri) bulunana kadar bakmaya çalışmaktadır.

Sorun şu ki: DefaultAdvisorAutoProxyCreator çekirdeği bildirdiğimde, bu nokta/engelleyiciyi düzgün bir şekilde uygulayacağınız zaman, arabirimler olmadan sınıflar hakkında bilgi verme davranışı bozulur. Görünüşe göre bir şeyler ters gidiyor ve Spring bir kez CGLIB ve daha sonra da JDK ile iki kez dersimi proxy yapmaya çalışıyor.

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

Diğer bazı fasulye autowire deneyin:

<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> 

Bu, hiçbir arayüzlerle ben proxy uygulanacak istiyorum sınıfıdır:

Bu

benim yapılandırma dosyasıdır gibi:

public class BaseService { 
    @Autowired 
    private ServiceExecutorImpl serviceExecutorImpl; 

    ... 
} 

Aşağıdaki özel durumu elde ediyorum:

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

Bu

Bahar çıkışının bazı satırlar şunlardır:

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]] 

birisi yardımcı olacaktır düşünürse ben tam çıkış kaynağı olabilir. Spring'in neden sınıfımı "çifte-proxy" yapmaya çalıştığı konusunda hiçbir fikrim yok ve neden DefaultAdvisorAutoProxyCreator fasulyesini bildirdiğimde böyle oluyor.

Şimdi bir süredir bununla mücadele ediyorum ve herhangi bir yardım veya fikir çok takdir edilecektir.

DÜZENLEME: Bu benim önleme kaynak kodu, istenen şekilde

. Temel olarak yöntem yürütme işlemini günlüğe kaydeder (yalnızca @Trace ile kesişen yöntemler kesilir). Yöntem @Trace (false) ile açıklanmışsa, yöntem döndürene kadar günlük kaydı askıya alınır.

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(); 
    } 
} 

cevap

3

Burada bir şey eşleşmiyor - eğer bir $ProxyXX ise, bir arabirim olduğu anlamına gelir. Arabirim olmadığından emin olun. Diğer bazı notlar: senin pointcut içinde

  • hedef nesne zaten (x instanceof Advised) kullanan bir proxy olup olmadığını kontrol edebilirsiniz, o zaman vekil stratejisini per- tanımlamak için <aop:scoped-proxy /> kullanabilirsiniz Advised

  • yayın yapabilirsiniz fasulye

+0

Evet, sanırım CGLIB proxy'leri bazı arabirimleri uygular. Ama bence bu benim kontrolüm dışında değil mi? Yazdığım orijinal sınıf herhangi bir arabirimi uygulamıyor, hatta herhangi bir sınıfı genişletmiyor (elbette Nesne hariç). Tavsiye Edilsin diye kontrol etmem bana yardımcı olabilir, çünkü bu sınıf Spring tarafından tavsiye ediliyor (@ Transactional nedeniyle) ve yazdığım herhangi bir nokta ile değil. Bana yardım edip edemeyeceğini görmek için adresine bir göz atacağım. Teşekkürler! –

+0

önleme kodunu gösterebilir misin? – Bozho

+0

Sorumu engelleyicinin kaynağıyla düzenledim. Bir ipucu? –

13

Bozho'nun önerdiği “scoped-proxy” yi kullanarak bir çözüm buldum. Neredeyse sadece ek açıklamalar kullanıyorum yana

, benim ServiceExecutor sınıfı şimdi şöyle görünür: şimdi her şey iyi çalışıyor gibi seens

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

kadar. Neden açık bir şekilde Spring'e söylemek zorunda olduğumu bilmiyorum, çünkü bu arayüz CGLIB kullanılarak proxy edilmeli, çünkü herhangi bir arayüz uygulanmadı. Belki de bir böcek, bilmiyorum.

Çok teşekkürler, Bozho.