前言
在软件开发中,代理模式是一种常用的结构型设计模式,其核心思想是通过代理对象控制对目标对象的访问,并可以在目标方法执行前后添加额外的增强逻辑。本文将详细讲解代理模式的三种实现方式:静态代理、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 静态代理缺点
- 代码冗余:代理类和目标类实现相同接口,接口方法越多,重复代码越多;
- 扩展性差:代理类仅服务于一种类型的目标对象,若需代理其他类(如 DeptService、UserService),需重新编写代理类;
- 维护成本高:接口新增方法时,所有实现类(目标类 + 代理类)都需修改。
三、JDK 动态代理
JDK 动态代理是基于反射机制 实现的动态代理,无需手动编写代理类,核心是 java.lang.reflect.Proxy 和 InvocationHandler 接口。
核心特点
- 目标类必须实现至少一个接口;
- 代理类由 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) |
| 代理类生成 | 编译期固定 | 运行期动态生成 | 运行期动态生成子类 |
| 性能 | 无额外开销 | 反射有轻微开销 | 字节码生成开销略低 |
| 扩展性 | 差(仅支持单一目标类) | 好(支持任意接口实现类) | 好(支持任意类) |
| 适用场景 | 简单场景、固定目标类 | 目标类实现接口的场景 | 目标类无接口的场景 |
六、总结
- 静态代理:适合简单场景,代码直观但扩展性差,适用于目标类和方法固定的场景;
- JDK 动态代理:Spring AOP 默认实现方式,依赖接口,通过反射动态生成代理类,适合大多数接口化的业务场景;
- CGLIB 动态代理 :弥补 JDK 动态代理的接口限制,通过继承生成子类,适合无接口的目标类(如 Spring 中的
@Configuration类代理)。
实际开发中,Spring 框架已封装了两种动态代理的实现(自动选择:有接口用 JDK,无接口用 CGLIB),掌握代理模式的核心思想,能帮助我们更好地理解 Spring AOP、事务管理等核心功能的底层原理。