Java~对比基于JDK和CGLIB实现的动态代理

简述动态代理

  • 想要理解动态代理就得先知道代理模式和静态代理
  • 代理模式顾名思义和我们生活中找代理人差不多, 处理的还是我们的是, 但是让他们帮我们去直接面对去处理问题, 而静态代理是一个代理者只能服务一个对象, 而动态代理就是这个代理者是万能的, 可以代理所有对象

JDK的动态代理

  • 也叫做基于接口的代理模式
  • 是JDK官方提供的一种依据类对象和反射得来的代理对象, 所以这种实现方式生成代理对象快, 效率高, 但是要求也高, 被代理被必须是实现接口, 生成代理对象也是基于接口的, 所以JDK实现动态代理是基于接口的, 不是很灵活, 第二是调用过程有性能问题,因为是通过反射来实现调用的,所以比正常的直接调用来得慢,并且通过生成类文件也会多消耗部分方法区空间,可能引起Full GC。
  • 常见的方法一个是:Proxy类中的newProxyInstance方法得到代理对象
  • 还有一个是: InvocationHandler接口:用于提供增强的代码熔断invoke方法, 如果我们想要调用被代理类中的方法时, 就会进入invoke方法

实现

  • 接口
public interface UserService {
    public String getName(int id);

    public Integer getAge(int id);
}
  • 被代理类
public class UserServiceImpl implements UserService {

    public String getName(int id) {
        System.out.println("------getName------");
        return "cat";
    }

    public Integer getAge(int id) {
        System.out.println("------getAge------");
        return 10;
    }
}
  • 动态代理
public class JDKProxyHandler implements InvocationHandler {
    private Object target;

    /**
     * 绑定委托对象并返回一个代理类
     *
     * @param target   传入被代理对象
     * @return  返回代理对象
     */
    public Object bind(Object target) {
        this.target = target;
        //取得代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("getName".equals(method.getName())) {
            System.out.println("------before " + method.getName() + "------");
            Object result = method.invoke(target, args);
            System.out.println("------after " + method.getName() + "------");
            return result;
        } else {
            return method.invoke(target, args);
        }
    }

}
  • 测试
/**
 * 测试类
 */
public class Client {

    public static void main(String[] args) {
        JDKProxyHandler proxy = new JDKProxyHandler();
        UserService userServiceProxy = (UserService) proxy.bind(new UserServiceImpl());
        System.out.println(userServiceProxy.getName(1));
        System.out.println(userServiceProxy.getAge(1));
    }
}

CGLIB的动态代理

  • JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理, 也就是不能对一个终端类实现CGLIB动态代理
  • CGLIB的实现来源是第三方, 他在生成代理对象上是使用继承来实现子类的方式实现代理, 所以在生成代理对象的过程中是慢于JDK的, 但是在调用方法过程中是直接调用, 比JDK的好, 如果对其进行优化的话就是将这个代理对象进行一个缓存, 这样就避免了代理对象的频繁创建和销毁的消耗, 但是这样还是消耗了一定的内存
  • 核心方法:
  • Enhancer - 生成代理对象的增强器, 所有核心参数都是使用它来, 包括设置被代理对象和得到代理对选哪个
  • MethodInterceptor - 方法拦截器, 顾名思义就是使用者在使用目标的方法时就会被方法拦截器拦截到, 进入到代理对象中的方法中 最后再intercept方法中执行增强后的方法
public class CGLIBProxy implements MethodInterceptor {
    private Object target;

    /**
     * 创建代理对象
     *
     * @param target  被代理对象
     * @return 代理对象
     */
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
        System.out.println(method.getName());
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("++++++after " + methodProxy.getSuperName() + "++++++");

        return result;
    }
}

-测试

/**
 * 测试CGLIB
 */
public class Client {

    public static void main(String[] args) {
        CGLIBProxy cglibProxy = new CGLIBProxy();
        UserService userService = (UserService) cglibProxy.getInstance(new UserServiceImpl());
        userService.getName(1);
        userService.getAge(1);
    }
}

Spring中的动态代理

  • Spring中, JDK和CGLIB都是支持的, 只是他会进行一个判断, 如果被代理对象实现了接口, 就会使用JDK的, 如果没有实现接口就会使用CGLIB的动态代理
  • Spring和Hibernate中的cglib是一个基于ASM的更高层次的自动代码生成工具
  • ASM 是一个 Java 字节码操控框架。可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为(也就是生成的代码可以覆盖原来的类也可以是原始类的子类)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
  • 不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇编指令级别,这要求ASM使用者要对class组织结构和JVM汇编指令有一定的了解。~那些大神太厉害了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值