java基础--动态代理的定义

经典总结

动态代理 是 Java 中的一种技术,它让程序在运行时能够自动创建代理对象,然后通过代理对象来拦截增强你原本的方法。

1. 怎么实现

  • JDK 动态代理:它用的是 Java 反射,所以被代理的类必须实现接口
  • CGLIB 动态代理:它通过 字节码操作,即使类没有实现接口也能代理。

2. 主要用途

  • 增强功能:你可以在方法运行前后加点额外的操作,比如记录日志、检查权限或者管理事务。
  • 解耦设计:把一些通用功能(比如日志、权限检查)和核心业务逻辑分开,让代码更清晰、更容易维护

3. 应用场景

  • Spring AOP:动态代理用于实现一些功能,比如记录日志和事务管理,和核心代码分开。
  • MyBatis:通过动态代理自动生成接口的实现类,省去了手动编写代码的麻烦。

详情内容

1. 动态代理的核心原理

1.1 什么是代理?

在编程中,代理模式 是一种结构型设计模式。代理对象是对目标对象的替代或包装,它可以:

  • 在调用目标对象的方法前后执行自定义逻辑。
  • 控制对目标对象的访问。

静态代理 是预先定义好的代理类,而 动态代理 则是在运行时动态生成代理类。


1.2 动态代理的两种实现
(1) JDK 动态代理
  • 实现基础:基于反射(java.lang.reflect)。
  • 核心类:
    • Proxy:动态生成代理类。
    • InvocationHandler:定义方法拦截逻辑。
  • 适用场景:被代理对象实现了接口。

示例代码:JDK 动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义一个接口
interface UserService {
    void addUser(String name);
}

// 接口的具体实现类
class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
}

// 动态代理处理器
class LoggingHandler implements InvocationHandler {
    private Object target;

    public LoggingHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志:调用方法 " + method.getName());
        Object result = method.invoke(target, args); // 调用实际方法
        System.out.println("日志:方法 " + method.getName() + " 执行完毕");
        return result;
    }
}

public class JDKProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();

        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            userService.getClass().getClassLoader(),
            userService.getClass().getInterfaces(),
            new LoggingHandler(userService)
        );

        // 调用代理方法
        proxy.addUser("张三");
    }
}

输出结果

日志:调用方法 addUser
添加用户:张三
日志:方法 addUser 执行完毕
解析
  1. Proxy.newProxyInstance 动态生成代理对象。
  2. LoggingHandler 是方法拦截器,定义了在方法调用前后要执行的逻辑。

(2) CGLIB 动态代理
  • 实现基础:基于字节码操作(ASM 框架)。

  • 核心类:

    • Enhancer:生成代理类。
    • MethodInterceptor:定义方法拦截逻辑。
  • 适用场景:目标类没有实现接口。

示例代码:CGLIB 动态代理

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

// 目标类
class ProductService {
    public void addProduct(String name) {
        System.out.println("添加产品:" + name);
    }
}

// 动态代理拦截器
class CglibLoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("日志:调用方法 " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 调用实际方法
        System.out.println("日志:方法 " + method.getName() + " 执行完毕");
        return result;
    }
}

public class CGLIBProxyExample {
    public static void main(String[] args) {
        // 创建代理
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ProductService.class);
        enhancer.setCallback(new CglibLoggingInterceptor());

        ProductService proxy = (ProductService) enhancer.create();
        proxy.addProduct("手机");
    }
}

输出结果

日志:调用方法 addProduct
添加产品:手机
日志:方法 addProduct 执行完毕
解析
  1. Enhancer 动态生成目标类的子类。
  2. CglibLoggingInterceptor 实现方法的拦截逻辑。

2. JDK 动态代理与 CGLIB 动态代理的对比

对比维度JDK 动态代理CGLIB 动态代理
实现方式基于反射机制基于字节码操作
代理对象要求目标类必须实现接口目标类无需实现接口
性能性能较低,反射开销较大性能更高,但生成字节码开销略大
典型应用Spring AOP 中对接口的增强Spring AOP 中对类的增强
限制无法代理没有接口的类无法代理被 final 修饰的类或方法

3. 动态代理的实际应用场景

(1) Spring AOP

动态代理是 Spring AOP 的核心技术,用于对方法调用进行增强。通过代理对象,Spring 可以在方法执行前后执行日志记录、事务管理等逻辑。

示例:Spring 中的事务管理
@Transactional
public void processOrder() {
    // 核心业务逻辑
}

Spring 使用动态代理在方法执行前后自动开启和提交事务。


(2) MyBatis Mapper 动态代理

MyBatis 中,Mapper 接口的实现类是在运行时通过动态代理生成的。

示例代码
// Mapper 接口
public interface UserMapper {
    User findUserById(int id);
}

// 动态代理生成的代理类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findUserById(1);

解析

  • getMapper 方法动态生成代理类。
  • 代理类负责执行 SQL 并将结果映射为实体对象。

知识拓展

1. ASM 框架与字节码操作

CGLIB 的底层实现依赖于 ASM 框架,通过直接操作字节码生成代理类。

  • 优势:性能优于反射。
  • 缺点:实现复杂,代码难以维护。

2. 动态代理的性能优化

性能瓶颈
  • JDK 动态代理 使用反射调用方法,性能略低。
  • CGLIB 动态代理 虽然性能更高,但生成字节码有一定开销。
优化方案
  1. JDK 11+ 引入了更高效的代理实现机制
  2. 使用 ByteBuddy 替代 CGLIB,支持更复杂的字节码操作场景。

总结

  1. 动态代理的核心
    • 运行时生成代理类,为方法调用添加增强逻辑。
    • JDK 动态代理基于反射,适用于接口。
    • CGLIB 动态代理基于字节码生成,适用于没有接口的类。
  2. 动态代理的优势
    • 高度灵活,可动态增强方法。
    • 广泛应用于框架(Spring、MyBatis)。
  3. 扩展建议
    • 深入研究字节码操作(如 ASM 和 ByteBuddy)。
    • 在实际项目中,根据场景选择合适的代理技术。

通过以上总结的内容,你是不是对 Java 动态代理有了更全面的理解?

如果还有疑问,欢迎评论区进一步交流!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值