java动态代理原理及解析

设计模式:动态代理

在这里插入图片描述
通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。

**java动态代理的类和接口(jdk1.6源码)**java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。

// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器

public static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象

public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类

public static boolean isProxyClass(Class<?> cl)

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,InvocationHandler h)

**2,java.lang.reflect.InvocationHandler:**调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问

public Object invoke(Object proxy, Method method, Object[] args)  throws Throwable;
/**
	该方法负责集中处理动态代理类上的所有方法调用。
	第一个参数既是代理类实例,
	第二个参数是被调用的方法对象
	第三个方法是调用参数。
	调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
*.


**3,java.lang.ClassLoader:**类装载器类,将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。

每次生成动态代理类对象时都需要指定一个类装载器对象:newProxyInstance()方法第一个参数
动态代理机制java动态代理创建对象的过程为如下步骤:

1,通过实现 InvocationHandler 接口创建自己的调用处理器;
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发

// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用

InvocationHandler handler = new InvocationHandlerImpl(..);

2,通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象

Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });

3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
// 通过反射从生成的类对象获得构造函数对象

Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

// 通过构造函数对象创建动态代理类实例

Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发

InvocationHandler handler = new InvocationHandlerImpl(..);

// 通过 Proxy 直接创建动态代理类实例

Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,  new Class[] { Interface.class }, handler );

代码实现通用

客户端测试

package com.zl.kaungshenProxy;

import com.zl.poxy.ChinaMovie;
import com.zl.poxy.Movie;

public class Client  {

    public static void main(String[] args) {
        //真实的角色
        ChinaMovie chinaMovie = new ChinaMovie();

        //代理角色,反射机制不存在de
        ProxyInvocationHandler handler = new ProxyInvocationHandler();

        //设置要代理的对象
        handler.setTarget(chinaMovie);

        //动态生成代理类
        //确定生成的类型是在里面这个getProxy来确定了
        Movie proxy = (Movie) handler.getProxy();//必须确定代理那个接口才能代理这个接口的功能
        proxy.play();


    }
}

代理类(实现代理接口)

package com.zl.kaungshenProxy;

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

public class ProxyInvocationHandler implements InvocationHandler {

    private Object target;

    //bei  daili  de  jiekou
    public void setTarget(Object target){
        this.target = target;
    }

    /**
     *dong  tai  shengcheng  dongtai daili  lei
     * @return
     */
    public Object getProxy(){
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(target, args);
        return invoke;
    }
}

源代码解析

首先在我们自己写的实现类里面实现这个接口,并写一个动态代理的方法返回一个代理实例(也就是动态生成动态代理类

return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);

按住ctrl加左键点击newProxyInstance方法,进入到安全检查的这个方法里面,

 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, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();
          // 这里根据参数配置,决定是否把生成的字节码(.class文件)保存到本地磁盘,我们可以通过把相应的class文件保存到本地,再反编译来看看具体的实现,这样更直观
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            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;
    }

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

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

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