JDK动态代理是代理模式的一种,且只能代理接口。spring也有动态代理,称为CGLib,现在主要来看一下JDK动态代理是如何实现的?

一、介绍

JDK动态代理是有JDK提供的工具类Proxy实现的,动态代理类是在运行时生成指定接口的代理类,每个代理实例(实现需要代理的接口)都有一个关联的调用处理程序对象,此对象实现了InvocationHandler,最终的业务逻辑是在InvocationHandler实现类的invoke方法上。

也即是在invoke方法上可以实现原方法中没有的业务逻辑,相当于spring aop的@Before、@After等注解。

二、样例

(1)接口

public interface ProxySource {    void test();}

(2)实现类

public class ProxySourceImpl implements ProxySource {    @Override    public void test() {        System.out.println("原有业务逻辑");    }}

(3)实现InvocationHandler接口和invoke方法

static class MyHandler implements InvocationHandler {    //需要代理的类    Object target;    public MyHandler(Object target) {        this.target = target;    }     /**     * @param proxy 动态代理实例     * @param method 需要执行的方法     * @param args 方法中参数     * @return     * @throws Throwable     */    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("do other things befor");        method.invoke(target, args);        System.out.println("do other things after");        return null;    }}

(4)利用Proxy实现代理类

//此参数设置是为了保存生成代理类的字节码文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");ProxySource proxySource = (ProxySource) Proxy.newProxyInstance(        ProxySourceImpl.class.getClassLoader(),        ProxySourceImpl.class.getInterfaces(),        new MyHandler(new ProxySourceImpl()));//执行方法 proxySource.test();

(5)方法调用结果




java 注解属性动态取值 java注解动态赋值_注解动态赋值


可以看到,在原有方法执行前后都执行了其他代码。

三、源码分析(主要看Proxy.newProxyInstance方法,省略非核心代码)

(1)newProxyInstance方法

Object newProxyInstance(ClassLoader loader,                                          Class>[] interfaces,                                          InvocationHandler h){              final Class>[] intfs = interfaces.clone();        //获取代理接口class        Class> cl = getProxyClass0(loader, intfs);        //获取到class之后用反射获取构造方法,然后创建代理类实例           try {            final Constructor> cons = cl.getConstructor(constructorParams);            final InvocationHandler ih = h;            return cons.newInstance(new Object[]{h});        } catch (Exception e) {            throw new InternalError(e.toString(), e);        }     }

由上述代码可知,主要是通过getProxyClass0方法获取到代理接口的class

(2)getProxyClass0方法

Class> getProxyClass0(ClassLoader loader,                        Class>... interfaces) {        //如果已经有相应的字节码文件,则之间返回,否则通过代理类工厂创建代理类        return proxyClassCache.get(loader, interfaces);    }

而proxyClassCache又是什么东东呢?

WeakCache[], Class>>        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

proxyClassCache是一个WeakCache对象,可知这是一个缓存对象,这个类结构是通过ConcurrentHashMap实现的,

ConcurrentMap>> map = new ConcurrentHashMap<>();

数据结构是(key,sub-key)->value

存的值也就是>

(3)get方法


java 注解属性动态取值 java注解动态赋值_字节码_02


public V get(K key, P parameter) {    Object cacheKey = CacheKey.valueOf(key, refQueue);    //根据classloader为key查看缓存中是否已有      ConcurrentMap> valuesMap = map.get(cacheKey);    if (valuesMap == null) {        ConcurrentMap> oldValuesMap            = map.putIfAbsent(cacheKey,                              valuesMap = new ConcurrentHashMap<>());        if (oldValuesMap != null) {            valuesMap = oldValuesMap;        }    }    //获取到weakcache种的sub-key     Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));    //根据sub-key去当前类加载器下是否有该代理接口的字节码        Supplier supplier = valuesMap.get(subKey);    Factory factory = null;    while (true) {        if (supplier != null) {            //supplier是代理类工厂实例              V value = supplier.get();            if (value != null) {                return value;            }        }        //创建代理类工厂         if (factory == null) {            factory = new Factory(key, parameter, subKey, valuesMap);        }        //将上述创建的代理类工厂直接赋值给supplier          if (supplier == null) {            supplier = valuesMap.putIfAbsent(subKey, factory);            if (supplier == null) {                supplier = factory;            }        } else {            if (valuesMap.replace(subKey, supplier, factory)) {                supplier = factory;            } else {                supplier = valuesMap.get(subKey);            }        }    }}


java 注解属性动态取值 java注解动态赋值_java 注解属性动态取值_03


这个supplier.get方法点进去,核心就是ProxyClassFactory的apply方法


java 注解属性动态取值 java注解动态赋值_动态代理_04


(4)ProxyClassFactory的apply方法

Class> apply(ClassLoader loader, Class>[] interfaces) {      for (Class> intf : interfaces) {                    Class> interfaceClass = null;          try {              //通过类权限定名反射获取class              interfaceClass = Class.forName(intf.getName(), false, loader);          } catch (ClassNotFoundException e) {          }          //判断是否可以通过系统加载器加载          if (interfaceClass != intf) {              throw new IllegalArgumentException(                  intf + " is not visible from class loader");          }          //校验是否是接口          if (!interfaceClass.isInterface()) {              throw new IllegalArgumentException(                  interfaceClass.getName() + " is not an interface");          }      }      long num = nextUniqueNumber.getAndIncrement();      //生成代理类的权限定名,例如com.sun.proxy.$Proxy0      String proxyName = proxyPkg + proxyClassNamePrefix + num;      //生成字节码文件      byte[] proxyClassFile = ProxyGenerator.generateProxyClass(          proxyName, interfaces, accessFlags);      try {          //调用本地方法生成class            return defineClass0(loader, proxyName,                              proxyClassFile, 0, proxyClassFile.length);      } catch (ClassFormatError e) {          throw new IllegalArgumentException(e.toString());      }  }

上述代码中核心是生成字节码,即是ProxyGenerator.generateProxyClass方法。

(5)ProxyGenerator.generateProxyClass方法。

public static byte[] generateProxyClass(final String var0, Class>[] var1, int var2) {        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);        //生成字节码        final byte[] var4 = var3.generateClassFile();        //开头的设置         if (saveGeneratedFiles) {            //保存生成代理类的字节码文件          }        return var4;    }

上述代码中saveGeneratedFiles点进去是这样的,也即是开头样例中的设置属性。

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

但主要还是ProxyGenerator的generateClassFile这个方法。


java 注解属性动态取值 java注解动态赋值_字节码_05


java 注解属性动态取值 java注解动态赋值_动态代理_06


其默认给代理类生成了hashcode、equals和toString方法,也限制了代理接口和字段都不能超过65535个。

现在来看一下保存的代理类字节码文件是怎么样的(通过idea反编译后)

public final class $Proxy0 extends Proxy implements ProxySource {    private static Method m1;    private static Method m3;    private static Method m2;    private static Method m0;    public $Proxy0(InvocationHandler var1) throws  {        super(var1);    }    public final boolean equals(Object var1) throws  {        //代码省略    }    public final void test() throws  {      //h是invocationhandler,所以最后是执行invoke方法        super.h.invoke(this, m3, (Object[])null);    }    public final String toString() throws  {        //代码省略    }    public final int hashCode() throws  {        //代码省略    }    static {          m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));          m3 = Class.forName("com.liusy.lang.ProxySource").getMethod("test");          m2 = Class.forName("java.lang.Object").getMethod("toString");          m0 = Class.forName("java.lang.Object").getMethod("hashCode");      }}

可以看到,生成的$Proxy0继承了Proxy,实现了我定义的接口ProxySource,里面有四个方法,m0~m3,通过静态代码块中根据类的全限定名和方法名反射获取,而最后是执行InvocationHandler的invoke方法。

至此,JDK动态代理已经说完,希望对你有所帮助。

=======================================================

我是Liusy,一个喜欢健身的程序员。