JAVA动态代理 - JDK -- 纯JAVA代码

来源:互联网 发布:淘宝内衣店铺名字大全 编辑:程序博客网 时间:2024/06/05 22:44

静态代理:编译时就已经确定了代理关系,代理类和被代理类均实现同一个接口,且代理类持有被代理类的引用。

动态代理:运行时编译确定代理关系,生成代理类,代理类持有特定的Handler类的引用,并由此特定类的引用通过反射调用被代理类的方法,在调用被代理类方法时,在被代理类方法前后插入代理类方法。


JDK动态代理:

接口:

/**  * @projectName:JavaProxy  * @fileName:People.java  * @packageName:club.younge.proxy  * @date:2016年9月4日上午11:29:44  * @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.  *  */    package club.younge.proxy;  /**  * @className:People  * @function: TODO ADD FUNCTION.   * @reason:   TODO ADD REASON.  * @date:     2016年9月4日 上午11:29:44 * @author   Younge  * @version    * @since    JDK 1.8  * @see        */public interface People {public void eat() throws Throwable;public void sleep() throws Throwable;public void sport() throws Throwable;}
被代理类:

/**  * @projectName:JavaProxy  * @fileName:Jason.java  * @packageName:club.younge.proxy  * @date:2016年9月4日上午11:31:55  * @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.  *  */    package club.younge.proxy;  /**  * @className:Jason  * @function: TODO ADD FUNCTION.   * @reason:   TODO ADD REASON.  * @date:     2016年9月4日 上午11:31:55 * @author   Younge  * @version    * @since    JDK 1.8  * @see        */public class Jason implements People{@Overridepublic void eat() {System.out.println("Jason is eating now.");}@Overridepublic void sleep() {System.out.println("Jason is sleeping now.");}@Overridepublic void sport() {System.out.println("Jason is sporting now.");}}
实现JDK的一个调用接口ProxyHandler:

/**  * @projectName:JavaProxy  * @fileName:ProxyHandler.java  * @packageName:club.younge.proxy.jdk  * @date:2016年9月4日上午11:34:57  * @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.  *  */    package club.younge.proxy.jdk;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import club.younge.proxy.People;/**  * @className:ProxyHandler  * @function: TODO ADD FUNCTION.   * @reason:   TODO ADD REASON.  * @date:     2016年9月4日 上午11:34:57 * @author   Younge  * @version    * @since    JDK 1.8  * @see        */public class ProxyHandler implements InvocationHandler {private People people;public ProxyHandler(People people) {this.people = people;}@Overridepublic Object invoke(Object obj, Method method, Object[] objArray) throws Throwable {before();method.invoke(people, objArray);after();return null;}private void before(){System.out.println("Before eat you should washes hands!");}private void after(){System.out.println("After eat you should do some work!");}}

JDK动态代理测试类:

