代理模式
1. 模式概述
1.1 定义
代理模式(Proxy Design Pattern)是一种结构型设计模式,它通过提供一个代理对象来控制对另一个对象的访问。代理对象作为客户端和目标对象之间的中介,可以在访问目标对象前后添加额外的处理逻辑,如权限控制、日志记录、性能监控等。
代理模式在不改变原始类(或者叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。
1.2 核心思想
- 控制访问:代理对象控制对目标对象的访问
- 透明性:客户端无需知道代理的存在,可以像使用目标对象一样使用代理对象
- 增强功能:在不修改目标对象的前提下,通过代理对象增强功能
1.3 现实生活类比
代理模式在现实生活中有很多例子:
- 海外代购:您不直接购买海外商品,而是通过代购代理
- 房产中介:您不直接与房东交易,而是通过中介代理
- 律师代理:您不直接出庭,而是通过律师代理
- 明星经纪人:粉丝不直接联系明星,而是通过经纪人代理

1.4 软件开发中的应用
代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到了中介的作用,它去掉客户不能看到的内容和服务或增加客户需要的额外的新服务。

2. 模式结构
2.1 角色定义
代理模式包含三个核心角色:
-
抽象主题(Subject)类:声明了真实主题和代理主题的共同接口,确保在任何使用真实主题的地方都可以用代理主题去替换。
-
真实主题(Real Subject)类:实现了抽象主题类中的具体业务,是代理对象所代表的真实对象,也是最终要引用的对象。
-
代理类(Proxy):代理也实现了抽象主题中的具体业务,其内部包含着对真实主题的引用,控制着对真实主题的访问。

2.2 类图关系
- 代理类持有对真实主题的引用
- 代理类和真实主题都实现相同的接口
- 客户端通过代理类访问真实主题
3. 实现方式
3.1 静态代理
静态代理在编译时就确定了代理关系,需要为每个目标类创建对应的代理类。
优点
- 可以在不修改目标类的前提下,扩展目标类的功能
- 实现简单,易于理解
- 编译时就能确定代理关系
缺点
- 会产生大量代理类,代码冗余
- 接口变更时,目标对象和代理对象都需要修改
- 每个目标类都需要对应的代理类
/**
* 抽象主题类 - 定义共同接口
*/
public interface IUserDao {
void save();
void update();
void delete();
}
/**
* 真实主题类 - 实现具体业务逻辑
*/
public class UserDaoImpl implements IUserDao {
@Override
public void save() {
System.out.println("保存用户数据到 数据库");
}
@Override
public void update() {
System.out.println("更新用户数据");
}
@Override
public void delete() {
System.out.println("删除用户数据");
}
}
/**
* 代理类 - 控制对真实主题的访问
* 必须有目标类的引用
*/
public class UserDaoProxy implements IUserDao {
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
@Override
public void save() {
System.out.println("开启事务");
try {
target.save();
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
throw e;
}
}
@Override
public void update() {
System.out.println("开启事务");
try {
target.update();
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
throw e;
}
}
@Override
public void delete() {
System.out.println("开启事务");
try {
target.delete();
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
throw e;
}
}
}
/**
* 测试静态代理
*/
@Test
public void testStaticProxy() {
// 创建目标对象
IUserDao target = new UserDaoImpl();
// 创建代理对象
IUserDao proxy = new UserDaoProxy(target);
// 通过代理对象调用方法
proxy.save();
proxy.update();
proxy.delete();
}
3.2 JDK 动态代理
JDK 动态代理基于 Java 反射机制实现,通过 java.lang.reflect.Proxy 类在运行时动态生成代理类。它要求目标对象必须实现接口,代理对象会实现相同的接口,并将方法调用委托给 InvocationHandler 处理。
核心类说明
java.lang.reflect.Proxy:主要方法为:
static Object newProxyInstance(
ClassLoader loader, // 目标对象使用的类加载器
Class<?>[] interfaces, // 目标对象实现的接口类型
InvocationHandler h // 事件处理器
)
// 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
java.lang.reflect.InvocationHandler:主要方法为:
Object invoke(Object proxy, Method method, Object[] args)
// 在代理实例上处理方法调用并返回结果。
实现示例
package com.e6yun.project.structural.proxy.example2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工厂 - 动态生成代理对象
*/
public class ProxyFactory {
// 维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 为目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类使用的类加载器
target.getClass().getInterfaces(), // 目标对象实现的接口类型
new InvocationHandler() {
/**
* proxy 代理对象
* method 对应于代理对象上调用的接口方法实例
* args 代理对象调用接口方法时传递的实际参数
* @return 返回目标对象的方法的返回值,没有返回值返回 null 就可以了
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
try {
// 执行目标对象的方法
Object result = method.invoke(target, args);
System.out.println("提交事务");
return result;
} catch (Exception e) {
System.out.println("回滚事务");
throw e;
}
}
} // 事件处理器
);
}
}
package com.e6yun.project.structural.proxy.example2;
import com.e6yun.project.structural.proxy.example1.IUserDao;
import com.e6yun.project.structural.proxy.example1.UserDaoImpl;
import org.junit.Test;
public class TestProxy {
@Test
public void testDynamicProxy() {
// 创建目标对象
IUserDao userDao = new UserDaoImpl();
System.out.println("目标对象类型: " + userDao.getClass());
// 创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory(userDao);
// 获取代理对象
IUserDao proxyInstance = (IUserDao) proxyFactory.getProxyInstance();
System.out.println("代理对象类型: " + proxyInstance.getClass());
// 通过代理对象调用方法
proxyInstance.save();
proxyInstance.update();
proxyInstance.delete();
}
}