一、动态代理的核心原理

  1. JDK动态代理
    • 原理:通过Proxy类和InvocationHandler接口,动态生成一个实现目标接口的代理类。代理类继承自Proxy,并持有InvocationHandler实例,调用方法时通过反射调用目标对象的方法。
    • 限制:必须基于接口实现,无法代理final类或未实现接口的类。
    • 底层机制:利用JVM反射机制生成代理类字节码,并通过类加载器加载。
  2. CGLIB动态代理
    • 原理:基于字节码生成技术(如ASM框架),动态创建目标类的子类作为代理类,通过覆盖方法实现增强逻辑。
    • 限制:无法代理final类、final方法或静态方法。
    • 底层机制:通过修改字节码生成代理类,无需依赖接口。

二、动态代理的实现步骤

1. JDK动态代理实现

步骤:

  • 定义接口:被代理类需实现接口。
  • 创建InvocationHandler:实现invoke方法,在方法调用前后添加增强逻辑。
  • 生成代理对象:通过Proxy.newProxyInstance()方法,传入类加载器、接口列表和InvocationHandler实例。
    示例代码(登录权限控制):
// 接口定义
public interface LoginManager {
    boolean login(String username, String password);
}
// 目标类实现接口
public class LoginService implements LoginManager {
    @Override
    public boolean login(String username, String password) {
        System.out.println("验证用户名密码");
        return true;
    }
}
// InvocationHandler实现类
public class LoginHandler implements InvocationHandler {
    private Object target;
    public LoginHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args);
        if ((boolean) result) {
            System.out.println("权限验证通过");
        } else {
            System.out.println("权限不足");
        }
        return result;
    }
}
// 生成代理对象
LoginManager proxy = (LoginManager) Proxy.newProxyInstance(
    LoginService.class.getClassLoader(),
    LoginService.class.getInterfaces(),
    new LoginHandler(new LoginService())
);

2. CGLIB动态代理实现

步骤:

  • 引入依赖:需添加CGLIB库(如cglib-nodep)。
  • 创建MethodInterceptor:实现intercept方法,通过proxy.invokeSuper()调用父类方法。
  • 生成代理对象:使用Enhancer类设置父类并创建子类实例。
    示例代码(日志记录):
// 目标类
public class UserDao {
    public void save() {
        System.out.println("保存用户数据");
    }
}
// MethodInterceptor实现类
public class UserDaoInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始执行方法");
        Object result = proxy.invokeSuper(obj, args); // 调用父类方法
        System.out.println("方法执行结束");
        return result;
    }
}
// 生成代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserDao.class);
enhancer.setCallback(new UserDaoInterceptor());
UserDao proxy = (UserDao) enhancer.create();

三、JDK与CGLIB动态代理的对比

维度JDK动态代理CGLIB动态代理
代理方式基于接口生成代理类基于类生成子类
性能JDK 8后性能优于CGLIB(反射优化)方法调用性能较高(无反射开销)
适用场景有接口的类增强无接口的类增强
限制无法代理final类/方法无法代理final类/方法/静态方法
依赖JDK内置需引入CGLIB库

四、动态代理的典型应用场景

  1. 事务管理
    在方法调用前后开启和提交事务,如Spring AOP中的@Transactional注解实现。
  2. 日志记录
    自动记录方法执行时间、参数及返回值,例如:
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = method.invoke(target, args);
        System.out.println("方法执行耗时:" + (System.currentTimeMillis() - startTime) + "ms");
        return result;
    }
    
  3. 权限控制
    在方法调用前验证用户权限,如文章1中示例的登录权限校验。
  4. 缓存优化
    缓存高频调用结果,减少重复计算:
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String cacheKey = method.getName() + Arrays.toString(args);
        if (cache.containsKey(cacheKey)) {
            return cache.get(cacheKey);
        }
        Object result = method.invoke(target, args);
        cache.put(cacheKey, result);
        return result;
    }
    

五、动态代理的选型建议

  1. 优先使用JDK动态代理:
    • 目标类已实现接口且性能要求较高(如JDK 8+环境)。
    • 需要与Spring AOP无缝集成(默认使用JDK代理)。
  2. 使用CGLIB动态代理:
    • 目标类未实现接口(如第三方库类)。
    • 需要强制使用(如Spring配置proxy-target-class=true)。
  3. 混合使用场景:
    • 在Spring中,若需同时支持接口和非接口类,可通过@EnableAspectJAutoProxy(proxyTargetClass=true)强制使用CGLIB。

六、总结

动态代理通过运行时生成代理类,实现了代码的解耦与功能增强,是AOP框架(如Spring)的核心技术。JDK动态代理适合接口驱动的场景,而CGLIB则弥补了无接口类的代理需求。实际开发中需根据目标类特性、性能要求及框架集成需求选择合适方案。

更多推荐