动态代理(代码篇)

前言

万丈高楼平地起,-- 基础篇。

一、SSA

之前写过这个知识点 ,现在补充一些。Talk is Cheap,。


二、GTTP

1.静态代理

  1. 先看下笔者的代码。

在这里插入图片描述

代码的大概意思就是: 钢铁侠代理了蜘蛛侠,两人一起打怪兽。
当然,这个是静态代理,是在有接口的前提下。
其中的问题显而易见,SpiderMan不能每次都要叫唐尼代理他,他必须自己能搞定,所以他后面继承高科技的衣钵,个人感觉是唐尼的片酬太贵了,It’s time to change。(代码上,这里就是需要一个通用的代理对象完成动态代理,只需要提供被代理对象,就可以实现代理。下面代码说明动态代理)


2.JDK动态代理

先看下笔者的基本类
在这里插入图片描述

非常常规的JDK动态代理 这里简单看下。


3.自定义JDK动态代理

Proxy.newProxyInstance的内部原理可以理解为下面的步骤(实际上JDK省去了部分步骤)

  1. 他会产生一段字符串 代理类的源码
  2. 把这个字符串输出到一个.java($Proxy.java)文件当中
  3. 会把这个 P r o x y . j a v a 文 件 动 态 编 译 他 成 为 一 个 Proxy.java文件动态编译他成为一个 Proxy.javaProxy.class
  4. 会通过一个类加载器把这个$Proxy.class加载到JVM当中
  5. Class.foranme(“xxxx”).newInstance 反射实例化这个对象
  6. 这个对象就是代理对象,调用他即可。

下面是具体展示
在这里插入图片描述

在这里插入图片描述

这里就贴下关键性的代码,懂得都懂。

个人理解:静态代理每代理一个对象就要生成几个磁盘文件(就是class),有个通用的代理对象来完成这个工作,传个要代理的对象和要代理的逻辑对象就是handle就可以了这样很方便,就是这里的JDK动态代理的活。JDK动态代理内部还对每个要自定义的处理逻辑进行了处理,就很通用。写这个底层的就是牛,写的步骤当然不是我这里列出来的这些步骤,JDK用字节码技术直接加载对象到内存中,不用生成这里的java文件。


package com.proxy.customDynProxy.handler;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author xiansheng lv
 * @date 2021/5/27 23:41
 */
public class ProxyCustom {

	/**
	 * io 把我们的代码写道一个.java文件当中,然后再手动把这个。java文件编译
	 * 编译完成之后肯定会产生一个.class文件,继而把这个class文件loader到JVM当中
	 * 然后通过反射区实例化这个对象,最终返回出去
	 * @return 返回一个对象--符合我们期望的代理对象
	 */
	public static Object createProxy(Class infce, CustomInvocationHandler cInvocationHandler) {

		/*构造Java文件*/
		String fileName = constructJavaFile(infce);
		//把Java原文件编译成class文件
		compilerJavaFile(fileName);

		//从磁盘或者网络上面加载一个类文件到JVM
		//并生成代理对象返回
		try {
			URL[] urls = new URL[]{new URL("file:/d:/")};
			URLClassLoader ul = new URLClassLoader(urls);
			Class c = ul.loadClass("com.proxy.$Proxy1");
			Constructor declaredConstructor = c.getDeclaredConstructor(CustomInvocationHandler.class);
			Object proxy = declaredConstructor.newInstance(cInvocationHandler);
			return proxy;
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}


	private static String constructJavaFile(Class infce){
		//方法的字符串
		String methodStr = "";
		//换行+一个tab
		String rt = "\r\n";String tab = "\t";String r = "\n";
		//获取接口当中的所有方法,方便后面遍历方法构建代理类的字符串
		Method[] methods = infce.getMethods();
		//每个方法的返回类型
		String rtype = "";
		//表示该方法有几个参数
		int args = 0;
		for (Method m : methods) {
			//得到方法的返回类型的字符串
			rtype = m.getReturnType().getSimpleName();
			//参数的字符串 有可能有参或者无参-----(mname(int p0,String p1))
			//int p0,String p1
			String argsStr = "";
			//最后执行invoke方法的时候需要传入的参数值
			String argsValueStr = "";
			//得到这个方法所有的参数个数
			int parameterCount = m.getParameterCount();
			Class[] classesParamArr = null;
			//再反射得到目标方法的时候需要的参数个数和类型的字符串
			String getMethodParamStr = "new Class[]{";
			if (parameterCount > 0) {
				classesParamArr = new Class[parameterCount];
				//得到所有的参数个数和类型
				Class<?>[] parameterTypes = m.getParameterTypes();
				int pc = 0;
				for (Class<?> parameterType : parameterTypes) {
					//classesParamArr[pc]=parameterType;
					getMethodParamStr += parameterType.getSimpleName() + ".class,";
					argsStr += parameterType.getSimpleName() + " p" + pc + ",";
					argsValueStr += "p" + pc + ",";
					pc++;
				}
				//截取最后一个逗号
				getMethodParamStr = getMethodParamStr.substring(0, getMethodParamStr.length() - 1);
				//截取最后一个逗号
				argsStr = argsStr.substring(0, argsStr.length() - 1);
				//截取最后一个逗号
				argsValueStr = argsValueStr.substring(0, argsValueStr.length() - 1);
			}

			getMethodParamStr += "}";
			boolean flag = false;
			String endReturnStr = "";
			String returnStr = "";
			if (!rtype.equals("void")) {
				returnStr = "return ";
				endReturnStr = "return null;";
				flag = true;
			}

			String convertStr = "";
			if (flag) {
				convertStr = "(" + rtype + ") ";
			}
			methodStr += r + tab + "@Override" + rt +
					tab + "public " + rtype + " " + m.getName() + "(" + argsStr + ") {" + rt +
					tab + tab + "try {" + rt +
					tab + tab + tab + "Method md = " + infce.getSimpleName() + ".class.getMethod(\"" + m.getName() + "\"," + getMethodParamStr + ");" + rt +

					tab + tab + tab + returnStr + convertStr + "h.invoke(this, md,new Object[]{" + argsValueStr + "});" + rt +
					tab + tab + "}catch(Exception e) {" + rt +
					tab + tab + tab + "e.printStackTrace();" + r + tab + tab + "}" + r +
					tab + tab + tab + endReturnStr + r +
					tab + "}";
		}

		String src = "package com.proxy;" + rt +
				"import java.lang.reflect.Method;" + rt +
				"import " + infce.getName() + ";" + rt +
				"import com.proxy.customDynProxy.handler.CustomInvocationHandler;" + rt +
				"public class $Proxy1 implements " + infce.getSimpleName() + "{" + rt +
				"\tCustomInvocationHandler h;" + rt +
				"\tpublic $Proxy1(CustomInvocationHandler h) {" + rt +
				"\t\tthis.h = h;" + rt +
				"\t}" + rt +
				methodStr +
				"\n}";


		//把产生的源代码输出到.java文件当中通过IO

		File root = new File("d:/com/proxy/");
		if (!root.exists()) {
			root.mkdirs();
		}
		String fileName = "d:/com/proxy/$Proxy1.java";
		File f = new File(fileName);
		if (!f.exists()) {
			try {
				f.createNewFile();

			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		FileWriter fw = null;
		try {
			fw = new FileWriter(f);
			fw.write(src);
			fw.flush();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return fileName;
	}


    private static  void compilerJavaFile(String fileName){
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
		Iterable units = fileMgr.getJavaFileObjects(fileName);
		JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
		t.call();
		try {
			fileMgr.close();
		} catch (IOException e) {
			e.printStackTrace();
		}


	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值