Skip to content

动态代理与反射边界 ​

学习目标 ​

  • 能为接口创建 JDK 动态代理,并在 InvocationHandler 中转发调用。
  • 能正确处理参数、返回值、Object 方法和目标异常。
  • 能判断 JDK Proxy、普通实现类和字节码代理框架的使用边界。

核心知识点 ​

专业术语 ​

  • Proxy:运行时生成实现一个或多个接口的代理对象。
  • InvocationHandler:代理每次方法调用都会进入的处理器。
  • 代理对象:调用方看到的接口实例,不一定有可直接使用的业务实现类。
  • default method:接口默认方法,代理可以选择调用默认实现或自行拦截。

白话解释与边界 ​

JDK 动态代理只能代理接口,适合日志、鉴权、事务、远程调用等横切边界。处理器要区分业务方法和 toString/equals/hashCode,并原样处理参数、返回值与异常。需要代理具体类或复杂字节码增强时才考虑其他工具;代理不是把任意对象“变成接口”的万能转换器。

常用用法 ​

Proxy.newProxyInstance:创建接口代理 ​

代理接口必须对代理类加载器可见;返回值要符合方法返回类型,否则调用处才会收到 ClassCastException 或 WrongMethodTypeException。

java
// 说明:Proxy.newProxyInstance:创建接口代理。
import java.lang.reflect.Proxy;

interface Greeter { String greet(String name); }

public class ProxyCreateDemo {
    public static void main(String[] args) {
        Greeter greeter = (Greeter) Proxy.newProxyInstance(
                Greeter.class.getClassLoader(),
                new Class<?>[]{Greeter.class},
                (proxy, method, arguments) -> "Hi, " + arguments[0]);
// 初始状态:greeter = (Greeter) Proxy.newProxyInstance(。
// 作用:Greeter greeter = (Greeter) Proxy.newProxyInstance(;创建接口代理。
        System.out.println(greeter.greet("Java"));
// 输出:Hi, Java
    }
}

InvocationHandler:集中处理调用 ​

处理器是每个代理方法的统一入口;复杂逻辑应拆成可测试的策略,不要把所有业务实现都塞进一个 lambda。

java
// 说明:InvocationHandler:集中处理调用。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

interface Calculator { int add(int left, int right); }

public class InvocationHandlerDemo {
    public static void main(String[] args) {
        InvocationHandler handler = (proxy, method, arguments) -> {
// 初始状态:handler = (proxy, method, arguments) ->。
// 作用:InvocationHandler handler = (proxy, method, arguments) -> {;集中处理调用。
            System.out.println(method.getName());
// 输出:add
            return (int) arguments[0] + (int) arguments[1];
        };
        var calculator = (Calculator) Proxy.newProxyInstance(
                Calculator.class.getClassLoader(), new Class<?>[]{Calculator.class}, handler);
// 初始状态:calculator = (Calculator) Proxy.newProxyInstance(。
// 作用:var calculator = (Calculator) Proxy.newProxyInstance(;集中处理调用。
        System.out.println(calculator.add(2, 3));
// 输出:5
    }
}

Method.invoke:转发到真实对象 ​

真实代理通常缓存目标 Method,避免每次按名称查找;反射调用抛出的异常需要解包,否则业务根因会被包装。

java
// 说明:Method.invoke:转发到真实对象。
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Service { String run(String value); }
class RealService implements Service {
    public String run(String value) { return value.toUpperCase(); }
// 作用:public String run(String value) { return value.toUpperCase(); };转发到真实对象。
}

public class ProxyDelegateDemo {
    public static void main(String[] args) {
        Service target = new RealService();
// 作用:Service target = new RealService();;转发到真实对象。
// 初始状态:target = new RealService()。
        Service proxy = (Service) Proxy.newProxyInstance(
                Service.class.getClassLoader(), new Class<?>[]{Service.class},
                (object, method, arguments) -> {
// 作用:Service proxy = (Service) Proxy.newProxyInstance(;转发到真实对象。
// 作用:Service.class.getClassLoader(), new Class<?>[]{Service.class},;转发到真实对象。
// 初始状态:proxy = (Service) Proxy.newProxyInstance(。
                    Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
// 作用:Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());;转发到真实对象。
// 初始状态:targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes())。
                    return targetMethod.invoke(target, arguments);
// 初始状态:return targetMethod.invoke(target, arguments)。
// 作用:return targetMethod.invoke(target, arguments);;转发到真实对象。
                });
        System.out.println(proxy.run("java"));
// 输出:JAVA
    }
}

