java设计模式-动态代理模式


[toc]

动态代理模式

定义

相比 静态代理,动态代理具有更强的 灵活性,因为它不用在我们设计实现的时候就指定 某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由 JVM来实现

类图

实例

动态代理类:在该类中需要指定需要传入被代理类的对象。

public class DynamicProxy implements InvocationHandler{
    Object o;
    public DynamicProxy(Object o){
        this.o = o;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before rquest");
        Object request = method.invoke(this.o,args);
        System.out.println("after request");
        return request;
    }
}

需要被代理的真实的类:

public interface Subject {
    public void request();
}

public class RealSubject implements Subject{ @Override public void request() { System.out.println("real subject request"); } }

client:先创建一个代理类。然后利用反射创建一个用真实类加载器创建的一个对象。该对象调用request方法实际上调用的是代理类的invoke方法。

public class Client {
    public static void main(String[] args){
        Subject subject = new RealSubject();
        InvocationHandler dynamicProxy = new DynamicProxy(subject);
        Subject proxySubject =(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),dynamicProxy);
        proxySubject.request();
    }
}

和静态代理模式比较的好处

在静态代理模式时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;而动态

源码分析

参考:http://rejoy.iteye.com/blog/1627405 主要原来:生成了一个代理类的class文件。 Proxy.newProInstance()方法

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
if (h == null) {
throw new NullPointerException();
}
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 这里是生成class的地方 Class<?> cl = getProxyClass0(loader, intfs);
// 使用我们实现的InvocationHandler作为参数调用构造方法来获得代理类的实例 try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}

其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,这里重点是看getProxyClass0这个方法的实现:

private static Class<?> getProxyClass0(ClassLoader loader,
                                          Class<?>... interfaces) {
        // 代理的接口数量不能超过65535(没有这种变态吧)
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        // JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理
        return proxyClassCache.get(loader, interfaces);
    }

其中代理缓存是使用WeakCache实现的,如下

    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

具体的缓存逻辑这里暂不关心,只需要关心ProxyClassFactory是如何生成代理类的,ProxyClassFactory是Proxy的一个静态内部类,实现了WeakCache的内部接口BiFunction的apply方法:

    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
        // 所有代理类名字的前缀
        private static final String proxyClassNamePrefix = "$Proxy";
        // 用于生成代理类名字的计数器
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            // 省略验证代理接口的代码……
            String proxyPkg = null;     // 生成的代理类的包名
            // 对于非公共接口,代理类的包名与接口的相同
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
            // 对于公共接口的包名,默认为com.sun.proxy
            if (proxyPkg == null) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            // 获取计数
            long num = nextUniqueNumber.getAndIncrement();
            // 默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次递增
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            // 这里才是真正的生成代理类的字节码的地方
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces);
            try {
                // 根据二进制字节码返回相应的Class实例
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

ProxyGenerator是sun.misc包中的类,它没有开源,但是可以反编译来一探究竟:

    public static byte[] generateProxyClass(final String var0, Class[] var1) {
        ProxyGenerator var2 = new ProxyGenerator(var0, var1);
        final byte[] var3 = var2.generateClassFile();
        // 这里根据参数配置,决定是否把生成的字节码(.class文件)保存到本地磁盘,我们可以通过把相应的class文件保存到本地,再反编译来看看具体的实现,这样更直观
        if(saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Void run() {
                    try {
                        FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + ".class");
                        var1.write(var3);
                        var1.close();
                        return null;
                    } catch (IOException var2) {
                        throw new InternalError("I/O exception saving generated file: " + var2);
                    }
                }
            });
        }
        return var3;
    }

saveGeneratedFiles这个属性的值从哪里来呢:

    private static final boolean saveGeneratedFiles = ((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();

GetBooleanAction实际上是调用Boolean.getBoolean(propName)来获得的,而Boolean.getBoolean(propName)调用了System.getProperty(name),所以我们可以设置sun.misc.ProxyGenerator.saveGeneratedFiles这个系统属性为true来把生成的class保存到本地文件来查看。

反编译class文件

自己创建文件写入

OK接下来,我们在客户端代码中设置这个值:

public class Client {
    public static void main(String[] args){
        //设置保存属性
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Subject subject = new RealSubject();
        InvocationHandler dynamicProxy = new DynamicProxy(subject);
        Subject proxySubject =(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),dynamicProxy);
        proxySubject.request();
    }

运行之后发现报错,报错信息如下:报错原因是因为,我们要在运行当前main方法的路径下创建com/sun/proxy目录,并创建一个$Proxy0.class文件,才能够正常运行并保存class`文件内容。

注意: 这个文件的路径为:System.getProperty("user.dir")+File.separator+该接口的包路径 for:/Users/nemo/03ws/sp_intel/com/sun/proxy

Exception in thread "main" java.lang.InternalError: I/O exception saving generated file: java.io.FileNotFoundException: com/sun/proxy/$Proxy0.class (No such file or directory)
    at sun.misc.ProxyGenerator$1.run(ProxyGenerator.java:336)
    at sun.misc.ProxyGenerator$1.run(ProxyGenerator.java:327)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.misc.ProxyGenerator.generateProxyClass(ProxyGenerator.java:326)
    at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:672)
    at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:592)
    at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:244)
    at java.lang.reflect.WeakCache.get(WeakCache.java:141)
    at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:455)
    at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:738)
    at com.nemo.design.dynamicproxy.Client.main(Client.java:14)

直接调用API进行源码 byte写入文件

在上面,我们需要手动穿件一个$Proxy0.class文件让系统写入。当然我们可以调用API直接写入到任何文件中。 参考:[http://rejoy.iteye.com/blog/1627405]http://rejoy.iteye.com/blog/1627405

工具类:

package dynamic.proxy;

import java.io.FileOutputStream; import java.io.IOException;

import sun.misc.ProxyGenerator;

/ * 代理类的生成工具 * @author zyb * @since 2012-8-9 */ public class ProxyGeneratorUtils { / * 把代理类的字节码写到硬盘上 * @param path 保存路径 */ public static void writeProxyClassToHardDisk(String path) { // 第一种方法,这种方式在刚才分析ProxyGenerator时已经知道了 // System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true); // 第二种方法 // 获取代理类的字节码 byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", UserServiceImpl.class.getInterfaces());
FileOutputStream out = null;
try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }

package dynamic.proxy;

import org.junit.Test;

/* * 动态代理测试类 * @author zyb * @since 2012-8-9 * / public class ProxyTest { @Test public void testProxy() throws Throwable { // 实例化目标对象 UserService userService = new UserServiceImpl();
// 实例化InvocationHandler MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);
// 根据目标对象生成代理对象 UserService proxy = (UserService) invocationHandler.getProxy();
// 调用代理对象的方法 proxy.add();

<span class="o">}</span>    

@Test public void testGenerateProxyClass() { ProxyGeneratorUtils.writeProxyClassToHardDisk("F:/$Proxy11.class"); } }

最后:反编译之后的源码如下

package com.sun.proxy;

import com.nemo.design.dynamicproxy.Subject; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Subject { private static Method m1; private static Method m3; private static Method m0; private static Method m2;

public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); }

public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }

public final void request() throws { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }

public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }

public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }

static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.nemo.design.dynamicproxy.Subject").getMethod("request", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }

nemotan /
Published under (CC) BY-NC-SA in categories 设计模式  tagged with 设计模式