2010-12-10 6 views
11

जेडीके प्रॉक्सी क्लास केवल फैक्ट्री विधि newProxyInstance() में इंटरफेस स्वीकार करता है।जेडीके गतिशील प्रॉक्सी केवल इंटरफेस के साथ क्यों काम करता है?

क्या कोई कामकाज उपलब्ध है, या वैकल्पिक कार्यान्वयन? यदि प्रॉक्सी के उपयोग के लिए उन्हें सक्षम करने के लिए मुझे इंटरफ़ेस में विधियों को निकालना है तो उपयोग के मामले सीमित हैं। मैं उन्हें रनटाइम के दौरान एनोटेशन आधारित क्रियाओं को लागू करने के लिए लपेटना चाहता हूं।

public static <T> T getProxy(T obj) { 
    InvocationHandler ih = new InjectProxy(obj); 
    ClassLoader classLoader = InjectProxy.class.getClassLoader(); 
    return (T) Proxy.newProxyInstance(classLoader, obj.getClass().getInterfaces(), ih); 
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
} 
+1

ठीक है, सिद्धांत रूप में आप एएसएम जैसे बाइटकोड लाइब्रेरी का उपयोग कक्षा-आधारित प्रॉक्सी बनाने के लिए कर सकते हैं। –

+0

@ क्रिक, कुछ इकाई परीक्षण नकली पुस्तकालय यह करते हैं। आपको एक व्युत्पन्न कक्षा बनाने की आवश्यकता है जो आपके इच्छित तरीकों को ओवरराइड करता है। (या तो स्वचालित रूप से या मैन्युअल रूप से) –

+0

क्या आपने एस्पेक्ट-जे माना है जो इस तरह की चीज करने के लिए डिज़ाइन किया गया है? –

उत्तर

15

आप इस तरह cglib उपयोग कर सकते हैं:

import java.lang.reflect.Array; 
import java.lang.reflect.Method; 
import java.lang.reflect.Modifier; 

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


public class AbstractFactory { 

    @SuppressWarnings("unchecked") 
    public static <A> A createDefaultImplementation(Class<A> abstractClass) { 
     Enhancer enhancer = new Enhancer(); 
     enhancer.setSuperclass(abstractClass); 
     enhancer.setCallback(new MethodInterceptor() { 
      public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 
       if (!Modifier.isAbstract(method.getModifiers())) { 
        return methodProxy.invokeSuper(proxy, args); 
       } else { 
        Class type = method.getReturnType(); 
        if (type.isPrimitive() && !void.class.equals(type)) { 
         return Array.get(Array.newInstance(type, 1), 0); 
        } else { 
         return null; 
        } 
       } 
      } 
     }); 
     return (A) enhancer.create(); 
    } 

    @SuppressWarnings("unchecked") 
    public static <A> A createDefaultImplementation(String className) throws ClassNotFoundException{ 
     return (A) createDefaultImplementation(Class.forName(className)); 
    } 
} 

यह उदाहरण के लिए आप एक डिफ़ॉल्ट कार्यान्वयन विधि के साथ सार वर्गों बना सकते हैं। लेकिन आप जो भी चाहते हैं उसे बढ़ाने के लिए आप बदल सकते हैं।

5

वहाँ एक समाधान उपलब्ध है ..?

हाँ। वहाँ है। मौजूदा कक्षाओं से इंटरफ़ेस निकालें।

upd

आप कुछ विशिष्ट वर्गों के लिए इसकी आवश्यकता है, तो आप लिख सकते हैं

तरह श्रीमती
//interface that already exists 
public interface IDomain { 
    String foo(); 
} 
//your class 
public class Domain implements IDomain{ 
    public String foo(){ 
     return "domain foo"; 
    } 
//method that doesn't placed in IDomain 
    public String bar(){ 
     return "domain bar"; 
    } 
} 
//So you need create new interface with bar() 
//it can extend IDomain 
public interface ExtendedIDomain extends IDomain { 
    public String bar(); 
} 
//than your wrapper factory will be like this 
public class Proxifier { 
    public static ExtendedIDomain getProxy(Domain obj) { 
     InvocationHandler ih = new InjectProxy(obj); 
     ClassLoader classLoader = InjectProxy.class.getClassLoader(); 
     return (ExtendedIDomain) Proxy.newProxyInstance(classLoader, new Class[]{ExtendedIDomain.class}, ih); 
    } 

    static class InjectProxy implements InvocationHandler { 
     private final Domain domain; 
     private InjectProxy(Domain domain){ 
      this.domain = domain; 
     } 

     public String invoke(Object proxy, Method method, Object[] args) throws Throwable{ 
      for(Method m : domain.getClass().getMethods()){ 
       //TODO: check signature(name, args etc) or implement some logic with annotations 
       if(m.getName().equals(method.getName())){ 
        return "wrapped " + m.invoke(domain, args); 
       } 
      } 
      throw new IllegalArgumentException(); 
     } 
    } 
} 
//test 
    public static void main(String[] args) { 
     ExtendedIDomain d = Proxifier.getProxy(new Domain()); 
     System.out.println(d.foo()); 
     System.out.println(d.bar()); 
    } 

आप कुछ "सार्वभौमिक" सामान आप @Peter Lawrey रूप AOP का उपयोग करना चाहिए की जरूरत है allready कहा ।

+1

धन्यवाद, लेकिन जैसा कि मैंने पहले ही उल्लेख किया है: "यदि आप प्रॉक्सी के साथ उपयोग के लिए सक्षम करने के लिए इंटरफ़ेस में विधियों को निकालना चाहते हैं तो उपयोग के मामले सीमित हैं" – stacker

+0

@stacker, ऐसा लगता है कि आपको कुछ उपयोग की आवश्यकता नहीं है, केवल ठोस समस्या हल करें .. मैंने इस उद्देश्य के लिए अपनी पोस्ट अपडेट की है। –

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