Java动态代理通过Proxy和InvocationHandler实现反射拦截:运行时为接口生成代理类,所有方法调用统一交由invoke()处理,利用method.invoke()反射执行目标方法,并在前后插入日志、权限等横切逻辑。

Java 中利用 JDK 动态代理结合反射实现方法拦截,核心是通过 java.lang.reflect.Proxy 创建代理对象,并用 InvocationHandler 拦截所有接口方法调用,在其中使用反射获取方法元信息、参数、注解等,进而实现日志、权限、事务等横切逻辑。
一、动态代理的前提:必须基于接口
JDK 动态代理只能为实现了接口的类生成代理。它在运行时生成一个实现了相同接口的代理类(如 $Proxy0),而不是修改目标类本身。
所以你需要:
- 定义一个接口(例如
UserService) - 提供一个实现类(例如
UserServiceImpl) - 不直接使用实现类,而是通过代理对象调用其接口方法
二、编写 InvocationHandler 实现拦截逻辑
这是拦截的核心。你需要重写 invoke 方法,在其中访问被调用的方法、参数、返回值,甚至可修改行为:
<font color="#888">// 示例:带日志和参数打印的通用处理器</font>
public class LoggingInvocationHandler implements InvocationHandler {
private final Object target; // 被代理的真实对象
<pre class="brush:java;toolbar:false;">public LoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【开始调用】" + method.getDeclaringClass().getSimpleName()
+ "." + method.getName());
// 利用反射获取方法上的注解(比如自定义 @NeedAuth)
if (method.isAnnotationPresent(NeedAuth.class)) {
System.out.println("→ 检测到 @NeedAuth,执行鉴权逻辑...");
// 这里可抛出异常或跳过执行
}
// 打印参数类型与值(反射获取参数名需 Java 8+ -parameters 编译选项)
Parameter[] parameters = method.getParameters();
for (int i = 0; i <p>}</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill11702" title="Nginx Reverse Proxy"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20e695c2374.jpg" alt="Nginx Reverse Proxy" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill11702" title="Nginx Reverse Proxy" class="overflowclass">Nginx Reverse Proxy</a>
<p class="overflowclass">配置 Nginx 反向代理,实现域名绑定。用户说“绑定域名”“配置 Nginx”“反向代理”时触发,将域名绑定到指定 IP:端口。</p>
</div>
<a rel="nofollow" href="/xiazai/skill11702" title="Nginx Reverse Proxy" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
三、创建代理实例
使用 Proxy.newProxyInstance 创建代理对象。注意三个参数:
-
ClassLoader:一般用目标对象的类加载器(
target.getClass().getClassLoader()) -
Class>[] interfaces:目标对象实现的所有接口(可用
target.getClass().getInterfaces()) - InvocationHandler:上面写的处理器实例
<font color="#888">// 使用示例</font>
UserService service = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new LoggingInvocationHandler(service)
);
<p>proxy.getUser(123L); // 此时会触发 invoke 方法</p>
四、进阶技巧:结合反射做更灵活的拦截
你可以在 invoke 中进一步利用反射能力:
- 通过
method.getReturnType()判断是否需要包装返回值(如统一封装成Result) - 用
method.getAnnotation(Class提取自定义注解配置(如超时时间、缓存 Key 表达式)) - 调用
method.invoke(...)前后插入 try-catch,实现异常统一处理或降级 - 配合
MethodHandles.Lookup(Java 7+)实现更高效的反射调用(但 JDK 代理中通常仍用Method.invoke)
不复杂但容易忽略:动态代理不会拦截类中非接口定义的方法(包括 toString()、hashCode() 等,除非接口显式声明),也不会拦截私有方法或静态方法——这些需用 CGLIB 或字节码工具。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










