手写实现精简版的jdk动态代理

一、前言

  • 接上篇jdk动态代理原理分析来手写一个精简版的jdk动态代理。
  • 上篇实现jdk动态代理的小总结:
    • 1、基本校验
    • 2、生成代理类
    • 3、加载到jvm
    • 4、构造方法实例化代理对象
  • jdk动态代理其实还有缓存之类的,其他功能也更高级,本篇文章只实现核心。

二、精简版动态代理代码实现

  • 代码实现基本上依然是按照小总结来:

    • 1、定义自己的类加载器InvocationHandlerProxy
    • 2、基本校验
    • 3、生成代理类,编译成class文件
    • 4、加载到jvm,构造方法实例化代理对象
  • 1.1、定义类加载器ClassLoaderTest.java,为了把生成的代理类加载到jvm内存中

package com.zzq.test.proxy;

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

/**
 * 自定义的类加载器
 */

public class ClassLoaderTest extends ClassLoader {
	
	private File dir;
	
	public ClassLoaderTest(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()){
				try {
					FileInputStream fis = new FileInputStream(classFile);
					ByteArrayOutputStream baos = new ByteArrayOutputStream();
					byte [] buffer = new byte[1024];
					
					int len;
					
					while ((len = fis.read(buffer)) != -1 ) {
						baos.write(buffer,0,len);
					}
					//把字节流的内容加载到内存
					return defineClass("com.zzq.test.proxy." +name,baos.toByteArray()
							,0,baos.size());
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			}
					
		}
		return super.findClass(name);
	}
	 
}
  • 1.2 定义自己的InvocationHandler MyInvocationHandler.java
package com.zzq.test.proxy;

import java.lang.reflect.Method;

public interface MyInvocationHandler {

	public Object invoke(Object proxy, Method method, Object[] args);
	
}

  • 1.3 定义自己的Proxy MyProxy.java,具体逻辑先不写,就先定义个newProxyInstance方法完事儿
package com.zzq.test.proxy;
public class MyProxy {
	/**
	 * 返回代理实例的方法
	 * **/
	public static Object newProxyInstance(ClassLoader loader,Class<?> claz,MyInvocationHandler h){
	return null;
	}
}

