java中通过接口+jdk动态代理实现无侵入日志打印:先定义业务接口,再用logginginvocationhandler拦截方法调用并增强日志逻辑,最后通过proxy.newproxyinstance生成代理对象,使业务代码完全 unaware 日志存在。

Java 中通过接口 + 动态代理实现无侵入日志打印,核心是让代理对象在不修改原始业务类的前提下,自动在方法调用前后插入日志逻辑。关键在于:接口定义行为契约,动态代理(JDK Proxy)基于接口生成代理实例,InvocationHandler统一拦截并增强方法执行。
1. 定义业务接口(必须)
JDK 动态代理只能代理接口,不能代理具体类(否则需用 CGLIB)。所以第一步是把业务逻辑抽象为接口:
public interface UserService {
String getName(int id);
void updateUser(User user);
}
注意:不要让实现类直接被代理;所有需要日志的方法都得在接口中声明。
2. 编写日志增强的 InvocationHandler
实现 InvocationHandler,在 invoke() 方法中统一处理日志:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
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.getName() +
",参数: " + Arrays.toString(args));
long start = System.currentTimeMillis();
try {
Object result = method.invoke(target, args);
long cost = System.currentTimeMillis() - start;
System.out.println("【日志】调用成功: " + method.getName() +
",耗时: " + cost + "ms,返回: " + result);
return result;
} catch (Exception e) {
long cost = System.currentTimeMillis() - start;
System.out.println("【日志】调用失败: " + method.getName() +
",耗时: " + cost + "ms,异常: " + e.getMessage());
throw e;
}
}}
3. 创建代理对象并使用
用 Proxy.newProxyInstance() 生成代理实例,传入类加载器、接口数组和 handler:
UserService realService = new UserServiceImpl();
UserService proxyService = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new LoggingInvocationHandler(realService)
);
<p>// 后续所有调用都走代理,自动打印日志
proxyService.getName(123);
proxyService.updateUser(new User("Alice"));</p>此时业务代码(如 controller 或 service 调用处)只需面向 UserService 接口编程,完全感知不到日志逻辑的存在 —— 这就是“无侵入”。
4. 进阶建议(提升实用性)
-
用注解控制开关:自定义
@Loggable注解,只对标注的方法生效,handler 中通过method.isAnnotationPresent(Loggable.class)判断 -
整合 SLF4J / Logback:替换
System.out.println为logger.info(),支持日志级别、异步、结构化输出 -
提取通用代理工厂:封装成工具方法,如
Proxies.wrap(service, new LoggingHandler()),降低重复模板代码 -
避免代理失效:确保目标对象是接口类型引用(如
UserService s = new UserServiceImpl()),而非UserServiceImpl s,否则可能绕过代理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