代理前后增加日志或计时 ​

横切逻辑应保证 finally 中的记录不会覆盖原异常;日志还要脱敏参数和限制耗时标签的 cardinality。

java
// 说明:代理前后增加日志或计时。
import java.lang.reflect.Proxy;

interface Repository { String find(); }

public class ProxyTimingDemo {
    public static void main(String[] args) {
        Repository repository = (Repository) Proxy.newProxyInstance(
                Repository.class.getClassLoader(), new Class<?>[]{Repository.class},
                (proxy, method, args1) -> {
// 初始状态:repository = (Repository) Proxy.newProxyInstance(。
// 作用:Repository repository = (Repository) Proxy.newProxyInstance(;创建实现 Repository 的代理并赋给 repository。
// 作用:Repository.class.getClassLoader(), new Class<?>[]{Repository.class},;代理使用 Repository.class 的类加载器和接口数组。
                    long start = System.nanoTime();
// 初始状态:start = System.nanoTime()。
// 作用:long start = System.nanoTime();;System.nanoTime() 返回的纳秒值写入 start。
                    try {
                        return "result";
                    } finally {
                        System.out.println(method.getName() + ":" + (System.nanoTime() >= start));
// 输出:find:true
                    }
                });
        System.out.println(repository.find());
// 输出:result
    }
}

检查动态代理:确认代理类并取得处理器 ​

这些 API 适合框架诊断和边界判断;不要把处理器暴露给不可信调用方,否则可能绕过代理层的安全策略。

java
// 说明:检查动态代理:确认代理类并取得处理器。
import java.lang.reflect.Proxy;

interface Clock { int hour(); }

public class ProxyInspectDemo {
    public static void main(String[] args) {
        Clock clock = (Clock) Proxy.newProxyInstance(
                Clock.class.getClassLoader(), new Class<?>[]{Clock.class}, (proxy, method, args1) -> 8);
// 初始状态:clock = (Clock) Proxy.newProxyInstance(。
// 作用:Clock clock = (Clock) Proxy.newProxyInstance(;确认代理类并取得处理器。
// 作用:Clock.class.getClassLoader(), new Class<?>[]{Clock.class}, (proxy, method, args1) -> 8);;确认代理类并取得处理器。
        System.out.println(Proxy.isProxyClass(clock.getClass()));
// 输出:true
        System.out.println(Proxy.getInvocationHandler(clock) != null);
// 输出:true(第2次输出)
    }
}

多接口代理 ​

多个接口的方法签名冲突时要设计统一语义;代理类也必须满足接口方法返回类型的兼容要求。

java
// 说明:多接口代理。
import java.lang.reflect.Proxy;

interface Named { String name(); }
interface Enabled { boolean enabled(); }

public class MultiInterfaceProxyDemo {
    public static void main(String[] args) {
        Object proxy = Proxy.newProxyInstance(
                Named.class.getClassLoader(), new Class<?>[]{Named.class, Enabled.class},
                (object, method, args1) -> method.getName().equals("name") ? "api" : true);
// 初始状态:proxy = Proxy.newProxyInstance(。
// 作用:Object proxy = Proxy.newProxyInstance(;创建同时实现 Named、Enabled 的代理并赋给 proxy。
// 作用:Named.class.getClassLoader(), new Class<?>[]{Named.class, Enabled.class},;使用 Named.class 的类加载器与 Named、Enabled 接口。
// 作用:(object, method, args1) -> method.getName().equals("name") ? "api" : true);;name() 返回 "api",其他接口方法返回 true。
        System.out.println(((Named) proxy).name() + "," + ((Enabled) proxy).enabled());
// 输出:api,true
    }
}

不常用但需要知道 ​

处理代理对象基础契约:统一对象方法语义 ​

Object 方法也会进入 InvocationHandler;若不处理,日志、集合键和断言可能出现令人困惑的默认行为。示例使用 switch 表达式是 JDK 14+ 稳定语法,不是预览特性。

java
// getProxyClass 用 LegacyApi 的类加载器生成只实现该接口的代理 Class,此处尚未创建 InvocationHandler 或实例。
import java.lang.reflect.Proxy;

interface Marker { }