2、基本校验

    /***
     * 必须的验证
     * */
    private static void requiredValidate(ClassLoader loader, Class<?> claz, MyInvocationHandler h) {
        if (null == loader || null == claz || null == h) {
			throw new RuntimeException("参数异常");
        }
    }
  • 3.1、生成代理类,并输出文件,文件我是输出到当前代码目录的,生成的java文件也没有删除。
    在这里插入图片描述
    /**
     * 字符串拼接代理类
     */
    private static String getProxyTest(Class<?> claz) {
        Method[] methods = claz.getMethods();
        StringBuffer sb = new StringBuffer();
        sb.append("package com.zzq.test.proxy;" + rt);
        sb.append("import java.lang.reflect.Method; " + rt);
        sb.append("public class ProxyTest implements " + claz.getName() + "{");
        sb.append(rt + "MyInvocationHandler h;" + rt);
        sb.append("public ProxyTest(MyInvocationHandler h){" + rt);
        sb.append("this.h = h;" + rt);
        sb.append("}" + rt);
        sb.append(getMethodString(methods, claz));
        sb.append(rt + "}");
        return sb.toString();
    }

   private static void outFile(String proxyClssStr) {
        File file = new File(path);
        try {
            FileWriter fw = new FileWriter(file);
            fw.write(proxyClssStr);
            fw.flush();
            fw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
  • 3.2 编译class文件,最终执行还是需要class文件的
    private static void complierJava() throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileManager> javaFileObjects = (Iterable<? extends JavaFileManager>) standardJavaFileManager.getJavaFileObjects(path);
        CompilationTask task = compiler.getTask(null, standardJavaFileManager, null, null, null, (Iterable<? extends JavaFileObject>) javaFileObjects);
        task.call();
        try {
            standardJavaFileManager.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
		 
  • 4、 加载到jvm内存,这里要用到上面写的自定义的类加载器了
   private static Object loaderClassToJvm(MyInvocationHandler h) {
        ClassLoaderTest loaderTest = new ClassLoaderTest(path.replace("/ProxyTest.java", ""));
        try {
            Class<?> clz = loaderTest.findClass("ProxyTest");

            Constructor<?> construct = clz.getConstructor(MyInvocationHandler.class);
            //把h参数传入代理类的构造方法  返回代理类的实例
            return construct.newInstance(h);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
  • 完整的代码 MyProxy.java
package com.zzq.test.proxy;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;


import javax.tools.JavaCompiler.CompilationTask;


public class MyProxy {

	private static final String path = MyProxy.class.getResource("/").getPath().substring(1).replace("/target/test-classes/","") +"/src/test/java/com/zzq/test/proxy/ProxyTest.java";

    //private static final String path = "/Volumes/新加卷/workspace/git/CeShi2/src/test/java/com/zzq/test/proxy/ProxyTest.java";


    private static final String rt = "\r\n";



    /**
     * 返回代理实例的方法
     **/
    public static Object newProxyInstance(ClassLoader loader, Class<?> claz, MyInvocationHandler h) {
        requiredValidate(loader, claz, h);
        //拼接代理类
        String proxyClssStr = getProxyTest(claz);

        //输出到文件
        //G:/win_workspace/git/CeShi2/src/test/java/com/zzq/test/proxy
        outFile(proxyClssStr);

        //编译文件
        try {
            complierJava();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //加載到jvm内存
        return loaderClassToJvm(h);

    }

    /***
     * 必须的验证
     * */
    private static void requiredValidate(ClassLoader loader, Class<?> claz, MyInvocationHandler h) {
        if (null == loader || null == claz || null == h) {
			throw new RuntimeException("参数异常");
        }
    }


    private static Object loaderClassToJvm(MyInvocationHandler h) {
        ClassLoaderTest loaderTest = new ClassLoaderTest(path.replace("/ProxyTest.java", ""));
        try {
            Class<?> clz = loaderTest.findClass("ProxyTest");

            Constructor<?> construct = clz.getConstructor(MyInvocationHandler.class);
            //把h参数传入代理类的构造方法  返回代理类的实例
            return construct.newInstance(h);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    private static void complierJava() throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileManager> javaFileObjects = (Iterable<? extends JavaFileManager>) standardJavaFileManager.getJavaFileObjects(path);
        CompilationTask task = compiler.getTask(null, standardJavaFileManager, null, null, null, (Iterable<? extends JavaFileObject>) javaFileObjects);
        task.call();
        try {
            standardJavaFileManager.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    private static void outFile(String proxyClssStr) {
        File file = new File(path);
        try {
            FileWriter fw = new FileWriter(file);
            fw.write(proxyClssStr);
            fw.flush();
            fw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    /**
     * 字符串拼接代理类
     */
    private static String getProxyTest(Class<?> claz) {
        Method[] methods = claz.getMethods();
        StringBuffer sb = new StringBuffer();
        sb.append("package com.zzq.test.proxy;" + rt);
        sb.append("import java.lang.reflect.Method; " + rt);
        sb.append("public class ProxyTest implements " + claz.getName() + "{");
        sb.append(rt + "MyInvocationHandler h;" + rt);
        sb.append("public ProxyTest(MyInvocationHandler h){" + rt);
        sb.append("this.h = h;" + rt);
        sb.append("}" + rt);
        sb.append(getMethodString(methods, claz));
        sb.append(rt + "}");
        return sb.toString();
    }


    /**
     * 循环拼接处接口的方法(这里是UserService接口)
     */
    private static Object getMethodString(Method[] methods, Class<?> claz) {
        String proxyMe = "";
        for (Method method : methods) {
            proxyMe += "public void " + method.getName();
            proxyMe += "() throws Throwable {" + rt + " Method md =";
            proxyMe += claz.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt;
            proxyMe += " this.h.invoke(this,md,null);" + rt + "}" + rt;
        }
        return proxyMe;
    }
	
	
 

}

  • 到这里核心已经写完了,下面就是写代码去测试这个精简版的动态代理了。

三、编写测试代码

  • 上面已经写完了精简版的动态代理,下面开始写测试代码。

  • 依旧把步骤列出来:

    • 1、写serviceservice实现类
    • 2、写MyInvocationHandler的实现类
    • 3、测试代码调用精简版的动态代理
  • 1、UserService.javaUserServiceImpl.java

package com.zzq.test.proxy;



public interface UserService {

	
	public void test() throws Throwable ;
}

package com.zzq.test.proxy;



public class UserServiceImpl implements UserService{

	
	@Override
	public void test() throws Throwable  {
		System.out.println("进入test方法");
	}
}

  • 2、MyCustomInvocationHandler.java
package com.zzq.test.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyCustomInvocationHandler implements MyInvocationHandler {

	UserService userService;
	
	public MyCustomInvocationHandler(	UserService userService) {
		this.userService = userService;
	}
	
	public Object invoke(Object proxy,Method method,Object []args){
		try {
			System.out.println("before");
			method.invoke(userService, args);
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
}

  • 3、测试代码 Test.java
package com.zzq.test.proxy;
public class Test {

	 
	public static void main(String[] args) {
		UserService service = (UserService) MyProxy.newProxyInstance(Test.class.getClassLoader()
				, UserService.class, new MyCustomInvocationHandler(new UserServiceImpl()));
		try {
			//service是一个生成的代理类 
			service.test();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
  • 运行测试:
    在这里插入图片描述
  • 这里有一个warning,可以不管它。
  • 再来看看生成的ProxyTest.java文件,我格式化一下。
package com.zzq.test.proxy;

import java.lang.reflect.Method;

public class ProxyTest implements com.zzq.test.proxy.UserService {
    MyInvocationHandler h;

    public ProxyTest(MyInvocationHandler h) {
        this.h = h;
    }

    public void test() throws Throwable {
        Method md = com.zzq.test.proxy.UserService.class.getMethod("test", new Class[]{});
        this.h.invoke(this, md, null);
    }

}
  • 和jdk动态代理原理几乎一致,我得到的service对象就是这个ProxyTest对象,调用service.test();
    在这里插入图片描述
    h对象就是我们传进来的MyCustomInvocationHandler对象,调用invoke方法
    在这里插入图片描述
    最后method.invoke(userService, args); method看前面ProxyTest的代码就可以知道是service实现类的test方法,调用到UserServiceImpl#test方法

四、小总结

  • 整个核心就在于: 生成代理类编译class文件加载到jvm内存中实例化代理对象

  • 本人水平有限,如果文章有误的地方,希望批评指正,感谢您的观看。

posted on 2020-06-12 13:43  愤怒的苹果ext  阅读(50)  评论(0编辑  收藏  举报

导航