天天看點

【原創】自己動手實作JDK動态代理

引言

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

【原創】自己動手實作JDK動态代理

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)處不同

【原創】自己動手實作JDK動态代理

針對(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動态代理類

作者:孤獨煙

出處: http://rjzheng.cnblogs.com/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】。