动态代理?JDK Proxy 和 CGLib 有什么区别?

4 篇文章 0 订阅

别告诉我,作为Java开发工程师你没用过Spring;别告诉我,不知道Spring充分运用了AOP思想;别告诉我,不知道AOP思想的实现没有用到动态代理。如果这些问题,全都不知道的话,emmm… 没事,大兄弟,我们一起学习吧,我们还年轻不是嘛!

动态代理是什么?

在这之前不得不提下Java的设计模式-代理模式。代理模式主要是代理类为委托类负责于预消息处理、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

实现代理的方式不止反射机制,还有这里的ASM啊。

JDK代理和CGLib代理的简单使用

拿大家都很熟悉的日志打印举例子。
对象的话,我们采取车和人。车和人都能跑 ,如果路程定位1公里的话,按照正常来说,两对象的速度决然不同。假设有一个人买了一辆可以无人驾驶的车,拿到车的第一天,心情激动的很,就想看看车正常跑个1公里是咋样的,但毕竟新车不太放心,但坐在车内怕怕的。于是选择了和车一起跑个1公里。
现在的需求就是打印出两对象的1公里的实际耗时。

maven CGLIB 是需要额外引入jar包的。

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

Runner.java

public interface Runner {
    void run();
}

Car.java

import java.util.Random;
import java.util.concurrent.TimeUnit;

public class Car implements Runner {
    @Override
    public void run() {
        //模拟时长:
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("===> Car 到达终点 ...");
    }
}

Person.java

import java.util.Random;
import java.util.concurrent.TimeUnit;

public class Person implements Runner {
    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(5) + 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("===> Person 到达终点 ...");
    }
}

JdkProxy.java 代理类

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

public class JdkProxy implements InvocationHandler {

    private Object object;


    public Object getProxy(Object object) {
        this.object = object;
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("=====> JdkProxy 记录开始时间:" + start);
        Object result = method.invoke(object, args);
        long end = System.currentTimeMillis();
        System.out.println("=====> JdkProxy 记录结束时间:" + end);
        System.out.println("=====> JdkProxy 计算出" + method.getName() + "耗时:" + (end - start) + "毫秒");
        return result;
    }
}

CglibProxy.java 代理类

package com.yuki.springsource.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {

    Object object;


    //生成代理对象
    public Object getProxy(Object object) {
        this.object = object;
        Enhancer enhancer = new Enhancer();
        //设置父类为构造时传的类型
        enhancer.setSuperclass(object.getClass());
        //MethodInterceptor 是Callback的父接口
        enhancer.setCallback(this);
        return enhancer.create();
    }

    //方法拦截
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("=====> CglibProxy 记录开始时间:" + start);
        //调用父类的方法
        Object result = methodProxy.invokeSuper(o, objects);
        long end = System.currentTimeMillis();
        System.out.println("=====> CglibProxy 记录结束时间:" + end);
        System.out.println("=====> CglibProxy 计算出" + method.getName() + "耗时:" + (end - start) + "毫秒");
        return result;
    }
}

ProxtTest.java 测试类

public class ProxyTest {
    public static void main(String[] args) {
        JdkProxy jdkProxy = new JdkProxy();
        Runner runner = (Runner) jdkProxy.getProxy(new Car());
        runner.run();

        CglibProxy cglibProxy = new CglibProxy();
        Runner runner1 = (Runner) cglibProxy.getProxy(new Person());
        runner1.run();
    }
}

运行结果:
在这里插入图片描述
神奇的嘞,这日志打印的妙啊。Car 和Person 完全不需要关心如何打印日志的逻辑,一个劲地run就完事了,剩下的交给代理就行啦。
Car.javaPerson.java,Runner.java 充当着委托类CglibProxy.javaJdkProxy.java 充当着代理类的角色。

区别分析

  • JDK代理底层原理是反射机制 重新生成一个代理类 ,在程序运行期间,可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及类对象中包含的属性及方法。
    可以通过看下源码里有类似 xxx.newInstance() 。和Spring IOC 实例对象的方法很像啦。
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述

而 CGLib 是基于 ASM(一个 Java 字节码操作框架)而非反射实现的,它是通过实现子类 super 父类的的方式来完成调用的。在编写代码的过程种也发现 setSupper

  • JDK 是Java自带的,而CGLib 是需要加载第三方类的;
  • JDK Proxy 只能代理继承接口的类,而CGLib 还可以代理普通的class ,除开被关键字 final 修饰类。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值