在Java开发中,代理主要有以下三种:

代理类型来源特点依赖
JDK 动态代理JDK 自带基于接口代理只需要 JDK
CGLIB 动态代理第三方库基于继承代理需要引入 cglib.jar
AspectJ 静态代理第三方库编译时织入需要特殊编译器

下面主要介绍 JDK 动态代理。

JDK 动态代理(基于接口)有 4 个核心角色,缺一不可:

  1. 目标接口:代理类和目标类必须实现的接口(如UserService);
  2. 目标类:被代理的类(如UserServiceImpl,实现UserService);
  3. InvocationHandler(调用处理器):核心!封装 “增强逻辑” 和 “目标方法调用”,代理对象的所有方法调用都会转发到这里;
  4. Proxy 类:JDK 提供的工具类,用于动态生成代理对象(无需手动写代理类)。

示例:给方法加日志增强

需求:为UserServiceaddUser()方法添加日志 —— 方法执行前打印 “开始执行 addUser”,执行后打印 “执行 addUser 结束”。

步骤 1:定义目标接口UserService

public interface UserService {
    // 目标方法:添加用户
    void addUser(String username);
}

步骤 2:实现目标类UserServiceImpl

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        // 目标方法核心逻辑
        System.out.println("添加用户:" + username);
    }
}

步骤 3:编写 InvocationHandler(核心增强逻辑)

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

public class LogInvocationHandler implements InvocationHandler {
    // 目标对象(被代理的对象,通过构造器传入)
    private Object target;

    // 构造器:接收目标对象
    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 代理对象的所有方法调用都会触发此方法
     * @param proxy 代理对象(一般不用)
     * @param method 当前调用的目标方法(如addUser)
     * @param args 目标方法的参数(如username)
     * @return 目标方法的返回值(无返回则为null)
     * @throws Throwable 目标方法可能抛出的异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 前置增强:方法执行前的逻辑(日志记录)
        System.out.println("【日志】开始执行方法:" + method.getName());

        // 2. 调用目标方法(核心!通过反射调用目标对象的方法)
        Object result = method.invoke(target, args); // 此处target是UserServiceImpl实例

        // 3. 后置增强:方法执行后的逻辑(日志记录)
        System.out.println("【日志】结束执行方法:" + method.getName());

        // 返回目标方法的结果(无返回则返回null)
        return result;
    }
}

步骤 4:用 Proxy 类生成代理对象并测试

import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象(被代理的对象)
        UserService target = new UserServiceImpl();

        // 2. 创建InvocationHandler实例(传入目标对象)
        LogInvocationHandler handler = new LogInvocationHandler(target);

        // 3. 动态生成代理对象(核心!Proxy.newProxyInstance())
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 1. 目标类的类加载器
                target.getClass().getInterfaces(),   // 2. 目标类实现的所有接口(必须传,JDK代理基于接口)
                handler                              // 3. 调用处理器(增强逻辑)
        );

        // 4. 调用代理对象的方法(实际会触发handler的invoke())
        proxy.addUser("动态代理");
    }
}

执行结果:

【日志】开始执行方法:addUser
添加用户:动态代理
【日志】结束执行方法:addUser
  • 调用proxy.addUser()时,并未直接执行UserServiceImpladdUser(),而是先进入LogInvocationHandlerinvoke()方法,执行增强逻辑后,再通过反射调用目标方法;
  • 若要代理OrderService,只需创建OrderService的目标对象,传入同一个LogInvocationHandler,即可实现日志增强 ——一套增强逻辑,代理所有接口
  • 反射是基础:动态代理生成代理对象、调用目标方法,底层都依赖反射(如Method.invoke());**
  • 动态代理是反射的高级应用:反射解决 “运行时操作类结构”,动态代理解决 “运行时动态增强方法”,二者结合实现了框架的核心能力(如 Spring 的 IOC 和 AOP)。
分类: Java-Backend 标签: Java

评论

暂无评论数据

暂无评论数据

目录