/**  * @projectName:JavaProxy  * @fileName:SelfTest.java  * @packageName:club.younge.test  * @date:2016年9月4日上午11:44:37  * @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.  *  */    package club.younge.test;import java.io.FileOutputStream;import java.io.IOException;import java.lang.reflect.Proxy;import club.younge.proxy.Jason;import club.younge.proxy.People;import club.younge.proxy.jdk.ProxyHandler;import sun.misc.ProxyGenerator;/**  * @className:SelfTest  * @function: TODO ADD FUNCTION.   * @reason:   TODO ADD REASON.  * @date:     2016年9月4日 上午11:44:37 * @author   Younge  * @version    * @since    JDK 1.8  * @see        */public class JdkTest {public static void main(String[] args) throws Throwable {People people = (People) Proxy.newProxyInstance(People.class.getClassLoader(), new Class[]{People.class}, new ProxyHandler(new Jason()));people.eat();System.out.println("People's actual class:" + people.getClass().getName());System.out.println("Game over!");//createProxyFile();}public static void createProxyFile() throws IOException{String proxyName = "ProxyPeople";byte[] data = ProxyGenerator.generateProxyClass(proxyName, new Class[]{People.class}); //调试时的名称为$Proxy0FileOutputStream fos = new FileOutputStream(proxyName + ".class");fos.write(data);fos.close();}}
测试结果:

Before eat you should washes hands!
Jason is eating now.
After eat you should do some work!
People's actual class:com.sun.proxy.$Proxy0
Game over!

从测试结果可以看出,JDK动态生成的代理类为com.sun.proxy.$Proxy0(类全名),将此动态生成的代理类的字节码输出为文件,然后通过JD-GUI反编译为java文件,代码如下:

package club.younge.proxy.jdk;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;import club.younge.proxy.People;/** * generated by jdkTest and decompiled by jd-gui. * @author Younge * */public final class ProxyPeople extends Proxy implements People {private static final long serialVersionUID = 1L;private static Method m1;private static Method m4;private static Method m2;private static Method m5;private static Method m3;private static Method m0;static {try {m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] { Class.forName("java.lang.Object") });m4 = Class.forName("club.younge.proxy.People").getMethod("eat", new Class[0]);m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m5 = Class.forName("club.younge.proxy.People").getMethod("sport", new Class[0]);m3 = Class.forName("club.younge.proxy.People").getMethod("sleep", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);// return;} catch (NoSuchMethodException localNoSuchMethodException) {throw new NoSuchMethodError(localNoSuchMethodException.getMessage());} catch (ClassNotFoundException localClassNotFoundException) {throw new NoClassDefFoundError(localClassNotFoundException.getMessage());}}public ProxyPeople(InvocationHandler paramInvocationHandler) {super(paramInvocationHandler);}public final boolean equals(Object paramObject) {try {return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();} catch (Error | RuntimeException localError) {throw localError;} catch (Throwable localThrowable) {throw new UndeclaredThrowableException(localThrowable);}}public final void eat() {try {this.h.invoke(this, m4, null);return;} catch (Error | RuntimeException localError) {throw localError;} catch (Throwable localThrowable) {throw new UndeclaredThrowableException(localThrowable);}}public final String toString() {try {return (String) this.h.invoke(this, m2, null);} catch (Error | RuntimeException localError) {throw localError;} catch (Throwable localThrowable) {throw new UndeclaredThrowableException(localThrowable);}}public final void sport() {try {this.h.invoke(this, m5, null);return;} catch (Error | RuntimeException localError) {throw localError;} catch (Throwable localThrowable) {throw new UndeclaredThrowableException(localThrowable);}}public final void sleep() {try {this.h.invoke(this, m3, null);return;} catch (Error | RuntimeException localError) {throw localError;} catch (Throwable localThrowable) {throw new UndeclaredThrowableException(localThrowable);}}public final int hashCode() {try {return ((Integer) this.h.invoke(this, m0, null)).intValue();} catch (Error | RuntimeException localError) {throw localError;} catch (Throwable localThrowable) {throw new UndeclaredThrowableException(localThrowable);}}}

从反编译的代码看出,动态生成的代理类,继承JDK的Proxy类,并实现了我们自定义的People接口,并持有,我们实现的ProxyHandler类的引用。在调用代理类方法时,实则调用了ProxyHandler的invoke方法,然后在此invoke方法中,反射调用被代理类方法。最终代理类调用了被代理类的方法。


自己实现JDK的动态代理过程:

接口和被代理类如上。

我们需要手动实现JDK定义的Proxy的newProxyInstance()方法。

/**  * @projectName:JavaProxy  * @fileName:MyProxy.java  * @packageName:club.younge.proxy.self  * @date:2016年9月5日下午5:34:24  * @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.  *  */package club.younge.proxy.self;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import javax.tools.JavaCompiler;import javax.tools.JavaCompiler.CompilationTask;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;/** * @className:MyProxy * @function: TODO ADD FUNCTION. * @reason: TODO ADD REASON. * @date: 2016年9月5日 下午5:34:24 * @author Younge * @version * @since JDK 1.8 * @see */public class MyProxy {private static String rt = "\r\n";@SuppressWarnings({ "rawtypes", "unchecked" })public static Object newProxyInstance(ClassLoader classLoader, Class interfs,MyInvocationHandler invocationHandler) {try {Method[] methods = interfs.getMethods();// 1.创建一个JAVA文件,用流的方式创建一个JAVA文件,生成JAVA文件String内容String proxyClass = "package club.younge.proxy.self;" + rt + "import java.lang.reflect.Method;" + rt+ "public class $Proxy0 implements " + interfs.getName() + "{" + rt + "MyInvocationHandler h;" + rt+ "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}"+ getMethodStr(methods, interfs) + rt + "}";// 2.类生成文件String fileName = "E:/GitLocal/Java/JavaProxy/JavaProxy"  //请务必注意文件路径是否正确,检查您的工程目录+ "/src/club/younge/proxy/self/$Proxy0.java";File file = new File(fileName);FileWriter fileWriter = new FileWriter(file);fileWriter.write(proxyClass);fileWriter.flush();fileWriter.close();// 3. 编译JAVA文件JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager stdFileMgr = compiler.getStandardFileManager(null, null, null);Iterable units = stdFileMgr.getJavaFileObjects(fileName);CompilationTask task = compiler.getTask(null, stdFileMgr, null, null, null, units);task.call();stdFileMgr.close();//file.delete(); //编译后可将该文件删除,造成与JDK动态代理一样的效果// 4.把class字节码加载到内存中  ////请务必注意文件路径是否正确,检查您的工程目录MyClassLoader classLoader2 = new MyClassLoader("E:/GitLocal/Java/JavaProxy/JavaProxy"+ "/src/club/younge/proxy/self/");Class proxy0Class = classLoader2.findClass("$Proxy0");Constructor constructor = proxy0Class.getConstructor(MyInvocationHandler.class);Object object = constructor.newInstance(invocationHandler);return object;} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return null;}@SuppressWarnings("rawtypes")private static String getMethodStr(Method[] methods, Class interfs) {StringBuilder builder = new StringBuilder();builder.append(rt);for (Method method : methods) {builder.append("public void ").append(method.getName()).append("() throws Throwable {").append(rt).append("Method md = ").append(interfs.getName()).append(".class.getMethod(\"").append(method.getName()).append("\", new Class[]{});").append(rt).append("this.h.invoke(this, md, null);").append(rt).append("}").append(rt);}return builder.toString();}}
此方法主要做:

1.拼凑动态代理类的字符串

2.将上述字符串写入.java文件中

3.编译.java文件为class字节码,

4.将字节码文件加载到内存中

自定义invocationhandler接口及对应接口实现类和ClassLoader类:

/**  * @projectName:JavaProxy  * @fileName:MyInvocationHandler.java  * @packageName:club.younge.proxy.self  * @date:2016年9月5日下午5:30:26  * @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.  *  */package club.younge.proxy.self;import java.lang.reflect.Method;/** * @className:MyInvocationHandler * @function: TODO ADD FUNCTION. * @reason: TODO ADD REASON. * @date: 2016年9月5日 下午5:30:26 * @author Younge * @version * @since JDK 1.8 * @see */public interface MyInvocationHandler {public Object invoke(Object obj, Method method, Object[] objArray) throws Throwable;}


/**  * @projectName:JavaProxy  * @fileName:MyProxyHandler.java  * @packageName:club.younge.proxy.self  * @date:2016年9月5日下午5:31:06  * @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.  *  */    package club.younge.proxy.self;import java.lang.reflect.Method;import club.younge.proxy.People;/**  * @className:MyProxyHandler  * @function: TODO ADD FUNCTION.   * @reason:   TODO ADD REASON.  * @date:     2016年9月5日 下午5:31:06 * @author   Younge  * @version    * @since    JDK 1.8  * @see        */public class MyProxyHandler implements MyInvocationHandler{private People people;public MyProxyHandler(People people) {this.people = people;}@Overridepublic Object invoke(Object obj, Method method, Object[] objArray) throws Throwable {before();people.eat();after();return null;}private void before(){System.out.println("Before eat you should washes hands!");}private void after(){System.out.println("After eat you should do some work!");}}



/**  * @projectName:JavaProxy  * @fileName:MyClassLoader.java  * @packageName:club.younge.proxy.self  * @date:2016年9月5日下午11:35:07  * @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.  *  */package club.younge.proxy.self;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;/** * @className:MyClassLoader * @function: TODO ADD FUNCTION. * @reason: TODO ADD REASON. * @date: 2016年9月5日 下午11:35:07 * @author Younge * @version * @since JDK 1.8 * @see */public class MyClassLoader extends ClassLoader {private File dir;public MyClassLoader(String path) {dir = new File(path);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {if (dir != null) {File classFile = new File(dir, name + ".class");if (classFile.exists()) {FileInputStream fis = null;try {fis = new FileInputStream(classFile);ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = fis.read(buffer)) != -1) {baos.write(buffer, 0, len);}return defineClass("club.younge.proxy.self." + name, baos.toByteArray(), 0, baos.size());} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}}return super.findClass(name);}}


自定义动态代理测试类

/**  * @projectName:JavaProxy  * @fileName:SelfTest.java  * @packageName:club.younge.test  * @date:2016年9月5日下午5:38:08  * @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.  *  */    package club.younge.test;import club.younge.proxy.Jason;import club.younge.proxy.People;import club.younge.proxy.self.MyProxy;import club.younge.proxy.self.MyProxyHandler;/**  * @className:SelfTest  * @function: TODO ADD FUNCTION.   * @reason:   TODO ADD REASON.  * @date:     2016年9月5日 下午5:38:08 * @author   Younge  * @version    * @since    JDK 1.8  * @see        */public class SelfTest {public static void main(String[] args) throws Throwable {People people = (People) MyProxy.newProxyInstance(People.class.getClassLoader(), People.class, new MyProxyHandler(new Jason()));people.eat();System.out.println("Game over!");}}
测试结果:

Before eat you should washes hands!
Jason is eating now.
After eat you should do some work!
Game over!


与JDK动态代理效果类似。









0 0