在正式介绍自定义动态代理之前,先提出以下几个问题:
1、为什么叫做动态代理,它得动态表现在哪些方面?
2、动态代理得声明,才是我们写代码得地方,实现了 invocationHandler接口的类叫做声明?
3、声明类中的invoke方法到底是谁用的 ?
4、动态代理类$Proxy0还原?
5、JDK内存中的动态代理是没有对被代理类进行任何操作的?
下面我们来创建自己的动态代理:
/**
* Created by xiaoxiangxu on 2016/11/28.
*/
public class MyProxy {
//回车换行
static String rt = "\r\n";
//代理类路径
static String generateProxyFilePath= "D:/workspace/demo/src/main/java/com/jr/demo/proxy/";
//代理文件
static String generateProxyFile= "$Proxy0.java";
public static Object createProxyInstance(ClassLoader loader,Class ints,MyInvocationHandler handler){
Method[] methods = ints.getMethods();
// 1、用流的方式创建JAVA文件;
String proxyClass ="package com.jr.demo.proxy;"+rt
+"import java.lang.reflect.Method;"+rt
+"public class $Proxy0 implements "+ ints.getName()+" {" +rt
+"MyInvocationHandler h;"+rt
+"public $Proxy0(MyInvocationHandler h){"+rt
+"this.h = h ;"+rt+"}"
+ getMethodString(methods,ints)+rt+"}";
// 2、类生成文件;
File f = new File(generateProxyFilePath + generateProxyFile);
try {
FileWriter fileWriter = new FileWriter(f);
fileWriter.write(proxyClass);
fileWriter.flush();
fileWriter.close();
//3、编译JAVA文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(null, null, null);
Iterable units =standardJavaFileManager.getJavaFileObjects(generateProxyFilePath + generateProxyFile);
JavaCompiler.CompilationTask t = compiler.getTask(null,standardJavaFileManager,null,null,null,units);
t.call();
standardJavaFileManager.close();
f.delete();
//4、加载class字节码通过自定义ClassLoader 加载到内存
MyClassLoader myClassLoader = new MyClassLoader(generateProxyFilePath);
try {
Class proxyClass0 = myClassLoader.findClass("$Proxy");
Constructor m =proxyClass0.getConstructor(MyInvocationHandler.class);
Object o = m.newInstance(handler);
return o;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 代理方法织入
* @param methods
* @param intf
* @return
*/
private static String getMethodString(Method[] methods,Class intf){
String proxyMe = "";
for(Method methods1 :methods){
proxyMe += rt+"public void "+ methods1.getName()+"()"+ " throws Throwable {"+rt
+" Method md ="+intf.getName()+".class.getMethod(\""+ methods1.getName()
+"\",new Class[]{});"+rt
+"this.h.invoke(this,md,null);"+rt+"}"+rt;
}
return proxyMe;
}
}
以上为我们自定义的简单版本的代理类,只是简单的对类进行了代理,并没有织入其他代码内容。通过对本类的扩展,我们完全可以实现对被代理类的动态插桩,可以随意在指定的方法体内动态插桩我们的代码或者直接创建一个全新的类。
下面介绍下自定义类加载器:
/**
* Created by xiaoxiangxu on 2016/11/28.
*/
public class MyClassLoader extends ClassLoader {
private File dir;
public MyClassLoader(String path) {
dir = new File(path);
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
if(null!=dir){
File clazzFile = new File(dir,name+".class");
if(clazzFile.exists()){
FileInputStream input = null;
try {
input = new FileInputStream(clazzFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) != -1){
baos.write(buffer,0,len);
}
return defineClass(name,baos.toByteArray(),0,baos.size());
} catch (Exception e) {
e.printStackTrace();
}finally {
if(null!=input){
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return super.findClass(name);
}
}
以上为自定义ClassLoader,当然我们可以提供出多个构造方法,便于指定不同ClassLoader进行加载。自定义ClassLoader关键之处在于我们需要重写父类的findClass方法,通过字节流的方式读取class 字节码文件,通过defineClass方法加载到JVM内存。
下面我们举个简单的例子,看下如何使用我们自定义的动态代理:
使用方式如下:
People people = (People) MyProxy.createProxyInstance(People.class.getClassLoader(), People.class, new MyProxyHandler(new Zhangsan()));
people.eat();
我们用自定义代理代理了People 这个接口实现类Zhangsan的eat()方法,下面看一下代理类做了什么事情:
public class MyProxyHandler<People> implements MyInvocationHandler {
People p = null;
public MyProxyHandler(People p) {
this.p = p;
}
public Object invoke(Object proxy, Method method, Object args) throws Throwable {
before();
method.invoke(p,null);
after();
return null;
}
private void before(){
System.out.println("吃饭之前要洗手");
}
private void after(){
System.out.println("吃饭以后要洗碗");
}
}
在执行eat()方法时,我们的代理在执行eat()方法前后分别调用了befor()、after()方法。