java中的动态代理(jdk和Cglib)实现详解

 更新时间:2025年04月26日 11:29:33   作者:自律的kkk  
这篇文章主要介绍了java中的动态代理(jdk和Cglib)的相关资料,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧

动态代理

一、JDK动态代理

准备

JDK动态代理只能代理接口,所以我们需要先准备一个接口,以及一个接口的实现类

// 需要代理的接口
public interface InvokeInterface {
    void test1(String test,int[] ints);
    String test3(String test);
}
//代理接口的实现类
public class InvokeBean implements InvokeInterface{
    public void test1(String test,int[] ints){
        System.out.println("代理实现方法1 :"+test + JSON.toJSON(ints));
    }
    public String test3(String test){
        System.out.println("代理实现方法1 :"+test);
        return test;
    }
}

有了代理的目标对象了,我们自然还需要一个处理方法,毕竟我们需要实现额外的逻辑呀,不然代理干嘛?对吧,这个处理方法可不能乱写,需要实现InvocationHandler接口,里面有个invoke方法,就是给我们自己写逻辑的地方,我们就可以对方法执行前、后填充自己的逻辑了

注意: 这个类实例化的时候传了一个Object参数,这个就是上面的实现类,为什么要传进来?因为一个接口是可以有多个实现类的,你不传一个具体的实现类,我怎么知道要执行哪个实现类里面的方法呢?

public class DemoInvokeHandler implements InvocationHandler {
    private Object object;
    public DemoInvokeHandler(Object object){
        this.object=object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 所以可以在这里  方法执行前写逻辑
        System.out.println("方法执行前执行");
        // 这个就是我们原本的方法执行
        Object invoke=method.invoke(object,args);
        // 所以可以在这里  方法执行后写逻辑
        System.out.println("方法执行后执行");
        return invoke;
    }
}

像以上这样呢,整个接口中的方法都会被代理,如果我们只想代理某个方法呢?就需要对方法判断一下如:

这里只是对方法名过滤了,还可以结合注解、参数等过滤

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("test1")){
            // 所以可以在这里  方法执行前写逻辑
            System.out.println("方法执行前执行");
            // 这个就是我们原本的方法执行
            Object invoke=method.invoke(object,args);
            // 所以可以在这里  方法执行后写逻辑
            System.out.println("方法执行后执行");
            return invoke;
        }else {
            return method.invoke(object,args);
        }
    }

使用

以上我们的准备工作就完成了,我们只需要在实际使用中,获取代理对象,执行代理对象即可,通过

Proxy.newProxyInstance获取代理对象,有三个传参:

  • 类加载器
  • 被代理的接口
  • 需要做的处理类(也就是实现了InvocationHandler的类)
// 获取代理对象
InvokeInterface invokeBean = (InvokeInterface) Proxy.newProxyInstance(InvokeBean.class.getClassLoader(),
    new Class[]{InvokeInterface.class}, new DemoInvokeHandler(new InvokeBean()));
invokeBean.test1("test1",new int[]{1,2});
String test3 = invokeBean.test3("test3");
System.out.println(test3);

二、Cglib动态代理

准备

这个我们需要先导入依赖包

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

Cglib和JDK实现起来差不多,但是Cglib代理的是类,所以我们需要先准备个目标类

public class CglibTarget {
    public void test(){
        System.out.println("test 本方法执行");
    }
}

同样的还需要自己的处理方法,这个要实现的接口就不一样。是Callback接口,而该接口有几种:

img

我们只看一下MethodInterceptorInvocationHandler两种,其他毕竟用得少:

InvocationHandler:用法和JDK动态代理一样,参考JDK的即可

MethodInterceptor

注意: 是net.sf.cglib.proxy.MethodInterceptor包下的,同时这个处理类实例化的时候就不需要再传参了

public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 所以可以在这里  方法执行前写逻辑
        System.out.println("方法执行前执行");
        // 这个就是我们原本的方法执行
        Object o1 = methodProxy.invokeSuper(o, objects);
        // 所以可以在这里  方法执行后写逻辑
        System.out.println("方法执行后执行");
        return o1;
    }
}

