基于Javassist实现JDK的动态代理Proxy

使用

首先复习一下怎么使用动态代理

  1. 定义一个用户接口
  2. 实现一个用户接口的实现类
  3. 实现一个InvocationHandler
  4. 使用Proxy.newProxyInstance创建一个代理即可使用
public interface SayHelloInterface {
    void addMessage(String msg,String from,String to);
    String say();
}

public class SayHelloImpl implements SayHelloInterface {

    private String msg = null;

    @Override
    public void addMessage(String msg, String from, String to) {
        String message = "实现类添加Message "+msg+"from "+from+" to "+to;
        this.msg = message;
        System.out.println(message);
    }

    @Override
    public String say() {
        System.out.println("实现类执行say函数并返回消息");
        return msg;
    }
}

public class SayHelloInvocation implements InvocationHandler {

    private SayHelloInterface target;

    public SayHelloInvocation(SayHelloInterface target) {
        this.target = target;
    }

    public static SayHelloInterface createProxy(SayHelloInterface target){
        return (SayHelloInterface) Proxy.newProxyInstance(target.getClass().getClassLoader()
                ,target.getClass().getInterfaces(),new SayHelloInvocation(target));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理方法前置内容执行");
        Object result= method.invoke(target,args);
        String txt = result == null ? "null" : result.toString();
        System.out.println("代理后置方法内容执行,返回值为"+ txt);
        System.out.println("-------------");

        return null;
    }
}

 	@Test
    public void testJdkProxy(){
        SayHelloInterface say = new SayHelloImpl();
        SayHelloInterface proxy= SayHelloInvocation.createProxy(say);
        proxy.addMessage("message","xiaoming","xiaowang");
        proxy.say();
    }

原理介绍

JDK的动态代理是如何实现的呢?我们跟踪Proxy的源码,大概总结一下其newProxyInstance的流程为:

  1. 记录handler
  2. 在缓存中找代理类Class,如果没有则创建一个并返回代理类Class
  3. 拿到代理类的有参构造器,传入handler进行newInstance并返回

我们先忽略缓存,重点看一下创建代理的流程,也就是ProxyGenerator.generateProxyClass这个方法,太深的源码我们不追,我们看一下它生成的代理文件是什么样的。为了篇幅,将toString()hashCode()方法删去,其实现和equals()是相同的

public final class proxy extends Proxy implements SayHelloInterface {
    private static Method m1;
    private static Method m3;
    private static Method m4;

