Java 动态代理详解与实战示例

简介: Java 动态代理详解与实战示例

Java 动态代理详解与实战示例

Java 动态代理是 Java 提供的一种机制,它允许程序在运行时创建代理类,并在不改变代理类代码的情况下为方法调用添加额外的功能。动态代理的核心是 java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口。

1. 动态代理的基本概念

动态代理是一种在运行时创建代理对象的技术,可以在不修改原始对象代码的情况下为方法调用添加额外的行为。Java 提供了两种代理方式:

  • 接口代理(基于 java.lang.reflect.Proxy):用于代理实现了接口的类。
  • 类代理(基于第三方库如 CGLIB):用于代理没有实现接口的类。
    本文主要介绍接口代理。

2. InvocationHandler 接口

InvocationHandler 是动态代理的核心接口。它定义了 invoke 方法,代理对象的每个方法调用都会被重定向到这个 invoke 方法。

public interface InvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

3. Proxy 类

Proxy 类提供了静态方法 newProxyInstance 用于创建代理对象:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
        throws IllegalArgumentException

参数说明

  • loader:定义代理类的类加载器。
  • interfaces:代理类实现的接口列表。
  • h:InvocationHandler 实例。

4. 动态代理示例

4.1 定义接口和实现类

interface Hello {
    void sayHello();
}

class HelloImpl implements Hello {
    public void sayHello() {
        System.out.println("Hello, world!");
    }
}

4.2 自定义 InvocationHandler

class HelloInvocationHandler implements InvocationHandler {
    private final Object target;

    public HelloInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}
class HelloInvocationHandler implements InvocationHandler {
    private final Object target;

    public HelloInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

4.3 创建代理对象并调用方法

public class DynamicProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        Hello hello = new HelloImpl();

        // 创建 InvocationHandler 实例
        InvocationHandler handler = new HelloInvocationHandler(hello);

        // 创建代理对象
        Hello proxy = (Hello) Proxy.newProxyInstance(
            hello.getClass().getClassLoader(),
            hello.getClass().getInterfaces(),
            handler
        );

        // 调用代理对象的方法
        proxy.sayHello();
    }
}

4.4 输出结果

Before method sayHello
Hello, world!
After method sayHello

5. 动态代理的高级用法

5.1 动态代理用于日志记录

动态代理可以用于在方法调用前后记录日志。

class LoggingInvocationHandler implements InvocationHandler {
    private final Object target;

    public LoggingInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Entering method: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("Exiting method: " + method.getName());
        return result;
    }
}

5.2 动态代理用于性能监控

动态代理可以用于监控方法的执行时间。

class TimingInvocationHandler implements InvocationHandler {
    private final Object target;

    public TimingInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.nanoTime();
        Object result = method.invoke(target, args);
        long end = System.nanoTime();
        System.out.println("Execution of " + method.getName() + " took " + (end - start) + " ns");
        return result;
    }
}

6. 动态代理的优势

  • 代码复用:可以通过动态代理实现常见的横切关注点(如日志记录、事务管理、性能监控等),减少重复代码。
  • 解耦:通过代理对象和目标对象分离,降低模块之间的耦合度。
  • 灵活性:可以在运行时动态地改变代理行为,增强程序的灵活性和扩展性。

7. 总结

