2014-08-27 8 views
6

मैं क्लाइब प्रॉक्सी को बाइटबड्डी में बदलने की कोशिश कर रहा हूं। सभी विधि कॉल को रोकने के लिए Cglib net.sf.cglib.proxy.Proxy इंटरफ़ेस है। मैं बाइटबड्डी के दस्तावेज़ीकरण की जांच करता हूं लेकिन ऐसा कोई उदाहरण नहीं मिला। बाइटबड्डी के साथ तत्काल प्रत्येक ऑब्जेक्ट के लिए इस तरह के इंटरफ़ेस के बिना मैं एक ही चीज़ दोबारा दोहरा रहा हूं और agin। बाइटबड्डी के साथ ऐसा करने का कोई बेहतर तरीका है?बाइटबड्डी प्रॉक्सी इंटरफेस

यहाँ मेरी उदाहरण कोड का टुकड़ा है:

सेवा:

public class MyService { 

    public void sayFoo() { 
     System.out.println("foo"); 
    } 

    public void sayBar() { 
     System.out.println("bar"); 
    } 
} 

इंटरसेप्टर:

public class MyServiceInterceptor { 

    public void sayFoo(@SuperCall Callable<Void> zuper) { 
     try { 
      zuper.call(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    public void sayBar(@SuperCall Callable<Void> zuper) { 
     try { 
      zuper.call(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

टेस्ट:

import net.bytebuddy.ByteBuddy; 
import net.bytebuddy.ClassFileVersion; 
import net.bytebuddy.dynamic.ClassLoadingStrategy; 
import net.bytebuddy.instrumentation.MethodDelegation; 
import net.bytebuddy.instrumentation.method.matcher.MethodMatchers; 

public class Main { 

    public static void main(String[] args) throws Exception { 
     ByteBuddy buddy = new ByteBuddy(ClassFileVersion.forCurrentJavaVersion()); 
     Class<? extends MyService> serviceClass = 
       buddy.subclass(MyService.class) 
       .method(MethodMatchers.named("sayFoo").or(MethodMatchers.named("sayBar"))) 
       .intercept(MethodDelegation.to(new MyServiceInterceptor())) 
       .make() 
       .load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) 
       .getLoaded(); 

     MyService service = serviceClass.newInstance(); 

     service.sayFoo(); 
     service.sayBar(); 
    } 
} 

उत्तर

9

बाइट बडी किसी भी संभावित लक्ष्य पर नजर डालेंगे विधि और अगर यह संभव है, इसे बांधो। यदि एक से अधिक संभावित लक्ष्य विधि हैं, तो यह सबसे विशिष्ट एक को बाध्य करेगा या अगर यह संदिग्ध है तो अपवाद फेंक देगा। अपने उदाहरण में, बाइंडिंग अस्पष्ट होगा, लेकिन जैसा कि आप इंटरसेप्टर तरीकों हूबहू रोक तरीकों के नाम पर रखा गया (MyServiceInterceptor में) (Service में), बाइट बडी लगा समान नाम का इंटरसेप्टर विधि के साथ प्रत्येक विधि में अवरोध उत्पन्न कर रहा है कि संभव है कि आप करना चाहते थे कर। जैसा कि javadoc of the MethodInterceptor बाइट बडी में उल्लेख किया गया है:

  1. किसी भी विधि को ढूंढें जो इसे बाध्य और दूसरों को त्याग सकता है।
  2. जांचें कि @BindingPriority के साथ कोई विधि एनोटेट की गई है और उच्चतम प्राथमिकता वाले लोगों को चुनें।
  3. एक विधि के साथ हस्तक्षेप करें जिसमें कम से कम एक है तो अवरोधित विधि के समान नाम है।
  4. सबसे विशिष्ट तर्क प्रकार के साथ विधि चुनें यदि @Argument एनोटेशन प्रयोग किया जाता है और जावा कम्पाइलर एक ओवरलोड विधि कॉल का लक्ष्य की पहचान करने के लिए तुलनात्मक रूप से सबसे विशिष्ट बंधन को हल।
  5. सबसे पैरामीटर के साथ विधि लेता है।

आप AmbiguityResolvers जोड़कर/हटाकर इस डिफ़ॉल्ट व्यवहार को और भी बदल सकते हैं।

आप एक ही इंटरसेप्टर तरीका है जिसके में अवरोध डालने के किसी भी विधि एक सुपर विधि से आप निम्नलिखित इंटरसेप्टर वर्ग लिख सकते है कि करने में सक्षम है निर्दिष्ट करने के लिए करना चाहते हैं:

public class MyServiceInterceptor { 
    @RuntimeType 
    public static Object intercept(@SuperCall Callable<?> zuper) throws Exception { 
    return zuper.call(); 
    } 
} 

विधि का नाम नहीं है बात है, बाइट बडी इंटरसेप्टर बाध्य होगा के रूप में यह तभी संभव लक्ष्य है। आप जोड़ने के लिए @RuntimeType annotation@SuperCall प्रॉक्सी रिटर्न के रूप में एक Object की जरूरत है और बाइट बडी रोक विधि के अंदर कास्ट करने के लिए (और संभवतः Unbox) मूल्य की जरूरत है।

इस इंटरसेप्टर साथ

(, ध्यान दें कि यह तरीका है कि विधि भी static है, बाइट बडी MyServiceInterceptor का एक उदाहरण के आयोजन के लिए एक क्षेत्र को जोड़ने की आवश्यकता नहीं है) तो आप बस लिख सकते हैं:

public class Main { 
    public static void main(String[] args) throws Exception { 
    Class<? extends MyService> serviceClass = new ByteBuddy() 
     .subclass(MyService.class) 
     .method(ElementMatchers.named("sayFoo").or(ElementMatchers.named("sayBar"))) 
     .intercept(MethodDelegation.to(MyServiceInterceptor.class)) 
     .make() 
     .load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) 
     .getLoaded(); 

    MyService service = serviceClass.newInstance(); 

    service.sayFoo(); 
    service.sayBar(); 
    } 
} 

और आपको मिल वांछित परिणाम।जैसा कि उदाहरण में दिखाया गया है, तो आप

new ByteBuddy(); 

new ByteBuddy(ClassFileVersion.forCurrentJavaVersion()); 

के बजाय लिख सकते हैं यह एक ही बात है।

बाइट बडी किसी भी इंटरफेस का उपयोग नहीं करता है क्योंकि यह किसी भी बाइट बडी कक्षाओं में जेनरेट क्लास की निर्भरता से बचना चाहता है। ऐसा करने से, आप ClassLoader के साथ लोड करते समय भी उत्पन्न कक्षाओं का पुन: उपयोग कर सकते हैं जो बाइट बडी निर्भरता से अनजान हैं।