java动态代理技术解析

jdk动态代理 所谓的动态代理,就是在运行时生成一个代理类,来执行被代理类的方法。
使用
1、创建一个接口对象

public interface Subject { void subject(); }

2、创建一个接口对象的实现类
public class RealSubject implements Subject { @Override public void subject() { System.out.println("real subject"); } }

3、创建一个InvocationHandler
这个handler就是代理subject对象。至于是哪个对象,就看有哪些实现。
public class Handler implements InvocationHandler { Subject subject; public Handler(Subject subject) { this.subject = subject; }@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("handler invoke method."); method.invoke(subject,args); return subject; } }

4、测试
public class Demo { public static void main(String[] args) { Subject sub = new RealSubject(); Subject subject = (Subject) Proxy.newProxyInstance(sub.getClass().getClassLoader(),sub.getClass().getInterfaces(),new Handler(sub)); System.out.println("$Proxy0.class全名: "+Proxy.getProxyClass(Subject.class.getClassLoader(), Subject.class)); subject.subject(); } }

原理
这里,我们可以看下生成的代理类,网络上搜索到的基本都是错误的方法,很多都是照抄的,我之前写过一篇文章,论技术人员的严谨性问题,说的就是这个现象。
1、这个$Proxy0的生成,是ProxyGenerator这个jdk包中的类操作的,我们看下他的方法:
很明显,有个saveGeneratedFiles判断,那么这个从哪里来?
static byte[] generateProxyClass(final String name, Class[] interfaces, int accessFlags) { ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); final byte[] classFile = gen.generateClassFile(); if (saveGeneratedFiles) { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Void run() { try { int i = name.lastIndexOf('.'); Path path; if (i > 0) { Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar)); Files.createDirectories(dir); path = dir.resolve(name.substring(i+1, name.length()) + ".class"); } else { path = Path.of(name + ".class"); } Files.write(path, classFile); return null; } catch (IOException e) { throw new InternalError( "I/O exception saving generated file: " + e); } } }); }return classFile; }

2、看下这段代码:
/** debugging flag for saving generated class files */ private static final boolean saveGeneratedFiles = java.security.AccessController.doPrivileged( new GetBooleanAction( "jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();

3、所以,正确的方式是在环境变量里加-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true。
4、执行main方法,就能看到$Proxy0.class文件了。
5、看下这个class文件:
基本明白几点:
  • 动态代理必须要实现接口,很多人问为什么需要接口,因为代理类不能多继承,只能实现接口。
  • 代理类会继承Proxy类。
  • 如果执行了具体的代理类的方法,代理类会调用invocationHandler的invoke方法。所以invocationHandler很重要。
  • 这个代理类中会有四个Method,代理类生成的时候,其中三个是hashcode,tostring,equals,剩下的就是被代理的方法。会直接初始化。
public final class $Proxy0 extends Proxy implements Subject { 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{ try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }public final void subject() throws{ try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }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 int hashCode() throws{ try { return (Integer)super.h.invoke(this, m0, (Object[])null); } 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")); m3 = Class.forName("proxy.Subject").getMethod("subject"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }

以上就是动态代理的本质,有兴趣的同学可以继续到super中去看看。
【java动态代理技术解析】tips:知识贵在积累。

    推荐阅读