Java 代理模式:从静态代理到动态代理

前言

代理模式是 Java 中常见的设计模式之一,它的核心思想是通过一个代理对象来控制对真实对象的访问。代理模式不仅可以扩展目标对象的功能,而且在不修改原目标对象的情况下,可以增加一些我们自定义的操作。

1. 代理模式简介

代理模式的核心思想是:通过代理对象来代替对真实对象的访问。这样做的好处是,我们可以在不修改原目标对象的前提下,扩展目标对象的功能。比如,在目标对象的某个方法执行前后,你可以增加一些自定义的操作。

举个例子:

新娘找来了自己的闺蜜来代替自己处理新郎的提问。新娘收到的提问都是经过闺蜜处理过滤之后的。闺蜜在这里就可以看作是新娘的代理对象,代理的行为(方法)是接收和回复新郎的提问。

再举个例子:

你是一个明星,每天有很多粉丝找你签名。但你是大忙人,没时间亲自处理这些请求,所以你找了一个经纪人(代理对象)来帮你处理。粉丝的请求先交给经纪人,经纪人再决定是否转交给你,或者帮你做一些额外的处理(比如记录谁来找你签名)。这样,你既不用亲自处理所有请求,又能保证签名工作顺利进行。

2. 静态代理

2.1 什么是静态代理?

静态代理是指,在编译时就已经确定了代理类和目标类的关系。我们需要手动为每个目标类创建一个代理类,并在代理类中调用目标类的方法。

静态代理的缺点:灵活性较差,一旦接口新增方法,目标类和代理类都需要进行修改。

2.2 静态代理的实现步骤

  1. 定义一个接口及其实现类;
  2. 创建一个代理类并实现该接口;
  3. 将目标对象注入代理类,并在代理类的方法中调用目标类的方法。

2.3 代码示例

1. 定义发送短信的接口
java 复制代码
public interface SmsService {
    String send(String message);
}
2. 实现发送短信的接口
java 复制代码
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}
3. 创建代理类并实现发送短信的接口
java 复制代码
public class SmsProxy implements SmsService {

    private final SmsService smsService;

    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public String send(String message) {
        // 调用方法之前,我们可以添加自己的操作
        System.out.println("before method send()");
        smsService.send(message);
        // 调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method send()");
        return null;
    }
}
4. 实际使用
java 复制代码
public class Main {
    public static void main(String[] args) {
        SmsService smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

运行上述代码之后,控制台打印出:

java 复制代码
before method send()
send message:java
after method send()

可以看到,我们成功地在 SmsServiceImpl 的 send() 方法前后增加了自定义操作。

3. 动态代理

3.1 什么是动态代理?

相比于静态代理,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,而是可以在运行时动态生成代理类。动态代理的实现方式有很多种,比如 JDK 动态代理和 CGLIB 动态代理。

3.2 JDK 动态代理

3.2.1 JDK 动态代理的核心

在 JDK 动态代理中,InvocationHandler 接口和 Proxy 类是核心。Proxy 类的 newProxyInstance() 方法用于生成代理对象,而 InvocationHandler 接口的 invoke() 方法则用于处理代理对象的方法调用。

3.2.2 JDK 动态代理的实现步骤
  1. 定义一个接口及其实现类;
  2. 自定义 InvocationHandler 并重写 invoke 方法;
  3. 通过 Proxy.newProxyInstance() 方法创建代理对象。
3.2.3 代码示例
1. 定义发送短信的接口
java 复制代码
public interface SmsService {
    String send(String message);
}
2. 实现发送短信的接口
java 复制代码
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}
3. 定义一个 JDK 动态代理类
java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DebugInvocationHandler implements InvocationHandler {
    private final Object target;

    public DebugInvocationHandler(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. 获取代理对象的工厂类
java 复制代码
import java.lang.reflect.Proxy;

public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new DebugInvocationHandler(target)
        );
    }
}
5. 实际使用
java 复制代码
public class Main {
    public static void main(String[] args) {
        SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
        smsService.send("java");
    }
}

运行上述代码之后,控制台打印出:

java 复制代码
before method send
send message:java
after method send

3.3 CGLIB 动态代理

3.3.1 CGLIB 动态代理的核心

CGLIB 动态代理通过继承目标类来生成代理类,因此它可以代理未实现任何接口的类。CGLIB 的核心是 MethodInterceptor 接口和 Enhancer 类。

3.3.2 CGLIB 动态代理的实现步骤
  1. 定义一个类;
  2. 自定义 MethodInterceptor 并重写 intercept 方法;
  3. 通过 Enhancer 类的 create() 方法创建代理类。
3.3.3 代码示例
1. 实现一个使用阿里云发送短信的类
java 复制代码
public class AliSmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}
2. 自定义 MethodInterceptor
java 复制代码
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class DebugMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("before method " + method.getName());
        Object object = methodProxy.invokeSuper(o, args);
        System.out.println("after method " + method.getName());
        return object;
    }
}
3. 获取代理类
java 复制代码
import net.sf.cglib.proxy.Enhancer;

public class CglibProxyFactory {
    public static Object getProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(clazz.getClassLoader());
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new DebugMethodInterceptor());
        return enhancer.create();
    }
}
4. 实际使用
java 复制代码
public class Main {
    public static void main(String[] args) {
        AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
        aliSmsService.send("java");
    }
}

运行上述代码之后,控制台打印出:

java 复制代码
before method send
send message:java
after method send

4. 静态代理和动态代理的对比

  • 灵活性:动态代理更加灵活,不需要针对每个目标类都创建一个代理类,且可以直接代理实现类。
  • JVM 层面:静态代理在编译时生成 class 文件,而动态代理在运行时动态生成类字节码。
相关推荐
FRing0316 分钟前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Fatbobman(东坡肘子)1 小时前
快速消失的硬盘容量 -- 肘子的 Swift 周报 #160
开发语言·人工智能·redis·ios·swiftui·iphone·swift
揽星河..1 小时前
【C语言】底层程序设计
c语言·开发语言
JavaPub-rodert2 小时前
从零搭建通用管理后台:ShiyuAdmin 的 Go、React 与 RBAC 实践
开发语言·react.js·golang
海的透彻3 小时前
dockerfile如何快速构建容器(java服务)
java·开发语言
code2cat3 小时前
Java工具篇之Gradle:用任务图组织构建与交付
java·开发语言·单元测试·gradle
北京中科新远科技3 小时前
高速交换机端口预算怎么做?下行、上联、拆分和冗余计算框架
开发语言·php
xiaoye-duck3 小时前
《C++ 项目:手写大模型接入 SDK》DeepSeek接口设计(下):流式响应实现和测试
开发语言·网络·c++·语言模型
后台模板学习3 小时前
大模型 Prompt 工程在电商订单系统中的踩坑记录
java·python·prompt
w_zero_one3 小时前
算法--构造
java·数据结构·算法