zoukankan      html  css  js  c++  java
  • 动态代理实现的比较

    有的时候我们需要拦截方法的调用,来执行我们自己的逻辑。如果你不是Java EE CDI规范的拥趸,也不想使用类似aspectj的AOP框架,你还可以用别的简单有效的方式。

    JDK1.5引入了java.lang.reflect.Proxy,你可以通过它给一个指定的接口创建动态代理。程序每次调用动态代理类的时候,都会调用到代理类的InvocationHandler。因此在框架或者库的代码执行之前,你可以动态控制应该执行什么代码。

    另外一个JDK代理的实现是字节码框架,比如javassist或者cglib,它们都提供了类似的功能。你可以通过子类来决定你应该调用父类的哪个方法,或者你想要拦截哪个方法。这当然需要引入一个第三方库到你的工程依赖中,并且可能需要时不时的更新下版本,而使用JDK的代理实现,则已经包含在Java的运行环境里面了。

    我们来进一步的研究下这三种实现方式。为了比较javassist/cglib和JDK的代理实现,我们需要一个接口以及一个实现,因为JDK的实现机制只支持接口方式,不能通过子类来完成:

    public interface IExample {
        void setName(String name);
    }
     
    public class Example implements IExample {
        private String name;
     
        public String getName() {
            return name;
        }
     
        public void setName(String name) {
            this.name = name;
        }
    }
    

    为了能把代理对象上的方法调用委托给实际的对象,我们先创建了一个Example对象的实例,放到一个final变量里,然后通过InvocationHandler来调用它。

    final Example example = new Example();
    InvocationHandler invocationHandler = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(example, args);
        }
    };
    return (IExample) Proxy.newProxyInstance(JavaProxy.class.getClassLoader(), new Class[]{IExample.class}, invocationHandler);
    

    从这个代码示例里面可以看到,创建一个代理非常简单:调用newProxyInstance静态方法,并且提供一个类加载器,一个代理可能实现的接口的数组,还有一个InvocationHandler的实现。为了方便演示,我们这个实现只是把调用转给了前面刚创建的一个Example实例。在实际过程中,你可以根据调用 方法名和参数来执行一些更复杂的操作。

    现在我们来看下javassist的实现:

    ProxyFactory factory = new ProxyFactory();
    factory.setSuperclass(Example.class);
    Class aClass = factory.createClass();
    final IExample newInstance = (IExample) aClass.newInstance();
    MethodHandler methodHandler = new MethodHandler() {
        @Override
        public Object invoke(Object self, Method overridden, Method proceed, Object[] args) throws Throwable {
            return proceed.invoke(newInstance, args);
        }
    };
    ((ProxyObject)newInstance).setHandler(methodHandler);
    return newInstance;
    

    这里我们创建了一个ProxyFactory,它需要知道哪个类需要生成子类。然后我们用这个代理工厂生成了一个Class对象,这个对象是可以无限使用的。这里的MethodHandler的作用和InvocationHandler差不多,当实例的方法被调用的时候,它们也会对应的被调起。这里我们还是只调用了一下Example的实例。

    最后一个但同样也很重要的,我们来看下cglib版的实现:

    final Example example = new Example();
    IExample exampleProxy = (IExample) Enhancer.create(IExample.class, new MethodInterceptor() {
        @Override
        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            return method.invoke(example, args);
        }
    });
    return exampleProxy;
    

    在cglib里面,我们用Enhancer来实现一个指定的接口,它需要传入一个MethodInterceptor的实例。这个回调方法的实现跟javassist的例子差不多,也是通过反射的API调用了Example的一个实现。

    现在我们看到了三个版本的不同实现,我们来分析下它们运行时的表现。这里写了一个简单的单元测试,用来衡量这三种实现的执行时间:

    @Test
    public void testPerformance() {
        final IExample example = JavaProxy.createExample();
        long measure = TimeMeasurement.measure(new TimeMeasurement.Execution() {
            @Override
            public void execute() {
                for (long i = 0; i < JavassistProxyTest.NUMBER_OF_ITERATIONS; i++) {
                    example.setName("name");
                }
            }
        });
        System.out.println("Proxy: "+measure+" ms");
    }
    

    我们对这个操作重复执行多次,来对JVM进行压测,以便Hotspot编译器对热点路径生成本地代码。下面的图表是这三种实现的平均运行时间: 

    为了更好的评估各个代理实现的效率,我们也列出了Example对象的方法正常调用的执行时间(“No Proxy”)。首先我们可以确定的是,代理的实现和原生实现相比,效率至少慢了十倍。同时我们还注意到几种代理实现的差别。令人意外的是JDK的代理类和cglib的实现一样快。只有javassist的版本,和cglib想比,差了大约一倍。

    结论:运行时代理非常简单易用,你可以使用不同的实现方式。JDK的代理只支持接口而javassist和cglib允许你通过子类来实现。它们在运行时的性能表现比标准实现要慢十倍,同时这三种实现的表现也各不相同。

    原文链接

    转载 http://it.deepinmind.com/java/2014/02/24/%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E5%AE%9E%E7%8E%B0%E7%9A%84%E6%AF%94%E8%BE%83.html

  • 相关阅读:
    自定义可下拉刷新列表
    在listview里面的checkbox被选中或取消
    [转]Android中音乐文件的信息详解【安卓源码解析二】
    service 和broadcastreceiver的简略小结
    闹铃
    hdu 2112赤裸裸的最短路
    hdu 2177错题,很水的测试数据
    hdu 2184模拟
    hdu1150最小点集覆盖
    hdu 3746KMP的应用
  • 原文地址:https://www.cnblogs.com/chenying99/p/3597630.html
Copyright © 2011-2022 走看看