自定义注解配合反射实现方法入参日志埋点需完成标记—识别—提取—记录四步闭环:定义runtime保留策略的方法级注解logpoint,通过反射按位置或@parameter获取参数并脱敏,结构化组装日志输出,可基于纯反射工具类或spring aop集成。

自定义注解配合反射实现方法入参的日志埋点,核心在于“标记—识别—提取—记录”四步闭环。不依赖 Spring AOP 也能落地,但结合 AOP 更易复用;纯反射调用则更轻量、适合工具类封装或非 Spring 环境。
一、定义运行时方法级注解
注解需声明为 @Retention(RetentionPolicy.RUNTIME),才能在运行时通过反射读取。常用字段包括日志标识、是否打印参数、是否记录耗时、是否忽略敏感值等:
- @Target(ElementType.METHOD):限定只能标注在方法上
- @Documented:生成 JavaDoc 时保留注解信息(可选)
- 示例定义:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface LogPoint {
String value() default ""; // 日志前缀,如 "[用户登录]"
boolean logArgs() default true; // 是否记录入参(默认开启)
boolean logResult() default false; // 是否记录返回值
boolean maskSensitive() default true; // 是否脱敏(如手机号、身份证号)
String[] sensitiveFields() default {}; // 指定需脱敏的参数名(按形参顺序索引或绑定名称)
}
二、反射提取入参的关键逻辑
方法入参本身没有名字,反射只能拿到类型和顺序。要精准提取并关联语义,有两种主流做法:
-
方式1:按参数位置匹配(简单直接)
调用method.invoke(target, args)前,args数组即为实际入参。遍历args,结合method.getParameterTypes()可做基础类型校验与格式化(如 Date → yyyy-MM-dd) -
方式2:结合 @Parameter 注解 + -parameters 编译参数(JDK8+)
启用编译选项后,可通过method.getParameters()[i].getName()获取真实形参名,便于做字段级脱敏或条件过滤 -
注意点:
• 基本类型(int/boolean)需转为包装类再判断是否为 null
• 对象参数建议用JSON.toJSONString(arg, SerializerFeature.WriteMapNullValue)统一序列化,避免空指针
• 集合/数组建议限制长度(如截取前 5 项),防日志爆炸
三、组装日志内容并输出
推荐将日志结构化,方便后续采集与分析。一个典型模板如下:
[LogPoint:用户查询] 开始执行 → 参数: {"uid":1001,"page":1,"size":10}
[LogPoint:用户查询] 执行完成 → 返回: {"code":200,"data":[{"id":1,"name":"张三"}]}
[LogPoint:用户查询] 耗时: 42ms
- 使用 SLF4J 的
log.info(String, Object...)支持占位符,性能优于字符串拼接 - 若需异步写入数据库或 MQ,建议封装为独立服务调用,避免阻塞主流程
- 敏感字段脱敏示例(以手机号为例):
str.replaceAll("(\d{3})\d{4}(\d{4})", "$1****$2")
四、集成到执行链路的两种典型方式
根据项目技术栈选择:
-
纯反射工具类调用(适合无 AOP 的老系统或工具模块):
手动在目标方法开头调用LogUtil.logBefore(method, args),结尾调用logAfter(method, result, elapsed) -
Spring AOP 切面自动织入(推荐生产环境):
定义切点@Around("@annotation(logPoint)"),在通知中统一完成参数提取、执行拦截、结果捕获与日志输出,业务代码零侵入










