1. 什么是代理?
代理就是替身、经纪人。
生活例子:
明星经纪人:明星不直接见粉丝,由经纪人代理
房产中介:房东不直接卖房,由中介代理
在程序中:
不直接调用目标对象,而是通过代理对象调用可以在调用前后添加额外操作(权限校验、日志记录等)
2. 静态代理
在编译时就确定代理类和目标类的关系
java
// 1. 定义接口
interface IPerson {
void findLove(); // 找对象
void findJob(); // 找工作
}
// 2. 目标类(明星本人)
class Star implements IPerson {
@Override
public void findLove() {
System.out.println("明星要求:肤白貌美大长腿");
}
@Override
public void findJob() {
System.out.println("明星要求:日薪208万");
}
}
// 3. 代理类(经纪人)
class Agent implements IPerson {
private Star star; // 持有目标对象的引用
public Agent(Star star) {
this.star = star;
}
@Override
public void findLove() {
System.out.println("经纪人筛选合适的人选"); // 前置操作
star.findLove(); // 实际调用目标
System.out.println("经纪人安排约会"); // 后置操作
}
@Override
public void findJob() {
System.out.println("经纪人洽谈合作");
star.findJob();
System.out.println("经纪人签约");
}
}
// 4. 使用
public class Test {
public static void main(String[] args) {
Star star = new Star();
Agent agent = new Agent(star);
agent.findLove(); // 通过代理调用
}
}
静态代理的优缺点
每个目标类都需要写一个代理类,代码冗余,接口修改,代理类也要改
3. 动态代理
程序运行时动态生成代理类
(1)JDK动态代理(基于接口): 核心类是**java.lang.reflect.Proxy**
java
// 1. 定义接口
interface IPerson {
void findLove();
void findJob();
}
// 2. 目标类
class Star implements IPerson {
@Override
public void findLove() {
System.out.println("明星要求:肤白貌美大长腿");
}
@Override
public void findJob() {
System.out.println("明星要求:日薪208万");
}
}
// 3. 使用JDK动态代理
public class Test {
public static void main(String[] args) {
Star star = new Star();
// 创建代理对象
IPerson proxy = (IPerson) Proxy.newProxyInstance(
Star.class.getClassLoader(), // 类加载器
Star.class.getInterfaces(), // 接口数组
new InvocationHandler() { // 调用处理器
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// proxy: 代理对象
// method: 正在调用的方法
// args: 方法参数
System.out.println("前置处理:" + method.getName());
// 调用目标方法
Object result = method.invoke(star, args);
System.out.println("后置处理");
return result;
}
}
);
// 通过代理调用
proxy.findLove();
}
}
执行流程:

JDK动态代理原理图

(2)CGLIB动态代理(基于继承)
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
class Star {
public void findLove() {
System.out.println("明星要求:肤白貌美大长腿");
}
}
public class Test {
public static void main(String[] args) {
Star star = new Star();
// 创建代理
Star proxy = (Star) Enhancer.create(
Star.class, // 目标类
new MethodInterceptor() { // 回调
@Override
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy proxy) {
System.out.println("前置处理");
Object result = method.invoke(star, args);
System.out.println("后置处理");
return result;
}
}
);
proxy.findLove();
}
}
CGLIB原理:
继承目标类,重写方法
通过superclass调用父类方法
(3)JDK动态代理 vs CGLIB
| 特性 | JDK动态代理 | CGLIB |
|---|---|---|
| 原理 | 基于接口(实现接口) | 基于继承(继承目标类 |
| 要求 | 目标类必须实现接口 | 目标类可以没有接口 |
| 性能 | 较快(直接生成类) | 较慢(生成子类) |
| JDK自带 | ✓ 是 | ✗ 需要导包 |
| 无法代理 | final类、private方法 | final类、final方法 |
4. 代理的实际应用
(1)AOP(面向切面编程)
java
@Aspect // 切面类
class LogAspect {
@Before("execution(* com.qcby.service.*.*(..))")
public void before() {
System.out.println("方法执行前记录日志");
}
@After("execution(* com.qcby.service.*.*(..))")
public void after() {
System.out.println("方法执行后记录日志");
}
}
(2)事务管理
java
public class TransactionProxy {
public static <T> T getProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
try {
System.out.println("开启事务");
Object result = method.invoke(target, args);
System.out.println("提交事务");
return result;
} catch (Exception e) {
System.out.println("回滚事务");
throw e;
}
}
);
}
}
// 使用
UserService userService = new UserServiceImpl();
UserService proxy = TransactionProxy.getProxy(userService);
proxy.save(user); // 自动开启/提交事务
(3)远程调用(RPC)
Dubbo、Feign等框架用代理模拟远程调用:
-
客户端调用代理方法
-
代理将调用封装成网络请求
-
服务器执行并返回结果
5. 代理的对比
| 代理类型 | 实现方式 | 适用场景 |
|---|---|---|
| 静态代理 | 手动写代理类 | 简单场景 |
| JDK动态代理 | 实现接口,运行时生成 | 有接口的类 |
| CGLIB代理 | 继承目标类 | 无接口的类 |