代理模式分为静态代理 与动态代理 两种模式,其中静态代理则必须在编译期就需要确认代理类与被代理类,灵活性不高,而动态代理则是在运行期来确认,代理类通过编译器在运行时动态生成,无需我们手动创建!
动态代理模式如果细分,又要分为接口代理 与子类代理 模式, 在我们常用的Spring AOP 中就用到了两种动态代理模式:JDK动态代理 和CGLIB动态代理 ,两种动态代理形成互补。
动态代理的条件
两个角色: 代理对象,被代理对象
代理对象需要完成被代理对象的需要完成的业务操作
代理对象持有被代理对象的引用
JDK动态代理 被代理对象必须实现接口,CGLIB动态代理被代理类和方法不能用final修饰
实现代码
被代理对象实现的目标接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.nqmysb.proxy.jdk;public interface Subject { void businessMethod () ; }
被代理的目标对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.nqmysb.proxy.jdk.impl;import com.nqmysb.proxy.jdk.Subject;public class RealSubject implements Subject { @Override public void businessMethod () { System.out.println("我在进行具体的业务操作。。。。。。" ); } }
被代理的目标对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package com.nqmysb.proxy.jdk;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;public class DynamicProxy implements InvocationHandler { private Subject target; public Object getInstance (Subject target) throws Exception { this .target = target; Class<? extends Subject> clazz = target.getClass(); System.out.println("被代理对象的class是:" +clazz); ClassLoader classLoader = clazz.getClassLoader(); System.out.println("被代理对象的类加载器:" +classLoader); Class<?>[] classs = clazz.getInterfaces(); System.out.println("被代理对象实现的接口:" +Arrays.asList(classs)); return Proxy.newProxyInstance(classLoader, classs, this ); } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始代理..." ); System.out.println("代理之前做的事情..." ); System.out.println("------------" ); method.invoke(this .target, args); System.out.println("------------" ); System.out.println("代理之后做的事情..." ); System.out.println("结束代理..." ); return null ; } }
JDK动态代理测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.nqmysb.proxy.jdk;import com.nqmysb.proxy.jdk.impl.RealSubject;public class DynamicProxyTest { public static void main (String[] args) { try { Subject subject = (Subject)new DynamicProxy().getInstance(new RealSubject()); System.out.println(subject.getClass()); subject.businessMethod(); } catch (Exception e) { e.printStackTrace(); } } }
运行结果如下
1 2 3 4 5 6 7 8 9 10 11 被代理对象的class是:class com.nqmysb.proxy.jdk.impl.RealSubject 被代理对象的类加载器:sun.misc.Launcher$AppClassLoader@73d16e93 被代理对象实现的接口:[interface com.nqmysb.proxy.jdk.Subject] class com.sun.proxy.$Proxy0 开始代理... 代理之前做的事情... ------------ 我在进行具体的业务操作。。。。。。 ------------ 代理之后做的事情... 结束代理...
手写JDK动态代理 JDK做了哪些事?
在自己手写JDK动态代理之前我们分析一下JDK动态代理帮我们做了哪些事情?
代理类必须实现JDK中的java.lang.reflect.InvocationHandler接口,这个接口中只有一个方法,如下:
1 2 3 4 5 6 7 8 9 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable ;
代理类需要重写和实现该方法,在这个方法里面我们可以进行附加业务操作,再调用被代理对象的方法。从而对被代理对象的方法进行扩展。
生成代理类对象,代理类中的getInstance(Subject target)方法产生代理对象。关键代码如下:
1 2 3 4 Proxy.newProxyInstance(classLoader, classs, this )
从上面两点可以得出JDK动态代理帮我们生成了代理类,代理实现了被代理类的所有接口,并将代理类加载到JVM中,生成代理实例,并回调我们实现的invoke方法进行动态代理。 JDK给我们提供了 java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 代理类 其中JDK1.8版本中的Proxy类中有一个私有的静态内部类 private static final class ProxyClassFactory 该类用于生成代理类的class对象。源代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 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) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { Class<?> interfaceClass = null ; try { 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" ); } if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null ) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null ; int accessFlags = Modifier.PUBLIC | Modifier.FINAL; for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; 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" ); } } } if (proxyPkg == null ) { proxyPkg = ReflectUtil.PROXY_PACKAGE + "." ; } long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; byte [] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0 , proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } }
手写JDK动态代理代码实现
前两个要素不变:目标接口和目标实现类。
被代理对象实现的目标接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.nqmysb.proxy.jdk;public interface Subject { void businessMethod () ; }
被代理的目标对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.nqmysb.proxy.jdk.impl;import com.nqmysb.proxy.jdk.Subject;public class RealSubject implements Subject { @Override public void businessMethod () { System.out.println("我在进行具体的业务操作。。。。。。" ); } }
自定义类加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 package com.nqmysb.myproxy.proxytools;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.util.HashMap;public class MyClassLoader extends ClassLoader { private File baseDir; public MyClassLoader () { Class<MyClassLoader> clazz = MyClassLoader.class; String basePath = clazz.getResource("" ).getPath(); this .baseDir = new java.io.File(basePath); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String className = MyClassLoader.class.getPackage().getName() + "." + name; if (baseDir != null ){ File classFile = new File(baseDir,name.replaceAll("\\." , "/" ) + ".class" ); if (classFile.exists()){ FileInputStream in = null ; ByteArrayOutputStream out = null ; try { in = new FileInputStream(classFile); out = new ByteArrayOutputStream(); byte [] buff = new byte [1024 ]; int len; while ((len = in.read(buff)) != -1 ) { out.write(buff, 0 , len); } return defineClass(className, out.toByteArray(), 0 ,out.size()); }catch (Exception e) { e.printStackTrace(); }finally { if (null != in){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (null != out){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } classFile.delete(); } } } return null ; } public static void main (String[] args) { System.out.println(String.class.getClassLoader()); System.out.println(HashMap.class.getClassLoader()); System.out.println(MyClassLoader.class.getClassLoader().getClass().getName()); System.out.println(ClassLoader.getSystemClassLoader().getClass().getName()); } }
自定义 InvocationHandler 接口
1 2 3 4 5 6 7 8 9 package com.nqmysb.myproxy.proxytools;import java.lang.reflect.Method;public interface MyInvocationHandler { Object invoke (Object proxy, Method method, Object[] args) throws Throwable ; }
自定义 Proxy 代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 package com.nqmysb.myproxy.proxytools;import java.io.File;import java.io.FileWriter;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import javax.tools.JavaCompiler;import javax.tools.JavaCompiler.CompilationTask;import javax.tools.JavaFileObject;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;public class MyProxy { private static String ln = "\r\n" ; public static Object newProxyInstance (MyClassLoader classLoader,Class<?>[] interfaces,MyInvocationHandler myInvocationHandler) { try { String proxySrc = generateSrc(interfaces[0 ]); String filePath = MyProxy.class.getResource("" ).getPath(); File f = new File(filePath + "$Proxy0.java" ); FileWriter fw = new FileWriter(f); fw.write(proxySrc); fw.flush(); fw.close(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compiler.getStandardFileManager(null , null , null ); Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(f); CompilationTask task = compiler.getTask(null , manager, null , null , null , iterable); task.call(); manager.close(); Class<?> proxyClass = classLoader.findClass("$Proxy0" ); Constructor<?> c = proxyClass.getConstructor(MyInvocationHandler.class); f.delete(); return c.newInstance(myInvocationHandler); }catch (Exception e) { e.printStackTrace(); } return null ; } private static String generateSrc (Class<?> interfaces) { StringBuffer src = new StringBuffer(); src.append("package com.nqmysb.myproxy.proxytools;" + ln); src.append("import java.lang.reflect.Method;" + ln); src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln); src.append("MyInvocationHandler h;" + ln); src.append("public $Proxy0(MyInvocationHandler h) {" + ln); src.append("this.h = h;" + ln); src.append("}" + ln); for (Method m : interfaces.getMethods()) { src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln); src.append("try{" + ln); src.append("Method m = " + interfaces.getName() + ".class.getMethod(\"" +m.getName()+"\",new Class[]{});" + ln); src.append("this.h.invoke(this,m,null);" + ln); src.append("}catch(Throwable e){e.printStackTrace();}" + ln); src.append("}" + ln); } src.append("}" ); System.out.println("动态生成的代理类$Proxy0:" +ln+src); return src.toString(); } }
使用自己手写的动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.nqmysb.myproxy;import java.lang.reflect.Method;import java.util.Arrays;import com.nqmysb.myproxy.proxytools.MyClassLoader;import com.nqmysb.myproxy.proxytools.MyInvocationHandler;import com.nqmysb.myproxy.proxytools.MyProxy;public class MyDynamicProxy implements MyInvocationHandler { private Subject target; public Object getInstance (Subject target) throws Exception { this .target = target; Class<? extends Subject> clazz = target.getClass(); Class<?>[] classs = clazz.getInterfaces(); System.out.println("被代理对象实现的接口:" +Arrays.asList(classs)); return MyProxy.newProxyInstance(new MyClassLoader(), classs, this ); } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始代理..." ); System.out.println("代理之前做的事情..." ); System.out.println("------------" ); method.invoke(this .target, args); System.out.println("------------" ); System.out.println("代理之后做的事情..." ); System.out.println("结束代理..." ); return null ; } }
测试动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.nqmysb.myproxy;import com.nqmysb.myproxy.impl.RealSubject;public class MyDynamicProxyTest { public static void main (String[] args) { try { Subject subject = (Subject)new MyDynamicProxy().getInstance(new RealSubject()); System.out.println(subject.getClass()); subject.businessMethod(); } catch (Exception e) { e.printStackTrace(); } } }
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 被代理对象实现的接口:[interface com.nqmysb.myproxy.Subject] 动态生成的代理类$Proxy0: package com.nqmysb.myproxy.proxytools;import java.lang.reflect.Method;public class $Proxy0 implements com .nqmysb .myproxy .Subject {MyInvocationHandler h; public $Proxy0(MyInvocationHandler h) {this .h = h;} public void businessMethod () {try {Method m = com.nqmysb.myproxy.Subject.class.getMethod("businessMethod",new Class[]{}); this .h.invoke(this ,m,null );}catch (Throwable e){e.printStackTrace();} } } class com .nqmysb .myproxy .proxytools .$Proxy0 开始代理... 代理之前做的事情... ------------ 我在进行具体的业务操作。。。。。。 ------------ 代理之后做的事情... 结束代理...
总结 动态代理的底层实现的原理:
反射机制:获取被代理类的方法,并实现动态字节码重组生成新的代理类 值得注意:JDK动态代理和CGLIB动态代理使用的反射机制是不一样的,
分别是:java.lang.reflect.Method和net.sf.cglib.reflect.FastClass 类实现的
函数回调机制:将代理类需要执行的附加方法传入新生成的代理类中,执行被代理类方法时会回调代理类的方法
代码工程 github地址:https://github.com/nqmysb/spring-source-analysis