【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)的一种常见应用。

相关推荐
星如雨グッ!(๑•̀ㅂ•́)و✧9 分钟前
Java NIO全面详解
java·python·nio
码界筑梦坊30 分钟前
基于Django的个人博客系统的设计与实现
后端·python·django·毕业设计
taopi20241 小时前
android java系统弹窗的基础模板
android·java·开发语言
松仔log2 小时前
Java多线程——对象的组合
java·开发语言·jvm
酷爱码2 小时前
springboot 动态配置定时任务
java·spring boot·后端
计算机-秋大田2 小时前
基于SpringBoot的美食烹饪互动平台的设计与实现(源码+SQL脚本+LW+部署讲解等)
vue.js·spring boot·后端·课程设计·美食
从未止步..3 小时前
Jenkins未在第一次登录后设置用户名,第二次登录不进去怎么办?
java·运维·jenkins
老马啸西风3 小时前
IM 即时通讯系统-42-基于netty实现的IM服务端,提供客户端jar包,可集成自己的登录系统
java
加油,旭杏3 小时前
【go语言】grpc 快速入门
开发语言·后端·golang
2501_903238653 小时前
Java 9模块开发:Eclipse实战指南
java·开发语言·eclipse·个人开发