【设计模式】 代理模式(静态代理、动态代理{JDK动态代理、JDK动态代理与CGLIB动态代理的区别})

代理模式

代理模式是一种结构型 设计模式,它提供了一种替代访问的方法,即通过代理对象来间接访问目标对象 。代理模式可以在不改变原始类代码的情况下,增加额外的功能,如权限控制、日志记录等。

静态代理

静态代理是指创建的或特定工具自动生成源代码,在程序运行前代理类的.class文件就已经存在了。每个代理类只能为一个接口服务,如果需要代理多个接口,则需要编写多个代理类,这会增加维护成本。

示例:房子中介(静态代理)
java 复制代码
// 房屋租赁服务接口
interface HouseLeaseService {
    void leaseHouse();
}

// 真实主题类 - 房东
class Landlord implements HouseLeaseService {
    public void leaseHouse() {
        System.out.println("房东: 出租房屋.");
    }
}

// 静态代理类 - 中介
class RealEstateAgent implements HouseLeaseService {
    private Landlord landlord;

    public RealEstateAgent(Landlord landlord) {
        this.landlord = landlord;
    }

    @Override
    public void leaseHouse() {
        // 增加额外功能,例如广告宣传
        System.out.println("中介: 发布租房信息.");
        landlord.leaseHouse();
        // 增加额外功能,例如收取中介费
        System.out.println("中介: 收取中介费用.");
    }
}

public class StaticProxyDemo {
    public static void main(String[] args) {
        Landlord landlord = new Landlord();
        HouseLeaseService agentService = new RealEstateAgent(landlord);
        agentService.leaseHouse();
    }
}

在这个例子中,RealEstateAgent作为代理类,不仅实现了HouseLeaseService接口,还包含了对Landlord对象的操作,并在调用前后添加了额外的行为。

--

动态代理

动态代理是在程序运行时动态生成代理类的字节码并加载到JVM中,因此不需要提前编写代理类的代码。Java提供了两种主要的动态代理方式:JDK动态代理和CGLIB。

JDK动态代理

在JDK动态代理中有两个重要的类ProxyInvocationHandler

  • Proxy

    Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

  • InvocationHandler

    InvocationHandler 是代理实例的调用处理程序 实现的接口。

    每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。

代码示例

首先,我们定义一个实现了InvocationHandler接口的处理器类ProxyInvocationHandler ,它负责封装中介的具体业务逻辑。

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

// 房屋租赁服务接口
interface HouseLeaseService {
    void leaseHouse();
}

// 真实主题类 - 房东
class Landlord implements HouseLeaseService {
    public void leaseHouse() {
        System.out.println("房东: 出租房屋.");
    }
}

// 实现InvocationHandler接口的处理器类 - 中介
class ProxyInvocationHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在调用真实方法之前可以做一些额外的工作
        System.out.println("中介: 发布租房信息.");
        
        // 调用目标对象的方法
        Object result = method.invoke(target, args);
        
        // 在调用真实方法之后也可以做一些额外的工作
        System.out.println("中介: 收取中介费用.");
        
        return result;
    }
}

public class JdkProxyDemo {

    public static void main(String[] args) {
        // 创建真实房东对象
        Landlord landlord = new Landlord();

        // 创建动态代理实例
        HouseLeaseService proxyInstance = (HouseLeaseService) Proxy.newProxyInstance(
                landlord.getClass().getClassLoader(),
                landlord.getClass().getInterfaces(),
                new ProxyInvocationHandler(landlord)
        );

        // 通过代理实例调用方法
        proxyInstance.leaseHouse();
    }
}

JDK动态代理与CGLIB动态代理的区别

特性 JDK 动态代理 CGLIB 动态代理
实现方式 只能代理实现了接口的类(基于接口) 通过生成目标类的子类进行代理(基于继承)
代理类 - Proxy - Invocation Handler - Enhancer - Method Interceptor
核心机制 使用反射调用目标方法 通过ASM字节码生成目标类的子类,重写方法实现代理
性能 (jdk8) 在一百万运行次数内,性能快了30%左右;到五百万运行次数后,性能快了将近一倍
相关推荐
10km13 分钟前
java:Apache Commons Configuration2占位符解析异常的正确解法:${prefix:name:-default}
java·apache·configuration2·变量插值·interpolation
customer0813 分钟前
【开源免费】基于SpringBoot+Vue.JS个人博客系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
灰色人生qwer21 分钟前
SpringBoot 项目配置日志输出
java·spring boot·后端
2301_7930698231 分钟前
Spring Boot +SQL项目优化策略,GraphQL和SQL 区别,Spring JDBC 等原理辨析(万字长文+代码)
java·数据库·spring boot·sql·jdbc·orm
阿华的代码王国37 分钟前
【从0做项目】Java搜索引擎(6)& 正则表达式鲨疯了&优化正文解析
java·后端·搜索引擎·正则表达式·java项目·从0到1做项目
服务端相声演员37 分钟前
Oracle JDK、Open JDK zulu下载地址
java·开发语言
是姜姜啊!38 分钟前
java连接redis
java·redis
hhw19911240 分钟前
spring boot知识点5
java·数据库·spring boot
EQUINOX142 分钟前
lab4 CSAPP:Cachelab
java·后端·spring
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS打卡健康评测系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源