代理模式详解:静态代理、JDK 动态代理、CGLIB 动态代理

前言

在软件开发中,代理模式是一种常用的结构型设计模式,其核心思想是通过代理对象控制对目标对象的访问,并可以在目标方法执行前后添加额外的增强逻辑。本文将详细讲解代理模式的三种实现方式:静态代理、JDK 动态代理和 CGLIB 动态代理,并结合「周杰伦演出」的场景进行代码演示,帮助大家理解不同代理方式的特点和适用场景。

一、代理模式核心概念

1. 作用

通过代理对象包裹目标对象,实现对目标方法的前置处理(如参数校验、权限控制)、后置处理(如日志记录、数据统计),核心是控制访问 + 增强功能

2. 核心角色

角色 说明
抽象角色 通常是接口,定义目标对象和代理对象的公共方法,规范核心业务逻辑
真实角色 实现抽象角色的具体类,包含真正的业务逻辑(如周杰伦本人唱歌)
代理角色 实现 / 继承抽象角色,持有真实角色的引用,调用真实角色方法并附加增强逻辑(如经纪人)

二、静态代理

静态代理是最基础的代理方式,代理类需要手动编写,且与目标类实现相同的接口。

2.1 代码实现

步骤 1:定义抽象角色(Star 接口)
java 复制代码
package com.hg.proxy.StaticProxy;

public interface Star {
    /** 面谈 */
    void confer();
    /** 签合同 */
    void signContract();
    /** 订票 */
    void bookTicket();
    /** 唱歌 */
    void sing();
    /** 收钱 */
    void collectMoney();
}
步骤 2:定义真实角色(RealStar - 周杰伦)
java 复制代码
package com.hg.proxy.StaticProxy;

// 真实角色:周杰伦(只负责核心业务------唱歌)
public class RealStar implements Star {
    @Override
    public void confer() {}

    @Override
    public void signContract() {}

    @Override
    public void bookTicket() {}

    @Override
    public void sing() {
        System.out.println("RealStar(周杰伦本人).sing() ------ 七里香~");
    }

    @Override
    public void collectMoney() {}
}
步骤 3:定义代理角色(ProxyStar - 经纪人)
java 复制代码
package com.hg.proxy.StaticProxy;

// 代理角色:经纪人(处理非核心业务,调用周杰伦的核心方法)
public class ProxyStar implements Star {
    
    // 持有真实角色的引用
    private Star star;

    public ProxyStar(Star star) {
        this.star = star;
    }

    @Override
    public void confer() {
        System.out.println("ProxyStar.confer() ------ 经纪人面谈");
    }

    @Override
    public void signContract() {
        System.out.println("ProxyStar.signContract() ------ 经纪人签合同");
    }

    @Override
    public void bookTicket() {
        System.out.println("ProxyStar.bookTicket() ------ 经纪人订机票");
    }

    @Override
    public void sing() {
        // 调用真实角色的核心方法
        star.sing();
    }

    @Override
    public void collectMoney() {
        System.out.println("ProxyStar.collectMoney() ------ 经纪人收钱");
    }
}
步骤 4:测试静态代理
java 复制代码
package com.hg.proxy.StaticProxy;

public class Client {
    public static void main(String[] args) {
        // 创建真实角色
        Star realStar = new RealStar();
        // 创建代理角色,传入真实角色
        Star proxy = new ProxyStar(realStar);
        
        // 调用代理方法(经纪人处理前置/后置,周杰伦只负责唱歌)
        proxy.confer();
        proxy.signContract();
        proxy.bookTicket();
        proxy.sing();
        proxy.collectMoney();
    }
}
输出结果
java 复制代码
ProxyStar.confer() ------ 经纪人面谈
ProxyStar.signContract() ------ 经纪人签合同
ProxyStar.bookTicket() ------ 经纪人订机票
RealStar(周杰伦本人).sing() ------ 七里香~
ProxyStar.collectMoney() ------ 经纪人收钱

2.2 静态代理缺点

  1. 代码冗余:代理类和目标类实现相同接口,接口方法越多,重复代码越多;
  2. 扩展性差:代理类仅服务于一种类型的目标对象,若需代理其他类(如 DeptService、UserService),需重新编写代理类;
  3. 维护成本高:接口新增方法时,所有实现类(目标类 + 代理类)都需修改。

