全网最通俗易懂 彻底搞懂 java proxy与 cglib 动态代理区别以及底层原理完全解析 (附带完整案例源码,文章最后附上面试此问题时最完整亮眼回答,建议收藏)

场景说明

java很多框架都应用到了动态代理,比如拦截器,aop 等
个人开发的时候有很多功能也可以用到动态代理,比如现有代码需要加统一拦截,比如请求日志,或者校验敏感字,参数正确性等
这时候动态代理实现起来简单方便,那么都有哪些动态代理实现方式以及都有哪些区别呢,各个方式底层原理又是什么
本文主要针对这些问题进行介绍


动态代理实现方式

java proxy 方式

使用案例如下:
首先准备一个接口和一个实现类

interface  IStudent {
    
    
        Integer getId();

        String getName();


    }


    public class Student implements IStudent {
    
    
        private Integer id = null;
        private String name = null;

        public Student(){
    
    }
        public Student(Integer id, String name) {
    
    
            this.id = id;
            this.name = name;
        }

        public Integer getId() {
    
    
            return id;
        }


        public String getName() {
    
    
            return name;
        }

        @Override
        public String toString() {
    
    
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }

    }

测试方法代码

@Test
    public void javaProxyTest(){
    
    

        Student student = new Student(1,"小明");
        IStudent iStudent = (IStudent) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(),( proxy,  method,args)->{
    
    

            System.out.println("before method "+method.getName()+" invoke");
            Object result = method.invoke(student,args);
            System.out.println("after method "+method.getName()+" invoke");

            return result;
        });
        System.out.println(iStudent.getId()+":"+iStudent.getName());
    }

执行结果如下:
在这里插入图片描述

cglib 方式

使用如下:

 @Test
    public void cglibTest(){
    
    
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Student.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
    
    
            System.out.println("before method "+method.getName()+" invoke");
            Object result = methodProxy.invokeSuper(o,objects);
            System.out.println("after method "+method.getName()+" invoke");
            return result;
        });
        Student student = (Student) enhancer.create(new Class[]{
    
    Integer.class,String.class},new Object[]{
    
    1,"小明"});
        System.out.println(student.getId()+":"+student.getName());

    }

执行结果:
在这里插入图片描述

java proxy与 cglib 原理解析

我们我们来看生成代理以及代理类执行方法时流程两方面

proxy

proxy 生成代理类源码解析

在这里插入图片描述

生成代理类class 重要方法
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

ProxyClassFactory是重点类,生成代理
在这里插入图片描述

修改代码将proxy生成的代理类class保存到本地磁盘

我们修改一下代码,将生成的代理类保存到本地磁盘,修改代码如下:
在这里插入图片描述

//添加保存类到本地功能
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", iStudent.getClass().getInterfaces());
        String path = "E:\\code\\proxy_cglib\\Proxy0.class";
        try(FileOutputStream fos = new FileOutputStream(path)) {
    
    
            fos.write(classFile);
            fos.flush();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

我们再运行,看一下保存的代理类
在这里插入图片描述

proxy 直接通过生成的代理类class源码来看运行流程

我们将Proxy1.class拖拽到idea里看一下源码
在这里插入图片描述
内容太多,只看重点部分
在这里插入图片描述
可以看到所有的方法调用内部其实都是调用的我们传入的InvocationHandler

下面是将所有原始类的方法都反射出method

在这里插入图片描述

cglib

cglib 生成代理类源码解析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

上面的方法时重点方法,调用asm 生成代理类

修改代码将cglib生成的代理类class保存到本地磁盘

我们添加一段代码,将cglib生成的代理类保存到本地磁盘,代码如下
在这里插入图片描述

        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\code\\proxy_cglib");

我们再运行一下然后看一下保存的代理类
在这里插入图片描述

我们看到,居然生成了3个代理类文件,我们拖进idea,然后看下都是什么
在这里插入图片描述
在这里插入图片描述

这个是最重要的也就是我们实际的代理类,看看关键部分
在这里插入图片描述

可以看到, 实际上与proxy生成的代理类逻辑差不多,都是有代理的时候直接执行代理类对应的方法,也就是执行我们传入的MethodInterceptor./intercept方法*

在这里插入图片描述

来看看这个的代码
在这里插入图片描述
在这里插入图片描述
再来看看这个生成的类的代码
在这里插入图片描述

cglib生成fastclass 子类作用讲解

为什么cglib要生成这俩类呢,看名字可以看出,表明的是fastdfs,也就是为了加速用的,怎么加速呢? 我们通过上图可以看见,cglib将每个方法都映射成了一个hashcode 对应的数字
类似map的key-value,key是数字,value是方法本身,这样做的目的是什么呢,主要是为了避免使用java的反射执行去执行方法,我们看proxy
最后回调的时候是使用method.invoke去反射执行原始类方法的,但是cglib不想这样,所以就做了一个类似如下这样的调用:
*

switch(方法hashcode)
 case: 方法1 的hashcode
   return 原始类.方法1
 case: 方法2的hashcode
   return 原始类.方法2

这样做是为了加速处理,但其实再jdk1.8之后 ,cglib的速度已经和proxy相比没有优势了

proxy与 cglib 主要区别总结(建议收藏,大家面试可能会用到)

1.proxy 是jdk提供的,生成代理类的代码是底层实现的 cglib是基于asm 字节码生成器生成的,cglib 是基于asm接口显示调用的生成代理类
2.proxy 是代理类是必须基于接口的,cglib 是不强制使用接口进行生成代理类的
3.proxy 的代理类执行方法时InvocationHandler 的method.invoke是通过java反射执行的 cglib 是通过生成了2个fastClass 进行类与方法的映射,内部通过switch(方法hashcode)然后

猜你喜欢

转载自blog.csdn.net/madness1010/article/details/128852425