SpringAop实际应用案例(一)之Controller请求方法和统计方法执行时间

通过spring aop来给springmvc的controller层的方法加日志,进入方法,方法执行完都记录日志,同时记录方法执行的耗时。日志输出级别为debug,通过log4j的分级别输出日志到不同的文件,下面记录下aop拦截的步骤。

JAR包导入如下:

compile group: 'org.springframework', name: 'spring-aop', version: '4.3.18.RELEASE'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.1'
compile group: 'org.aspectj', name: 'aspectjrt', version: '1.9.1'
compile group: 'aopalliance', name: 'aopalliance', version: '1.0'

导入JAR包需要注意的地方:

JAVA AOP 报错error at ::0 can’t find referenced pointcut pointCut02

原因在于引入的aspectjweaver.jar版本过低,替换为1.8.7版本后可正确运行。JDK环境为1.7时,应使用1.7.3及以上版本的aspectj的jar包。

jdk version spring version aspectjrt version and aspectjweaver version
1.6 3.0 + aspectjrt-1.6.2  and aspectjweaver-1.6.2
1.7 3.0 + aspectjrt-1.7.3 and aspectjweaver-1.7.3

1.applicationContext.xml中的配置

<!--开启注解-->
<context:annotation-config />
 
<!-- 使用annotation 自动注册bean,并保证@Required,@Autowired的属性被注入 -->
<context:component-scan base-package="com.***">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
 </context:component-scan>
 
<!-- 开启自动切面代理 -->
<aop:aspectj-autoproxy />

2.aspect类的编写 
@Before 进入方法打印带包路径的方法名和当前时间
@After 方法执行完打印带包路径的方法名和当前时间
@Around 大于常量的毫秒数,打印方法执行时间

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogInterceptor {
    private final Logger logger = Logger.getLogger(LogInterceptor.class);
 
    // 一分钟,即60000ms
    private static final long ONE_MINUTE = 5000;
 
    @Pointcut("execution(public * com.jam..controller..*.*(..))")
    public void myMethod(){};
 
    /**
     *  进入方法后打印日志
     * @param joinPoint
     */
    @Before("myMethod()")
    public void before(JoinPoint joinPoint) {
        logger.debug(this.getMethodName(joinPoint)+" start "+ DateUtil.getDateTime(new Date()));
    }
 
    /**
     * 方法结束打印日志
     * @param joinPoint
     */
    @After("myMethod()")
    public void after(JoinPoint joinPoint) {
        logger.debug(this.getMethodName(joinPoint)+" after"+ DateUtil.getDateTime(new Date()));
    }
 
 
    @Around("execution(* com.jam..controller..*.*(..))")
    public Object processLog(ProceedingJoinPoint joinPoint) throws Throwable {
        // 定义返回对象、得到方法需要的参数
        Object obj = null;
        Object[] args = joinPoint.getArgs();
        long startTime = System.currentTimeMillis();
 
        try {
            obj = joinPoint.proceed(args);
        } catch (Throwable e) {
            logger.error("统计某方法执行耗时环绕通知出错", e);
        }
 
        // 获取执行的方法名
        long endTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
 
        // 打印耗时的信息
        this.printExecTime(methodName, startTime, endTime);
 
        return obj;
    }
 
    /**
     * 打印方法执行耗时的信息,如果超过了一定的时间,才打印
     * @param methodName
     * @param startTime
     * @param endTime
     */
    private void printExecTime(String methodName, long startTime, long endTime) {
        long diffTime = endTime - startTime;
        if (diffTime > ONE_MINUTE) {
            logger.debug( methodName + " 方法执行耗时:" + diffTime + " ms");
        }
    }
 
    /**
     * 获取方法名(类的详细包路径)
     * @param joinPoint
     * @return
     */
    private String getMethodName(JoinPoint joinPoint){
        return joinPoint.getSignature().getDeclaringTypeName() +
                "." + joinPoint.getSignature().getName();
    }
 
    /**
     * AfterReturning  拦截执行
     */
    @AfterReturning("execution(public * com.jam.finance..controller..*.*(..))")
    public void AfterReturning() {
        logger.debug("method AfterReturning");
    }
 
    /**
     *  AfterThrowing 拦截执行
     */
    @AfterThrowing("execution(public * com.jam.finance..controller..*.*(..))")
    public void AfterThrowing() {
        logger.debug("method AfterThrowing");
    }
}

3.步骤2中的日志打印都是debug或者info级别,分级别输出日志就好,我们这里使用的是log4j日志,log4j可以这样配置

4.配置好后运行我们Spring项目即可,然后只要一有方法执行,则会通过日志进行记录输出到控制台,有助于了解方法执行快慢对系统进行性能调试