jdk动态代理原理
1.手写动态代理
场景:
1.生成一个java文件 2.动态编译一个.class文件 3.拿到这个class文件后,我们通过反射获取一个对象
target对象-----通过代理生成java文件-------编译成class文件------调用反射方式class.newIstance-----------------得到代理对象proxy
public class StaticProxy implements MyService {
private MyService target;
public StaticProxy(MyService target){
this.target = target;
}
@Override
public void test01() {
System.out.println("proxy print log for test01");
target.test01();
}
@Override
public void test02(String s) {
System.out.println("proxy print log for test02");
target.test02(s);
}
}
上面是静态代理
1.拼接字符串,将上面的代码以字符串的形式拼接出来并写入到磁盘文件上
2.编译java文件生成class文件
3.加载class文件到jvm内存中,得到一个class对象
4.调用反射方法得到对象
代码如下:
public class ProxyUtil {
public static Object newInstance(Object target){
Object proxy = null;
// 拼接字符串java文件
Class targetInf = target.getClass().getInterfaces()[0];
// 获取所有的方法
Method[] declaredMethods = targetInf.getDeclaredMethods();
// \r\n
String line = System.lineSeparator();
String tab = "\t";
// 获取类的名称 System.class System
String infName = targetInf.getSimpleName();
String context = "";
// package com;
String packageContext = "package com.hxy.proxy;" + line;
// import com.MyService;
String importContext = "import "+ targetInf.getName() + ";" +line;
String clazzFirstLineContext = "public class $Proxy implements " +infName + "{" +line;
String filedContent = tab + "private " + infName + " target;" + line;
String constructorContent = tab + "public $Proxy (" + infName + " target){" + line
+ tab + tab + "this.target =target;"
+ line + tab + "}" + line;
String methodContent = "";
for (Method method : declaredMethods) {
String returnTypeName = method.getReturnType().getSimpleName();
String methodName = method.getName();
// Sting.class String.class
Class args[] = method.getParameterTypes();
String argsContent = "";
String paramsContent = "";
int flag = 0;
for (Class arg : args) {
String temp = arg.getSimpleName();
//String
//String p0,Sting p1,
argsContent += temp + " p" + flag + ",";
paramsContent += "p" + flag + ",";
flag++;
}
if (argsContent.length() > 0) {
argsContent = argsContent.substring(0, argsContent.lastIndexOf(",") - 1);
paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(",") - 1);
}
methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {" + line
+ tab + tab + "System.out.println(\"proxy print log for " + methodName + "\");" + line
+ tab + tab + "target." + methodName + "(" + paramsContent + ");" + line
+ tab + "}" + line;
}
// 以上是拼接java文件
context = packageContext + importContext + clazzFirstLineContext + filedContent + constructorContent + methodContent + "}";
// 生成java文件
File file = new File("E:\\com\\hxy\\proxy\\$Proxy.java");
try {
if(!file.exists()){
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(context);
fw.flush();
fw.close();
// 开始编译java
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(file);
JavaCompiler.CompilationTask task = compiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);
task.call();
standardFileManager.close();
// 加载class
URL[] urls = new URL[]{
new URL("file:E:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class<?> clazz = urlClassLoader.loadClass("com.hxy.proxy.$Proxy");
System.out.println(targetInf);
// 通过构造方法去创建对象
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
Constructor<?> declaredConstructor = declaredConstructors[0];
proxy = declaredConstructor.newInstance(target);
// 也可以用推断构造方法去生成对象
} catch (Exception e) {
e.printStackTrace();
}
return proxy;
}
public static void main(String[] args) {
MyServiceImpl target = new MyServiceImpl();
MyService o = (MyService) ProxyUtil.newInstance(target);
o.test01();
o.test02("test02");
}
运行程序可以看到:
以上就是通过先生成java文件在把java文件编译成class文件,再通过把class对象加载到内存中通过确定的构造方法去创建对象
新增接口CoustomInvocationHandler
public interface CoustomInvocationHandler {
public Object invoke(Method method);
}
实现类如下:
public class TestCustomHandler implements CoustomInvocationHandler {
Object target;
public TestCustomHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Method method) {
try {
System.out.println("----------------");
return method.invoke(target);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
需要修改上面的ProxyUtil类主要是生成java文件的方法
生成java文件相关方法改为
public static Object newInstance(Class targetInf, CoustomInvocationHandler h){
Object proxy=null;
//String handlerName = CoustomInvocationHandler.class.
Method methods[] =targetInf.getDeclaredMethods();
String line="\n";
String tab ="\t";
String infName = targetInf.getSimpleName();
String content ="";
String packageContent = "package com.hxy;"+line;
String importContent = "import "+targetInf.getName()+";"+line
+"import com.luban.dao.CoustomInvocationHandler;"+line
+"import java.lang.Exception;" +line
+"import java.lang.reflect.Method;"+line;
String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
String filedContent =tab+"private CoustomInvocationHandler h;"+line;
String constructorContent =tab+"public $Proxy (CoustomInvocationHandler h){" +line
+tab+tab+"this.h = h;"
+line+tab+"}"+line;
String methodContent = "";
for (Method method : methods) {
String returnTypeName = method.getReturnType().getSimpleName();
String methodName =method.getName();
// Sting.class String.class
Class args[] = method.getParameterTypes();
String argsContent = "";
String paramsContent="";
int flag =0;
for (Class arg : args) {
String temp = arg.getSimpleName();
//String
//String p0,Sting p1,
argsContent+=temp+" p"+flag+",";
paramsContent+="p"+flag+",";
flag++;
}
if (argsContent.length()>0){
argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1);
}
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line;
if(method.getReturnType().equals(Void.TYPE)){
methodContent += tab+tab+ "h.invoke(method);"+line;
}else{
methodContent += tab+tab+"return ("+returnTypeName+")h.invoke(method);"+line;
}
methodContent+=tab+"}"+line;
}
content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
File file =new File("E:\\com\\hxy\\$Proxy.java");
效果如下: