2011-08-04 5 views
11

मैं वर्तमान में कुछ मौजूदा जेएक्स/आरएस संसाधनों को प्रॉक्सी करने की कोशिश कर रहा हूं ताकि मुझे हाइबरनेट वैलिडेटर के विधि सत्यापन समर्थन का उपयोग करने की अनुमति मिल सके। हालांकि, जब मैं अपनी कक्षा को प्रॉक्सी करता हूं (वर्तमान में cglib 2.2 का उपयोग कर रहा हूं), फॉर्मपैम एनोटेशन प्रॉक्सी क्लास में पैरामीटर पर मौजूद नहीं है, और इसलिए जेएक्स/आरएस रनटाइम (अपाचे विंक) पैरामीटर को पॉप्युलेट नहीं कर रहा है।मैं जावा में गतिशील प्रॉक्सी कैसे बना सकता हूं जो विधियों पर पैरामीटर एनोटेशन बनाए रखता है?

 
no proxy for Class: ProxyTester$ProxyMe 
Method: aMethod has 1 parameters 
    Param 0 has 1 annotations 
    Annotation @ProxyTester.TestAnnotation() 
javassist proxy for Class: ProxyTester$ProxyMe 
Method: aMethod has 1 parameters 
    Param 0 has 0 annotations 
cglib proxy for Class: ProxyTester$ProxyMe 
Method: aMethod has 1 parameters 
    Param 0 has 0 annotations 
jdk proxy for Class: ProxyTester$ProxyMe 
Method: aMethod has 1 parameters 
    Param 0 has 0 annotations 

क्या कोई अन्य विकल्प हैं:

import static java.lang.annotation.ElementType.*; 
import static java.lang.annotation.RetentionPolicy.*; 

import java.lang.annotation.Annotation; 
import java.lang.annotation.Retention; 
import java.lang.annotation.Target; 
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 

import net.sf.cglib.proxy.Enhancer; 
import net.sf.cglib.proxy.MethodInterceptor; 
import net.sf.cglib.proxy.MethodProxy; 

import javassist.util.proxy.ProxyFactory; 

public class ProxyTester { 

    @Target({ PARAMETER }) 
    @Retention(RUNTIME) 
    public static @interface TestAnnotation { 
    } 

    public static interface IProxyMe { 
     void aMethod(@TestAnnotation int param); 
    } 

    public static class ProxyMe implements IProxyMe { 
      public void aMethod(@TestAnnotation int param) { 
     } 
    } 

    static void dumpAnnotations(String type, Object proxy, Object forObject, 
      String forMethod) { 
     String className = forObject.getClass().getName(); 

     System.err.println(type + " proxy for Class: " + className); 

     for (Method method : proxy.getClass().getMethods()) { 
      if (method.getName().equals(forMethod)) { 
       final int paramCount = method.getParameterTypes().length; 
       System.err.println(" Method: " + method.getName() + " has " 
         + paramCount + " parameters"); 
       int i = 0; 
       for (Annotation[] paramAnnotations : method 
         .getParameterAnnotations()) { 
        System.err.println(" Param " + (i++) + " has " 
          + paramAnnotations.length + " annotations"); 
        for (Annotation annotation : paramAnnotations) { 
         System.err.println(" Annotation " 
           + annotation.toString()); 
        } 
       } 
      } 
     } 
    } 

    static Object javassistProxy(IProxyMe in) throws Exception { 
     ProxyFactory pf = new ProxyFactory(); 
     pf.setSuperclass(in.getClass()); 
     Class c = pf.createClass(); 
     return c.newInstance(); 
    } 

    static Object cglibProxy(IProxyMe in) throws Exception { 
     Object p2 = Enhancer.create(in.getClass(), in.getClass() 
       .getInterfaces(), new MethodInterceptor() { 
      public Object intercept(Object arg0, Method arg1, Object[] arg2, 
        MethodProxy arg3) throws Throwable { 
       return arg3.invokeSuper(arg0, arg2); 
      } 
     }); 
     return p2; 

    } 

