Skip to content

代理模式

基础概念

什么是代理模式?

答案: 代理模式(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]);
    }
}

练习题

  1. 静态代理和动态代理的区别?
  2. JDK动态代理为什么必须基于接口?
  3. CGLIB如何代理final方 Spring AOP如何选择代理方式?
  4. 如何实现一个简单的RPC框架?

Released under the MIT License.