Spring AOP – 用于类和方法的自定义注解

huangapple go评论89阅读模式
英文:

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(&quot;execution(* (@com.myproj.core.utils.annotation.LogExecutionTime *).*(..)) || execution(@com.myproj.core.utils.annotation.LogExecutionTime * *(..))&quot;)
	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 &gt; 0) {
				LOG.debug(&quot;METHOD EXECUTION TIME LAPSED: {}ms | {}.{}&quot;, end, className, methodName);
			}
		}

	}
	
}

Spring Bean Definition in spring.xml

&lt;bean class=&quot;com.myproj.core.utils.MyMethodExecutionLoggingAspect&quot; /&gt;

答案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 * *(..))

huangapple
  • 本文由 发表于 2020年10月7日 17:39:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/64241314.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定