设计模式之动态代理模式源码分析

1、动态代理的实现

 

JDK动态代理java

jdk自带的动态代理主要是经过实现InvocationHandler数组

InvocationHandler的主要方法缓存

Object invoke(Object proxy, Method method,Object[] args)throws Throwable安全

在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。即调用真实业务的方法都会进入到此invoke方法,至于为何,稍后再说明多线程

方法详细介绍app

  • 参数:proxy - 调用方法的代理实例对象 。
  • Method-指代具体被代理的方法。 
    args -包含传入代理实例上方法调用的参数,若是接口方法不使用参数,则为 null。ide

  • return: 从代理实例的方法调用返回的值。函数

  • throws: Throwable - 从代理实例上的方法调用抛出的异常。测试

少说点废话吧 ,关于什么是动态代理网上不少介绍,这里直接从源码开始。。ui

1.业务接口

public interface HelloWorld {
    void say();
}

2.业务实现类

public class HelloWorldImp implements HelloWorld {
    @Override
    public void say() {
        System.out.println("hello world!");
    }
}

3.代理类

public class HelloProxy implements InvocationHandler {

    private Object target;//被代理对象

    public Object bind(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("berfor.....");
        method.invoke(target, args);
        System.out.println("after.....");
        return null;
    }
}

4.测试类

public class Client {

    public static void main(String[] args) {
        HelloProxy h = new HelloProxy();
        HelloWorld helloWorld = (HelloWorld) h.bind(new HelloWorldImp());
        helloWorld.say();
    }
}

5.运行结果

2、原理

        咱们利用ProxyGenerator类的generateProxyClass方法生成代理对象的class文件在idea中打开,豁然开朗其实主要一步就在于生成类的过程,类文件中创建被代理对象接口的invoke调用

String path = "D://aaa.class";
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", HelloWorldImp.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();
    }
}

下面直接看源码吧

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

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

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

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void say() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.canyou.proxy.HelloWorld").getMethod("say");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

        生成的代理对象一个有4个方法,其中就有咱们的say方法,里面调用handle的invoke方法,其余三个是全部类都有的,直接用的Method的反射机制实现的代理。

3、代理类的生成

        那jdk源码是怎么生成代理类的呢?

首先看Proxy类的newProxyInstance方法吧

/**
 * 这里有三个参数,
 * 第一个是传入classloader,通常状况是传入当前的classloader.
 * 第二个参数表示的是接口,
 * 第三个是Invocationhandler
 */
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);
    //获取须要代理类的全部实现的接口
    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        //检查是否有生成代理类的权限
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    //查找或者生成代理类
    Class<?> cl = getProxyClass0(loader, intfs);

    //生成构造函数
    try {
        if (sm != null) {
            //生成构造函数
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            //访问修饰符设置
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        //返回代理类的对象
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

而后看getProxyClass0方法

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    //从缓存中获取,若是不存在就建立
    return proxyClassCache.get(loader, interfaces);
}

使用proxyClassCache作缓存,其目的是为了复用,同时防止多线程重复建立

//获取或生成代理类 此处由于不是线程安全的作了屡次判断
public V get(K key, P parameter) {
    Objects.requireNonNull(parameter);
    //删除过时条目
    expungeStaleEntries();
    //建立cacheKey
    Object cacheKey = CacheKey.valueOf(key, refQueue);

    //查看key是否已经存在valuemaps中
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) { //不存在的话经过,再次尝试尝试获取,若是没有就插入
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

    //生成代理对象的key 为弱引用类型,这里重要的subKeyFactory.apply方法
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    //尝试从valuemap中获取
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;
    //这个后面的逻辑基本上是若是建立已经有了 就替换或者直接添加到缓存
    while (true) {
        if (supplier != null) {

            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        // else no supplier in cache
        // or a supplier that returned null (could be a cleared CacheValue
        // or a Factory that wasn't successful in installing the CacheValue)

        // lazily construct a Factory
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // else retry with winning supplier
        } else {
            if (valuesMap.replace(subKey, supplier, factory)) {
                // successfully replaced
                // cleared CacheEntry / unsuccessful Factory
                // with our Factory
                supplier = factory;
            } else {
                // retry with current supplier
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

ProxyClassFactory.apply中的重要片断方法,全部代理的class对象就这么来的

/*
* 这里才是重点  就像以前生成的class文件同样
* 生成代理类的字节数组
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);
try {
    return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
    /*
     * A ClassFormatError here means that (barring bugs in the
     * proxy class generation code) there was some other
     * invalid aspect of the arguments supplied to the proxy
     * class creation (such as virtual machine limitations
     * exceeded).
     */
    throw new IllegalArgumentException(e.toString());
}
//生成代理类
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    /**
     * 生成具体文件字节数组
     * 1.找到全部接口的方法
     * 2.添加object类的三个方法 tostring hashcode equils
     * 3.遍历生成具体的代理方法,代理方法的逻辑都想似,回调咱们的代理类
     */
    final byte[] var4 = var3.generateClassFile();
    // private static final boolean saveGeneratedFiles = GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
    //设置sun.misc.ProxyGenerator.saveGeneratedFiles = true,就会生成代理类class的文件
    if (saveGeneratedFiles) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int var1 = var0.lastIndexOf(46);
                    Path var2;
                    if (var1 > 0) {
                        //生成path 将.替换成系统文件分隔符
                        Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                        //建立文件夹
                        Files.createDirectories(var3);
                        //具体文件
                        var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                    } else {
                        //没包就放在项目根目录下
                        var2 = Paths.get(var0 + ".class");
                    }
                    //写入到文件中
                    Files.write(var2, var4, new OpenOption[0]);
                    return null;
                } catch (IOException var4x) {
                    throw new InternalError("I/O exception saving generated file: " + var4x);
                }
            }
        });
    }

    return var4;
}

4、总结

        之前老是知道有动态代理这么一回事,理解也不是很深,经过源代码的阅读,对JDK的动态代理实现清晰不少吧,应该对后面的Spring AOP的具体实现会更有帮助。不过里面也有不少地方没有深刻了,仍是能力不够吧,好比缓存代理,权限验证等。

相关文章
相关标签/搜索