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

本文深入解析Java动态代理的原理与实现方式,包括JDK动态代理的使用方法、自定义动态代理的过程及其与静态代理的区别。

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

动态代理:运行时确定代理关系,生成代理类,代理类持有特定的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{

	@Override
	public void eat() {
		System.out.println("Jason is eating now.");
		
	}

	@Override
	public void sleep() {
		System.out.println("Jason is sleeping now.");
		
	}

	@Override
	public 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;
	}
	@Override
	public 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}); //调试时的名称为$Proxy0
		FileOutputStream 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;
	}
	@Override
	public 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);
	}

	@Override
	protected 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动态代理效果类似。









评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值