    static Object jdkProxy(final IProxyMe in) throws Exception { 
     return java.lang.reflect.Proxy.newProxyInstance(in.getClass() 
       .getClassLoader(), in.getClass().getInterfaces(), 
       new InvocationHandler() { 
        public Object invoke(Object proxy, Method method, 
          Object[] args) throws Throwable { 
         return method.invoke(in, args); 
        } 
       }); 
    } 

    public static void main(String[] args) throws Exception { 
     IProxyMe proxyMe = new ProxyMe(); 
     dumpAnnotations("no", proxyMe, proxyMe, "aMethod"); 
     dumpAnnotations("javassist", javassistProxy(proxyMe), proxyMe, 
      "aMethod"); 
     dumpAnnotations("cglib", cglibProxy(proxyMe), proxyMe, "aMethod"); 

     dumpAnnotations("jdk", jdkProxy(proxyMe), proxyMe, "aMethod"); 
    } 
} 

यह मैं निम्नलिखित उत्पादन देता है: यहाँ कुछ परीक्षण कोड है कि इस से पता चलता है?

उत्तर

0

CGLib Enhancer तकनीकी रूप से बस आपकी कक्षा से विस्तार कर रहा है। मुझे नहीं पता कि यह आपके लिए व्यवहार्य है (ऑब्जेक्ट्स की संख्या) लेकिन कक्षा के बजाय इंटरफ़ेस को उजागर करने के बारे में कैसे?

Object p2 = Enhancer.create(resource.getClass(), 
    new Class[] { IResource.class }, 
    new MethodInterceptor() { 
     public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) 
      throws Throwable { 
      return arg3.invokeSuper(arg0, arg2); 
      } 
    }); 

बढ़ाया वर्ग से एनोटेशन निकालें और उन्हें इंटरफ़ेस में डाल दिया। इंटरफ़ेस के खिलाफ मान्य करें। यह ऐसे कई संसाधनों के लिए बहुत सारे बॉयलर-प्लेट इंटरफेस हो सकता है, लेकिन बैकिंग ऑब्जेक्ट्स या डीटीओ बनाने के लिए सब कुछ मैप करने से कहीं भी बेहतर है।

+0

मैंने जावा प्रॉक्सी के साथ यह कोशिश की, लेकिन विशेष रूप से इसे cglib के लिए प्रयास नहीं किया। मैं इसे आज़माउंगा और देख सकता हूं कि यह कुछ भी बदलता है या नहीं। मुझे कहना है कि मैं आशावादी नहीं हूं, हालांकि। –

+0

