如何动态创建两个不相关类的公共代理类?

时间:2016-10-18 09:11:57

标签: java code-generation cglib dynamic-class-loaders

我有两个不相关的java类(只有*.class,没有*.java),如下所示:

public class Trick {
    public String getName() { return "Jack"; }
    public String trick() { ... }
}

public class Treat {
    public String getName() { return "John"; }
    public String treat() { ... }
}

我希望在运行时生成一种 Proxy 类,它表示两个类的并集,并将它们转发到相应的实例,如果不可能则抛出它们。我认为可以使用cglib完成,但我不知道从哪里开始。

这就是我想做的事(伪代码):

// prepare: generate a common interface
TrickOrTreat trickOrTreat = magic.createUnionInterface(Trick.class, Treat.class);

// use with concrete interface A:
Trick trick = new Trick();
TrickOrTreat proxyA = magic.createProxy(trickOrTreat.class, trick);
System.out.println("trick name: " + proxyA.getName());

// use with concrete interface B:
Treat treat = new Treat();
TrickOrTreat proxyB = magic.createProxy(trickOrTreat.class, treat);
System.out.println("treat name: " + proxyB.getName());

或者那种效果。我想完全动态地完成它,可能基于cglib?如果那不可能,我会用它之间的代码生成步骤吗?

2 个答案:

答案 0 :(得分:1)

  • 如果您需要两个类/接口的功能,可以使用
public <TT extends Trick & Treat> void process(TT thing){
    //...
}

编辑

  • 实施新的界面MyProxyHandler
public interface MyProxyHandler {}
  • 使用类的接口扩展它,例如TreatInterface和TrickInterface

  • 创建实现java.lang.reflect.InvocationHandler的类ProxyManager

public abstract class ProxyManager<T extends MyProxyHandler> implements InvocationHandler {

    protected static String LOCK_OBJECT = new String("LOCK");

    protected T proxyHandler;
    protected List<T> handlers = new ArrayList<>();

    @SuppressWarnings("unchecked")
    public ProxyManager(Class<T> _clazz) {
        proxyHandler = (T) Proxy.newProxyInstance(_clazz.getClassLoader(), new Class[]{_clazz}, this);
    }

    public T getProxy() {
        return proxyHandler;
    }

    public List<T> getHandlers() {
        return handlers;
    }

    public void setHandlers(List<T> handlers) {
        this.handlers = handlers;
    }

    public boolean registerHandler(T handler) {
        synchronized (LOCK_OBJECT) {
            boolean add = true;
            for (T item : this.handlers) {
                if (item.getClass().equals(handler.getClass())) {
                    add = false;
                }
            }
            if (add)
                this.handlers.add(handler);
            return add;
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String result = "";
        for (MyProxyHandler handler : getHandlers()) {
            try {
                //I recommend that methods returns some enum like HANDLED/NOTHANDLED 
                result = (String) method.invoke(handler, args);
                if (result.equals("Some flag"))
                    break;
            } catch (InvocationTargetException e) {
                throw e.getCause();
            }
        }
        return result;
    }
}
  • 使用您的具体类扩展该类
public class TreatTrickProxyManager<T extends TreatInterface & TreatInterface> extends ProxyManager<T> {
     public TreatTrickProxyManager(Class<T> _clazz) {
          super(_clazz);
     }
}
  • 在您的bussines逻辑类中获取TreatTrickProxyManager的实例

  • 在您的方法中

public void retrieveSomeData(){
     ((TreatTrickProxyManager)getTreatTrickProxyManager().getProxy()).someMethodInvocation()
}

答案 1 :(得分:1)

如果您愿意使用cglib进行交易,可以使用Byte Buddy进行交易。我通常拒绝称它为魔术,但是你去了:

class Magic {
    Class<?> createUnionInterface(Class<?> a, Class<?> b) {
        DynamicType.Builder<?> builder = new ByteBuddy().makeInterface();
        Set<MethodDescription.SignatureToken> tokens = new HashSet<>();
        for (MethodDescription m : new TypeDescription.ForLoadedType(a)
                .getDeclaredMethods()
                .filter(ElementMatchers.isVirtual())) {
            tokens.add(m.asSignatureToken());
            builder = builder.defineMethod(m.getName(),
                    m.getReturnType(),
                    m.getModifiers()).withoutCode();
        }
        for (MethodDescription m : new TypeDescription.ForLoadedType(b)
                .getDeclaredMethods()
                .filter(ElementMatchers.isVirtual())) {
            if (!tokens.contains(m.asSignatureToken())) {
                builder = builder.defineMethod(m.getName(),
                        m.getReturnType(),
                        m.getModifiers()).withoutCode();
            }
        }
        return builder.make()
                .load(Magic.class.getClassLoader())
                .getLoaded();
    }

    Object createProxy(Class<?> m, final Object delegate) throws Exception {
        return new ByteBuddy()
                .subclass(m)
                .method(new ElementMatcher<MethodDescription>() {
                    @Override
                    public boolean matches(MethodDescription target) {
                        for (Method method : delegate.getClass()
                               .getDeclaredMethods()) {
                            if (new MethodDescription.ForLoadedMethod(method)
                                   .asSignatureToken()
                                   .equals(target.asSignatureToken())) {
                                return true;
                            }
                        }
                        return false;
                    }
                }).intercept(MethodDelegation.to(delegate))
                .make()
                .load(Magic.class.getClassLoader())
                .getLoaded()
                .newInstance();
    }
}

请注意,您无法在编译时引用运行时生成的类型。但是,这是运行时代码生成的给定约束。

Magic magic = new Magic();

Class<?> trickOrTreat = magic.createUnionInterface(Trick.class, Treat.class);

Trick trick = new Trick();
Object proxyA = magic.createProxy(trickOrTreat, trick);
System.out.println("trick name: " + trickOrTreat.getDeclaredMethod("getName").invoke(proxyA));

Treat treat = new Treat();
Object proxyB = magic.createProxy(trickOrTreat, treat);
System.out.println("trick name: " + trickOrTreat.getDeclaredMethod("getName").invoke(proxyB));

您可以通过在运行时之前生成TrickOrTreat类来克服此问题,以便您可以在运行时引用该类型。

对于建议的union类型方法,这将要求您至少有一个类作为接口类型,因为Java不支持多重继承。