【问题标题】:Why does JDK dynamic Proxy only work with Interfaces?为什么 JDK 动态代理只适用于接口?
【发布时间】:2011-05-23 14:32:46
【问题描述】:

JDK Proxy 类只接受工厂方法 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 );
                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
}

【问题讨论】:

  • 嗯,原则上你可以使用像 ASM 这样的字节码库来自己创建一个基于类的代理。
  • @Krik,一些单元测试模拟库会这样做。您需要创建一个派生类来覆盖您想要的方法。 (自动或手动)
  • 您是否考虑过 Aspect-J 旨在做这种事情?
  • @Peter Lawrey 我希望使用核心 java 的解决方案。
  • 您可以为派生类创建代码并在运行时编译/加载它。您可以使用 Compiler API 来执行此操作。

标签: java dynamic-proxy


【解决方案1】:

你可以像这样使用 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));
    }
}

例如,这允许您使用默认实现方法构建抽象类。但是您可以将增强器更改为您想要的任何内容。

【讨论】:

    【解决方案2】:

    有没有可用的解决方法..?

    是的。有。从现有类中提取接口。

    更新

    如果某些特定的类需要它,可以写smt like

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

    【讨论】:

    • 谢谢,但正如我已经提到的:“如果我必须将方法提取到接口以使它们能够与代理一起使用,则用例是有限的”
    • @stacker,看来你不需要一些工具,只需要解决具体问题。我已经为此更新了我的帖子。
    【解决方案3】:

    或替代实现

    您可以使用cglib

    关于 cglib 替代品的类似帖子:Are there alternatives to cglib?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-28
      • 2012-05-26
      • 1970-01-01
      • 2018-01-09
      • 2016-01-09
      • 1970-01-01
      • 1970-01-01
      • 2015-08-09
      相关资源
      最近更新 更多