三、JDK 动态代理

JDK 动态代理是基于反射机制 实现的动态代理,无需手动编写代理类,核心是 java.lang.reflect.ProxyInvocationHandler 接口。

核心特点

  • 目标类必须实现至少一个接口;
  • 代理类由 JVM 动态生成(类名格式:com.sun.proxy.$Proxy0);
  • 可代理任意实现了接口的目标类,扩展性强。

3.1 代码实现

步骤 1:定义抽象角色(Star 接口)
java 复制代码
package com.hg.JdkProxy;

public interface Star {
    /** 唱歌(核心业务) */
    void sing();
}
步骤 2:定义真实角色(RealStar)
java 复制代码
package com.hg.JdkProxy;

// 真实角色:周杰伦(仅实现核心业务)
public class RealStar implements Star {
    @Override
    public void sing() {
        System.out.println("周杰伦:快使用双截棍,哼哼哈嘿....");
    }
}
步骤 3:定义动态代理工厂
java 复制代码
package com.hg.JdkProxy;

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

// 代理工厂:动态生成代理对象
public class ProxyFactory {

    // 目标对象(可接收任意类型的实现类)
    private Object realObj;

    public ProxyFactory(Object realObj) {
        this.realObj = realObj;
    }

    // 获取动态代理对象
    public Object getProxyObject() {
        return Proxy.newProxyInstance(
                // 1. 目标类的类加载器
                realObj.getClass().getClassLoader(),
                // 2. 目标类实现的所有接口(代理类会实现这些接口)
                realObj.getClass().getInterfaces(),
                // 3. 调用处理器:定义增强逻辑
                new InvocationHandler() {
                    /**
                     * 代理方法的核心逻辑
                     * @param proxy  代理对象(一般不用)
                     * @param method 目标方法(如sing())
                     * @param args   目标方法的参数
                     * @return       目标方法的返回值
                     * @throws Throwable 异常
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 前置增强:经纪人处理非核心业务
                        System.out.println("真正的方法执行前!");
                        System.out.println("面谈,签合同,预付款,订机票");

                        // 调用目标对象的核心方法
                        Object result = method.invoke(realObj, args);

                        // 后置增强:经纪人收尾
                        System.out.println("真正的方法执行后!");
                        System.out.println("收尾款");

                        return result;
                    }
                }
        );
    }
}
步骤 4:测试 JDK 动态代理
java 复制代码
package com.hg.JdkProxy;

public class Client {
    public static void main(String[] args) {
        // 1. 创建目标对象
        RealStar realStar = new RealStar();
        // 2. 创建代理工厂,传入目标对象
        ProxyFactory proxyFactory = new ProxyFactory(realStar);
        // 3. 获取动态代理对象
        Star proxyObject = (Star) proxyFactory.getProxyObject();
        
        // 打印代理类类型(com.sun.proxy.$Proxy0)
        System.out.println(proxyObject.getClass());
        // 4. 调用代理方法
        proxyObject.sing();
    }
}
输出结果
java 复制代码
class com.sun.proxy.$Proxy0
真正的方法执行前!
面谈,签合同,预付款,订机票
周杰伦:快使用双截棍,哼哼哈嘿....
真正的方法执行后!
收尾款

四、CGLIB 动态代理

CGLIB(Code Generation Library)是基于字节码生成的动态代理,无需目标类实现接口,通过继承目标类生成子类作为代理类。

核心特点

  • 目标类无需实现接口;
  • 基于 ASM 框架生成目标类的子类,重写目标方法;
  • Spring 核心包已集成 CGLIB,无需额外引入(若单独使用需引入依赖)。

4.1 依赖引入(可选)

XML 复制代码
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

4.2 代码实现

步骤 1:定义真实角色(RealStar - 无需实现接口)
java 复制代码
package com.hg.proxy.CglibProxy;

// 真实角色:周杰伦(无接口)
public class RealStar {
    public void sing() {
        System.out.println("RealStar(周杰伦本人).sing() ------ 青花瓷~");
    }
}
步骤 2:定义 CGLIB 代理工厂
java 复制代码
package com.hg.proxy.CglibProxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// CGLIB代理工厂:生成目标类的子类作为代理类
public class ProxyFactory implements MethodInterceptor {

    // 目标对象
    private Object realObj;

    public ProxyFactory(Object realObj) {
        this.realObj = realObj;
    }

    // 获取代理对象(子类)
    public Object getProxyObject() {
        // 1. 创建CGLIB增强器
        Enhancer en = new Enhancer();
        // 2. 设置父类(目标类)
        en.setSuperclass(realObj.getClass());
        // 3. 设置回调函数(当前类实现MethodInterceptor)
        en.setCallback(this);
        // 4. 生成子类(代理类)并返回
        return en.create();
    }

    /**
     * 增强逻辑入口
     * @param obj          代理对象(子类)
     * @param method       目标方法(父类)
     * @param args         方法参数
     * @param methodProxy  代理方法(子类)
     * @return             目标方法返回值
     * @throws Throwable   异常
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 前置增强
        System.out.println("真正的方法执行前!");
        System.out.println("面谈,签合同,预付款,订机票");

        // 调用目标对象的方法
        Object result = method.invoke(realObj, args);

        // 后置增强
        System.out.println("真正的方法执行后!");
        System.out.println("收尾款");

        return result;
    }
}
步骤 3:测试 CGLIB 动态代理
java 复制代码
package com.hg.proxy.CglibProxy;

public class Client {
    public static void main(String[] args) {
        // 1. 创建目标对象
        RealStar realStar = new RealStar();
        // 2. 创建代理工厂
        ProxyFactory proxyFactory = new ProxyFactory(realStar);
        // 3. 获取代理对象(子类)
        RealStar proxyObject = (RealStar) proxyFactory.getProxyObject();
        // 4. 调用代理方法
        proxyObject.sing();
    }
}
输出结果
java 复制代码
真正的方法执行前!
面谈,签合同,预付款,订机票
RealStar(周杰伦本人).sing() ------ 青花瓷~
真正的方法执行后!
收尾款

五、三种代理方式对比

特性 静态代理 JDK 动态代理 CGLIB 动态代理
目标类要求 实现接口 必须实现接口 无需实现接口(继承)
实现方式 手动编写代理类 反射机制 字节码生成(ASM)
代理类生成 编译期固定 运行期动态生成 运行期动态生成子类
性能 无额外开销 反射有轻微开销 字节码生成开销略低
扩展性 差(仅支持单一目标类) 好(支持任意接口实现类) 好(支持任意类)
适用场景 简单场景、固定目标类 目标类实现接口的场景 目标类无接口的场景

六、总结

  1. 静态代理:适合简单场景,代码直观但扩展性差,适用于目标类和方法固定的场景;
  2. JDK 动态代理:Spring AOP 默认实现方式,依赖接口,通过反射动态生成代理类,适合大多数接口化的业务场景;
  3. CGLIB 动态代理 :弥补 JDK 动态代理的接口限制,通过继承生成子类,适合无接口的目标类(如 Spring 中的@Configuration类代理)。

实际开发中,Spring 框架已封装了两种动态代理的实现(自动选择:有接口用 JDK,无接口用 CGLIB),掌握代理模式的核心思想,能帮助我们更好地理解 Spring AOP、事务管理等核心功能的底层原理。

相关推荐
prince_zxill1 小时前
Raspberry PI传感器数据上云:Python IoT集成
开发语言·python·物联网
loading小马1 小时前
解决idea2024版本Services栏没有显示Springboot窗口问题
java·spring boot·后端
好学且牛逼的马1 小时前
Spring Boot 3 核心实战指南
java·spring boot·后端
东离与糖宝1 小时前
Spring Boot 4最新适配指南:Java 21+虚拟线程+AOT编译,冷启动压到100ms内
java·人工智能
etcix1 小时前
go cli translator that use bing api and youdao api
开发语言·elasticsearch·golang
147API1 小时前
Claude API 429 限速治理:RPM/ITPM/OTPM + 令牌桶(Kotlin)
java·spring·kotlin·claude
亚历山大海1 小时前
AiPPT接口文件PHP版本全,智能生成PPT文件并下载
开发语言·ai·php
北凉军1 小时前
idea无限试用30天
java·ide·intellij-idea
ノBye~1 小时前
Spring的IOC详解
java·开发语言
147API1 小时前
Claude 模型选型:Opus/Sonnet/Haiku + 成本/限速预算(Kotlin)
android·开发语言·kotlin·147api