【Java】大明星类会唱跳rap,如何使用动态代理为其增强功能,扩展类的行为

一、前言

今天学习Java的动态代理,我们来探讨动态代理的概念和应用。

二、内容

2.1 简介

(1)什么是动态代理

动态代理是Java中一种强大的编程机制,它允许我们在运行时创建代理对象,以代替直接访问对象。通常,动态代理用于实现横切关注点(cross-cutting concerns),如日志记录、事务管理和权限控制等,而无需修改原始类的代码。换句话说,动态代理能够以无侵入的方式为类的方法添加其他功能。代理类在其方法中调用目标类的方法,并可以在调用前后添加额外的逻辑。

Java中的动态代理主要依赖于Java的反射机制和代理对象的InvocationHandler接口。

(2)为什么需要动态代理

  1. 分离关注点:动态代理允许将关注点(如日志、事务、权限控制等)与核心业务逻辑分离,提高了代码的可维护性和可重用性。
  2. 减少重复代码:动态代理可以减少在不同类中相似的代理代码的重复编写,从而降低了代码冗余。
  3. 避免对原始类的修改:静态代理需要为每个需要代理的类编写一个代理类,而动态代理可以动态地创建代理,无需修改原始类的代码。

Java中的动态代理主要使用两个核心类来实现:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler。动态代理不需要为每个目标类编写一个代理类,而是在运行时生成代理对象。

  • Proxy :通过Proxy.getProxyClass方法生成代理类的Class对象,然后通过反射机制创建代理对象。
  • InvocationHandler :代理对象的方法调用会委托给InvocationHandler对象来处理。
  • newProxyInstance方法Proxy类提供了newProxyInstance方法,可以直接生成代理对象,隐藏了代理类的生成细节。

(3)动态代理 vs 静态代理

静态代理通常需要为每个目标类编写一个代理类,将前后逻辑添加到代理类中,然后在客户端使用代理类来间接调用目标类的方法以实现功能增强。然而,这种方法存在一些缺点,如高维护成本、对接口的依赖和对大规模项目的不适用。

与之相比,动态代理的优势包括:

  1. 代码灵活性:动态代理允许在运行时生成代理类,更加灵活,适应不断增长的业务需求。
  2. AOP编程:动态代理可以实现面向切面编程(AOP),将横切关注点从业务逻辑中分离出来,提高代码的可维护性。
  3. 解耦:在Web开发中,动态代理有助于实现数据层和业务层的分离,提高代码的可维护性。
  4. 无侵入式扩展:动态代理实现无侵入式的代码扩展,而静态代理可能导致代理类的代码量庞大,难以维护。

(4)应用场景

动态代理在多个领域中广泛应用,提供了一种灵活且非侵入性的方式来实现功能增强和控制。一些应用场景包括:

  1. Spring AOPSpring框架利用动态代理实现面向切面编程(AOP),在方法执行前、后或异常时执行横切关注点的逻辑,如事务管理、日志记录等。
  2. RPC框架 :常见的RPC框架如Apache ThriftgRPC使用动态代理来创建远程服务的代理对象,使本地调用远程方法变得透明。
  3. 用户鉴权:动态代理可用于实施用户鉴权和权限控制,允许在方法执行前或后进行身份验证和授权检查。
  4. 日志记录:通过动态代理,可以在方法执行前后记录日志信息,以便监控和故障排除。
  5. 更多其他领域。

2.2 如何使用

(1)创建代理对象

在Java中,动态代理对象通常使用Proxy.newProxyInstance方法创建。

java 复制代码
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)

可以看到,这个方法接受三个参数:

  1. 类加载器 (ClassLoader):用于加载生成的代理类,通常是目标接口的类加载器。
  2. 要代理的接口 (Class[]):指定代理对象要实现的接口。
  3. InvocationHandler对象 (InvocationHandler):用于处理方法调用的逻辑。

(2) InvocationHandler接口

InvocationHandler是一个函数式接口,它包含一个invoke方法,用于处理代理对象方法的调用。在invoke方法中,你可以添加自定义的逻辑,如在方法执行前后执行额外的操作。

java 复制代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

invoke方法中,可以根据method参数中的方法信息和args参数中的参数来自定义方法的执行逻辑,例如在方法执行前后添加额外的操作。invoke方法的返回值应该与被代理方法的返回值类型相匹配,并需要处理可能抛出的Throwable异常。

2.3 示例

(1)需求

