首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Java 之动态代理详解

Java 之动态代理详解

作者头像
艾伦耶格尔
发布2025-08-28 14:21:16
发布2025-08-28 14:21:16
1850
举报
文章被收录于专栏:Java基础Java基础

动态代理就像是一位神奇的魔术师,它能悄无声息地在你的代码运行时,为你的对象添加各种功能,而这一切,你不需要修改任何已有的代码!

一、什么是代理?

想象一下,你想要出国旅游,但你不会外语,也不熟悉当地的风俗习惯。这时候,你就可以找一家旅行社,让他们帮你安排行程、预订酒店、办理签证等等。对你来说,旅行社就相当于你的代理,你只需要告诉他们你的需求,他们就会帮你完成所有的事情。

在软件开发中,代理也是类似的概念。代理对象就像中间人一样,它会拦截对目标对象的访问,并在访问前后执行一些额外的操作,比如记录日志、权限校验、缓存等等。

二、动态代理的魔力:运行时创建

代理模式有很多种实现方式,而动态代理则是其中最灵活的一种。它最大的特点就是在程序运行时,动态地创建代理对象和代理类,而不需要我们手动编写代理类的代码。

那么,Java是如何实现动态代理的呢?答案就是——反射!(点击查看什么是反射

三、Java动态代理三剑客

Java提供了以下三种方式来实现动态代理:

  1. JDK动态代理: 基于接口的动态代理,使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。
  2. CGLIB动态代理: 基于继承的动态代理,使用第三方库CGLIB。
  3. ASM动态代理: 基于字节码操作的动态代理,使用第三方库ASM。

本文将重点介绍使用最广泛的JDK动态代理,其他两种方式留待后续文章中探讨。

四、JDK动态代理实战:打造万能日志记录器

假设我们有一个 UserService 接口和一个 UserServiceImpl 实现类,现在我们想在调用 UserServiceImpl 的每个方法前后都打印一条日志,记录方法的执行时间。

1. 定义接口和实现类:

代码语言:javascript
复制
// UserService 接口
public interface UserService {
    void createUser(String username, String password);
    void deleteUser(String username);
}

// UserServiceImpl 实现类
public class UserServiceImpl implements UserService {
    @Override
    public void createUser(String username, String password) {
        System.out.println("创建用户:" + username);
        // 模拟业务逻辑执行
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteUser(String username) {
        System.out.println("删除用户:" + username);
        // 模拟业务逻辑执行
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2. 创建 InvocationHandler 实现类:

代码语言:javascript
复制
// InvocationHandler 实现类,用于处理代理逻辑
public class LogInvocationHandler implements InvocationHandler {
    // 目标对象
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法执行前的逻辑
        long startTime = System.currentTimeMillis();
        System.out.println("开始执行方法:" + method.getName());

        // 调用目标对象的方法
        Object result = method.invoke(target, args);

        // 方法执行后的逻辑
        long endTime = System.currentTimeMillis();
        System.out.println("方法执行结束:" + method.getName() + ",耗时:" + (endTime - startTime) + "ms");
        return result;
    }
}

3. 创建代理对象:

代码语言:javascript
复制
public class Main {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();

        // 创建 InvocationHandler 对象
        LogInvocationHandler handler = new LogInvocationHandler(userService);

        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(), // 类加载器
                userService.getClass().getInterfaces(), // 接口数组
                handler);                            // InvocationHandler 对象

        // 调用代理对象的方法
        proxy.createUser("zhangsan", "123456");
        proxy.deleteUser("lisi");
    }
}

代码解释:

  • LogInvocationHandler 实现了 InvocationHandler 接口,并在 invoke() 方法中定义了代理逻辑,即在目标方法执行前后打印日志。
  • Proxy.newProxyInstance() 方法用于创建代理对象。它需要三个参数:
    • 类加载器: 用于加载代理类。
    • 接口数组: 目标对象实现的接口数组,代理类会实现这些接口。
    • InvocationHandler 对象: 用于处理代理逻辑。
  • 调用代理对象的方法时,实际上会调用 LogInvocationHandler 的 invoke() 方法,从而实现记录日志的功能。

运行结果:

代码语言:javascript
复制
开始执行方法:createUser
创建用户:zhangsan
方法执行结束:createUser,耗时:104ms
开始执行方法:deleteUser
删除用户:lisi
方法执行结束:deleteUser,耗时:103ms

4.invoke() 方法

在Java中,invoke方法主要用于反射机制,它属于java.lang.reflect.Method类。通过这个方法,可以动态地调用某个类的实例方法或静态方法。

invoke方法的基本用法

代码语言:javascript
复制
public Object invoke(Object obj, Object... args) throws IllegalAccessException, InvocationTargetException;
  • 参数
    • obj:调用方法的对象实例。如果是静态方法,可以传null
    • args:调用方法时需要的参数,可以是可变参数数组。

使用示例

下面是一个简单的例子,演示如何使用invoke方法:

代码语言:javascript
复制
import java.lang.reflect.Method;

class Example {
    public void sayHello(String name) {
        System.out.println("Hello, " + name + "!");
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            // 创建 Example 类的实例
            Example example = new Example();
            
            // 获取 sayHello 方法
            Method method = Example.class.getMethod("sayHello", String.class);
            
            // 调用 sayHello 方法
            method.invoke(example, "World");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意事项

  1. 权限问题:如果方法是私有的,使用反射调用时需要设置为可访问。
  2. 异常处理invoke方法可能会抛出多个异常,如IllegalAccessExceptionInvocationTargetException,需要适当处理。
  3. 性能开销:反射会比直接调用方法慢,因此在性能敏感的场合需谨慎使用。

五、总结

动态代理是Java中一个非常强大的机制,它可以帮助我们实现很多灵活的功能,比如:

  • AOP(面向切面编程)
  • RPC(远程过程调用)
  • 事务管理
  • 缓存等等

希望通过本文的介绍,你已经对Java动态代理有了初步的了解。在实际开发中,我们可以根据具体的需求,灵活运用动态代理,让我们的代码更加优雅高效!感谢各位看官的观看,下期见,谢谢~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、什么是代理?
  • 二、动态代理的魔力:运行时创建
  • 三、Java动态代理三剑客
  • 四、JDK动态代理实战:打造万能日志记录器
  • 五、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档