反射、注解和动态代理 (3)

类加载器采用当前类的加载器,默认为应用程序类加载器(sun.misc.Launcher$AppClassLoader);接口数组以Subject.class为例,调用方法处理类MyInvocationHandler实现InvocationHandler接口,并在构造器中传入Subject的真正的业务功能服务类RealSubject,在执行invoke方法时,可以在实际方法调用前后织入自定义的处理逻辑,这也就是AOP(面向切面编程)的原理。
关于JDK动态代理,有两个问题需要清楚:

Proxy.newProxyInstance的代理类是如何生成的?Proxy.newProxyInstance生成代理类的核心分成两步:

// 1. 获取代理类的Class对象 Class<?> cl = getProxyClass0(loader, intfs); // 2. 利用Class获取Constructor,通过反射生成对象 cons.newInstance(new Object[]{h});

与反射获取Class对象时搜索classpath路径的.class文件不同的是,这里的Class对象完全是“无中生有”的。getProxyClass0根据类加载器和接口集合返回了Class对象,这里采用了缓存的处理。

// 缓存(key, sub-key) -> value,其中key为类加载器,sub-key为代理的接口,value为Class对象 private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); // 如果实现了代理接口的类已存在就返回缓存对象,否则就通过ProxyClassFactory生成 private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } return proxyClassCache.get(loader, interfaces); }

如果实现了代理接口的类已存在就返回缓存对象,否则就通过ProxyClassFactory生成。ProxyClassFactory又是通过下面的代码生成Class对象的。

// 生成代理类字节码文件 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); try { // defineClass0为native方法,生成Class对象 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); }

generateProxyClass方法是用来生成字节码文件的,根据生成的字节码文件,再在native层生成Class对象。

InvocationHandler的invoke方法是怎样调用的?
回答这个问题得先看下上面生成的Class对象究竟是什么样的,将ProxyGenerator生成的字节码保存成文件,然后反编译打开(IDEA直接打开),可见生成的Proxy.class主要包含equals、toString、hashCode和代理接口的request方法实现。

public final class $Proxy extends Proxy implements Subject { // m1 = Object的equals方法 private static Method m1; // m2 = Object的toString方法 private static Method m2; // Subject的request方法 private static Method m3; // Object的hashCode方法 private static Method m0; // 省略m1/m2/m0,此处只列出request方法实现 public final void request() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } }

由于生成的代理类继承自Proxy,super.h即是Prxoy的InvocationHandler,即代理类的request方法直接调用了InvocationHandler的实现,这就回答了InvocationHandler的invoke方法是如何被调用的了。

3.2 Cglib动态代理接口和类

Cglib的动态代理是通过Enhancer类实现的,其create方法生成动态代理的对象,有五个重载方法:

create():Object create(Class, Callback):Object create(Class, Class[], Callback):Object create(Class, Class[], CallbackFilter, Callback):Object create(Class[], Object):Object

常用的是第二个和第三个方法,分别用于动态代理类和动态代理接口,其使用方法如下:

private Object getProxy() { // 1. 动态代理类 return Enhancer.create(RealSubject.class, new MyMethodInterceptor()); // 2. 动态代理接口 return Enhancer.create(Object.class, new Class<?>[]{Subject.class}, new MyMethodInterceptor()); } private static class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Some thing before method invoke"); Object result = proxy.invokeSuper(obj, args); System.out.println("Some thing after method invoke"); return result; } }

从上小节可知,JDK只能代理接口,代理生成的类实现了接口的方法;而Cglib是通过继承被代理的类、重写其方法来实现的,如:create方法入参的第一个参数就是被代理类的类型。当然,Cglib也能代理接口,比如getProxy()方法中的第二种方式。

四、案例:Android端dubbo:reference化的网络访问

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspzfx.html