Java每日一点:为什么Jdk生成的代理类依赖接口,而cglib缺不需要呢?

 

前言:我们每次面试都要问到的一个问题:spring aop 的底层实现原理是什么,我们当然都知道是动态代理,然后还知道动态代理分为JDK动态代理和Cglib动态代理,并且还知道JDK依赖接口,Cglib不依赖,面试官一问为什么,卧槽,很尴尬不知道,下面我们就直接用例子来演示一下为什么?

JDK代理类的生产与结构

jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。

首先有个“演员”接口类,有演戏功能:

public interface Actor {
    /**
     * 演戏
     */
    void acting();
​
}

再来一个演员实现类

public class ZhengShuang implements Actor {
    @Override
    public void acting() {
        System.out.println("开始表演!");
        System.out.println("台词功底十分强悍,上来就123456789");
        System.out.println("表演完了!");
    }
}

演员演戏演戏前后需要联系演员排期以及结账,由于这些事情比较繁琐,演员自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的演员接口,无法对外服务,它仅仅是一个wrapper。

public class ActorProxy 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("找到经纪人联系演员准备开始演戏");
​
        Object result = method.invoke(target, args);
​
        System.out.println("付钱给演员经纪人,共计支付1.2亿元");
        return result;
    }
​
    public Object creatProxyObj() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

上述例子中,方法creatProxyObj返回的对象才是我们的代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个creatProxyObj方法不一定非得在我们的ActorProxy类中,往往放在一个工厂类中。上述代理的代码使用过程一般如下:

public class Test {
    public static void main(String[] args) {
        // 创建一个演员对象
        Actor zs = new ZhengShuang();
        // 创建一个InvocationHandler对象
        ActorProxy proxy = new ActorProxy();
        // 将演员对象传递到InvocationHandler对象中去
        proxy.setTarget(zs);
        // 调用creatProxyObj生成代理类
        Object proxyObj = proxy.creatProxyObj();
        // 将代理类转换成我们的演员类
        Actor actor = (Actor)proxyObj;
        // 执行对应方法
        actor.acting();
    }
}

看下输出吧

找到经纪人联系演员准备开始演戏 开始表演! 台词功底十分强悍,上来就123456789 表演完了! 付钱给演员经纪人,共计支付1.2亿元

但是为什么依赖借口呢,下面我们就来看一看这个creatProxyObj生成代理类是什么鬼东西,我们打个断点研究研究

 

可以看到代理类原来还穿了一层衣服,继承了Proxy,由于Java不支持多继承,所以,如果使用JDK动态代理来生成代理类的话不能使用普通的类来生成,必须要使用接口来生成。

Cglib代理类的生产与结构

使用cglib实现代理感觉的简单多了,下边直接上代码

public class CglibProxy implements MethodInterceptor {
​
    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 这里可以做增强
        System.out.println("找到经纪人联系演员准备开始演戏");
​
        Object result = methodProxy.invokeSuper(object, objects);
​
        System.out.println("付钱给演员经纪人,共计支付1.2亿元");
​
        return result;
    }
​
    /**
     * 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
     *
     * @param clazz
     * @return
     */
    public Object creatProxyObj(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
}

紧接着我们生成一个爽子类的代理类测试一下吧

public class Test {
    public static void main(String[] args) {
        // 创建一个MethodInterceptor对象
        CglibProxy proxy = new CglibProxy();
        // 调用creatProxyObj生成ZhengShuang的代理类
        Object shuagnZiProxyObj = proxy.creatProxyObj(ZhengShuang.class);
        // 类型转换
        Actor shuangZi = (Actor)shuagnZiProxyObj;
        shuangZi.acting();
    }
}

看下输出吧

找到经纪人联系演员准备开始演戏 开始表演! 台词功底十分强悍,上来就123456789 表演完了! 付钱给演员经纪人,共计支付1.2亿元

可以看到们对爽子直接生成了代理类,那我们也去看看爽子类的结构吧,debug

可以看到这个生成的代理类是继承我们被代理类的,所以就不会又必须实现接口这个限制了。

总结:

SpringAop实现代理类的方法有两种,JDK动态代理和Cglib动态代理,JDK针对实现接口的类进行代理,Cglib则针对所有类都可以使用。

JDK动态代理针对接口的限制是因为JDK动态代理生成的类是实现了Proxy的,由于Java不能多继承,所以不能使用JDK动态代理去处理没有实现接口的类。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值