Java 代理从 0 到彻底搞懂

一、为什么出现代理?

咱们先抛开编程,想象一下生活中的场景。假如你是一位大明星,每天都有无数的活动邀约、采访请求,还有各种商务合作的洽谈。要是你亲自去处理这些事情,那你哪还有时间去拍戏、唱歌、提升自己的业务能力呢?所以,你就会找一个经纪人。这个经纪人就相当于你的 “代理”。

经纪人会帮你筛选合适的活动,和合作方谈合同细节,安排你的行程,在你参加活动前后帮你处理各种杂事。这样一来,你就可以把精力都放在自己擅长的演艺事业上了。

在软件开发里,代理模式的出现也是出于类似的原因:

  • 增强功能:就像经纪人会在你参加活动前帮你做好形象设计,活动后帮你做宣传推广一样。在不改变原本代码功能的基础上,给代码添加一些额外的功能,比如记录程序运行的日志、管理数据库事务、验证用户的权限等。
  • 解耦职责:把一些通用的功能,比如记录日志、管理事务,从主要的业务逻辑中分离出来。就好比经纪人负责处理各种杂事,你专注于演艺事业,让代码的各个部分职责更清晰,也更容易维护和复用。
  • 控制访问:经纪人会帮你筛选合作对象,只有符合你要求的合作才会让你参与。在程序里,代理可以控制对某个对象的访问,比如在调用方法之前检查用户有没有权限。
  • 远程调用:假如你要和国外的一个剧组合作,你不可能每次都亲自飞去国外谈合作。这时候就可以通过你的经纪人在国外的代表和剧组沟通,这个代表就相当于远程代理。在分布式系统里,代理可以隐藏网络通信的复杂细节,让你调用远程的对象就像调用本地的对象一样方便。

二、什么是代理代码?

代理代码就是实现代理模式的具体代码。在 Java 里,代理可以分为静态代理和动态代理,下面我们来详细看看。

1. 静态代理

还是以明星和经纪人为例。假如你是明星,经纪人是你的代理。静态代理就像是你提前和经纪人签好了一份详细的合同,规定了他只能帮你处理哪些活动,在活动前后要做哪些事情,而且这些规定都是固定不变的。

下面是一个简单的静态代理代码示例:

// 定义一个接口,就好比是明星要参加的活动类型
// 接口名为 Subject,里面定义了一个抽象方法 request,没有返回值
interface Subject {
    // 声明一个抽象方法 request,代表要执行的操作,这里可以理解为明星要参加的活动
    void request();
}

// 真实的明星类,实现了活动接口
// RealSubject 类实现了 Subject 接口,表明它可以执行接口中定义的活动
class RealSubject implements Subject {
    // 重写接口中的 request 方法,实现具体的活动逻辑
    @Override
    public void request() {
        // 打印信息,表示正在处理请求,即明星正在参加活动
        System.out.println("RealSubject: Handling request.");
    }
}

// 经纪人代理类,也实现了活动接口
// ProxySubject 类同样实现了 Subject 接口,作为 RealSubject 的代理类
class ProxySubject implements Subject {
    // 声明一个 RealSubject 类型的私有成员变量,用于持有真实的明星对象
    private RealSubject realSubject;

    // 构造方法,用于接收真实的明星对象
    public ProxySubject(RealSubject realSubject) {
        // 将传入的真实明星对象赋值给成员变量
        this.realSubject = realSubject;
    }

    // 重写接口中的 request 方法,在调用真实对象的方法前后添加额外逻辑
    @Override
    public void request() {
        // 打印信息,表示在请求处理之前的操作,就像经纪人在明星参加活动前做的准备工作
        System.out.println("ProxySubject: Before request.");
        // 调用真实明星对象的 request 方法,让明星去参加活动
        realSubject.request();
        // 打印信息,表示在请求处理之后的操作,就像经纪人在明星参加活动后做的总结工作
        System.out.println("ProxySubject: After request.");
    }
}

// 测试类
// 用于测试静态代理的功能
public class StaticProxyTest {
    // 程序的入口方法
    public static void main(String[] args) {
        // 创建一个真实的明星对象
        RealSubject realSubject = new RealSubject();
        // 创建一个经纪人代理对象,并将真实明星对象传递给它
        ProxySubject proxySubject = new ProxySubject(realSubject);
        // 通过经纪人代理对象调用 request 方法,触发代理逻辑
        proxySubject.request();
    }
}

在这个例子中,RealSubject 就像是明星本人,ProxySubject 就像是经纪人。经纪人在明星参加活动前后会做一些额外的事情,比如活动前的准备工作和活动后的总结工作。

2. 动态代理

动态代理就好比是你和经纪人的合作更加灵活。你不需要提前把所有的事情都规定好,经纪人可以根据不同的活动情况,在活动前后动态地决定要做哪些额外的事情。Java 里有两种实现动态代理的方式:JDK 动态代理和 CGLIB 动态代理。

JDK 动态代理

JDK 动态代理要求被代理的类必须实现一个或多个接口。还是以明星为例,假如明星参加的活动都有一些固定的规则(接口),经纪人就可以根据这些规则在活动前后做一些额外的事情。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义活动接口
// 接口名为 Subject,定义了一个抽象方法 request,代表明星要参加的活动
interface Subject {
    // 声明一个抽象方法 request,没有返回值
    void request();
}

// 真实的明星类,实现活动接口
// RealSubject 类实现了 Subject 接口,具备执行活动的能力
class RealSubject implements Subject {
    // 重写接口中的 request 方法,实现具体的活动逻辑
    @Override
    public void request() {
        // 打印信息,表示正在处理请求,即明星正在参加活动
        System.out.println("RealSubject: Handling request.");
    }
}

// 经纪人的调用处理器,负责处理活动前后的额外事情
// SubjectInvocationHandler 类实现了 InvocationHandler 接口,用于处理代理对象方法的调用
class SubjectInvocationHandler implements InvocationHandler {
    // 声明一个 Object 类型的私有成员变量,用于持有被代理的对象
    private Object target;

    // 构造方法,用于接收被代理的对象
    public SubjectInvocationHandler(Object target) {
        // 将传入的被代理对象赋值给成员变量
        this.target = target;
    }

    // 重写 InvocationHandler 接口的 invoke 方法,该方法会在代理对象的方法被调用时触发
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 打印信息,表示在方法调用之前的操作,就像经纪人在明星参加活动前做的准备工作
        System.out.println("JDK Proxy: Before method call.");
        // 通过反射调用被代理对象的方法,method 表示要调用的方法,args 表示方法的参数
        Object result = method.invoke(target, args);
        // 打印信息,表示在方法调用之后的操作,就像经纪人在明星参加活动后做的总结工作
        System.out.println("JDK Proxy: After method call.");
        // 返回方法调用的结果
        return result;
    }
}

// 测试类
// 用于测试 JDK 动态代理的功能
public class JdkProxyTest {
    // 程序的入口方法
    public static void main(String[] args) {
        // 创建一个真实的明星对象
        RealSubject realSubject = new RealSubject();
        // 创建一个调用处理器对象,并将真实明星对象传递给它
     

你可能感兴趣的:(java,代理模式,开发语言)