• 【原创】自己动手实现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 = "
    ";
    
    	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动态代理类

  • 相关阅读:
    window.onload和DOMContentLoaded的区别
    存储
    JSONP的实现原理
    对于一个无线下拉加载图片的页面,如何给每个图片绑定事件
    事件冒泡
    通用的事件绑定函数
    拆解url的各部分
    如何检测浏览器的类型
    DOM节点操作
    es6基本用法
  • 原文地址:https://www.cnblogs.com/rjzheng/p/8750265.html
Copyright © 2020-2023  润新知