英文:
Spring AOP - Custom Annotation for Class and Method
问题
以下是翻译好的部分:
现有的答案很好地解释了如何使用自定义注解来记录方法执行时间。我想知道是否有办法在类和方法都使用相同的注解,但切入点在使用时应该是不同的。
@LogExecutionTime
public class MyServiceImpl implements MyService {
public void run(){
// 逻辑
}
public void walk(){
// 逻辑
}
private void breather(){
// 逻辑
}
}
如果注解用于类,则类内的所有方法都应被视为在切面类中进行执行时间记录(类似于 execution(* com.me.package.MyServiceImpl.*(..))
)。然而,如果注解仅用于类内的单个方法,也应该将仅有的该方法视为在切面日志类中进行记录(类似于 execution(* com.you.package.YourServiceImpl.forward(..))
)。
public class YourServiceImpl implements YourService {
@LogExecutionTime
public void forward(){
// 逻辑
}
@LogExecutionTime
public void backward(){
// 逻辑
}
private void upward(){
// 逻辑
}
}
注解类:
package com.myproj.core.utils.annotation;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
@Retention(RUNTIME)
public @interface LogExecutionTime {
}
用于注解的切面类(使用 @kriegaex 建议的切入点):
package com.myproj.core.utils;
import java.time.Duration;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 记录方法执行时间的切面类
*/
@Aspect
public class MyMethodExecutionLoggingAspect {
private static final Logger LOG = LoggerFactory.getLogger(MyMethodExecutionLoggingAspect.class);
/**
* 此方法将记录方法执行时间
*
* @param joinPoint
* @return object
* @throws Throwable
*/
@Around("execution(* (@com.myproj.core.utils.annotation.LogExecutionTime *).*(..)) || execution(@com.myproj.core.utils.annotation.LogExecutionTime * *(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Instant start = Instant.now();
try {
return joinPoint.proceed();
} finally {
long end = Duration.between(start, Instant.now()).toMillis();
if (end > 0) {
LOG.debug("方法执行时间:{}毫秒 | {}.{}", end, className, methodName);
}
}
}
}
在 spring.xml 中进行 Spring Bean 定义:
<bean class="com.myproj.core.utils.MyMethodExecutionLoggingAspect" />
英文:
Existing answers gives nice explanation on how to use Custom Annotation for method execution time logging. I am wondering if there is way to use same annotation for both Class and Method, but Pointcut should be different where it is used.
@LogExecutionTime
public class MyServiceImpl implements MyService {
public void run(){
// logic
}
public void walk(){
// logic
}
private void breather(){
// logic
}
}
If Annotation is used for class all methods inside class should be considered for Execution Time Logging in Aspect Class (like execution(* com.me.package.MyServiceImpl.*(..))
). However if the Annotation is only used for single method inside the class, it is should also consider that only method in Aspect Logging Class. (like execution(* com.you.package.YourServiceImpl.forward(..))
).
public class YourServiceImpl implements YourService {
@LogExecutionTime
public void forward(){
// logic
}
@LogExecutionTime
public void backward(){
// logic
}
private void upward(){
// logic
}
}
Annotation Class
package com.myproj.core.utils.annotation;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
@Retention(RUNTIME)
public @interface LogExecutionTime {
}
Aspect Class for Annotation (using pointcuts as suggested by @kriegaex)
package com.myproj.core.utils;
import java.time.Duration;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Aspect class to Log Method Execution time
*/
@Aspect
public class MyMethodExecutionLoggingAspect {
private static final Logger LOG = LoggerFactory.getLogger(MyMethodExecutionLoggingAspect.class);
/**
* This method will log Method Execution Time
*
* @param joinPoint
* @return object
* @throws Throwable
*/
@Around("execution(* (@com.myproj.core.utils.annotation.LogExecutionTime *).*(..)) || execution(@com.myproj.core.utils.annotation.LogExecutionTime * *(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Instant start = Instant.now();
try {
return joinPoint.proceed();
} finally {
long end = Duration.between(start, Instant.now()).toMillis();
if (end > 0) {
LOG.debug("METHOD EXECUTION TIME LAPSED: {}ms | {}.{}", end, className, methodName);
}
}
}
}
Spring Bean Definition in spring.xml
<bean class="com.myproj.core.utils.MyMethodExecutionLoggingAspect" />
答案1
得分: 1
你的示例代码中的 @annotation()
部分没有意义,因为你没有指定方法注解类型。应该像这样使用 @annotation(fully.qualified.AnnotationType)
。
如果要匹配类注解,可以使用 @within(fully.qualified.AnnotationType)
,如此处所述(https://stackoverflow.com/a/57385491/1082681)。
因此,你可以使用如下的切入点:
@annotation(fully.qualified.AnnotationType)
|| @within(fully.qualified.AnnotationType)
或者,根据我在这里的回答(https://stackoverflow.com/a/53452483/1082681),你还可以使用更加难懂的版本:
execution(* (@fully.qualified.AnnotationType *).*(..))
|| execution(@fully.qualified.AnnotationType * *(..))
英文:
Your sample code with @annotation()
does not make sense because you do not specify a method annotation type. It should be something like @annotation(fully.qualified.AnnotationType)
instead.
For matching class annotations you want to use @within(fully.qualified.AnnotationType)
, as described here.
So you could use a pointcut like
@annotation(fully.qualified.AnnotationType)
|| @within(fully.qualified.AnnotationType)
Alternatively, according to my answer here, you could also use the more cryptic version
execution(* (@fully.qualified.AnnotationType *).*(..))
|| execution(@fully.qualified.AnnotationType * *(..))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论