Java 动态代理是一种强大的技术,可以在不修改现有代码的情况下为方法调用添加额外的行为。通过使用 InvocationHandler 和 Proxy 类,可以轻松地实现各种代理模式,如日志记录、性能监控等。这种机制在很多 Java 框架(如 Spring AOP)中得到了广泛应用。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
3天前
|
Java 开发者 UED
【实战宝典】Java异常处理大师级教程:throws关键字,让异常声明成为你的专属标签!
【6月更文挑战第19天】在Java中,`throws`关键字是异常处理的关键,它提升了方法签名的透明度和代码质量。不使用`throws`时,未捕获的异常可能导致程序崩溃。例如,`readFileContent`方法若不声明`throws IOException`,则隐藏了可能的风险。而明确声明如`throws IOException`,提醒调用者需处理异常,增强代码稳定性。
|
2天前
|
Java 测试技术
Java多线程同步实战:从synchronized到Lock的进化之路!
【6月更文挑战第20天】Java多线程同步始于`synchronized`关键字,保证单线程访问共享资源,但为应对复杂场景,`Lock`接口(如`ReentrantLock`)提供了更细粒度控制,包括可重入、公平性及中断等待。通过实战比较两者在高并发下的性能,了解其应用场景。不断学习如`Semaphore`等工具并实践,能提升多线程编程能力。从同步起点到专家之路,每次实战都是进步的阶梯。
|
2天前
|
Java 应用服务中间件 开发者
【实战指南】Java Socket编程:构建高效的客户端-服务器通信
【6月更文挑战第21天】Java Socket编程用于构建客户端-服务器通信。`Socket`和`ServerSocket`类分别处理两端的连接。实战案例展示了一个简单的聊天应用,服务器监听端口,接收客户端连接,并使用多线程处理每个客户端消息。客户端连接服务器,发送并接收消息。了解这些基础,加上错误处理和优化,能帮你开始构建高效网络应用。
|
1天前
|
Java
【实战演练】JAVA网络编程高手养成记:URL与URLConnection的实战技巧,一学就会!
【6月更文挑战第22天】在Java网络编程中,理解和运用URL与URLConnection是关键。URL代表统一资源定位符,用于标识网络资源;URLConnection则用于建立与URL指定资源的连接。通过构造URL对象并调用openConnection()可创建URLConnection。示例展示了如何发送GET请求读取响应,以及如何设置POST请求以发送数据。GET将参数置于URL,POST将参数置于请求体。练习这些基本操作有助于提升网络编程技能。
|
4天前
|
Java 开发者 UED
【实战攻略】Java异常处理进阶:throw关键字,打造坚不可摧的错误防御体系!
【6月更文挑战第19天】在Java中,`throw`关键字用于主动抛出异常,特别是在检测到错误条件如非法参数时。通过`throw`,开发者能控制何时中断程序并提供清晰的错误信息。例如,在验证订单金额时,如果金额小于等于零,可以抛出`IllegalArgumentException`。此外,`throw`还可用于构建异常链,保留错误上下文,便于问题追溯。掌握`throw`使用,是构建健壮异常处理和提升用户体验的关键。
|
2天前
|
IDE Java 开发工具
从零开始学Java Socket编程:客户端与服务器通信实战
【6月更文挑战第21天】Java Socket编程教程带你从零开始构建简单的客户端-服务器通信。安装JDK后,在命令行分别运行`SimpleServer`和`SimpleClient`。服务器监听端口,接收并回显客户端消息;客户端连接服务器,发送“Hello, Server!”并显示服务器响应。这是网络通信基础,为更复杂的网络应用打下基础。开始你的Socket编程之旅吧!
|
3天前
|
Java
死锁是线程间争夺资源造成的无限等待现象,Java示例展示了两个线程各自持有资源并等待对方释放,导致死锁。`
【6月更文挑战第20天】死锁是线程间争夺资源造成的无限等待现象,Java示例展示了两个线程各自持有资源并等待对方释放,导致死锁。`volatile`保证变量的可见性和部分原子性,确保多线程环境中值的即时更新。与`synchronized`相比,`volatile`作用于单个变量,不保证原子操作,同步范围有限,但开销较小。`synchronized`提供更全面的内存语义,保证原子性和可见性,适用于复杂并发控制。
12 3
|
2天前
|
存储 算法 Java
Java查找算法概览:二分查找适用于有序数组,通过比较中间元素缩小搜索范围;哈希查找利用哈希函数快速定位,示例中使用HashMap存储键值对,支持多值关联。
【6月更文挑战第21天】Java查找算法概览:二分查找适用于有序数组,通过比较中间元素缩小搜索范围;哈希查找利用哈希函数快速定位,示例中使用HashMap存储键值对,支持多值关联。简单哈希表实现未涵盖冲突解决和删除操作。
10 1
|
3天前
|
Java 调度
【实战指南】Java多线程高手秘籍:线程生命周期管理,掌控程序命运的钥匙!
【6月更文挑战第19天】Java多线程涉及线程生命周期的五个阶段:新建、就绪、运行、阻塞和死亡。理解这些状态转换对性能优化至关重要。线程从新建到调用`start()`变为就绪,等待CPU执行。获得执行权后进入运行状态,执行`run()`。遇到阻塞如等待锁时,进入阻塞状态。完成后或被中断则死亡。管理线程包括合理使用锁、利用线程池、处理异常和优雅关闭线程。通过控制这些,能编写更高效稳定的多线程程序。
|
1天前
|
缓存 负载均衡 安全
Java并发编程实战--简介
Java并发编程实战--简介
6 0