2011-10-03 13 views
16

के भीतर जेडीके और सीजीएलबीबी प्रॉक्सी मिश्रण करना मेरे पास स्प्रिंग के साथ चल रहा एक एप्लीकेशन है, और मैं कुछ स्थानों पर एओपी का उपयोग कर रहा हूं। चूंकि मैं इंटरफ़ेस स्तर पर @ ट्रांसेक्शनल एनोटेशन का उपयोग करना चाहता हूं, इसलिए मुझे वसंत को जेडीके प्रॉक्सी बनाने की अनुमति है। इसलिए, मैं प्रॉक्सी-लक्ष्य-श्रेणी संपत्ति को सत्य पर सेट नहीं करता हूं। दूसरी तरफ, मैं सलाह देने के लिए हर एक वर्ग के लिए एक इंटरफेस नहीं बनाना चाहता हूं: अगर इंटरफ़ेस सिर्फ समझ में नहीं आता है, तो मैं केवल कार्यान्वयन करना चाहता हूं, और वसंत को एक CGLIB प्रॉक्सी बनाना चाहिए।स्प्रिंग

सबकुछ ठीक से काम कर रहा था, जैसा मैंने वर्णन किया था। लेकिन मैं कुछ अन्य टिप्पणियां (मेरे द्वारा बनाई गई) इंटरफेस में जा रहा था और कार्यान्वयन वर्गों द्वारा "विरासत" प्राप्त करना चाहता था (जैसे @ ट्रान्सएक्शनल एक)। यह पता चला है कि मैं वसंत में एओपी के लिए अंतर्निहित समर्थन के साथ ऐसा नहीं कर सकता (कम से कम मैं इसे समझ नहीं पाया कि कुछ शोध के बाद इसे कैसे किया जाए। इंटरफ़ेस में एनोटेशन कार्यान्वयन कक्षा में दिखाई नहीं दे रहा है, और इसलिए उस वर्ग को सलाह नहीं दी जाती है)।

तो मैं अपने ही pointcut और इंटरसेप्टर लागू करने के लिए, अन्य विधि एनोटेशन इंटरफेस पर जाने के लिए अनुमति का फैसला किया। असल में, मेरा पॉइंटकट विधि पर एनोटेशन की तलाश करता है और जब तक कि क्लास या उसके सुपरक्लास लागू नहीं होता है, उसी इंटरफेस के समान विधि (समान नाम और पैरामीटर प्रकार) में नहीं मिला।

समस्या यह है: जब मैं एक डिफ़ॉल्ट एडवाइज़रऑटोप्रोक्सी क्रिएटर बीन घोषित करता हूं, जो इस पॉइंटकट/इंटरसेप्टर को उचित रूप से लागू करेगा, बिना किसी इंटरफेस वाले कक्षाओं को सलाह देने का व्यवहार टूटा हुआ है। स्पष्ट रूप से कुछ गलत हो जाता है और वसंत मेरी कक्षाओं को दो बार प्रॉक्सी करने की कोशिश करता है, एक बार CGLIB के साथ और फिर जेडीके के साथ।

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

जब मैं इसे किसी और सेम में autowire करने की कोशिश:

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

इस वर्ग मैं प्रॉक्सी किया करना चाहते हैं, कोई इंटरफेस के साथ है:

यह मेरा विन्यास फाइल है , जैसे:

public class BaseService { 
    @Autowired 
    private ServiceExecutorImpl serviceExecutorImpl; 

    ... 
} 

मुझे निम्न अपवाद मिलता है:

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

यह वसंत उत्पादन में से कुछ पंक्तियां हैं:

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

कोई सोचता है कि अगर यह मदद मिलेगी मैं पूर्ण उत्पादन आपूर्ति कर सकता है। मुझे नहीं पता कि वसंत मेरी कक्षा को "डबल-प्रॉक्सी" क्यों करने का प्रयास कर रहा है, और यह तब होता है जब मैं DefaultAdvisorAutoProxyCreator बीन घोषित करता हूं।

मैं अब कुछ समय से इसके साथ संघर्ष कर रहा हूं, और किसी भी मदद या विचारों की बहुत सराहना की जाएगी।

संपादित करें:

यह मेरा इंटरसेप्टर स्रोत कोड, के रूप में अनुरोध किया गया है। यह मूल रूप से विधि निष्पादन लॉग करता है (केवल @ ट्रेस के साथ एनोटेटेड विधियों को अवरुद्ध किया जाता है)। यदि विधि @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 है, तो इसका मतलब है कि एक इंटरफ़ेस है। सुनिश्चित करें कि कोई इंटरफ़ेस नहीं है। कुछ अन्य नोट्स:

  • अपने pointcut में

    आप देख सकते हैं कि लक्ष्य वस्तु पहले से ही (x instanceof Advised) का उपयोग कर एक प्रॉक्सी है, तो आप Advised

  • पर कास्ट कर सकते आप <aop:scoped-proxy /> उपयोग कर सकते हैं प्रॉक्सी रणनीति प्रति- परिभाषित करने के लिए बीन

+0

ठीक है, मुझे लगता है कि CGLIB प्रॉक्सी कुछ इंटरफ़ेस लागू करते हैं। लेकिन मुझे लगता है कि यह मेरे नियंत्रण से बाहर है, है ना? मूल वर्ग, जिसे मैंने लिखा है, किसी भी इंटरफेस को लागू नहीं करता है, या यहां तक ​​कि किसी वर्ग को भी बढ़ाता है (ऑब्जेक्ट को छोड़कर)। मुझे नहीं पता कि सलाह के लिए जांच करना मेरी मदद कर सकता है, क्योंकि इस वर्ग को स्प्रिंग (@ ट्रांसेक्शनल के कारण) द्वारा सलाह दी जा रही है, और मैंने लिखा किसी भी बिंदु से नहीं। मैं यह देखने के लिए पर एक नज़र डालेगा कि यह मेरी मदद कर सकता है या नहीं। धन्यवाद! –

+0

क्या आप इंटरसेप्टर कोड दिखा सकते हैं? – Bozho

+0

मैंने अपने प्रश्न को इंटरसेप्टर के स्रोत के साथ संपादित किया। कोई सुराग? –

13

मुझे बोझो द्वारा सुझाए गए 'स्कॉप्ड-प्रॉक्सी' का उपयोग करके समाधान मिला।

जब से मैं लगभग केवल एनोटेशन उपयोग कर रहा हूँ, मेरे ServiceExecutor वर्ग अब इस तरह दिखता है:

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

तक अब सब कुछ ठीक से काम कर रहा seens। मुझे नहीं पता कि मुझे स्प्रिंग को स्पष्ट रूप से क्यों कहना है कि इस कक्षा को CGLIB का उपयोग करके प्रॉक्सी किया जाना चाहिए, क्योंकि यह किसी भी इंटरफ़ेस को लागू नहीं करता है। शायद यह एक बग है, मुझे नहीं पता।

बहुत बहुत धन्यवाद, Bozho।