欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-17 19:58:20
...

使用

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

  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();
    }

效果

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