public class ProxyObjectMethodDemo {
    public static void main(String[] args) {
        Marker marker = (Marker) Proxy.newProxyInstance(
                Marker.class.getClassLoader(), new Class<?>[]{Marker.class},
                (proxy, method, args1) -> switch (method.getName()) {
// 初始状态:marker = (Marker) Proxy.newProxyInstance(。
// 作用:Marker marker = (Marker) Proxy.newProxyInstance(;统一对象方法语义。
// 作用:Marker.class.getClassLoader(), new Class<?>[]{Marker.class},;统一对象方法语义。
                    case "toString" -> "MarkerProxy";
                    case "hashCode" -> 42;
                    case "equals" -> proxy == args1[0];
                    default -> null;
                });
        System.out.println(marker + "," + marker.hashCode() + "," + marker.equals(marker));
// 输出:MarkerProxy,42,true
    }
}

InvocationHandler.invokeDefault:调用接口默认方法 ​

默认方法调用受接口可见性和模块边界影响;代理若要统一改写默认方法,不应悄悄混用目标实现和默认实现。

java
// 作用:通过 InvocationHandler.invokeDefault 调用接口默认方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

interface Greeting {
    default String hello() { return "default"; }
}

public class ProxyDefaultMethodDemo {
    public static void main(String[] args) {
        InvocationHandler handler = (proxy, method, args1) ->
                method.isDefault() ? InvocationHandler.invokeDefault(proxy, method, args1) : "custom";
// 初始状态:handler = (proxy, method, args1) ->。
// 作用:method.isDefault() ? InvocationHandler.invokeDefault(proxy, method, args1) : "custom";;调用接口默认方法,返回读取结果。
        Greeting greeting = (Greeting) Proxy.newProxyInstance(
                Greeting.class.getClassLoader(), new Class<?>[]{Greeting.class}, handler);
// 作用:Greeting greeting = (Greeting) Proxy.newProxyInstance(;创建实现 Greeting 的代理并赋给 greeting。
// 作用:Greeting.class.getClassLoader(), new Class<?>[]{Greeting.class}, handler);;调用接口默认方法,返回读取结果。
// 初始状态:greeting = (Greeting) Proxy.newProxyInstance(。
        System.out.println(greeting.hello());
// 输出:default
    }
}

UndeclaredThrowableException:检查异常包装 ​

处理器抛出的检查异常若不在接口声明中,会被包装;代理层要决定异常翻译契约,不能让调用方随机面对包装层次。

java
// 作用:通过 UndeclaredThrowableException 检查异常包装。
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

interface SafeAction { void run(); }

public class ProxyThrowableDemo {
    public static void main(String[] args) {
        SafeAction action = (SafeAction) Proxy.newProxyInstance(
                SafeAction.class.getClassLoader(), new Class<?>[]{SafeAction.class},
                (proxy, method, args1) -> { throw new Exception("checked"); });
// 初始状态:(proxy, method, args1) -> { throw new Exception("checked"); })。
// 初始状态:action = (SafeAction) Proxy.newProxyInstance(。
// 作用:SafeAction action = (SafeAction) Proxy.newProxyInstance(;检查异常包装。
        try {
            action.run();
// 作用:action.run();;检查异常包装。
        } catch (UndeclaredThrowableException e) {
            System.out.println(e.getUndeclaredThrowable().getMessage());
// 输出:checked
        }
    }
}

Proxy.getProxyClass 的旧式动态类获取 ​

新代码优先使用 newProxyInstance,它同时完成类生成和实例创建;直接获取代理类容易把实现细节泄漏到业务代码。

java
// 说明:Proxy.getProxyClass 使用 LegacyApi 的类加载器生成代理 Class;类名包含 "$Proxy",但此时尚未创建代理实例。
import java.lang.reflect.Proxy;

interface LegacyApi { }

public class ProxyClassDemo {
    public static void main(String[] args) {
        Class<?> proxyClass = Proxy.getProxyClass(
                LegacyApi.class.getClassLoader(), LegacyApi.class);
// 初始状态:proxyClass = Proxy.getProxyClass(。
// 作用:Class<?> proxyClass = Proxy.getProxyClass(;按 LegacyApi.class 获取代理 Class,结果写入 proxyClass。
// 作用:LegacyApi.class.getClassLoader(), LegacyApi.class);;使用 LegacyApi 的类加载器和接口类型。
        System.out.println(proxyClass.getName().contains("$Proxy"));
// 输出:true
    }
}