使用

Cglib的使用同样是需要先生成一个代理对象,一般是靠Enhancer.create() 方法,但该方法有几个重载,我们一个一个介绍

先是最简单也是最常用的

Enhancer.create(Class type, Callback callback)

  • type: 需要代理的类
  • callback:处理方法类
// MethodInterceptor 方式
CglibTarget cglibTarget = (CglibTarget) Enhancer.create(CglibTarget.class, new CglibProxy());
cglibTarget.test();
// InvocationHandler 方式  与JDK一样,不需要类加载器了
Interface cglibTarget = (Interface) Enhancer.create(Interface.class, new CglibProxy(new 实现类));
cglibTarget.test();

结果:

img

####Enhancer.create((Class superclass, Class[] interfaces, Callback callback))

  • superclass: 需要代理的类
  • interfaces:需要实现的接口
  • callback:处理方法类

这个就比上面那个更厉害了,上面那个只是代理原有类里面的方法,这个可以对代理原有的类帮他实现接口,并实现接口逻辑,这等于一个类凭空多了几个方法出来,怎么用?

上述的目标类不需要改,处理方法需要改一下,然后新建一个接口:

// 我们新建一个接口不需要任何实现类  
public interface TestTarget {
    public void test1();
}
// 修改处理方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    // 这里新增对上面接口里面方法的处理,其他的不需要变
    if(method.getName().equals("test1")){
        // 这里可以实现那个接口方法的逻辑 
        System.out.println("这里写实现那个接口的逻辑方法");
        return "我是那个接口返回的";
    }else {
        // 所以可以在这里  方法执行前写逻辑
        System.out.println("方法执行前执行");
        // 这个就是我们原本的方法执行
        Object o1 = methodProxy.invokeSuper(o, objects);
        // 所以可以在这里  方法执行后写逻辑
        System.out.println("方法执行后执行");
        return o1;
    }
}

调用:

// 先用接口生成器 对我们要实现的接口做一个处理
InterfaceMaker interfaceMaker=new InterfaceMaker();
interfaceMaker.add(TestTarget.class);
Class aClass = interfaceMaker.create();
// 然后我们就可以正常的传参调用了 
CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibProxy());
// 类中方法正常调用
o.test();
// 接口方法调用需要用反射 因为毕竟那接口的方法不存在对象方法里面
Method test1 = o.getClass().getMethod("test1");
test1.invoke(o);

结果:

Enhancer. create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks)

  • superclass: 需要代理的类
  • interfaces:需要实现的接口
  • filter: 这个就是用来选择处理类的,毕竟处理类有多个了
  • callbacks:处理方法类数组(处理类可以有多个)

为了演示效果,我们新建一个处理类CglibProxyOther,上面的都不需要变

public class CglibProxyOther implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if(method.getName().equals("test1")){
            System.out.println("新建处理类的逻辑方法");
            return "我是那个新建处理类返回的";
        }else {
            // 所以可以在这里  方法执行前写逻辑
            System.out.println("新建处理类——方法执行前执行");
            // 这个就是我们原本的方法执行
            Object o1 = methodProxy.invokeSuper(o, objects);
            // 所以可以在这里  方法执行后写逻辑
            System.out.println("新建处理类——方法执行后执行");
            return o1;
        }
    }
}

然后新增一个过滤选择器filter

实现CallbackFilter接口即可,返回值是处理类数组的下标(我这里用方面名称来选择,实际还可以用其他)

public class CglibFilter implements CallbackFilter {
    @Override
    public int accept(Method method) {
        // test方法用下标为0的处理类
        if(method.getName().equals("test")){
            return 0;
        }
        // 其他方法用下标为1的处理类
        return 1;
    }
}

调用:

