9.5动态代理

Java 动态代理是一种强大的机制,允许在运行时创建代理类和对象,而无需显式编写代理类的源代码。它广泛应用于 AOP(面向切面编程)、RPC 框架、事务管理等场景。

一、核心概念

  1. 代理模式:通过代理对象控制对目标对象的访问,在不修改目标对象的前提下增强其功能。
  2. 静态代理:代理类在编译期就已确定,需为每个目标类手动编写代理类。
  3. 动态代理:代理类在运行时动态生成,无需手动编写,支持灵活扩展。

静态代理案例:

// UserService.java
public interface UserService {
    void createUser(String username, String password);
    void deleteUser(int userId);
    String getUser(int userId);
}

// UserServiceImpl.java
public class UserServiceImpl implements UserService {
    @Override
    public void createUser(String username, String password) {
        System.out.println("创建用户: " + username);
        // 实际业务逻辑
    }

}


// UserServiceProxy.java
public class UserServiceProxy implements UserService {
    private final UserService target; // 目标对象

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public void createUser(String username, String password) {
        beforeMethod("createUser");
        try {
            // 开启事务
            beginTransaction();
            
            // 调用目标方法
            target.createUser(username, password);
            
            // 提交事务
            commitTransaction();
        } catch (Exception e) {
            // 回滚事务
            rollbackTransaction();
            throw e;
        }
        afterMethod("createUser");
    }    

    // 增强方法:日志记录
    private void beforeMethod(String methodName) {
        System.out.println("[日志] 开始调用方法: " + methodName);
    }

    private void afterMethod(String methodName) {
        System.out.println("[日志] 方法调用结束: " + methodName);
    }

    // 增强方法:事务管理
    private void beginTransaction() {
        System.out.println("[事务] 开启事务");
    }

    private void commitTransaction() {
        System.out.println("[事务] 提交事务");
    }

    private void rollbackTransaction() {
        System.out.println("[事务] 回滚事务");
    }
}

// 使用代理类 Main.java
public class Main {
    public static void main(String[] args) {
        // 创建目标对象
        UserService target = new UserServiceImpl();
        
        // 创建代理对象
        UserService proxy = new UserServiceProxy(target);
        
        // 调用代理方法
        proxy.createUser("test", "123456");
        System.out.println();
        
    }
}

 

二、Java 动态代理的两种实现

Java 提供了两种动态代理机制:

  1. JDK 动态代理:基于接口的代理,要求目标对象实现至少一个接口。
  2. CGLIB 动态代理:基于继承的代理,通过生成子类覆盖方法实现,不要求接口。

三、JDK 动态代理详解

1. 核心 API
  • java.lang.reflect.Proxy:生成代理对象的核心类。
  • java.lang.reflect.InvocationHandler:处理代理方法调用的接口。
2. 实现步骤
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 1. 定义接口
interface UserService {
    String getName(int id);
    void updateName(int id, String name);
}

// 2. 实现接口
class UserServiceImpl implements UserService {
    @Override
    public String getName(int id) {
        System.out.println("获取用户姓名,ID: " + id);
        return "张三";
    }

    @Override
    public void updateName(int id, String name) {
        System.out.println("更新用户姓名,ID: " + id + ",新姓名: " + name);
    }
}

// 3. 实现InvocationHandler
class LoggingHandler implements InvocationHandler {
    private final 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());
        if (args != null) {
            System.out.println("参数: " + java.util.Arrays.toString(args));
        }

        // 调用目标方法
        Object result = method.invoke(target, args);

        // 方法调用后增强
        System.out.println("方法调用结束,返回值: " + result);
        return result;
    }
}

// 4. 创建代理对象并使用
public class JdkProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        UserService target = new UserServiceImpl();

        // 创建InvocationHandler
        InvocationHandler handler = new LoggingHandler(target);

        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            UserService.class.getClassLoader(),
            new Class<?>[]{UserService.class},
            handler
        );

        // 调用代理方法
        String name = proxy.getName(1);
        proxy.updateName(1, "李四");
    }
}
3. 关键方法解析
  • Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

    • loader:定义代理类的类加载器。
    • interfaces:代理类要实现的接口列表。
    • h:调用处理器,处理代理方法的调用。
  • InvocationHandler.invoke(Object proxy, Method method, Object[] args)

    • proxy:代理对象本身。
    • method:被调用的方法。
    • args:方法参数。

四、CGLIB 动态代理详解

1. 核心依赖

CGLIB 是第三方库,需添加依赖:

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
2. 实现步骤
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 1. 定义目标类(无需实现接口)
class UserService {
    public String getName(int id) {
        System.out.println("获取用户姓名,ID: " + id);
        return "张三";
    }

    public void updateName(int id, String name) {
        System.out.println("更新用户姓名,ID: " + id + ",新姓名: " + name);
    }
}

// 2. 实现MethodInterceptor
class LoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 方法调用前增强
        System.out.println("开始调用方法: " + method.getName());
        if (args != null) {
            System.out.println("参数: " + java.util.Arrays.toString(args));
        }

        // 调用目标方法(两种方式)
        // 方式一:通过代理对象调用父类方法(推荐)
        Object result = proxy.invokeSuper(obj, args);
        
        // 方式二:通过反射调用目标对象方法(可能导致无限递归)
        // Object result = method.invoke(obj, args);

        // 方法调用后增强
        System.out.println("方法调用结束,返回值: " + result);
        return result;
    }
}

// 3. 创建代理对象并使用
public class CglibProxyExample {
    public static void main(String[] args) {
        // 创建Enhancer
        Enhancer enhancer = new Enhancer();
        
        // 设置父类(目标类)
        enhancer.setSuperclass(UserService.class);
        
        // 设置回调
        enhancer.setCallback(new LoggingInterceptor());
        
        // 创建代理对象
        UserService proxy = (UserService) enhancer.create();
        
        // 调用代理方法
        String name = proxy.getName(1);
        proxy.updateName(1, "李四");
    }
}
3. 关键类解析
  • Enhancer:CGLIB 的核心类,用于生成代理对象。
  • MethodInterceptor:方法拦截器,类似于 JDK 的 InvocationHandler。
  • MethodProxy:方法代理对象,用于调用父类方法。

五、JDK 代理 vs CGLIB 代理

特性JDK 动态代理CGLIB 动态代理
代理机制基于接口基于继承(生成子类)
目标对象要求必须实现接口无需实现接口,可以是普通类
性能调用开销较大(反射)调用开销较小(直接调用)
生成类大小较小较大(生成子类)
方法限制只能代理接口方法不能代理 final、static 方法

六、注意事项

  1. 性能考虑:反射调用开销较大,频繁调用时需注意性能。
  2. final 方法:CGLIB 无法代理 final 方法,因为无法被子类覆盖。
  3. 类加载问题:动态生成的代理类可能导致类加载器泄漏。
  4. 调试困难:代理逻辑增加了代码复杂度,调试时需注意调用栈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chxii

小小打赏,大大鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值