    public proxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void addMessage(String var1, String var2, String var3) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1, var2, var3});
        } catch (RuntimeException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

    public final String say() throws  {
        try {
            return (String)super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("cn.jwb5.proxy.SayHelloInterface").getMethod("addMessage", Class.forName("java.lang.String"), Class.forName("java.lang.String"), Class.forName("java.lang.String"));
            m4 = Class.forName("cn.jwb5.proxy.SayHelloInterface").getMethod("say");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

我们可以看到,代理类继承了Proxy类,并实现了用户接口。在代理类实现的用户接口的方法中,所有操作均委托给了父类handler.invoke()方法来处理。同时通过反射将target对象和被方法Method通过参数传递了过去,使handler.invoke()能够通过method.invoke(target,args)调用被代理对象的方法。
在其父类Proxy中存在一个protected InvocationHandler h的属性,存着对应的InvocationHandler对象。Proxy在拿到代理类后,会通过反射拿到代理类的有参构造器(继承自Proxyprotected Proxy(InvocationHandler h)),通过有参构造器传入InvocationHandler实例化代理对象并返回。

自己实现

了解了动态代理的具体实现,我们接下来借用javassist来自己写一个简单版的动态代理。这里省略了Object方法的代理以及异常包围块,仅对用户接口方法实现代理。

定义处理器接口InvocationHandler

public interface InvocationHandler {
    Object invoke(Object proxy, Method method,Object[] args);
}

定义Proxy类

这个类的作用是

  1. 维护代理类的缓存
  2. 实例化代理类对象并返回
public class Proxy {

    protected InvocationHandler handler;

    private static Map<String,Class> cache = new ConcurrentHashMap<>();

    public static Object newProxy(ClassLoader loader,Class interfaceClass,InvocationHandler h) throws Exception {
        Class proxyClass = cache.get(interfaceClass.getName());
        if (proxyClass == null){
            proxyClass = ProxyGenerator.generateClass(interfaceClass);
            cache.putIfAbsent(interfaceClass.getName(),proxyClass);
        }
        Object instance = proxyClass.newInstance();
        Method m = instance.getClass().getMethod("setHandler",InvocationHandler.class);
        m.setAccessible(Boolean.TRUE);
        m.invoke(instance,h);
        return instance;
    }

// 这里我使用set的方法注入handler而不是构造器
    public void setHandler(InvocationHandler handler) {
        this.handler = handler;
    }
}

定义ProxyGenerator

这个类的作用是生成代理类并进行加载

public class ProxyGenerator {

    private static volatile AtomicInteger count = new AtomicInteger(0);
    private static String PRE_FIX = "JWB_Proxy&";
    public static Class generateClass(Class interfaceClass) throws NotFoundException, CannotCompileException {
        String proxyClassName = interfaceClass.getName()+PRE_FIX+count.getAndIncrement();

        // 新建一个代理类,继承Proxy类,并实现给定的接口
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass(proxyClassName);
        ctClass.setSuperclass(pool.getCtClass(Proxy.class.getName()));
        ctClass.addInterface(pool.getCtClass(interfaceClass.getName()));
        // 接口的所有方法
        Method[] methods = interfaceClass.getMethods();
        // method方法计数器,用于名称生成,生成顺序按照上面methods中的顺序来,生成m0,m1,m2.....
        int methodCount = 0;

        for (Method m : methods){
            String methodName = m.getName();
            Class returnType = m.getReturnType();
            Parameter[] parameters = m.getParameters();

            // 1. 通过反射拿到用户接口的方法引用,这些引用要在调用invoke方法时传递过去,从而让invoke方法能够去调用target的对应方法
            StringBuilder fieldBuilder = new StringBuilder();
            fieldBuilder.append("private static java.lang.reflect.Method ").append("m").append(methodCount)
                    .append(" = ").append("Class.forName(").append("\"").append(interfaceClass.getName()).append("\"").append(")").append(".")
                    .append("getMethod(").append("\"").append(m.getName()).append("\"").append(",");
            if (parameters.length == 0){
                fieldBuilder.append("null");
            }else {
                fieldBuilder.append("new Class[]{");
                for (Parameter p : parameters){
                    fieldBuilder.append("Class.forName(").append("\"").append(p.getType().getName()).append("\"").append(")").append(",");
                }
                fieldBuilder.delete(fieldBuilder.length()-1,fieldBuilder.length());
                fieldBuilder.append("}");
            }
            fieldBuilder.append(");");

//private static java.lang.reflect.Method m0 = Class.forName("cn.jwb5.proxy.SayHelloInterface").getMethod("say",null);
//private static java.lang.reflect.Method m1 = Class.forName("cn.jwb5.proxy.SayHelloInterface").getMethod("addMessage",new Class[]{Class.forName("java.lang.String"),Class.forName("java.lang.String"),Class.forName("java.lang.String")});
            String fieldTxt = fieldBuilder.toString();
            ctClass.addField(CtField.make(fieldTxt,ctClass));

            //实现接口方法,委托给handler.invoke()去实现

            // 方法括号内的参数的文本
            String methodParamTxt = buildParamTxt(parameters);

            // 第三个方法args的构建
            StringBuilder argsTxtBuilder = new StringBuilder();
            for (int i = 0;i<m.getParameterCount();i++){
                argsTxtBuilder.append("arg").append(i).append(",");
            }
            String argsTxt = argsTxtBuilder.length()>0 ? argsTxtBuilder.delete(argsTxtBuilder.length()-1,argsTxtBuilder.length()).toString() : null;
            argsTxt = argsTxt == null ? "null" : "new Object[]{"+argsTxt+"}";

            // 有返回值要进行返回并强转,没有不返回
            String returnTxt = returnType.getName().equals("void")? " " : " return ("+returnType.getName()+")";

            // method
            StringBuilder methodTxtBuilder = new StringBuilder();
            methodTxtBuilder.append("public ").append(returnType.getName()).append(" ").append(methodName)
                    .append("(").append(methodParamTxt).append(")").append("{")
                    .append(returnTxt).append(" super.handler.invoke").append("(")
                    .append("this").append(",").append("m").append(methodCount).append(",").append(argsTxt).append(");").append("};");
//public java.lang.String say( ){ return (java.lang.String) super.handler.invoke(this,m0,null);};
//public void addMessage(java.lang.String arg0,java.lang.String arg1,java.lang.String arg2){  super.handler.invoke(this,m1,new Object[]{arg0,arg1,arg2});};
            ctClass.addMethod(CtMethod.make(methodTxtBuilder.toString(),ctClass));
            methodCount++;
        }

        // 传递handler
        String setHandlerTxt = "public void setHandler(cn.jwb5.proxy.InvocationHandler h){super.setHandler(h);};";
        ctClass.addMethod(CtMethod.make(setHandlerTxt,ctClass));
        return ctClass.toClass();

    }

    // java.lang.String arg0,java.lang.String arg1
    private static String buildParamTxt(Parameter[] parameters){
        if (parameters.length == 0) return " ";
        StringBuilder builder = new StringBuilder();
        int argCount = 0;
        for (Parameter p : parameters){
            builder.append(p.getType().getName()).append(" ").append("arg").append(argCount++).append(",");
        }
        return builder.toString().substring(0,builder.length()-1);
    }

测试

public interface SayHelloInterface {
    void addMessage(String msg,String from,String to);
    String say();
}

public class SayHelloImpl implements SayHelloInterface {

    private String msg = null;

    @Override
    public void addMessage(String msg, String from, String to) {
        String message = "实现类添加Message "+msg+"from "+from+" to "+to;
        this.msg = message;
        System.out.println(message);
    }

    @Override
    public String say() {
        System.out.println("实现类执行say函数并返回消息");
        return msg;
    }
}

public class MyInvocationImpl implements InvocationHandler {

    private SayHelloInterface target;

    public MyInvocationImpl(SayHelloInterface target) {
        this.target = target;
    }

@Override
public Object invoke(Object proxy, Method method, Object[] args) {
        try {
            System.out.println("代理方法前置内容执行");
            Object result= null;
            result = method.invoke(target,args);
            String txt = result == null ? "null" : result.toString();
            System.out.println("代理后置方法内容执行,返回值为"+ txt);
            System.out.println("-------------");
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

  @Test
  public void test() throws Exception {
        SayHelloInterface target = new SayHelloImpl();
        SayHelloInterface proxy = (SayHelloInterface) Proxy.newProxy(target.getClass().getClassLoader(),
                SayHelloInterface.class,new MyInvocationImpl(target));
        proxy.addMessage("message","my","you");
        proxy.say();
    }

效果

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值