Javassist实现动态代理

介绍

Javassist 也是一个字节码框架,和其他字节码框架不同的是,它提供了两种层级的API,源层级和字节码层级,源层级不需要对字节码规则了解太多就可以操作。Hibernate的懒加载就使用到了Javassist。官网

使用Javassist提供的动态代理接口实现

maven依赖

<dependency>
  <groupId>org.javassist</groupId>
  <artifactId>javassist</artifactId>
  <version>3.27.0-GA</version>
</dependency>

代理接口

/**
 * 可以唱歌的
 */
public interface Singable {
  /**
   * 唱歌
   */
  void sing();
}

被代理类

/**
 * 歌手
 */
public class Singer implements Singable {
  @Override
  public void sing() {
    System.out.println("I am singing...");
  }
}

创建动态代理

public class Client {

  public static void main(String[] args) throws Exception {
    Singable proxy = createJavassistDynamicProxy();
    proxy.sing();
  }

  private static Singable createJavassistDynamicProxy()
      throws Exception {
    ProxyFactory proxyFactory = new ProxyFactory();
// 设置实现的接口
    proxyFactory.setInterfaces(new Class[]{Singable.class});
    Class<?> proxyClass = proxyFactory.createClass();
    Singable javassistProxy = (Singable) proxyClass.getDeclaredConstructor().newInstance();
    ((ProxyObject) javassistProxy).setHandler(new JavassistInterceptor(new Singer()));
    return javassistProxy;
  }

  private static class JavassistInterceptor implements MethodHandler {

    // 被代理对象
    private Object delegate;

    private JavassistInterceptor(Object delegate) {
      this.delegate = delegate;
    }

    /**
     * @param self 创建的代理对象
     * @param m 被代理方法
     * @param proceed 如果代理接口,此参数为null,如果代理类,此参数为父类的方法
     * @param args 方法参数
     */
    public Object invoke(Object self, Method m, Method proceed,
        Object[] args) throws Throwable {
      System.out.println("javassist proxy before sing");
      Object ret = m.invoke(delegate, args);
      System.out.println("javassist proxy after sing");
      return ret;
    }
  }
}

和JDK的动态代理创建方式类似,但Javassist也可以代理类。

public class Client {

  public static void main(String[] args) throws Exception {
    Singable proxy = createJavassistDynamicProxy();
    proxy.sing();
  }

  private static Singable createJavassistDynamicProxy()
      throws Exception {
    ProxyFactory proxyFactory = new ProxyFactory();
// 设置父类
    proxyFactory.setSuperclass(Singer.class);
    Class<?> proxyClass = proxyFactory.createClass();
    Singable javassistProxy = (Singable) proxyClass.getDeclaredConstructor().newInstance();
    ((ProxyObject) javassistProxy).setHandler(new JavassistInterceptor());
    return javassistProxy;
  }

  private static class JavassistInterceptor implements MethodHandler {

    /**
     * @param self 创建的代理对象
     * @param m 被代理方法
     * @param proceed 如果代理接口,此参数为null,如果代理类,此参数为父类的方法
     * @param args 方法参数
     */
    public Object invoke(Object self, Method m, Method proceed,
        Object[] args) throws Throwable {
      System.out.println("javassist proxy before sing");
// 调用父类的sing方法
      Object ret = proceed.invoke(self, args);
      System.out.println("javassist proxy after sing");
      return ret;
    }
  }
}

Javassist创建的代理类反编译之后为

