代理模式
| 类型 | 优点 | 缺点 |
|---|---|---|
| 静态代理 | 简单直观,类型安全 | 代码重复,每个接口都要写代理类 |
| JDK动态代理 | 无需每个接口写代理类 | 只能代理接口 |
| CGLIB动态代理 | 可以代理类 | 需要第三方库,性能略低 |
| SpringAop | 声名式,零入侵 | 只能代理public方法 |
代码
第一部分:普通例子(静态代理,编译期绑定)
核心痛点:为了给每个方法加日志,你必须手动创建一个实现相同接口的代理类,代码冗余且难以维护。
java
// 1. 定义业务接口
public interface UserService {
void saveUser(String name);
String findUser(Long id);
}
// 2. 真实业务实现(只关心核心逻辑)
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String name) {
System.out.println("核心业务:保存用户 " + name);
}
@Override
public String findUser(Long id) {
System.out.println("核心业务:查询用户 " + id);
return "User_" + id;
}
}
// 3. 静态代理类(必须实现同一接口,并持有真实对象引用)
public class UserServiceStaticProxy implements UserService {
private final UserService target; // 目标对象
public UserServiceStaticProxy(UserService target) {
this.target = target;
}
@Override
public void saveUser(String name) {
System.out.println("[静态代理] 开始保存...");
long start = System.currentTimeMillis();
target.saveUser(name); // 调用真实逻辑
System.out.println("[静态代理] 保存结束,耗时: " + (System.currentTimeMillis() - start) + "ms");
}
@Override
public String findUser(Long id) {
System.out.println("[静态代理] 开始查询...");
long start = System.currentTimeMillis();
String result = target.findUser(id);
System.out.println("[静态代理] 查询结束,耗时: " + (System.currentTimeMillis() - start) + "ms");
return result;
}
}
// 4. 客户端调用(必须显式创建代理对象)
public class Client {
public static void main(String[] args) {
// 真实对象
UserService realService = new UserServiceImpl();
// 手动包装成代理
UserService proxy = new UserServiceStaticProxy(realService);
proxy.saveUser("张三");
// 输出:开始保存... -> 核心业务... -> 结束,耗时...
proxy.findUser(1L);
}
}
静态代理的致命伤:如果我有 OrderService、ProductService 等几十个接口,就得写几十个代理类,且每个方法都要重复写日志代码,完全违背 DRY 原则。
第二部分:Spring中的例子(动态代理 + AOP,运行时绑定)
在Spring中,你完全不需要手动创建 UserServiceStaticProxy 类。Spring 会在容器启动时,利用 JDK动态代理 或 CGLIB 自动生成代理对象,并将横切逻辑织入。
java
// 1. 业务代码保持纯净(无任何侵入)
@Service // 交给Spring管理
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String name) {
System.out.println("核心业务:保存用户 " + name);
}
@Override
public String findUser(Long id) {
System.out.println("核心业务:查询用户 " + id);
return "User_" + id;
}
}
// 2. 定义一个切面类(拦截所有Service层的public方法)
@Aspect
@Component
public class LoggingAspect {
// 切点表达式:拦截 service 包下所有类的所有方法
@Around("execution(* com.example.service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
System.out.println("[Spring AOP] 开始执行: " + methodName);
long start = System.currentTimeMillis();
try {
// 重点:这里执行目标对象的真实方法(相当于静态代理里的 target.saveUser())
Object result = joinPoint.proceed();
return result;
} finally {
long duration = System.currentTimeMillis() - start;
System.out.println("[Spring AOP] 执行结束,耗时: " + duration + "ms");
}
}
}
// 3. 业务调用(完全无感知,直接从容器拿Bean)
@RestController
public class UserController {
@Autowired
private UserService userService; // 注意:Spring注入的其实是代理对象!
@GetMapping("/save")
public void test() {
userService.saveUser("李四");
// 实际输出:Spring AOP 开始 -> 核心业务 -> Spring AOP 结束
}
}