jdk Proxy的源码解析在下面这篇文章。建议看完在看今天的自己实现,否则可能会看不懂。
开始动手
InvocationHandler接口,替换jdk的。
package debug_jdk8;
import java.lang.reflect.Method;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
Proxy类,也替换JDK的Proxy。
package debug_jdk8;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
public class Proxy {
// 生成的 class 缓存
private static final Map<String, Class<?>> classMapper = new HashMap<>();
// 类的名称 自增 计数
final static AtomicLong atomicLong = new AtomicLong();
// 利用 Javassist 作为底层实现 , 你也可以用cglib , asm ,javax.tools.JavaCompiler 等
final static ClassGenerator classGenerator = new JavassistClassGenerator();
public static Object newProxyInstance(Class<?> interClazz, InvocationHandler handler) {
// form 缓存
Class<?> proxyClass = classMapper.get(interClazz.getName());
if (proxyClass == null) {
//
proxyClass = apply(interClazz, handler);
}
try {
return proxyClass.getConstructor(InvocationHandler.class).newInstance(handler);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Class<?> apply(Class<?> interClazz, InvocationHandler handler) {
// 接口名 比如 : com.a.b.A
String name = interClazz.getName();
int n = name.lastIndexOf('.');
// 截取包名 : com.a.b
String proxyPkg = ((n == -1) ? "" : name.substring(0, n + 1));
// 生成的代理类名称
String className = "$Proxy" + atomicLong.getAndIncrement();
try {
// 生成 类
return classGenerator.createClass(proxyPkg, className, interClazz);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
代码还是很容易懂的,主要就是2步曲
- 是否命中缓存,命中直接取class,然后通过反射去生成实例。
- 没有命中缓存,去调用ClassGenerator组件去动态生成class。
关键就是我们的ClassGenerator组件。
public interface ClassGenerator {
public Class<?> createClass(String packageName , String className , Class<?> inter) throws Exception ;
}
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
public class JavassistClassGenerator implements ClassGenerator {
private void addField(ClassPool pool , CtClass ctClass , String name , String type , int modifier) throws Exception{
// 添加成员变量
CtField enameField = new CtField(pool.getCtClass("debug_jdk8.InvocationHandler"),name,ctClass);
enameField.setModifiers(Modifier.PRIVATE);
ctClass.addField(enameField);
}
@Override
public Class<?> createClass(String packageName, String className, Class<?> inter) throws Exception {
// ClassPool:CtClass对象的容器
ClassPool pool = ClassPool.getDefault();
// 通过ClassPool生成一个public新类
CtClass ctClass = pool.makeClass(packageName + "."+className);
// 添加接口
ctClass.addInterface(pool.get(inter.getName()));
// 添加 InvocationHandler 接口成员
addField(pool, ctClass, "invocationHandler", "debug_jdk8.InvocationHandler", Modifier.PRIVATE);
//添加构造函数 this.invocationHandler = invocationHandler;
CtConstructor cons = new CtConstructor(new CtClass[]{pool.get("debug_jdk8.InvocationHandler")}, ctClass);
// $0=this / $1,$2,$3... 代表方法参数
cons.setBody("{$0.invocationHandler = $1;}");
ctClass.addConstructor(cons);
//------------ 添加方法-----------------
for(Method method : inter.getDeclaredMethods()){
// 方法名称
String mName = method.getName();
// 方法的类型
CtClass returnType = null ;
if(method.getReturnType() == Void.class){
returnType = CtClass.voidType ;
}else{
returnType = pool.get(method.getReturnType().getName());
}
// 获取参数
Parameter[] ps = method.getParameters() ;
if(ps != null && ps.length > 0){
// 以逗号分隔的参数类型
StringBuilder types = new StringBuilder();
CtClass[] pCtClasses = new CtClass[ps.length];
for(int i=0;i<pCtClasses.length;i++){
// 参数类型
pCtClasses[i] = pool.get(ps[i].getType().getName());
types.append(ps[i].getType().getName()+".class").append(",");
}
types.deleteCharAt(types.length()-1) ; // 删除最后一个字符 ,
// 构造方法 对象
CtMethod ctMethod = new CtMethod(returnType,mName, pCtClasses, ctClass);
// 方法的修饰符
ctMethod.setModifiers(method.getModifiers());
// 添加方法的 代码
ctMethod.setBody("{"
+ "java.lang.reflect.Method curMethod = Class.forName(\"" + inter.getName()+"\").getMethod(\""+mName+"\",new Class[]{"+types+"});"
+ "invocationHandler.invoke(this,curMethod, $args);"
+ "}");
// 添加方法
ctClass.addMethod(ctMethod);
}else{
// 空参数
CtMethod ctMethod = new CtMethod(returnType,mName, null, ctClass);
ctMethod.setModifiers(method.getModifiers());
ctMethod.setBody("{"
+ "java.lang.reflect.Method curMethod = Class.forName(\"" + inter.getName()+"\").getMethod(\""+mName+"\",null);"
+ "this.invocationHandler.invoke(this,curMethod,null);"
+ "}");
ctClass.addMethod(ctMethod);
}
}
Object write = System.getProperties().get("com.hadluo.ProxyGenerator.saveGeneratedFiles");
if(write != null && write.equals("true")){
// 写入文件
System.err.println("write class file");
ctClass.writeFile();
}
return ctClass.toClass();
}
}
其实就是 Javassist 用法,上面代码基于 javassist-3.12.1.GA.jar 。主要是代理接口的每一个方法都要提供实现。实现的代码下面运行后会给出。
Javassist 详细的使用文档
Javassist 文档
客户端使用
public class Main {
// 相亲代理
public static class FindWomanProxy implements InvocationHandler{
// 被代理的对象
private FindWoman woman;
public FindWomanProxy(FindWoman woman) {
this.woman = woman ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在真实的对象执行之前我们可以添加自己的操作
System.out.println("相亲代理正在筛选A照杯的女士");
// 筛选 完成 交给 程序员去消费相亲
return method.invoke(woman, args);
}
}
// 相亲 接口
public static interface FindWoman {
public void find();
}
// java 程序员相亲
public static class JavaFindWoman implements FindWoman{
@Override
public void find() {
System.err.println("java程序员相亲 ");
}
}
public static void main(String[] args) throws IOException {
// 构造相亲代理 , 把java程序员传进去
FindWomanProxy proxy = new FindWomanProxy(new JavaFindWoman());
// 将动态生成的class 输出到文件
System.getProperties().put("com.hadluo.ProxyGenerator.saveGeneratedFiles", "true");
FindWoman findWoman = (FindWoman) Proxy.newProxyInstance(FindWoman.class, proxy);
findWoman.find();
}
}
启动输出
write class file
相亲代理正在筛选A照杯的女士
java程序员相亲
同时在项目目录会生成包名文件夹/class文件
用 jd-gui 工具打开
package aaa;
import debug_jdk8.InvocationHandler;
import debug_jdk8.JavassistClassGenerator;
import java.lang.reflect.Method;
public class $Proxy implements JavassistClassGenerator.FindWoman {
private InvocationHandler invocationHandler;
public $Proxy(InvocationHandler paramInvocationHandler) {
this.invocationHandler = paramInvocationHandler;
}
public void find(int paramInt) {
Method method = Class.forName("debug_jdk8.JavassistClassGenerator$FindWoman").getMethod("find", new Class[] { int.class });
this.invocationHandler.invoke(this, method, new Object[] { paramInt });
}
}
这就是 我们生成的class, 跟jdk 原生的还是有点差异,不过都是同样的思想。都是转向回调invocationHandler的invoke方法。
import debug_jdk8.;
import debug_jdk8.Main;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class Proxy0 extends Proxy implements Main.FindWoman {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void find() {
try {
this.h.invoke(this, m3, null);
return;
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("debug_jdk8.Main$FindWoman").getMethod("find", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
} catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
}
Javassist 还是很强大的,这篇文章,我收录了详细的使用文档