美文网首页
代理设计模式

代理设计模式

作者: gczxbb | 来源:发表于2019-03-15 17:44 被阅读0次

一、静态代理

代理设计模式,代理类和真实类有相同接口,代理类对外隐藏真实类,使用者只关系使用的接口功能,不清楚使用的是代理类还是真实类,满足功能即可。
静态代理,编译时,将接口,代理类,真实类定义好,class文件生成,按照Jvm正常的加载流程运行。
举例,接口。

public interface Base {
    String hi();
}

代理类

public class ProxyBase implements Base{
    RealBase real ;
    public ProxyBase(RealBase real) {
        super();
        this.real = real;
    }

    @Override
    public String hi() {
        return real.hi();
    }
}

真实类

public class RealBase implements Base {
    @Override
    public String hi() {
        return "Real";
    }   
}

真实类和代理类都实现Base接口,代理类引用真实类对象,代理类方法调用真实类方法。

二、动态代理

动态代理,运行时,根据接口动态生成一个新代理类,优点是可以对所有的代理类实现的接口方法统一处理,路由到一个地方,InvocationHandler的invoke方法,通过Method识别调用的代理方法,触发真实类target的对应方法,也可以对所有方法统一加内容,比如统计打点时间。
举例,Retrofit类,利用动态代理生成interface的代理对象。

public <T> T create(final Class<T> service) {
    ...
    return (T) Proxy.newProxyInstance(service.getClassLoader(), 
        new Class<?>[] { service },  new InvocationHandler() {
            //该对象传入$Proxy0的构造方法中
            private final Platform platform = Platform.get();
            @Override 
            public Object invoke(Object proxy, Method method, Object... args) 
                        throws Throwable {
                ...
              }
        });
}

Retrofit类的create方法,调用Proxy类的newProxyInstance方法,参数是类加载器ClassLoader,interface数组,InvocationHandler。

public static Object newProxyInstance(ClassLoader loader, 
                Class<?>[] interfaces,
                InvocationHandler invocationHandler)
                throws IllegalArgumentException {
    Exception cause;
    try {
        return getProxyClass(loader, interfaces)
                .getConstructor(InvocationHandler.class)
                .newInstance(invocationHandler);
    } catch (NoSuchMethodException e) {
    }
}

Class<?>[]数组只有一个interface元素,Xxx.class服务类型。

public static Class<?> getProxyClass(ClassLoader loader, 
                    Class<?>... interfaces)
                    throws IllegalArgumentException {
    final List<Class<?>> interfaceList = 
                    new ArrayList<Class<?>>(interfaces.length);
    ...
    synchronized (loader.proxyCache) {
        //缓存中查找
        Class<?> proxy = loader.proxyCache.get(interfaceList);
        if (proxy != null) {
            return proxy;
        }
    }
    ...
    List<Method> methods = getMethods(interfaces);
    Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
    ...
    String baseName = commonPackageName != null && !commonPackageName.isEmpty()
                ? commonPackageName + ".$Proxy"
                : "$Proxy";
    Class<?> result;
    synchronized (loader.proxyCache) {
        result = loader.proxyCache.get(interfaceList);
        if (result == null) {
            //Proxy每生成一个$Proxy,nextClassNameIndex(静态)自动+1
            String name = baseName + nextClassNameIndex++;
            result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
            loader.proxyCache.put(interfaceList, result);
        }
    }
    return result
}

保存一份Map缓存,List<Class<?>>是key,value是interface服务的代理类Class<?> proxy。
遍历Class<?>数组,获取List<Method>,排序,增加equal,toString,hashCode三个基类方法。
根据name,Class<?>数组和List<Method>,native方法,即generateProxy方法,创建一个新类,命名为Xxx$Proxy数字,新类继承Proxy类,实现interface的方法。
generateProxy方法,创建Class<?>类型。
Map缓存防止每次对相同interface服务创建动态代理时都生成一次新类,反射影响性能。
getConstructor方法,获取新类带InvocationHandler参数的构造方法,创建新类实例对象。

protected Proxy(InvocationHandler h) {
    this.h = h;
}

将新类的对象返回,对Proxy0类反编译,该类中所有的interface方法会触发super.h.invoke方法。父类的h是创建新类对象时传入的InvocationHandler对象。因此,每一个interface方法调用,被InvocationHandler的invoke方法拦截。

三、总结

动态代理这一套固定代码可以无侵入扩展各种服务interface的api。核心逻辑是创建一个实现interface接口方法的Proxy代理子类,实现的interface方法均由我们上层强行插入的InvocationHandler统一路由处理。

利用Proxy#方法,根据虚拟机运行时动态生成新类,创建一个对象,命名方式以$开头,Proxy+数字,继承Proxy。

新创建类和interface的关系加入Map缓存,防止每次创建同一个服务的动态代理类时都生成一次新类,影响性能。


任重而道远

相关文章

网友评论

      本文标题:代理设计模式

      本文链接:https://www.haomeiwen.com/subject/loxlmqtx.html