मैंने इसका कोई फायदा नहीं लिया :-( –

+0

क्या आप कोशिश कर सकते हैं कि यह वसंत वैधता इंटरफ़ेस के साथ काम करता है? तो कक्षा से एनोटेशन हटाएं, इंटरफ़ेस को कार्यान्वित करें और इसे वैधकर्ता में डाल दें? –

1

मुझे संदेह है कि एनोटेशन प्रॉक्सी उदाहरणों में गतिशील रूप से जोड़े नहीं गए हैं। (शायद क्योंकि यह सीधा नहीं है)। हालांकि आमंत्रण (या फ़िल्टरिंग) के दौरान वास्तविक विधि उदाहरण से एनोटेशन प्राप्त करना संभव है। उदाहरण के लिए,

import static java.lang.annotation.ElementType.PARAMETER; 
import static java.lang.annotation.RetentionPolicy.RUNTIME; 

import java.lang.annotation.Annotation; 
import java.lang.annotation.Retention; 
import java.lang.annotation.Target; 
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 

import javassist.util.proxy.MethodHandler; 
import javassist.util.proxy.ProxyFactory; 
import net.sf.cglib.proxy.Enhancer; 
import net.sf.cglib.proxy.MethodInterceptor; 
import net.sf.cglib.proxy.MethodProxy; 

public class ProxyTester 
{ 
    @Target({ PARAMETER }) 
    @Retention(RUNTIME) 
    public static @interface TestAnnotation {} 

    public static interface IProxyMe { 
     void aMethod(@TestAnnotation int param); 
    } 

    public static class ProxyMe implements IProxyMe { 
     public void aMethod(@TestAnnotation int param) { 
      System.out.println("Invoked " + param); 
      System.out.println("-----------------"); 
     } 
    } 

    static void dumpAnnotations(String type, Object proxy, Object forObject, String forMethod) 
    { 
     String className = forObject.getClass().getName(); 
     System.out.println(type + " proxy for Class: " + className); 

     for(Method method : proxy.getClass().getMethods()) { 
      if(method.getName().equals(forMethod)) { 
       printAnnotations(method); 
      } 
     } 
    } 

    static void printAnnotations(Method method) 
    { 
     int paramCount = method.getParameterTypes().length; 
     System.out.println("Method: " + method.getName() + " has " + paramCount + " parameters"); 

     for(Annotation[] paramAnnotations : method.getParameterAnnotations()) 
     { 
      System.out.println("Annotations: " + paramAnnotations.length); 

      for(Annotation annotation : paramAnnotations) 
      { 
       System.out.println(" Annotation " + annotation.toString()); 
      } 
     } 
    } 

    static Object javassistProxy(IProxyMe in) throws Exception 
    { 
     ProxyFactory pf = new ProxyFactory(); 
     pf.setSuperclass(in.getClass()); 

     MethodHandler handler = new MethodHandler() 
      { 
       public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable 
       { 
        if(thisMethod.getName().endsWith("aMethod")) 
         printAnnotations(thisMethod); 

        return proceed.invoke(self, args); 
       } 
      }; 
     return pf.create(new Class<?>[0], new Object[0], handler); 
    } 

    static Object cglibProxy(IProxyMe in) throws Exception 
    { 
     Object p2 = Enhancer.create(in.getClass(), in.getClass().getInterfaces(), 
      new MethodInterceptor() 
      { 
       public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable 
       { 
        printAnnotations(arg1); 
        return arg3.invokeSuper(arg0, arg2); 
       } 
      }); 

     return p2; 
    } 

    static Object jdkProxy(final IProxyMe in) throws Exception 
    { 
     return java.lang.reflect.Proxy.newProxyInstance(in.getClass().getClassLoader(), in.getClass().getInterfaces(), 
      new InvocationHandler() 
      { 
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
       { 
        printAnnotations(method); 
        return method.invoke(in, args); 
       } 
      }); 
    } 

    public static void main(String[] args) throws Exception 
    { 
     IProxyMe proxyMe = new ProxyMe(); 
     IProxyMe x = (IProxyMe) javassistProxy(proxyMe); 
     IProxyMe y = (IProxyMe) cglibProxy(proxyMe); 
     IProxyMe z = (IProxyMe) jdkProxy(proxyMe); 

     dumpAnnotations("no", proxyMe, IProxyMe.class, "aMethod"); 
     dumpAnnotations("javassist", x, IProxyMe.class, "aMethod"); 
     dumpAnnotations("cglib", y, IProxyMe.class, "aMethod"); 
     dumpAnnotations("jdk", z, IProxyMe.class, "aMethod"); 

     System.out.println("<<<<< ---- Invoking methods ----- >>>>>"); 
     x.aMethod(1); 
     y.aMethod(2); 
     z.aMethod(3); 
    } 
} 
0

मैं cglib के साथ काम किया कभी नहीं किया है लेकिन मुझे पता है कि Javassist के साथ एक वर्ग का एक उदाहरण पाने के लिए invocationhandlers उपयोग कर सकते हैं, और फिर वहाँ अपने एनोटेशन को बदले बिना कुछ को लक्षित करने के तरीके के सभी प्रकार हैं । मेरे पसंदीदा तरीकों में से एक उस विधि में हुक करना है जो उस वर्ग के अंदर नहीं है, लेकिन इसे कॉल करता है, फिर जब वह उस कक्षा के अंदर की विधि को कॉल करता है तो यह बाइटकोड स्तर में परिवर्तन कर सकता है, या थोड़ा धीमा लेकिन मानव पठनीय उच्च उपयोग कर सकता है अपने समायोजन करने के लिए स्तर एपीआई।

यह मेरे कुछ जीवन चलने वाले कोडों में से एक स्निपिट है जिसने बिना किसी समस्या के 2 साल से अधिक समय तक काम किया है। यह जावासवादी का उपयोग कर रहा है।

ClassPool myPool = new ClassPool(ClassPool.getDefault()); 
        myPool.appendClassPath("./mods/bountymod/bountymod.jar"); 
        CtClass ctTHIS = myPool.get(this.getClass().getName()); 
        ctCreature.addMethod(CtNewMethod.copy(ctTHIS.getDeclaredMethod("checkCoinBounty"), ctCreature, null)); 
        ctCreature.getDeclaredMethod("modifyFightSkill").instrument(new ExprEditor() { 
         public void edit(MethodCall m) 
           throws CannotCompileException { 
          if (m.getClassName().equals("com.wurmonline.server.players.Player") 
            && m.getMethodName().equals("checkCoinAward")) { 
           String debugString = ""; 
           if (bDebug) 
            debugString = "java.util.logging.Logger.getLogger(\"org.gotti.wurmunlimited.mods.bountymod.BountyMod" 
              + "\").log(java.util.logging.Level.INFO, \"Overriding checkCoinAward to checkCoinBounty\");\n"; 
           m.replace(debugString + "$_ = checkCoinBounty(player);"); 
          } 
         } 
        }); 

यह डिफ़ॉल्ट कक्षा पूल प्राप्त करता है। फिर एक स्थिर वर्ग के लिए खत्म जोड़ता है जिसे हम एक विधि को छीनना चाहते हैं। हालांकि यह विधि अंतिम कक्षा में नहीं है, यह मॉड क्लास में है कि हम रनटाइम पर मूल जार में इंजेक्शन दे रहे हैं। फिर यह उस वर्ग के अंदर एक विधि हो जाता है। फिर यह प्रत्येक कक्षा का पूरा वर्गपूल प्राप्त करता है जो कि एकल कक्षा के उदाहरण से जुड़ा हुआ है जिसे हम myPool से प्राप्त करते हैं। ऐसा क्यों करते हैं, हम कितना गड़बड़ कर रहे हैं या गड़बड़ कर सकते हैं, कोड पीढ़ी के दौरान स्मृति से थोड़ा सा लेता है।

फिर यह ctCreature के लिए एक नई विधि जोड़ता है जो कि एक वर्ग है जिसे हमने पहले एक चर में शुरू किया था। विधि इस वर्ग में बनाई गई है, लेकिन उसके बाद दूसरी कक्षा में कॉपी की गई है जहां हम इसका उपयोग करना चाहते हैं। फिर यह संशोधित FightSkill की घोषित विधि में हुक करता है, जो इस मामले में सही होता है जब हम अपना कोड बुलाते हैं। तो जब यह आग लगती है, तो हम एक नया अभिव्यक्ति संपादक शुरू करते हैं।

वह अभिव्यक्ति संपादक तब सुनिश्चित करता है कि वास्तविक वर्ग जिसे हम अंदर रखना चाहते हैं, लेकिन इसके किसी भी मूल निर्माण को गड़बड़ नहीं करते हैं, और उस वर्ग के अंदर विधि प्राप्त होती है जिसे हम अपनी मूल कक्षा को बदलने के बिना कुछ करना चाहते हैं।

एक बार ऐसा होता है और यह सभी ifs और उपहारों के साथ मान्य है, तो विधि प्रतिस्थापन के माध्यम से हमारी नई विधि के साथ मूल विधि को प्रतिस्थापित करती है। पुराने व्यक्ति को बाहर ले जाता है, नया डालता है। युक्त कक्षा से आपकी सभी टिप्पणियां छूटे रहती हैं क्योंकि क्योंकि हम तरफ से चुपके से आते हैं।

अब हम जिस वर्ग की जरूरत है उसमें हम सीधे कूद गए थे, उस विधि को दबाएं, जो हम चाहते हैं। हालांकि हम समस्याओं, या गठबंधन रचनाकारों या अन्य चीजों के साथ समाप्त हो गया होता। आपके कोड इंजेक्शन में आने का तरीका एक बड़ी परियोजना के साथ काम करते समय कुछ भी महत्वपूर्ण है।

यह उत्तर थोड़ा लंबा हो सकता है, लेकिन InvokationHandler पहले समझने के लिए थोड़ा मुश्किल हो सकता है।

संबंधित मुद्दे