InterfaceMaker interfaceMaker=new InterfaceMaker();
interfaceMaker.add(TestTarget.class);
Class aClass = interfaceMaker.create();
CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibFilter(),new Callback[]{new CglibProxy(),new CglibProxyOther()});
o.test();
Method test1 = o.getClass().getMethod("test1");
test1.invoke(o);

结果: 很明显的看到test1方法走了第二个处理类

img

至此重载方法全介绍完毕!

三、总结对比

  • 拓展性:要是考虑使用角度Cglib无疑是更好的,因为JDK只能代理接口
  • 原理:JDK代理是利用反射机制生成匿名类,调用也是通过反射来调用 Cglib是采用字节码技术,通过修改字节码生成子类
  • 效率:JDK创建对象效率较高,但执行较慢,Cglib创建对象效率低,但执行较快
  • 局限性: Cglib需要额外导入第三方包,而Jdk代理不需要,但JDK局限于代理接口

到底用什么相信大家有选择了,如果没特殊需求,就直接JDK得了,有特殊需求不用Cglib,JDK能满足吗?至于效率,这种效率的差距都体现在一定量往上的程度,没到这个量无需考虑太多!

到此这篇关于java中的动态代理(jdk和Cglib)实现详解的文章就介绍到这了,更多相关java 动态代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • IDEA配置和启动maven项目详细步骤

    IDEA配置和启动maven项目详细步骤

    本文介绍了从SVN检出Web项目并进行Maven化、JDK和项目结构配置、Spring和Tomcat环境搭建的详细步骤,帮助读者顺利完成Java Web项目的开发环境搭建
    2025-10-10
  • idea设置回车换行缩进空格数为4的实现方式

    idea设置回车换行缩进空格数为4的实现方式

    文章主要讲述了解决在设置里回车换行仅缩进两空格的问题,通过将源代码设置从回车两空格改为回车四空格,解决了作者习惯上的不适
    2026-04-04
  • Java中将MultipartFile和File互转的方法详解

    Java中将MultipartFile和File互转的方法详解

    我们在开发过程中经常需要接收前端传来的文件,通常需要处理MultipartFile格式的文件,今天来介绍一下MultipartFile和File怎么进行优雅的互转,需要的朋友可以参考下
    2023-10-10
  • Java代码是如何被CPU狂飙起来的

    Java代码是如何被CPU狂飙起来的

    无论是刚刚入门Java的新手还是已经工作了的老司机,恐怕都不容易把Java代码如何一步步被CPU执行起来这个问题完全讲清楚。本文就带你详细了解Java代码到底是怎么运行起来的。感兴趣的同学可以参考阅读
    2023-03-03
  • Spring框架十一种常见异常的解决方法汇总

    Spring框架十一种常见异常的解决方法汇总

    今天小编就为大家分享一篇关于Spring框架十一种常见异常的解决方法汇总,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03
  • Java 生产者/消费者问题实例详解

    Java 生产者/消费者问题实例详解

    这篇文章主要实例分析了java中生产者消费者问题的方法,需要的朋友可以可以参考下
    2017-04-04
  • 自定义Spring Security的身份验证失败处理方法

    自定义Spring Security的身份验证失败处理方法

    在本篇文章里小编给大家整理了一篇关于自定义Spring Security的身份验证失败的处理方法,有需要的朋友们学习下。
    2019-05-05
  • 一文带你搞懂Java中的递归

    一文带你搞懂Java中的递归

    这篇文章主要为大家详细介绍了Java中的递归的实现以及应用,文中的示例代码讲解详细,对我们学习Java有一定帮助,需要的可以参考一下
    2022-10-10
  • Java 其中翻转字符串的实现方法

    Java 其中翻转字符串的实现方法

    这篇文章主要介绍了Java 其中翻转字符串的实现方法,需要的朋友可以参考下
    2014-02-02
  • 全面分析Java文件上传

    全面分析Java文件上传

    本片文章给大家详细分析了Java文件上传的相关知识点,以及相关代码做了详细分析,有兴趣的朋友学习下。
    2018-02-02

最新评论