代理模式
基础概念
什么是代理模式?
答案: 代理模式(Proxy Pattern)为其他对象提供一种代理以控制对这个对象的访问。
使用场景:
- 远程代理:访问远程对象
- 虚拟代理:延迟加载
- 保护代理:权限控制
- 缓存代理:缓存结果
优点:
- 职责清晰
- 扩展性好
- 智能化
缺点:
- 增加系统复杂度
- 可能影响性能
静态代理
静态代理的实现?
答案:
java
// 接口
public interface UserService {
void save(User user);
User getUser(Long id);
}
// 真实对象
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
System.out.println("保存用户: " + user.getName());
}
@Override
public User getUser(Long id) {
System.out.println("查询用户: " + id);
return new User(id, "张三");
}
}
// 代理对象
public class UserServiceProxy implements UserService {
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void save(User user) {
System.out.println("代理:保存前日志");
target.save(user);
System.out.println("代理:保存后日志");
}
@Override
public User getUser(Long id) {
System.out.println("代理:查询前日志");
User user = target.getUser(id);
System.out.println("代理:查询后日志");
return user;
}
}
// 使用
UserService userService = new UserServiceImpl();
UserService proxy = new UserServiceProxy(userService);
proxy.save(new User(1L, "张三"));缺点:
- 每个接口都需要创建代理类
- 代码重复
JDK动态代理
JDK动态代理的实现?
答案:
java
// 接口
public interface UserService {
void save(User user);
User getUser(Long id);
}
// 真实对象
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
System.out.println("保存用户: " + user.getName());
}
@Override
public User getUser(Long id) {
System.out.println("查询用户: " + id);
return new User(id, "张三");
}
}
// 动态代理处理器
public class LogInvocationHandler implements InvocationHandler {
private Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前: " + method.getName());
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("方法执行后: " + method.getName() + ", 耗时: " + (end - start) + "ms");
return result;
}
}
// 使用
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new LogInvocationHandler(userService)
);
proxy.save(new User(1L, "张三"));原理:
java
// Proxy.newProxyInstance()做了什么?
1. 根据接口生成代理类的字节码
2. 加载代理类
3. 创建代理对象
4. 代理对象的方法调用会转发到InvocationHandler.invoke()限制:
- 只能代理接口
- 目标类必须实现接口
CGLIB动态代理
CGLIB动态代理的实现?
答案:
java
// 目标类(无需实现接口)
public class UserService {
public void save(User user) {
System.out.println("保存用户: " + user.getName());
}
public User getUser(Long id) {
System.out.println("查询用户: " + id);
return new User(id, "张三");
}
}
// CGLIB代理
public class LogMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("方法执行前: " + method.getName());
long start = System.currentTimeMillis();
Object result = proxy.invokeSuper(obj, args);
long end = System.currentTimeMillis();
System.out.println("方法执行后: " + method.getName() + ", 耗时: " + (end - start) + "ms");
return result;
}
}
// 使用
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new LogMethodInterceptor());
UserService proxy = (UserService) enhancer.create();
proxy.save(new User(1L, "张三"));原理:
- 通过继承目标类生成子类
- 重写父类方法
- 方法调用会转发到MethodInterceptor.intercept()
限制:
- 不能代理final类
- 不能代理final方法
JDK vs CGLIB
JDK动态代理和CGLIB的区别?
答案:
| 特性 | JDK动态代理 | CGLIB |
|---|---|---|
| 实现方式 | 基于接口 | 基于继承 |
| 要求 | 必须实现接口 | 不能是final类 |
| 性能 | 较低 | 较高 |
| 生成速度 | 快 | 慢 |
| 用场景 | 有接口 | 无接口 |
Spring的选择策略:
java
// 有接口 → JDK动态代理
public interface UserService {
void save(User user);
}
@Service
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
// ...
}
}
// 无接口 → CGLIB代理
@Service
public class UserService {
public void save(User user) {
// ...
}
}
// 强制使用CGLIB
@EnableAspectJAutoProxy(proxyTargetClass = true)实际应用
日志代理
答案:
java
public class LogProxy {
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("=== 方法调用开始 ===");
System.out.println("类名: " + target.getClass().getName());
System.out.println("方法名: " + method.getName());
System.out.println("参数: " + Arrays.toString(args));
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("返回值: " + result);
System.out.println("耗时: " + (end - start) + "ms");
System.out.println("=== 方法调用结束 ===");
return result;
}
);
}
}事务代理
答案:
java
public class TransactionProxy {
public static <T> T createProxy(T target, DataSource dataSource) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false);
Object result = method.invoke(target, args);
conn.commit();
return result;
} catch (Exception e) {
conn.rollback();
throw e;
} finally {
conn.close();
}
}
);
}
}缓存代理
答案:
java
public class CacheProxy {
private static Map<String, Object> cache = new ConcurrentHashMap<>();
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
// 生成缓存key
String key = method.getName() + Arrays.toString(args);
// 查询缓存
if (cache.containsKey(key)) {
System.out.println("从缓存获取: " + key);
return cache.get(key);
}
// 执行方法
Object result = method.invoke(target, args);
// 存入缓存
cache.put(key, result);
System.out.println("存入缓存: " + key);
return result;
}
);
}
}权限代理
答案:
java
public class PermissionProxy {
public static <T> T createProxy(T target, String currentUser) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
// 检查权限注解
RequirePermission annotation = method.getAnnotation(RequirePermission.class);
if (annotation != null) {
String permission = annotation.value();
if (!hasPermission(currentUser, permission)) {
throw new SecurityException("无权限: " + permission);
}
}
return method.invoke(target, args);
}
);
}
private static boolean hasPermission(String user, String permission) {
// 检查权限逻辑
return true;
}
}
// 权限注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequirePermission {
String value();
}
// 使用
public interface OrderService {
@RequirePermission("order:delete")
void deleteOrder(Long id);
}Spring AOP
Spring AOP的实现原理?
答案:
Spring AOP基于代理模式实现。
java
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("方法执行前");
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long end = System.currentTimeMillis();
System.out.println("方法执行后,耗时: " + (end - start) + "ms");
return result;
}
}原理:
1. Spring扫描@Aspect注解
2. 为目标Bean创建代理对象
3. 代理对象拦截方法调用
4. 执行切面逻辑
5. 调用目标方法MyBatis中的代理
MyBatis的Mapper代理?
答案:
java
// Mapper接口
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
}
// MyBatis生成代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectById(1L);原理:
java
// MapperProxy
public class MapperProxy<T> implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 解析SQL注解
// 2. 构建SQL语句
// 3. 执行SQL
// 4. 封装结果
return sqlSession.selectOne(statement, args[0]);
}
}练习题
- 静态代理和动态代理的区别?
- JDK动态代理为什么必须基于接口?
- CGLIB如何代理final方 Spring AOP如何选择代理方式?
- 如何实现一个简单的RPC框架?