前言
万丈高楼平地起,-- 基础篇。一、SSA
之前写过这个知识点 ,现在补充一些。Talk is Cheap,。
二、GTTP
1.静态代理
- 先看下笔者的代码。
代码的大概意思就是: 钢铁侠代理了蜘蛛侠,两人一起打怪兽。
当然,这个是静态代理,是在有接口的前提下。
其中的问题显而易见,SpiderMan不能每次都要叫唐尼代理他,他必须自己能搞定,所以他后面继承高科技的衣钵,个人感觉是唐尼的片酬太贵了,It’s time to change。(代码上,这里就是需要一个通用的代理对象完成动态代理,只需要提供被代理对象,就可以实现代理。下面代码说明动态代理)
2.JDK动态代理
先看下笔者的基本类
非常常规的JDK动态代理 这里简单看下。
3.自定义JDK动态代理
Proxy.newProxyInstance的内部原理可以理解为下面的步骤(实际上JDK省去了部分步骤)
- 他会产生一段字符串 代理类的源码
- 把这个字符串输出到一个.java($Proxy.java)文件当中
- 会把这个 P r o x y . j a v a 文 件 动 态 编 译 他 成 为 一 个 Proxy.java文件动态编译他成为一个 Proxy.java文件动态编译他成为一个Proxy.class
- 会通过一个类加载器把这个$Proxy.class加载到JVM当中
- Class.foranme(“xxxx”).newInstance 反射实例化这个对象
- 这个对象就是代理对象,调用他即可。
下面是具体展示
这里就贴下关键性的代码,懂得都懂。
个人理解:静态代理每代理一个对象就要生成几个磁盘文件(就是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();
}
}
}