public class Singer_$$_jvst3e4_0 extends Singer
    implements ProxyObject
{

    public final Object _d0clone()
        throws CloneNotSupportedException
    {
        return super.clone();
    }

    protected final Object clone()
        throws CloneNotSupportedException
    {
        Method amethod[] = _methods_;
        return (Object)handler.invoke(this, amethod[0], amethod[1], new Object[0]);
    }

    public final boolean _d1equals(Object obj)
    {
        return super.equals(obj);
    }

    public final boolean equals(Object obj)
    {
        Method amethod[] = _methods_;
        return ((Boolean)handler.invoke(this, amethod[2], amethod[3], new Object[] {
            obj
        })).booleanValue();
    }

    public final void _d2finalize()
        throws Throwable
    {
        super.finalize();
    }

    protected final void finalize()
        throws Throwable
    {
        Method amethod[] = _methods_;
        handler.invoke(this, amethod[4], amethod[5], new Object[0]);
    }

    public final int _d4hashCode()
    {
        return super.hashCode();
    }

    public final int hashCode()
    {
        Method amethod[] = _methods_;
        return ((Integer)handler.invoke(this, amethod[8], amethod[9], new Object[0])).intValue();
    }

// 调用父类方法
    public final void _d7sing()
    {
        super.sing();
    }

    public final void sing()
    {
        Method amethod[] = _methods_;
// amethod[14]为sing方法,amethod[15]为_d7sing方法,可以看静态代码块的初始化
        handler.invoke(this, amethod[14], amethod[15], new Object[0]);
    }

    public final String _d8toString()
    {
        return super.toString();
    }

    public final String toString()
    {
        Method amethod[] = _methods_;
        return (String)handler.invoke(this, amethod[16], amethod[17], new Object[0]);
    }

    public void setHandler(MethodHandler methodhandler)
    {
        handler = methodhandler;
    }

    public MethodHandler getHandler()
    {
        return handler;
    }

    Object writeReplace()
        throws ObjectStreamException
    {
        return RuntimeSupport.makeSerializedProxy(this);
    }

    private MethodHandler handler;
    public static byte _filter_signature[];
    public static final long serialVersionUID = -1L;
    private static Method _methods_[];

    static 
        throws ClassNotFoundException
    {
        Method amethod[] = new Method[24];
        Class class1 = Class.forName("com.imooc.sourcecode.java.dynamicproxy.javassist.test3.Singer_$$_jvst3e4_0");
        RuntimeSupport.find2Methods(class1, "clone", "_d0clone", 0, "()Ljava/lang/Object;", amethod);
        RuntimeSupport.find2Methods(class1, "equals", "_d1equals", 2, "(Ljava/lang/Object;)Z", amethod);
        RuntimeSupport.find2Methods(class1, "finalize", "_d2finalize", 4, "()V", amethod);
        RuntimeSupport.find2Methods(class1, "hashCode", "_d4hashCode", 8, "()I", amethod);
// 下标14赋值为sing方法,15赋值为_d7sing方法
        RuntimeSupport.find2Methods(class1, "sing", "_d7sing", 14, "()V", amethod);
        RuntimeSupport.find2Methods(class1, "toString", "_d8toString", 16, "()Ljava/lang/String;", amethod);
        _methods_ = amethod;
    }

    public Singer_$$_jvst3e4_0()
    {
        handler = RuntimeSupport.default_interceptor;
        super();
    }
}

看一下RuntimeSupport的find2Methods方法

/**
     * Finds two methods specified by the parameters and stores them
     * into the given array.
     *
     * @throws RuntimeException     if the methods are not found.
     * @see javassist.util.proxy.ProxyFactory
     */
    public static void find2Methods(Class<?> clazz, String superMethod,
                                    String thisMethod, int index,
                                    String desc, java.lang.reflect.Method[] methods)
    {
        methods[index + 1] = thisMethod == null ? null
                                                : findMethod(clazz, thisMethod, desc);
        methods[index] = findSuperClassMethod(clazz, superMethod, desc);
    }

使用Javassist提供的字节码API实现

代理接口和被代理类同上

public class Client {

  public static void main(String[] args) throws Exception {
    Singable proxy = createJavassistBytecodeDynamicProxy(new Singer());
    proxy.sing();
  }

  private static Singable createJavassistBytecodeDynamicProxy(Singable delegate) throws Exception {
    ClassPool mPool = new ClassPool(true);
    CtClass mCtc = mPool.makeClass(Singable.class.getName() + "JavaassistProxy");
    mCtc.addInterface(mPool.get(Singable.class.getName()));
    mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
    mCtc.addField(CtField.make("public " + Singable.class.getName() + " delegate;", mCtc));
    String src = "public void sing() { "
        + "System.out.println(\"javassist bytecode proxy before sing\");"
        + "delegate.sing();"
        + "System.out.println(\"javassist bytecode proxy after sing\"); "
        + "}";
    mCtc.addMethod(CtNewMethod.make(src, mCtc));
    Class<?> pc = mCtc.toClass();
    Singable bytecodeProxy = (Singable) pc.getDeclaredConstructor().newInstance();
    Field filed = bytecodeProxy.getClass().getField("delegate");
    filed.set(bytecodeProxy, delegate);
    return bytecodeProxy;
  }

}

Javassist可以直接拼接java源码生成字节码,这是比ASM易用的地方,但也会造成一定的性能损失。

生成的代理类反编译为

public class SingableJavaassistProxy
    implements Singable
{

    public SingableJavaassistProxy()
    {
    }

    public void sing()
    {
        System.out.println("javassist bytecode proxy before sing");
        _flddelegate.sing();
        System.out.println("javassist bytecode proxy after sing");
    }

    public Singable _flddelegate;
}

可以看到使用字节码生成的类相比代理工厂(JDK,CGLIB,Javassist的ProxyFactory)生成的类要小很多,所以速度也会更快。

posted @ 2020-08-23 09:06  strongmore  阅读(1795)  评论(0编辑  收藏  举报