自己实现Java 动态代理 Proxy

jdk Proxy的源码解析在下面这篇文章。建议看完在看今天的自己实现,否则可能会看不懂。

Java 动态代理 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 还是很强大的,这篇文章,我收录了详细的使用文档

强烈推荐一套Java进阶博客,都是干货,走向架构师不是梦!

Java架构师修炼

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值