JDK动态代理和CGLIB动态代理

代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。

使用代理好处还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。代理其他的应用可能还有很多。

上述例子中,类A写死持有B,就是B的静态代理。如果A代理的对象是不确定的,就是动态代理。动态代理目前有两种常见的实现,jdk动态代理和cglib动态代理。

JDK动态代理

jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。首先有个“明星”接口类,有唱、跳两个功能:

public interface Star {
    void sing();
    void dance();
}

然后有明星实现类,“刘德华”

public class Liudehua implements Star {
    @Override
    public void sing() {
        System.out.println("唱歌");
    }
@Override
public void dance() {
    System.out.println("跳舞");
}

}

明星演出前需要有人收钱,由于要准备演出,自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的明星接口,无法对外服务,它仅仅是一个wrapper。

public class JdkProxy implements InvocationHandler {
private  Object target;
public void setTarget(Object target){
    this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("JDK动态代理--收钱");
    Object invoke = method.invoke(target, args);
    return invoke;
}

public Object  createStarProxy(){
    return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}

}

CGLIB动态代理

下面是CGLIB的代理类

public class CglibProxy implements MethodInterceptor {
public Object createCglibProxy(Class<?> tclass){
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(tclass);
    enhancer.setCallback(this);
    return enhancer.create();
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println("CGLIB动态代理--收钱");
    return methodProxy.invokeSuper(o,objects);
}

}

测试类

public class TestProxy {
    public static void main(String[] args) {
        Liudehua liudehua = new Liudehua();
        JdkProxy jdkProxy = new JdkProxy();
        jdkProxy.setTarget(liudehua);
        Star star = (Star)jdkProxy.createStarProxy();
        star.dance();
        star.sing();
    CglibProxy cglibProxy = new CglibProxy();
    Liudehua cglibLiudehua =(Liudehua) cglibProxy.createCglibProxy(liudehua.getClass());
    cglibLiudehua.dance();
    cglibLiudehua.sing();
}

}

控制台输出

JDK动态代理--收钱
跳舞
JDK动态代理--收钱
唱歌
CGLIB动态代理--收钱
跳舞
CGLIB动态代理--收钱
唱歌

JDK动态代理和CGLIB的区别

DK动态代理只能对实现了接口的类生成代理,而不能针对类。

CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final, 对于final类或方法,是无法继承的

使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类

在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐Spring如何选择用

JDK还是CGLIB?

当Bean实现接口时,Spring就会用JDK的动态代理。
当Bean没有实现接口时,Spring使用CGlib是实现。
为什么继承只能使用CGLib,因为JDK代理生成的代理类,默认会继承一个类,由于java是单继承,所以当原始类继承一个类的时候,只能使用CGLib动态代理

总结

如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。

如果目标对象实现了接口,可以强制使用CGLIB实现AOP。

如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换

JDK代理是不需要第三方库支持,只需要JDK环境就可以进行代理,使用条件:

  • 实现InvocationHandler

  • 使用Proxy.newProxyInstance产生代理对象

  • 被代理的对象必须要实现接口

CGLib必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值