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

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值