0

0

Java动态代理与静态代理的实例分析

PHPz

PHPz

发布时间:2023-05-06 23:46:06

|

1071人浏览过

|

来源于亿速云

转载

一、静态代理

静态代理的使用

静态代理,代理类和被代理的类实现了同样的接口,代理类同时持有被代理类的引用,这样,当我们需要调用被代理类的方法时,可以通过调用代理类的方法来做到。

举例:假设领导的工作是开会和给员工考评。

先定义一个接口:

package com.sharpcj;
public interface IWork {
    void meeting();
    int evaluate(String name);
}

然后定义领导类:

package com.sharpcj;
import java.util.Random;
public class Leader implements IWork {
    @Override
    public void meeting() {
        System.out.println("领导早上要组织会议");
    }
    @Override
    public int evaluate(String name) {
        int score = new Random(System.currentTimeMillis()).nextInt(20) + 80;
        System.out.println(String.format("领导给%s的考评为%s分", name, score));
        return score;
    }
}

秘书类:

立即学习Java免费学习笔记(深入)”;

package com.sharpcj;
public class Secretary implements IWork {
    private Leader mLeader;
    public Secretary(Leader mLeader) {
        this.mLeader = mLeader;
    }
    @Override
    public void meeting() {
        System.out.println("秘书先给老板准备材料");
        mLeader.metting();
    }
    @Override
    public int evaluate(String name) {
        return mLeader.evaluate(name);
    }
}

测试类:

package com.sharpcj;
public class TestApp {
    public static void main(String[] args) {
        Leader leader = new Leader();
        Secretary secretary = new Secretary(leader);
        secretary.meeting();
        secretary.evaluate("Joy");
    }
}

执行结果:

Java动态代理与静态代理实例分析

这个代码很简单,注意在调用Secretary类的 meeting 方法时,我们调用了Leader类的 meeting 的方法,在此之前,我们还扩充了该方法。这时有的人可能有疑惑了,这看起来有点是装饰者模式了。这到底怎么回事?

与装饰者模式的区别

实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。

先看看两者的 UML 类图区别:

代理模式:

Java动态代理与静态代理实例分析

装饰者模式:

Java动态代理与静态代理实例分析

两者伪代码:

代理模式:

Interface Subject {
    void doAction()
}
public class RealSubject implements Subject{
    @Override
    public void doAction() {};
}
public class Proxy implements Subject{
       private RealSubject realSubject;

       public Proxy(RealSubject realSubject) {
             //关系在编译时确定
            this.realSubject = realSubject;
       }
       @Override
       public void doAction() {
             ….
             realSubject.doAction();
             ….
       }
}

装饰者模式;

Interface Component {
    void doAction()
}
public class ConcreteComponent implement Component {
    @Override
    public void doAction() {};
}
public class Decorator implements Component {
       private Component component;

       public Decorator(Component component) {
             //关系在编译时确定
            this.component = new component;
       }
       public void doAction() {
             ….
             component.doAction();
             ….
       }
}

其实代理模式和装饰者模式侧重点不一样,代理模式重点在于明确了被代理的类。如上例中,秘书很明确要代理的是的领导。而装饰者模式侧重于拓展类的方法,装饰类持有的实现Component接口的类的对象不是固定的,也就是说,装饰类可以根据在调用时传入的参数,装饰任意一个实现了 Component 接口的类。

二、动态代理

动态代理的根据实现方式的不同可以分为 JDK 动态代理和 CGlib 动态代理。

JDK 动态代理:利用反射机制生成一个实现代理接口的类,在调用具体方法前调用InvokeHandler来处理。
CGlib 动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

JDK 动态代理

还是以上面的例子为例:
首先,定一个类实现 InvocationHandler 接口,并实现 invoke 方法:

MVM mall 网上购物系统
MVM mall 网上购物系统

采用 php+mysql 数据库方式运行的强大网上商店系统,执行效率高速度快,支持多语言,模板和代码分离,轻松创建属于自己的个性化用户界面 v3.5更新: 1).进一步静态化了活动商品. 2).提供了一些重要UFT-8转换文件 3).修复了除了网银在线支付其它支付显示错误的问题. 4).修改了LOGO广告管理,增加LOGO链接后主页LOGO路径错误的问题 5).修改了公告无法发布的问题,可能是打压

下载
package com.sharpcj;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WorkInvocationHandler implements InvocationHandler {
    private Object object;
    public WorkInvocationHandler(Object object) {
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("object: " + object.getClass().getSimpleName());
        System.out.println("proxy: " + proxy.getClass().getSimpleName());

        if ("meeting".equals(method.getName())) {
            System.out.println("代理先准备会议材料...");
            return method.invoke(object, args);
        } else if ("evaluate".equals(method.getName())) {
            if(args[0] instanceof String) {
                if ("James".equals(args[0])) {
                    System.out.println("James 犯过错误,所以考评分数较低...");
                    return 70;
                }
            }
            return method.invoke(object, args);
        }
        return null;
    }
}

然后通过 Proxy.newProxyInstance() 方法创建代理对象:

