JDK动态代理和Cglib动态代理的分析

JDK动态代理和Cglib动态代理的分析

前言

在我们学习spring和使用spring的过程中有个至关重要的一个东西,

AOP (Aspect Oriented Programing)

  • 面向切面编程 = Spring动态代理开发
  • 切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建。
  • 切面 = 切入点 + 额外功能

AOP本质上是Spring的一种动态代理机制,在不改变被代理类的核心方法上增强和扩展被代理类的一种方式

然而在spring动态代理中有两种非常重要的代理模式分别是:JDK动态代理和Cglib动态代理.


JDK动态代理

JDK动态代理的特点是创建的代理类要和被代理类实现一样的接口从而在实现同样接口的方法下实现对相应方法的增强。

其中最重要是JDK动态代理的: Proxy.newPorxyInstance() 方法

这个方法因为在lang.lang.reflect.Proxy下 所以可能这就是叫做JDK动态代理的原因吧(如果不对请见谅!!!😓 )

然而我们在创建jdk动态代理的过程中

此方法的三个参数:

 Proxy.newProxyInstance(service.getClass().getClassLoader(), interfaces, new InvocationHandler() {});

接下来是我对三个参数的理解:

第一个为类加载器

首先JVM在创建对象的时候需要先将java文件编译为.class文件然后JVM会给每一个.class文件分配一个类加载器,然后通过类加载器将class文件加载到JVM中,然后又会通过类加载器创建Class对象,然后再创建类的对象.

然而有一个问题,动态代理其实是只需要我们创建被代理类,也就是说我们不会去编写代理类的java文件,因此JVM并不会给该类分配类加载器,所以第一个参数的意义就是我们手动给代理类分配一个类加载器,这个类加载器任意一个已经加载类的类加载器都可以

那么第二个问题就来了既然没写java文件那么如何来的代理类呢?

因为此时spring使用了一种动态字节码技术能在JVM中创建或者添加字节码从而创建JAVA对象,从而完成了对代理类的创建


第二个为所被代理类实现的所有接口的一个数组

在上文我们就说了JDK动态代理中需要代理类和被代理类实现同样的接口,这样也可能是为了迷惑使用者到底是调用的是代理对象的方法还是被代理对象的方法,这样也可能是为了方便加强原因方法的加强

第三个invocationHandler

这个参数是一个接口,这个接口是实现对原本所代理类的方法的加强

 InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("---- proxy Before ----");
                Object obj = method.invoke(userService, args);
                System.out.println("---- proxy After ----");
                return obj;
            }
        };

第一个参数为代理类的对象 第二个参数为对应所代理的类的方法 第三个则为该方法的参数

我们可以通过Method对象来调用对应对象的对应的方法返回值则是对应方法的返回值我们可以在该方法执行的前后添加我们需要加强的功能

整个JDK动态代码例子的代码实现如下:

public class TestJDKProxy {
    public static void main(String[] args) {
        //1.创建原始对象
        UserService service = new UserServiceImpl();
        Class<?>[] interfaces = service.getClass().getInterfaces();
        //2.JDK创建动态代理
        /*
           newProxyInstance的三个参数: 类加载器 ,所代理对象所实现的接口.class , 第三个参数是实现的额外功能
         */
        UserService userServiceProxy =
                (UserService) Proxy.newProxyInstance(service.getClass().getClassLoader(), interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               System.out.println("---- proxy Before ----");
                Object obj = method.invoke(userService, args);
                System.out.println("---- proxy After ----");
                return obj;
            }
        });
        userServiceProxy.login("daitou","666");
    }
}

Cglib动态代理

Cglib动态代理的实现与JDK动态代理不同他们需要继承被代理类,因为继承的特性我们可以很方便的加强代理的方法,因为父亲的东西儿子都有

在此代理类当中我们需要创建一个类的实例

Enhancer enhancer  = new Enhancer();

该对象其实和JDK动态代理的Proxy.newPorxyInstance()很相像

该对象我们需要使用几个方法


         Enhancer.setClassLoader()
         Enhancer.setSuperClass()
         Enhancer.setCallBack() 
         Enhancer.createProxy() 
         

第一个也是一样也是获取一个类加载器 同样也是使用了动态字节码技术创建代理类

第二个就是获取所需代理类的对象 之前我们说过代理类需要继承被代理类

第三个则和JDK代理的第三个参数差不多 这里实现的是MethodInterceptor接口,顾名思义进行方法的拦截,我们在这可以实现对代理对象方法的加强

            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("--- cglib Before ----");
                Object obj = method.invoke(userService, args); 
                System.out.println("--- cglib After ----");
                return obj;
            }
        };
   

第四个方法则是创建对象,我们就得到了被代理类

整个Cglib例子代码实现如下:

public class TestCglib {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        /*
          通过cglib创建动态代理对象
          Enhancer(翻译为增强).create() ---> 代理
         */
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(orderService.getClass().getClassLoader());
        enhancer.setSuperclass(orderService.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("--- cglib Before ----");
                Object obj = method.invoke(userService, args); 
                System.out.println("--- cglib After ----");
            }
        });
        OrderService orderService1 = (OrderService) enhancer.create();
        orderService1.login("daitou","daitou666");

    }
}

总结

我么在创建动态代理的过程中都是为了一个目标对目标方法的加强所以我们都是在对应方法的前后加入我们自己所需要的方法,当然这里是我的说辞实际并不只是这样,但是我们的宗旨是在不改变原有方法的基础上在继续增添我们的代码原有的代码我们只需要是我们自定义的核心代码

JDK和Cglib的区别:

Cglib 通过父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证 2 者方法⼀致,同时在代理类中可以提供新的实现(额外功能+原始方法)。

JDK 通过接口实现的关系创建代理对象可以保证所实现的方法都一致 这样我们可以在实现的基础上再来对原有的方法进行增强

注意:在Spring工厂获取对象的时候我们获取的是该对象的代理对象,有兴趣的话可以自行去探究!

以上就是我对两种代理的总结,如果您有不同的看法,可以在评论区讨论,如果我的说法有错误也希望能得到您的改正!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值