假设我们有一个明星接口 Star,其中定义了明星可以唱歌和跳舞的方法。我们还有一个具体的明星类 BigStar,它实现了 Star 接口。现在,我们希望使用动态代理来创建一个代理对象,控制对 BigStar 对象方法的访问,同时在方法执行前后添加额外的操作。

我们将创建一个经纪人来代理明星,以便在明星的行为前后添加一些额外的功能。

(2)明星接口和大明星类

首先,我们有一个明星接口 Star,它定义了明星的行为,包括唱歌、跳舞和说唱。接着,我们有一个实现了 Star 接口的大明星类 BigStar,它可以执行这些行为。

java 复制代码
/**
 * 明星接口: 唱跳rap
 */
interface Star {
    // 唱歌
    void sing(String name);
    // 跳舞
    void dance();
    // rap
    void rap();
}

/**
 * 大明星:能唱能跳能rap
 */
class BigStar implements Star {
    private String name;

    public BigStar(String name) {
        this.name = name;
    }

    // 唱歌
    @Override
    public void sing(String name){
        System.out.println(this.name + "正在唱" + name);
    }

    // 跳舞
    @Override
    public void dance(){
        System.out.println(this.name + "正在跳舞");
    }

    @Override
    public void rap() {
        System.out.println(this.name + "正在说唱");
    }

    // ... 其他方法和属性
}

(3)动态代理增强功能

现在,我们将创建一个经纪人类 DynamicProxy,并使用动态代理来为大明星增强功能。经纪人将在大明星的行为前后添加额外的功能。

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxy {
    public static void main(String[] args) {
        // 1. 创建大明星对象(也就是代理的对象)
        BigStar bigStar = new BigStar("吉哥");

        // 2. 创建一个 InvocationHandler 对象,用于处理方法调用(代理对象的处理程序)
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 在方法执行前添加功能
                System.out.println("经纪人准备安排行程...");

                // 执行方法
                Object result = method.invoke(bigStar, args);

                // 在方法执行后添加功能
                System.out.println("经纪人处理后事务...");

                return result;
            }
        };

        // 3. 创建代理对象
        Star proxy = (Star) Proxy.newProxyInstance(
                Star.class.getClassLoader(),
                new Class[] { Star.class },
                handler
        );

        // 4. 使用代理对象调用方法
        proxy.sing("只因你太美");
        System.out.println("----------------------");
        proxy.dance();
        System.out.println("----------------------");
        proxy.rap();
    }
}

(4)运行结果

bash 复制代码
经纪人准备安排行程...
吉哥正在唱只因你太美
经纪人处理后事务...
----------------------
经纪人准备安排行程...
吉哥正在跳舞
经纪人处理后事务...
----------------------
经纪人准备安排行程...
吉哥正在说唱
经纪人处理后事务...

通过使用Java的动态代理机制,我们成功为大明星添加了额外的功能,而不需要修改原始的 BigStar 类。这使得我们可以轻松地在不同场景下扩展类的行为,从而更好地符合开闭原则和单一职责原则。

三、总结

总的来说,动态代理就是允许在运行时创建代理对象,以代替直接访问对象。我们在示例代码中展示了如何使用动态代理来扩展类的行为,通过代理对象在原有行为上添加额外的逻辑,这也是面向切面编程(AOP)的一种常见应用。

相关推荐
s9123601011 分钟前
Rust std::thread::spawn(move) 的作用
开发语言·后端·rust
是店小二呀2 分钟前
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
后端
pjx9872 分钟前
超越单体:进入微服务世界与Spring Cloud概述
java·spring cloud·微服务
Victor3563 分钟前
Dubbo(86)如何设计一个多租户的Dubbo服务?
后端
天天摸鱼的java工程师4 分钟前
爆肝 30 天!从 JVM 调优到百万级 QPS,我的 Java 性能飞升全记录
java·后端
TDengine (老段)7 分钟前
TDengine 订阅不到数据问题排查
java·数据库·tdengine
刘立军9 分钟前
本地大模型编程实战(26)用langgraph实现基于SQL数据构建的问答系统(5)
人工智能·后端·python
冼紫菜9 分钟前
Spring Cloud 项目中优雅地传递用户信息:基于 Gateway + ThreadLocal 的用户上下文方案
java·开发语言·spring boot·后端·spring cloud·gateway
为美好的生活献上中指10 分钟前
java每日精进 4.29【框架之自动记录日志并插入如数据库流程分析】
java·linux·数据库
初心_202416 分钟前
2. python协程/异步编程详解
java·前端·python