代理具体类的边界 ​

JDK Proxy 的接口前提不能代理普通具体类;需要类级别拦截时应重新设计接口,或选择明确的字节码工具并评估模块、调试和维护成本。

java
import java.lang.reflect.Proxy;

class ConcreteService { public String run() { return "ok"; } }

public class ConcreteProxyBoundaryDemo {
    public static void main(String[] args) {
        System.out.println(ConcreteService.class.isInterface());
// 输出:false
        System.out.println(Proxy.isProxyClass(ConcreteService.class));
// 输出:false(第2次输出)
    }
}

代理与模块可见性 ​

接口必须对代理类加载器可见;命名模块中还要检查导出、开放和包可访问性,不能只在 classpath 上测试后直接迁移。

java
import java.lang.reflect.Proxy;

interface PublicService { String call(); }

public class ProxyModuleBoundaryDemo {
    public static void main(String[] args) {
        Object proxy = Proxy.newProxyInstance(
                PublicService.class.getClassLoader(), new Class<?>[]{PublicService.class},
                (object, method, args1) -> "ok");
// 初始状态:proxy = Proxy.newProxyInstance(。
// 作用:Object proxy = Proxy.newProxyInstance(;创建实现 PublicService 的代理并赋给 proxy。
// 作用:PublicService.class.getClassLoader(), new Class<?>[]{PublicService.class},;使用 PublicService.class 的类加载器与接口类型。
        System.out.println(proxy.getClass().getInterfaces()[0].getSimpleName());
// 输出:PublicService
    }
}

简单案例 ​

java
import java.lang.reflect.Proxy;

interface UserService { String find(String id); }

public class DynamicProxyDemo {
    public static void main(String[] args) {
        UserService service = (UserService) Proxy.newProxyInstance(
                UserService.class.getClassLoader(), new Class<?>[]{UserService.class},
                (proxy, method, args1) -> {
// 初始状态:service = (UserService) Proxy.newProxyInstance(。
// 作用:UserService service = (UserService) Proxy.newProxyInstance(;创建实现 UserService 的代理并赋给 service。
// 作用:UserService.class.getClassLoader(), new Class<?>[]{UserService.class},;使用 UserService.class 的类加载器与接口类型。
                    System.out.println("call " + method.getName());
// 输出:call find
                    return "user-" + args1[0];
                });
        System.out.println(service.find("42"));
// 输出:user-42
    }
}

这个代理把日志和返回值放在接口边界,真实项目还要处理鉴权、异常、超时、重试和 Object 方法,不要把代理当成业务容器。

易混点 ​

  • JDK Proxy 代理接口,不能直接代理具体类;类代理需要不同工具或重新抽象接口。
  • InvocationHandler 的异常会按接口声明传播或包装为 UndeclaredThrowableException。
  • Object 方法同样进入处理器,equals/hashCode 的一致性会影响集合行为。
  • sendAsync、反射 Method.invoke 和动态代理都提供动态性,但职责分别是异步 I/O、成员调用和接口拦截。

课后小问 ​

  1. 为什么一个具体类没有实现接口时不能直接用 JDK Proxy 拦截? 答案:JDK Proxy 生成的类只能实现指定接口,不能继承并重写任意具体类。 解析:优先抽出稳定接口;必须拦截类方法时再评估字节码代理方案及其模块影响。

  2. 为什么代理处理器必须处理 equals 和 hashCode? 答案:集合、缓存和断言会直接调用 Object 方法,它们也会进入 InvocationHandler。 解析:如果返回值不稳定,代理作为 Map key 或 Set 元素时会出现难以追踪的行为。

本节小结 ​

  • Proxy 创建接口代理,InvocationHandler 集中处理调用。
  • 代理可以做横切逻辑,但必须维护参数、返回值、异常和 Object 契约。
  • 默认方法、检查异常、类代理限制和模块可见性是低频但关键边界。
  • 代理解决的是调用拦截,不替代接口设计、业务错误处理和性能测量。

快速回顾 ​

  • newProxyInstance 需要类加载器、接口数组和处理器。
  • method.invoke 可转发到真实对象,但异常需要解包。
  • Object 方法、默认方法和检查异常都要单独考虑。
  • JDK Proxy 只面向接口,类代理是另一类工具问题。