Javassist实现动态代理

摘要:
Javassist也是一个字节码框架。与其他字节码框架不同,它提供了两个级别的API,源代码级别和字节码级别。源代码级别不需要了解太多字节码规则即可操作。Hibernate使用Javassist进行延迟加载。官方网站使用Javassist提供的动态代理接口来实现maven对<dependency><groupId>org的依赖。javaassistantjavassist3.27.0-GA代理接口/*****/publicinterfaceSingable{/***Singing*/voiding();}委托类/***Singer*/publicclassSingerimplementsSingable{@Overridepublicvoiding(){System.out.println;}}创建动态代理publicclassClient{publicstatic void mainthrowsException{Singableproxy=createJavassistDynamicProxy();proxy.sing();}PrivatestaticSingablecreateJavassistDynamicProxy()throwsException{ProxyFactoryproxyFactory=newProxyFactory();//设置实现的接口proxyFactory.setInterfaces;Class<?

介绍

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 = "http://t.zoukankan.com/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)生成的类要小很多,所以速度也会更快。

免责声明:文章转载自《Javassist实现动态代理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇threejs 坐标转换tar命令结合find搜索将指定条件的文件进行打包压缩下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ceph ---(ceph简介)

ceph简介: Ceph是一种为优秀的性能、可靠性和可扩展性而设计的统一的、分布式文件系统。ceph 的统一体现在可以提供文件系统、块存储和对象存储,分布式体现在可以动态扩展。在国内一些公司的云环境中,通常会采用 ceph 作为openstack 的唯一后端存储来提高数据转发效率。 Ceph项目最早起源于Sage就读博士期间的工作(最早的成果于2004年发...

Java获取Object属性值

做了一个拦截参数的需求,需要获取普通参数和对象参数 参数是Object类型,Object[] paramValues = pjp.getArgs(); 1.获取普通参数 for(int i=0;i<paramValues.length;i++){ accessToken = paramValues[i].toString()...

Servlet第六篇【Session介绍、API、生命周期、应用】

什么是Session Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session 如果说Cookie是检查用户身上的”通行证“来确认用户的身份,那么Session就是通过检查服务器上的”客户明细表“来确认用...

Mac系统修改Intellij Idea默认JDK版本

Intellij IDEA 默认情况下,使用的jdk的版本是1.6,当第一次启动IDEA的时候,如果系统中未安装jdk,则系统会自动到苹果官网下载jdk安装文件。如果你的系统已经安装了jdk1.7或是更高的版本,同样首次打开IDEA的时候要求你安装苹果官网jdk1.6。 为了免去多余的jdk安装,解决办法如下: 到/Applications下找到Intel...

c# List深度复制

原文:https://www.cnblogs.com/MRRAOBX/articles/6979479.html 由于List之间的相等的话,等于是把List的地址给赋值过去了,赋值后的List变化,会改变原有的List,并没有起到备份原始数据的作用,对于没有嵌套的List,可以采用遍历重新赋值的方法去赋值(传递的是值类型,并非引用类型),若List内的数...

XML文件与实体类的互相转换

一.将XML文件反序列化为实体类对象   1. 通常程序的配置信息都保存在程序或者网站的专门的配置文件中(App.config/web.config)。但是现在为了演示XML序列化和反序列化,将配置信息保存在一个XML文件(config.xml)中,通过反序列化将配置信息读取出来保存到一个单独的类(Config.cs)中。这样如果需要用到配置信息,没必要每...