package com.sharpcj;
import java.lang.reflect.Proxy;
public class TestApp {
    public static void main(String[] args) {
        /*Leader leader = new Leader();
        Secretary secretary = new Secretary(leader);
        secretary.meeting();
        secretary.evaluate("Joy");*/
        Leader leader = new Leader();
        IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
                new Class[]{IWork.class}, new WorkInvocationHandler(leader));
        proxy.meeting();
        proxy.evaluate("Joy");
        proxy.evaluate("James");
    }
}

输出结果:

Java动态代理与静态代理实例分析

我们看到,通过 WorkInvocationHandler 类,我们同样可以代理 Leader 类的方法的实现,实际上我们实现的是任意的方法的实现,只是我们在创建代理对象的时候传入的是 Iwork 接口以及 Leader 类对象。

这里需要注意的是:在 InvocationHandler 接口的 invoke 方法中第一个参数 proxy, 并不是我们调用方法的对象,那这个参数是什么呢?代码中,我特别增加相应打印,打印出了 proxy 的类名,实际上 proxy 是代理对象本身,它的意义在于,我们可以在 invoke 方法中,返回该代理对象,然后进行连续调用。

看如下例子:

package com.sharpcj.proxytest;
public interface IWork {
    IWork work(String subject);
}
package com.sharpcj.proxytest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WorkInvocationHandler implements InvocationHandler {
    private Object object;
    public WorkInvocationHandler(Object object) {
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("work".equals(method.getName())){
            System.out.println("--- work: " + args[0]);
            return proxy;
        }
        return null;
    }
}
package com.sharpcj.proxytest;
import java.lang.reflect.Proxy;
public class TestApp {
    public static void main(String[] args) {
        IWork worker = (IWork) Proxy.newProxyInstance(IWork.class.getClassLoader(), new Class[]{IWork.class},
                new WorkInvocationHandler(new IWork() {
                    @Override
                    public IWork work(String subject) {
                        return null;
                    }
                }));
        worker.work("AAA").work("BBB").work("CCC");
    }
}

结果如下:

Java动态代理与静态代理实例分析

CGlib 动态代理实现

首先添加 cglib 依赖

build.gradle 文件:

... 
dependencies {
    // 引入 cglib 库
    compile 'cglib:cglib:3.1'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}
...

前面说了,cglib 针对类进行代理,我们以上面的 Leader 类为例,先创建一个类实现 MethodInterceptor接口:

package com.sharpcj;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LeaderMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if ("meeting".equals(method.getName())) {
            System.out.println("代理先准备会议材料...");
            return methodProxy.invokeSuper(o, objects);
        } else if ("evaluate".equals(method.getName())) {
            if(objects[0] instanceof String) {
                if ("James".equals(objects[0])) {
                    System.out.println("James 犯过错误,所以考评分数较低...");
                    return 70;
                }
            }
            return methodProxy.invokeSuper(o, objects);
        }
        return null;
    }
}

测试代码:

package com.sharpcj;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import java.lang.reflect.Proxy;
public class TestApp {
    public static void main(String[] args) {
        // System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\temp\code");  //保存生成的 class 文件
        Enhancer enhancer = new Enhancer(); // 通过CGLIB动态代理获取代理对象的过程
        enhancer.setSuperclass(Leader.class); // 设置enhancer对象的父类
        enhancer.setCallback(new LeaderMethodInterceptor()); // 设置enhancer的回调对象
        Leader proxy= (Leader)enhancer.create(); // 创建代理对象

        // 通过代理对象调用目标方法
        proxy.meeting();
        proxy.evaluate("Joy");
        proxy.evaluate("James");
    }
}

结果如下:

Java动态代理与静态代理实例分析

MethodInterceptor 接口只有一个 intercept 方法,这个方法有4个参数:

  • 1)obj表示增强的对象,即实现这个接口类的一个对象;

  • 2)method表示要被拦截的方法;

  • 3)args表示要被拦截方法的参数;

  • 4)proxy表示要触发父类的方法对象;

需要注意的是:实际调用是 methodProxy.invokeSuper(), 如果使用 invoke() 方法,则需要传入被代理的类对象,否则出现死循环,造成 stackOverflow 。

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
云朵浏览器入口合集
云朵浏览器入口合集

本专题整合了云朵浏览器入口合集,阅读专题下面的文章了解更多详细地址。

0

2026.01.20

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

20

2026.01.20

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

62

2026.01.19

java用途介绍
java用途介绍

本专题整合了java用途功能相关介绍,阅读专题下面的文章了解更多详细内容。

87

2026.01.19

java输出数组相关教程
java输出数组相关教程

本专题整合了java输出数组相关教程,阅读专题下面的文章了解更多详细内容。

39

2026.01.19

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

10

2026.01.19

xml格式相关教程
xml格式相关教程

本专题整合了xml格式相关教程汇总,阅读专题下面的文章了解更多详细内容。

13

2026.01.19

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

19

2026.01.19

微信聊天记录删除恢复导出教程汇总
微信聊天记录删除恢复导出教程汇总

本专题整合了微信聊天记录相关教程大全,阅读专题下面的文章了解更多详细内容。

160

2026.01.18

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.7万人学习

C# 教程
C# 教程

共94课时 | 7.1万人学习

Java 教程
Java 教程

共578课时 | 48.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号