【原創】自己動手實現JDK動態代理


引言

項目結構如下圖所示,maven項目

image

1、JDK動態代理

先來一段jdk動態代理的demo,
首先創建一個接口,Person

package bean;

public interface Person {
	
	public void eat();
	
}

然后寫一個實現類PersonImpl

package bean;

public class PersonImpl implements Person{
	
	@Override
	public void eat() {
		// TODO Auto-generated method stub
		System.out.println("time to eat ");
	}
	
}

然后寫個使用類PersonInvocationHandler

package jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class PersonInvocationHandler implements InvocationHandler {

	private Object obj;

	public PersonInvocationHandler(Object obj) {
		this.obj = obj;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("before time to eat");
		method.invoke(obj, args);
		System.out.println("after time to eat");
		return null;
	}

}

最后 再寫個測試類

package jdk;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;
import bean.Person;
import bean.PersonImpl;

public class jdkTest {

	public static void main(String[] args) throws Exception {
		PersonInvocationHandler personInvocationHandler = new PersonInvocationHandler(
				new PersonImpl());
		Person personProxy = (Person) Proxy.newProxyInstance(
				PersonImpl.class.getClassLoader(),
				PersonImpl.class.getInterfaces(), personInvocationHandler);
		personProxy.eat();
	}
}

輸出如下

before time to eat
time to eat 
after time to eat

接下里我們不使用JDK的API,自己實現一套代理類

2、自定義動態代理

先上測試類的代碼,如下圖所示,共有(1)(2)(3)處不同
image-w500

針對(1),我們有如下代碼,先抄襲JDK的InvocationHandler,改個名字成為MyInvocationHandler

package custom;

import java.lang.reflect.Method;

public interface MyInvocationHandler {
	
	 public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable;
	 
}


編寫一個JAVA類MyPersonInvocationHandler繼承MyInvocationHandler,這段代碼與PersonInvocationHandler的代碼無異,如下所示

package custom;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyPersonInvocationHandler implements MyInvocationHandler {

	private Object obj;

	public MyPersonInvocationHandler(Object obj) {
		this.obj = obj;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("before time to eat");
		method.invoke(obj, args);
		System.out.println("after time to eat");
		return null;
	}

}

針對(2),我們實現一個自己的代理生成類MyProxy,其生成java代理類的步驟分為以下5步

  1. 生成java源碼
  2. 將源碼輸出到java文件中
  3. 將java文件編譯成class文件
  4. 將class加載進jvm
  5. 返回代理類對象

具體代碼如下

package custom;

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.StandardJavaFileManager;
import javax.tools.ToolProvider;


public class MyProxy {

	public static final String ln = "\r\n";

	public static Object newProxyInstance(MyClassLoader myClassLoder,
			Class<?>[] interfaces, MyInvocationHandler h) {
		try{
			// 1 java源碼
			String src = generateSrc(interfaces);
	
			// 2 將源碼輸出到java文件中
			String filePath = MyProxy.class.getResource("").getPath();
	        System.out.println(filePath);
	        File f = new File(filePath + "$Proxy0.java");
	        FileWriter fw = new FileWriter(f);
	        fw.write(src);
	        fw.flush();
	        fw.close();
	        
	        //3、將java文件編譯成class文件
	        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
	        StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
	        Iterable iterable = manage.getJavaFileObjects(f);

	        JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
	        task.call();
	        manage.close();
	        
	        //4、將class加載進jvm
	        Class proxyClass=myClassLoder.findClass("$Proxy0");
	        f.delete();
	        
	        //5、返回代理類對象
	        Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
	        return constructor.newInstance(h);
		}catch(Exception e){
			 e.printStackTrace();
		}
		return null;
	}

	private static String generateSrc(Class<?>[] interfaces) {
		// TODO Auto-generated method stub
		StringBuffer sb = new StringBuffer();
		sb.append("package custom;" + ln);
		sb.append("import java.lang.reflect.Method;" + ln);
		sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
		sb.append("private MyInvocationHandler h;"+ln);
		sb.append("public $Proxy0(MyInvocationHandler h) { " + ln);
		sb.append("this.h = h;"+ln);
		sb.append("}" + ln);
		for (Method m : interfaces[0].getMethods()) {
			sb.append("public " + m.getReturnType().getName() + " "
					+ m.getName() + "() {" + ln);
			sb.append("try{" + ln);
			sb.append("Method m = " + interfaces[0].getName()
					+ ".class.getMethod(\"" + m.getName()
					+ "\",new Class[]{});" + ln);
			sb.append("this.h.invoke(this,m,null);" + ln);
			sb.append("}catch(Throwable e){" + ln);
			sb.append("e.printStackTrace();" + ln);
			sb.append("}"+ln);
			sb.append("}"+ln);
		}
		sb.append("}" + ln);
		return sb.toString();
	}
}


針對(3),我們繼承ClassLoader,實現一套自己的類加載機制MyClassLoader,如下所示,

package custom;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class MyClassLoader extends ClassLoader {

	private File classPathfile;

	public MyClassLoader() {
		String classpth = MyClassLoader.class.getResource("").getPath();
		classPathfile = new File(classpth);
	}

	@Override
	public Class<?> findClass(String name) throws ClassNotFoundException {
		String className = MyClassLoader.class.getPackage().getName() + "." +name;
		if (classPathfile != null) {
			File file = new File(classPathfile, name + ".class");
			FileInputStream fileInputStream = null;
			ByteArrayOutputStream outputStream = null;
			try{
				fileInputStream = new FileInputStream(file);
				outputStream = new ByteArrayOutputStream();
				byte[] buff = new byte[1024];
				int len;
				while((len=fileInputStream.read(buff))!=-1){
					outputStream.write(buff, 0, len);
				}
				return defineClass(className, outputStream.toByteArray(), 0, outputStream.size());
			}catch(Exception e){
				e.printStackTrace();
			}finally{
				if(null!=fileInputStream){
					try {
						fileInputStream.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				if(null!=outputStream){
					try {
						outputStream.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		return null;
	}

}

最后測試類代碼如下所示

package custom;

import java.lang.reflect.Proxy;

import custom.MyPersonInvocationHandler;
import bean.Person;
import bean.PersonImpl;

public class CustomTest {

	public static void main(String[] args) {
		MyPersonInvocationHandler personInvocationHandler = new MyPersonInvocationHandler(
				new PersonImpl());
		Person personProxy = (Person) MyProxy.newProxyInstance(
				new MyClassLoader(), PersonImpl.class.getInterfaces(),
				personInvocationHandler);
		personProxy.eat();
	}
}

輸出如何所示

before time to eat
time to eat 
after time to eat

至此,我們已完全實現了一套自定義的jdk動態代理類


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM