【设计模式】——纯手写实现JDK动态代理

前言

在【设计模式】——代理模式(静态)以及【设计模式】——代理模式(动态)中,我们已经基本了解了什么是代理模式,以及什么是静态代理,什么是动态代理。

JDK动态代理以及Cglib动态代理其实底层实现原理都是字节码的重组,不过各自对应的代理场景不同,本文我们重点研究jdk动态代理。

通过前文的了解,我们已经知道在JDK动态代理中是JDK动态的帮我们生成一个名为$Proxy0的代理类,那么,我们本文来纯手写实现JDK动态代理,也就是我们自己生成这个代理类,与JDK动态代理的思想一致,只为简化,细节部分本文并未过多研究。

实现

在前文动态代理实现jdk动态代理中我们有这样一个类的调用关系图:

【设计模式】——纯手写实现JDK动态代理_第1张图片

其中需要引用到jdk的Proxy和InvocationHandler这两个类,这里我们自己简单实现MyProxy.java和MyInvocationHandler.java

由于需要动态生成代理类,那么就需要生成,编译,加载到jvm,因此我们实现了一个简单的类加载器MyClassLoader.java

此时类调用关系图略微变化:

【设计模式】——纯手写实现JDK动态代理_第2张图片

不多说,上代码,大多地方都有比较详细的注释 

 MyInvocationHandler.java 

/** 用于自定义代理逻辑处理
 * @author WangZhiJun
 */
public interface MyInvocationHandler {
	/** invoke
	 * @param proxy 指被代理的对象
	 * @param method 要调用的方法
	 * @param args 方法调用时所需要的参数
	 * @return Object
	 * @throws Throwable 异常
	 */
	Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

MyProxy.java: 这个类就是JDK动态代理的关键,其中进行了代理类的动态生成:

  1. 生成源代码
  2. 将生成的源代码输出到磁盘,保存为.java文件
  3. 编译源代码,并且生成.class文件
  4. 将class文件中的内容,动态加载到JVM中来
  5. 返回被代理后的代理对象
/** 生成代理对象的代码
 * @author WangZhiJun
 */
class MyProxy {

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

    /** 通过此类为一个或多个接口动态的生成实现类
     * @param classLoader 类加载器
     * @param interfaces 得到全部的接口
     * @param h 得到InvocationHandler接口的子类实例
     * @return Object
     */
	static Object newProxyInstance(MyClassLoader classLoader, Class[] interfaces, MyInvocationHandler h){
		try{
			//1、生成源代码
			String proxySrc = generateSrc(interfaces[0]);
			//2、将生成的源代码输出到磁盘,保存为.java文件
			String filePath = MyProxy.class.getResource("").getPath();
			File f = new File(filePath + "$Proxy0.java");
			FileWriter fw = new FileWriter(f);
			fw.write(proxySrc);
			fw.flush();
			fw.close();
			//3、编译源代码,并且生成.class文件
			JavaCompiler  compiler = ToolProvider.getSystemJavaCompiler();
			StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
			Iterable iterable = manager.getJavaFileObjects(f);
			CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
			task.call();
			manager.close();
			//4.将class文件中的内容,动态加载到JVM中来
			//5.返回被代理后的代理对象
			Class proxyClass = classLoader.findClass("$Proxy0");
			Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
			//这里先不删除生成的$Proxy0.java文件,实际上要删除的
			f.delete();
			return c.newInstance(h);

		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

    /** 生成代理对象$Proxy0的源代码
     * @param interfaces 抽象对象
     * @return String
     */
	private static String generateSrc(Class interfaces){
		StringBuilder src = new StringBuilder();
        src.append("package com.wang.proxy.custom.jdk.proxy;" + ln);
        //引入反射相关的包
        src.append("import java.lang.reflect.Method;" + ln);
        //动态代理类实现被代理接口,在此为Person类
        src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln);
        src.append("MyInvocationHandler h;" + ln);
        src.append("public $Proxy0(MyInvocationHandler h) {" + ln);
        src.append("this.h = h;" + ln);
        src.append("}" + ln);
        //通过反射获取代理接口的所有方法并激活
        for (Method m : interfaces.getMethods()) {
            src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);

            src.append("try{" + ln);
            src.append("Method m = " + interfaces.getName() + ".class.getMethod(\"" +m.getName()+"\",new Class[]{});" + ln);
            src.append("this.h.invoke(this,m,null);" + ln);
            src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
            src.append("}" + ln);
        }
        src.append("}");
		return src.toString();
	}
}

MyClassLoader.java

/**将class重新动态load到JVM
 * @author WangZhiJun
 */
public class MyClassLoader extends ClassLoader{

	private File baseDir;
	
	MyClassLoader(){
		String basePath = MyClassLoader.class.getResource("").getPath();
		this.baseDir = new File(basePath);
	}
	
	@Override
	protected Class findClass(String name) {
		String className = MyClassLoader.class.getPackage().getName() + "." + name;
		if(baseDir != null){
			File classFile = new File(baseDir,name.replaceAll("\\.", "/") + ".class");
			if(classFile.exists()){
				FileInputStream in = null;
				ByteArrayOutputStream out = null;
				try{
					in = new FileInputStream(classFile);
					out = new ByteArrayOutputStream();
					byte [] buff = new byte[1024];
					int len;
					while ((len = in.read(buff)) != -1) {
						out.write(buff, 0, len);
					}
					return defineClass(className, out.toByteArray(), 0,out.size());
				}catch (Exception e) {
					e.printStackTrace();
				}finally{
					if(null != in){
						try {
							in.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
					if(null != out){
						try {
							out.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
					//先不删除,可以看到class文件内容
					//classFile.delete();
				}
			}
		}
		return null;
	}
}

这里同样是实现【设计模式】——代理模式(动态)中媒婆的例子,其中Person接口以及Girl类在此不再赘述,MeiPo类因为Proxy以及MyInvocation的变化略微改动:

/** 媒婆 代理类
 * @author WangZhiJun
 */
public class MyMeiPo implements MyInvocationHandler {

	private Person target;

	/**获取被代理人的个人资料
	 * @param target 被代理对象
	 * @return Object
	 */
	Object getInstance(Person target) {
		this.target = target;
		Class clazz = target.getClass();
		return MyProxy.newProxyInstance(new MyClassLoader(), clazz.getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("我是媒婆,按照你的要求");
		System.out.println("开始进行海选...");
		System.out.println("------------");
		//调用的时候
		method.invoke(this.target, args);
		System.out.println("------------");
		System.out.println("选择结束,如果合适的话,就准备办事");
		return null;
	}
}

客户端:

/**
 * @description: 客户端
 * @author: WangZhiJun
 * @create: 2019-08-22 13:51
 **/
public class TestCustomJDKProxy {
    public static void main(String[] args) {
        Person person= (Person)new MyMeiPo().getInstance(new Girl());
        person.findLove();
    }
}

运行结果:

【设计模式】——纯手写实现JDK动态代理_第3张图片

此时的结果与上节中我们利用jdk的Proxy和InvocationHanlder是一模一样的!

我们看看这个动态生成的$Proxy0代理类是怎么写的:

public class $Proxy0 implements Person {
    MyInvocationHandler h;

    public $Proxy0(MyInvocationHandler var1) {
        this.h = var1;
    }

    public void findLove() {
        try {
            Method var1 = Person.class.getMethod("findLove");
            this.h.invoke(this, var1, (Object[])null);
        } catch (Throwable var2) {
            var2.printStackTrace();
        }

    }
}

 这就是生成的动态代理类,同样是Person的实现类,同样也实现了findLove方法。

现在可以得知MyProxy.newProxyInstance返回的是动态生成的代理类$Proxy0的对象,也可以称作是Person接口的一个实现类的对象。当调用person.findLove()时,其实是调用$Proxy0.findLove(),然后对照刚刚的类调用关系图,即可调用到被代理对象Girl实例的findLove方法,从而实现了动态代理。

 此时我们就简单的实现了JDK的动态代理,不过JDK中实际的实现会更加复杂,更加细节!

看到这里记得点个赞哦,感激不尽

有兴趣的话可以加入我们咸鱼交流群哦,这里不问技术,只谈兴趣:892480622

你可能感兴趣的:(★JAVA面试汇总★,★★设计模式算法★,★JAVA学习之路★)