代理的名词
代理对象:增强后的对象
目标对象:被增强的对象
他们不是绝对的,会根据情况发生变化
java当中如何实现代理
java实现的代理的两种办法
静态代理
继承 代理对象继承目标对象,重写需要增强的方法;
缺点:会代理类过多,会产生类爆炸
聚合 目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。
缺点:依然会产生类爆炸,只不过比继承少而已.
总结:尽量不要去使用静态代理。耦合度高,代码复用性差,类过多。
动态代理
通过实现InvocationHandler接口来完成动态代理。
自己实现一个山寨版的动态代理
思路:
不需要手动创建类文件(因为一旦手动创建类文件,就会到了静态代理的问题),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。
代码实现
接口:
public interface Dao {
void query();
void query(String string);
}
实现类:
public class DaoImpl implements Dao {
public void query() {
System.out.println("从数据库查询数据。。。");
}
public void query(String string) {
System.out.println(string);
}
}
代理对象生产器:
package com.study.util;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class ProxyGenerator {
/**
* content --->string
* 将string通过io编译成.java文件
* 通过.java文件得到.class文件
* 根据class文件反射处对象
*/
public static Object newInstance(Object target) {
Object proxy = null;
// 得到实现的接口,在这里就是Dao
Class targetInf = target.getClass().getInterfaces()[0];
Method methods[] = targetInf.getDeclaredMethods();
String line = "\n";
String tab = "\t";
String infName = targetInf.getSimpleName();
String content = "";
String packageContent = "package com.test;" + line;
String importContent = "import " + targetInf.getName() + ";" + line;
String clazzFirstLineContent = "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 : methods) {
String returnTypeName = method.getReturnType().getSimpleName();
String methodName = method.getName();
Class args[] = method.getParameterTypes();
String argsContent = "";
String paramsContent = "";
int flag = 0;
for (Class arg : args) {
String temp = arg.getSimpleName();
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(\"模拟动态代理\");" + line
+ tab + tab + "target." + methodName + "(" + paramsContent + ");" + line
+ tab + "}" + line;
}
content = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}";
File file = new File("d:\\com\\test\\Proxy.java");
try {
if (!file.exists()) {
file.createNewFile();
}
// 通过io操作,将拼接的.java文件写入到磁盘
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
//编译成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
// 从本地中,将java文件加载成class文件
URL[] urls = new URL[]{new URL("file:D:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazz = urlClassLoader.loadClass("com.test.Proxy");
// 由于有显示的构造参数,所以先得到构造方法
Constructor constructor = clazz.getConstructor(targetInf);
// 然后通过有参构造方法反射出代理对象
proxy = constructor.newInstance(target);
} catch (Exception e) {
e.printStackTrace();
}
return proxy;
}
}
测试:
public class Test {
public static void main(String[] args) {
Dao dao = (Dao)ProxyGenerator.newInstance(new DaoImpl());
dao.query();
System.out.println("============");
dao.query("带参查询");
}
}
输出入下:
模拟动态代理
从数据库查询数据。。。
============
模拟动态代理
带参查询