计算机 · 2021年12月18日 0

Java Dynamic Proxies

利用InvocationHandler接口和Proxy类的静态方法我们可以实现对任何一个接口对象的动态代理(类似于wrapper,就是通过对proxy的调用实现对真正要调用对象的操作):

InvocationHandler接口:

package java.lang.reflect;
public interface InvocationHandler {
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

Proxy.newProxyInstance方法:

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

譬如,对于MyInterface接口:

public interface MyInterface {
    void doSomething ();
}

我们可以通过自己实现一个InvocationHandler接口(MyInvocationHandler),然后通过Proxy.newProxyInstance方法返回一个MyInterface类的动态代理(这个代理实现了MyInterface的所有方法,但是这个代理不是一个MyInterface对象,它就是一个代理而已,一个动态创建的类,通过调用这个代理的方法实现调用MyInterface的方法)。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class MyInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
        
         Arrays.stream(Thread.currentThread()
                            .getStackTrace())
              .forEach(System.out::println);
        System.out.println(method);
        

        System.out.println("the invoked method: " + method);
        return null;
    }

    public static void main (String[] args) {
        MyInvocationHandler handler = new MyInvocationHandler();

        MyInterface o = (MyInterface) Proxy.newProxyInstance(
                            MyInvocationHandler.class.getClassLoader(),
                            new Class[]{MyInterface.class}, handler);
        o.doSomething();
    }
}

在上述代码中,调用代理o的doSomething方法时,代理o会调用handler的invoke方法,并且分别将invoke的参数Object proxy设置为代理o、参数Method method设置为doSomething方法、Object[] args设置为调用doSomething时使用的参数。

动态代理的作用

实现指定类的wrapper,拦截对该类的方法调用:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInterceptor<T> implements InvocationHandler {

    private T t;

    public MyInterceptor(T t) {
        this.t = t;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before method call : " + method.getName());
        Object result = method.invoke(t, args);
        System.out.println("after method call : " + method.getName());
        return result;
    }

    @SuppressWarnings("unchecked")
    public static <T> T getProxy(T t, Class<? super T> interfaceType) {
        MyInterceptor handler = new MyInterceptor(t);
        return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(),
                new Class<?>[]{interfaceType}, handler
        );
    }
}

对于Javabean类型的类实现带cache的decorator

public interface IObject {

  String getData ();

}

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class GenericCacheDecorator implements InvocationHandler {

    private Map<String, Object> cachedData = new HashMap<>();
    private Object EMPTY = new Object();
    private Object obj;

    private GenericCacheDecorator (Object obj) {
        this.obj = obj;
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            for (PropertyDescriptor desc : beanInfo.getPropertyDescriptors()) {
                cachedData.put(desc.getReadMethod()
                                   .getName(), EMPTY);
            }

        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
    }

    public static <I, T extends I> I decorate (T t, Class<I> interfaceClass) {
        GenericCacheDecorator cacheableDecorator = new GenericCacheDecorator(t);
        return (I) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                                          new Class[]{interfaceClass}, cacheableDecorator);

    }

    public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
        if (cachedData.containsKey(method.getName())) {
            Object o = cachedData.get(method.getName());
            if (o == EMPTY) {
                Object returned = method.invoke(obj, args);
                cachedData.put(method.getName(), returned);
                return returned;
            } else {
                return o;
            }
        }
        return method.invoke(args);
    }

    public static void main (String[] args) {
        MyObject object = new MyObject();
        IObject iObject = GenericCacheDecorator.decorate(object, IObject.class);
        System.out.println(iObject.getData());
        System.out.println(iObject.getData());
    }
}