通过jdk和cglib的动态代理计算执行方法的时间,用于测试List集合有参与无参的性能差异

参考文章:https://www.cnblogs.com/Pjson/p/8783284.html

JDK动态代理步骤

1.创建一个实现InvocationHandler接口的类,它必须实现invoke()方法
2. 创建被代理的类及接口
3. 调用Proxy的静态方法,创建一个代理类
4.通过代理调用方法

根据以上步骤

1,创建一个工厂类,思路:通过该工厂类将一个目标对象生成目标代理类,这里通过proxy对象的newProxyInstance的方法来生成代理对象

JDK中生成代理对象的AP代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

package com.skindow.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by Administrator on 2019/7/30.
 */
public class MyJDKProxyFactory {

    /**
     * 维护一个目标对象
     */
    private Object target;
    public MyJDKProxyFactory (Object target)
    {
        this.target = target;
    }

    /**Proxy.newProxyInstance注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
     ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
     Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
     InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
     * @return 通过目标对象创建代理对象
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("开始记录时间");
                long begin = System.currentTimeMillis();
                Object invoke = method.invoke(target, args);
                long end = System.currentTimeMillis();
                System.out.println(target.getClass().getName() + " 总耗时:" + (end - begin ) + "ms");
                return invoke;
            }
        });
    }
}

2,因为jdk动态代理是通过接口来调用的,所以这里我们新建一个接口类
package com.skindow.forInterface;

/**
 * Created by Administrator on 2019/7/30.
 */
public interface For {
    public void doSomething();
}

3,新建类来实现该接口类
package com.skindow.forInterface;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2019/7/30.
 */
public class ForImpl_forTest_1 implements For{
    private Integer size;
    public ForImpl_forTest_1(Integer size)
    {
        this.size = size;
    }
    @Override
    public void doSomething() {
        List<Integer> list = new ArrayList<>();
        for (Integer i = 0; i < size; i++)
        {
            list.add(i);
        }
    }
}

package com.skindow.forInterface;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2019/7/30.
 */
public class ForImpl_forTest_2 implements For{
    private Integer size;
    public ForImpl_forTest_2(Integer size)
    {
        this.size = size;
    }
    @Override
    public void doSomething() {
        List<Integer> list = new ArrayList<>(size);
        for (Integer i = 0; i < size; i++)
        {
            list.add(i);
        }
    }
}

4,测试
package com.skindow.mainTest;

import com.skindow.forInterface.For;
import com.skindow.forInterface.ForImpl_forTest_1;
import com.skindow.forInterface.ForImpl_forTest_2;
import com.skindow.proxy.MyJDKProxyFactory;

/**
 * Created by Administrator on 2019/7/30.
 */
public class ForTestMain {
    public static void main(String[] args)
    {
        Integer size = 1000000;
        // 目标对象
        For for_1 = new ForImpl_forTest_1(size);
        // 给目标对象创建代理对象
        for_1 =(For) new MyJDKProxyFactory(for_1).getProxy();
        //通过代理对象执行目标对象的方法
        for_1.doSomething();
        // 目标对象
        For for_2 = new ForImpl_forTest_2(size);
        // 给目标对象创建代理对象
        for_1 =(For) new MyJDKProxyFactory(for_2).getProxy();
        //通过代理对象执行目标对象的方法
        for_1.doSomething();
    }
}

测试结果如下

在这里插入图片描述

总结如下
  • 通过控制台的打印可以看到有参和无参的性能差异,相差接近90ms 这里需要着重讲一下
  • 在测试的时候发生了以下错误,错误原因是我并没有用接口去调用而是使用实现类去调用,结果导致了以下错误。切忌一定要用接口去调用

在这里插入图片描述
错误代码如下

package com.skindow.mainTest;

import com.skindow.forInterface.ForImpl_forTest_1;
import com.skindow.proxy.MyJDKProxyFactory;

/**
 * Created by Administrator on 2019/7/30.
 */
public class ForTestMain {
    public static void main(String[] args)
    {
        // 目标对象
        ForImpl_forTest_1 forTest_1 = new ForImpl_forTest_1(Integer.MAX_VALUE);
        // 给目标对象创建代理对象
        forTest_1 =(ForImpl_forTest_1) new MyJDKProxyFactory(forTest_1).getProxy();
        //通过代理对象执行目标对象的方法
        forTest_1.doSomething();
    }
}

cglib动态代理

- Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.

  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring
    AOP和synaop,为他们提供方法的interception(拦截)

  • Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

1,创建一个工厂类,通过这个工厂类将目标对象来生成对应的代理类,该工厂类必须实现MethodInterceptor接口

package com.skindow.proxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * Created by Administrator on 2019/7/30.
 */
public class MyCglibProxyFactory implements MethodInterceptor {
    private Object target;
    public MyCglibProxyFactory (Object target)
    {
        this.target = target;
    }

    //给目标对象创建一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();

    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始记录时间");
        long begin = System.currentTimeMillis();
        //执行目标对象的方法
        Object returnValue = method.invoke(target, objects);
        long end = System.currentTimeMillis();
        System.out.println(target.getClass().getName() + " 总耗时:" + (end - begin ) + "ms");
        return returnValue;
    }
}

2,测试

ForImpl_forTest_1 forTest1 = new ForImpl_forTest_1(size);
        forTest1 = (ForImpl_forTest_1)new MyCglibProxyFactory(forTest1).getProxyInstance();
        forTest1.doSomething();

3,测试结果

在这里插入图片描述

总结

  • 相比较jdk动态代理,不用接口去调用实现类的方法,而直接以实现类去调用

  • 在期间遇到了一个错误
    Exception in thread “main” java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
    在这里插入图片描述
    解决方案是在ForImpl_forTest_1类种添加无参的构造函数如下
    在这里插入图片描述

  • **从以下控制台打印的日志能看出cglib比jdk动态代理的性能更高在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值