一.代码实现
- 前提知识:
- 静态代理
- 继承:代理对象继承目标对象,重写需要增强的方法;缺点:会代理类过多,非常复杂
- 聚合:目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。缺点:也会产生类爆炸,只不过比继承少一点点
总结:如果在不确定的情况下,尽量不要去使用静态代理。因为一旦你写代码,就会产生类,一旦产生类就爆炸。
- 思路:要生成一对象一般来说都是通过class文件,被类加载器加载到jvm的方法区中才能实例化对象。但是我们要生成的代理对象没有对应的class文件,怎么办?
通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。
代码如下:
ProxyUtil.newInstanse() 相当于jdk中的
WxgDao jdkproxy = (WxgDao) Proxy.newProxyInstance(Test.class.getClassLoader(),
new Class[]{WxgDao.class},new WxgInvocationHandler(new WxgDaoImpl()));
public class ProxyUtil {
/**
* content --->string
* .java io
* .class
* .new 反射----》class
* @return
*/
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.google;"+line;
String importContent = "import "+targetInf.getName()+";"+line
+"import com.Wxg.dao.CoustomInvocationHandler;"+line
+"import java.lang.Exception;"
+"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
+tab+tab+"return ("+returnTypeName+")h.invoke(method);"+line;
methodContent+=tab+"}"+line;
}
content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
File file =new File("d:\\com\\google\\$Proxy.java");
try {
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
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();
URL[] urls = new URL[]{new URL("file:D:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazz = urlClassLoader.loadClass("com.google.$Proxy");
Constructor constructor = clazz.getConstructor(CoustomInvocationHandler.class);
proxy = constructor.newInstance(h);
//clazz.newInstance();
//Class.forName()
}catch (Exception e){
e.printStackTrace();
}
return proxy;
}
}
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;
}
}
测试方法:
public class Test {
public static void main(String[] args) {
//自定义,这里没有指定类加载器,是因为在代码中已经写死了用UrlClassLoader加载器,从磁盘文件中获取class文件。
LubanDao proxy = (LubanDao) ProxyUtil.newInstance(LubanDao.class,new TestCustomHandler(new LubanDaoImpl()));
try {
proxy.proxy();
} catch (Exception e) {
e.printStackTrace();
}
// byte[] bytes=ProxyGenerator.generateProxyClass("$Proxy18",new Class[]{LubanDao.class});
//
// try {
// FileOutputStream fileOutputStream = new FileOutputStream("d:\\$Proxy18.class");
// fileOutputStream.write(bytes);
// fileOutputStream.flush();
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// } catch (IOException e) {
// e.printStackTrace();
// }
// // System.out.println(proxy.proxy());
// LubanDao jdkproxy = (LubanDao) Proxy.newProxyInstance(Test.class.getClassLoader(),
// new Class[]{LubanDao.class},new LubanInvocationHandler(new LubanDaoImpl()));
//
// //jdkproxy.query();
// try {
// jdkproxy.proxy();
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
总结:和jdk动态代理的区别
缺点:首先要生成文件
缺点:动态编译文件 class
缺点:需要一个URLclassloader
软件性能的最终体现在IO操
而jdk动态代理是直接通过接口反射得到字节码,然后把字节码转成class ,这过程是调用了native的本地方法用 c++ 实现。且jdk动态代理默认会继承Proxy这个类。
二.为什么jdk动态代理必须是基于接口的?
因为jdk动态代理底层产生的代理对象默认继承了Proxy对象,不能再继续继承其他对象,只能通过实现接口的方式操作。