java___0">java每日一学 ---- 面向切面编程
怎么通过方法注解的方式打印日志呢?自定义注解
1、首先定义一个@ExecuteLog 注解类
java">@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExecuteLog {/*** 执行方法的描述,默认方法名** @return 描述*/String value() default "";boolean includeRequest() default true;boolean includeResult() default true;
}
方法讲解:
(1)、@Target(ElementType.METHOD)
作用:指定该注解只能应用于方法上。 解释:@Target 注解表示自定义注解的作用范围。这里的 ElementType.METHOD
表示该注解只能应用于方法。
(2)、@Retention(RetentionPolicy.RUNTIME)
作用:指定注解的生命周期。 解释:@Retention 注解表示注解的存活时间。RUNTIME 表示注解在运行时有效,可以通过反射机制访问。
(3)、String value() default “”;
作用:定义一个字符串属性 value,默认为空字符串。 解释:这个属性用于给方法添加一个描述信息,默认情况下,value
为方法名。如果方法没有显式提供描述,value 默认将是方法的名字。
(4)、boolean includeRequest() default true;
作用:定义一个布尔属性 includeRequest,默认为 true。 解释:决定在日志中是否包括请求参数。如果为
true,会记录请求参数;如果为 false,则不记录请求参数。
(5)、boolean includeResult() default true;
作用:定义一个布尔属性 includeResult,默认为 true。 解释:决定在日志中是否包括方法执行结果。如果为
true,会记录方法的返回值;如果为 false,则不记录方法的返回值。
2、ExecuteLogAspect 切面类,再定义一个切面类
java">@Aspect
@Component
public class ExecuteLogAspect {private static final Logger LOGGER = LoggerFactory.getLogger(ExecuteLogAspect.class);@Around("@annotation(executeLog)")public Object recordIntervalLog(ProceedingJoinPoint pjp, ExecuteLog executeLog) throws Throwable {LocalDateTime beginTime = LocalDateTime.now();MethodSignature signature = (MethodSignature) pjp.getSignature();Object[] args = pjp.getArgs();Method method = signature.getMethod();String methodName = StringUtils.defaultIfBlank(executeLog.value(), method.getName());String processId = RandomUtils.getShortUUID();if (executeLog.includeRequest()) {LOGGER.info("start to process [{}], clientId [{}], userId [{}], params: [{}], startTime: [{}], processId: [{}]", methodName, AppContext.getClientId(), AppContext.getFineUid(), args, beginTime, processId);} else {LOGGER.info("start to process [{}], clientId [{}], userId [{}], startTime: [{}], processId: [{}]", methodName, AppContext.getClientId(), AppContext.getFineUid(), beginTime, processId);}Object result = pjp.proceed();LocalDateTime endTime = LocalDateTime.now();if (executeLog.includeResult()) {LOGGER.info("process[{}],Id: [{}] finished, result:[{}], endTime: [{}], costTime: [{}] ms", methodName, processId, result, endTime, beginTime.until(endTime, ChronoUnit.MILLIS));} else {LOGGER.info("process[{}],Id: [{}] finished, endTime: [{}], costTime: [{}] ms", methodName, processId, endTime, beginTime.until(endTime, ChronoUnit.MILLIS));}return result;}
}
(1)、@Aspect
作用:表示该类是一个切面类(Aspect)。 解释:@Aspect 是 AOP
编程中的关键注解,标记这个类为切面类,意味着这个类将包含切面逻辑,即方法执行前后做一些操作。
(2)、@Component
作用:表示该类是一个 Spring Bean。 解释:@Component 注解将这个类注册到 Spring 上下文中,Spring
容器会自动扫描并创建该类的实例,供其他组件使用。
(3)、@Around(“@annotation(executeLog)”)
作用:这是一个环绕通知,用来定义一个环绕方法。 解释:@Around 注解指定了一个切点,该切点是所有标注了 @ExecuteLog
注解的方法。通过 @annotation(executeLog),Spring 会将方法上注解的实例传递给方法参数 executeLog。
(4)、public Object recordIntervalLog(ProceedingJoinPoint pjp, ExecuteLog
executeLog) throws Throwable
作用:定义一个环绕通知方法。 解释:ProceedingJoinPoint
提供了对目标方法的控制,允许我们在方法执行前后做一些操作。ExecuteLog executeLog
用来接收目标方法上注解的实例,throws Throwable 表示该方法可能会抛出异常。
java"> LocalDateTime beginTime = LocalDateTime.now();MethodSignature signature = (MethodSignature) pjp.getSignature();Object[] args = pjp.getArgs();Method method = signature.getMethod();String methodName = StringUtils.defaultIfBlank(executeLog.value(), method.getName());String processId = RandomUtils.getShortUUID();
(5)、MethodSignature signature = (MethodSignature) pjp.getSignature();
作用:获取方法的签名信息。 解释:pjp.getSignature() 返回的是 MethodSignature
类型的对象,包含了方法的签名信息,MethodSignature 是 Signature 类的子类,提供了获取方法、参数类型等信息的方法。
(6)、Object[] args = pjp.getArgs();
作用:获取方法参数。 解释:pjp.getArgs() 获取目标方法的参数列表,供后续在日志中记录请求参数时使用。
(7)、Method method = signature.getMethod();
作用:获取目标方法的 Method 对象。 解释:signature.getMethod() 返回当前方法的 Method
对象,用来获取方法的详细信息。
(8)、String methodName = StringUtils.defaultIfBlank(executeLog.value(), method.getName());
作用:获取方法的名称,如果注解中 value 属性有设置,则使用该属性值,否则使用方法名。
解释:StringUtils.defaultIfBlank 会检查 executeLog.value()
是否为空或空白,如果是,则使用方法的名称 method.getName()。
(9)、Object result = pjp.proceed();
作用:执行目标方法。 解释:pjp.proceed() 调用目标方法并返回其结果。这里的 proceed() 是
ProceedingJoinPoint 提供的执行方法的 API。
3、 如何使用呢?
java"> @ExecuteLogfun getXXX(event: StreamStatusChangeEvent) {....}
结果:
打印日志:start to process [setExtractPendingAfterStreamFinished], clientId
[null], userId [null], params:
[[StreamStatusChangeEvent(connectId=xxx, taskId=xx, pluginId=xxx,
tableId=xx, status=Complete, task=ConnectionTaskDO(id=xx,
fineUid=null, pluginId=11024, connectId=xx, config=null, state=null,
configuredCatalog=null, streamNames=null, attempts=null,
syncRecordStat=null, status=null, startedAt=null, syncEndAt=null,
finishedAt=null, fullSync=null, errorMsg=null, createdAt=null,
updatedAt=Fri Nov 22 10:00:24 CST 2024, deletedAt=null, groupId=null,
extractTasks=null))]], startTime: [2024-11-22T10:03:16.101050],
processId: [b1edd217]