今天复习了一下马士兵老师讲的动态代理,理解稍微深了一些。这边写个博客记录一下。
源码链接:https://github.com/wenrongyao/proxy.git
类别:动态代理分为静态代理和动态代理两种。
实现方式:继承和聚合
1、静态代理
这边比较简单,有兴趣的自己可以把代码拉下来看看。
2、动态代理
功能:可以实现任何类的任何方法的任何代理。(所谓代理简单来说就是在不改变原来的方法的前提下增加逻辑)
工程结构
其中最重要的类就是Proxy。代码上方都有注释,需要了解的知识有,JavaCompiler, java的反射机制。
Proxy类
package com.wry.dynamicproxy;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* @author rongyao wen
* @date 2018/7/29
*/
public class Proxy {
public static Object newInstance(Class inface, InvocationHandler ih) throws Exception{
String rt = "\r\t";
String methodStr= "";
Method methods[] = inface.getMethods();
/**
* 循环生成被代理的方法
*/
for(Method m: methods){
methodStr =
" @Override"+rt +
" public void "+m.getName()+"() {"+rt +
" try{"+rt+
" Method md = "+inface.getName()+".class.getMethod(\""+ m.getName() + "\");"+rt +
" ih.invoke(md);"+rt +
" }catch(Exception e){e.printStackTrace();}"+rt+
" }";
}
/**
* src是根据传入的接口,动态的生成的代理类的源码。
*/
String src =
"package com.wry.dynamicproxy;" + rt +
"import java.lang.reflect.Method; "+rt+
"public class $Proxy implements "+inface.getName()+" {" +rt +
" com.wry.dynamicproxy.InvocationHandler ih;"+rt +
" public $Proxy(InvocationHandler ih) {"+rt +
" this.ih = ih;"+rt +
" }"+rt +
methodStr +
"}";
/**
* 这一部分将源码写入到硬盘上,具体如下。
*/
String fileName = System.getProperty("user.dir") + "/com/wry/dynamicproxy/$Proxy.java";
File file = new File(fileName);
FileWriter fw = new FileWriter(file);
fw.write(src);
fw.flush();
fw.close();
/**
* 这边利用java1.6以后提供的编译接口,编译java文件(相当于运行javac命令)生成class文件
* 可以直径利用cglib直接生成class文件,就可以不用生成java文件)
*/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(fileName);
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
fileManager.close();
/**
* 这一部分将生成的字节码文件加载到内存,利用java的反射机制,生成代理类。
*/
URL urls[] = new URL[]{new URL("file:/" + System.getProperty("user.dir")+"/")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class c = urlClassLoader.loadClass("com.wry.dynamicproxy.$Proxy");
Object obj = c.getConstructor(InvocationHandler.class).newInstance(ih);
return obj;
}
}
下面展示一份由上述代码生成的java文件
package com.wry.dynamicproxy;
import java.lang.reflect.Method;
public class $Proxy implements com.wry.dynamicproxy.Moveable {
com.wry.dynamicproxy.InvocationHandler ih;
public $Proxy(InvocationHandler ih) {
this.ih = ih;
}
@Override
public void moveable() {
try{
Method md = com.wry.dynamicproxy.Moveable.class.getMethod("moveable");
ih.invoke(md);
}catch(Exception e){e.printStackTrace();}
}}
上述就可以在方法加逻辑实现了。那么具体的逻辑实现加什么,这就看你自己需要什么了。实现方式
首先逻辑有个接口叫InvocationHandler,这是一个接口,这个接口在代理在代理类中被赋上了真正的实现。
package com.wry.dynamicproxy;
import java.lang.reflect.Method;
/**
* @author rongyao wen
* @date 2018/7/29
*/
public interface InvocationHandler {
public void invoke(Method m);
}
逻辑实现类,这边以LogInvocationHandler
package com.wry.dynamicproxy;
import java.lang.reflect.Method;
/**
* @author rongyao wen
* @date 2018/7/29
*/
public class LogInvocationHandler implements InvocationHandler {
Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public void invoke(Method md) {
System.out.println("日志记录开始");
try {
md.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("日志记录结束");
}
}
其中target对象是被代理的那个类,invoke中是具体代理逻辑的实现。
被代理类
package com.wry.dynamicproxy;
/**
* @author rongyao wen
* @date 2018/7/29
*/
public class Tank implements Moveable {
@Override
public void moveable() {
System.out.println("Tank start....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Tank stop....");
}
}
测试函数:
package com.wry.dynamicproxy;
/**
* @author rongyao wen
* @date 2018/7/29
*/
public class Client {
public static void main(String args[]) throws Exception {
Tank t = new Tank();
InvocationHandler ih = new LogInvocationHandler(t);
Moveable m = (Moveable) Proxy.newInstance(Moveable.class, ih);
m.moveable();
}
}
结果:在没有修改tank的方法的前提下,加入了日志的处理。
整体调用流程如下