Spring AOP - 注解实现统计service中方法的执行时间

来源:互联网 发布:mysql 表空间大小限制 编辑:程序博客网 时间:2024/05/17 05:14

一、概述

Spring 2.0引入了更简单和更强大的方式来实现AOP——基于schema的方式和基于@AspectJ的方式。
本文介绍如何使用Spring AOP提供的@AspectJ注解注入切面,来统计service中方法的执行时间。

二、AOP概念

先简单介绍下AOP的主要概念和术语。

  • Aspect: 切面,即横切多个类的关注点的组合。事务管理即为横切关注点的一个很好的示例。Sprint AOP中,切面由一般类实现(基于schema的方式),或者由带有@Aspect注解的一般类(注解方式)来实现。
  • Join point: 接入点,指程序执行中的某个点,例如一个方法的执行,或者异常的处理。Spring AOP中,一个接入点通常指一个方法的执行。
  • Advice: 切入点在一个特殊接入点上执行的动作。有很多类型的advice,如around, before, after。Spring AOP中将advice实现为拦截器,管理围绕接入点的一串拦截器。
  • Pointcut:匹配接入点的谓词。每个advice都关联到一个pointcut表达式,并且在匹配接入点时(例如,某个特定名字方法的执行时)运行。Spring默认使用AspectJ的pointcut表达式语言。
  • target object: 被一个或多个切面advice的对象。由于Spring AOP通过运行时代理来实现,因此此处的对象总是指被代理对象。
  • AOP proxy:AOP为实现切面而创建的对象。Spring中一个AOP代理可能是JDK动态代理,也可以是CGLIB代理。
  • Weaving: 织入,链接切面和其他应用类型或对象,以创建一个被advice的对象。可以在编译时(例如使用AspectJ编译器)、加载时或者运行时执行。Spring AOP在运行时织入。

advice类型:

  • Before : 在接入点执行,一般不会阻止接入点的运行(除非抛出异常)
  • after returning: 在接入点正常终止时执行,例如在一个方法无任何异常返回后执行。
  • after throwing: 方法抛出异常时执行
  • after(finally): 不论接入点正常返回还是异常返回。
  • around:环绕一个接入点,如一个方法调用。最强大的advice。可以在方法调用之前和之后进行一些定制化行为。也可以控制是否执行接入点,或者干脆返回自己的结果,甚至抛出一个异常。本文即使用around advice实现在方法执行前开始秒表计时,在方法执行后关闭秒表,并统计方法运行时间。

三、@AspectJ注解

使用此注解结合普通java类即可实现声明式切面编程。@AspectJ注解由AspectJ项目在AspectJ 5时引入。Spring中的同名注解使用AspectJ提供的库来实现pointcut的解析和匹配,但是Spring AOP运行时仍然是纯Spring AOP实现,无需依赖AspectJ编译器或者织入器。

四、Junit测试时,使用@AspectJ统计调用的所有service方法执行时间

1. 开启@AspectJ支持

方法1: 纯java配置

@Configuration@EnableAspectJAutoProxypublic class AppConfig {}

方法2:XML配置(也是本文中使用的配置)

<aop:aspectj-autoproxy proxy-target-class="true" />

2. 声明和定义切面

xml中声明bean:

    <beans:bean id="logServiceMethodTimeAspectBean"        class="com.test.aop.LogServiceMethodTimeAspect">    </beans:bean>

定义java类:

@Aspectpublic class LogServiceMethodTimeAspect {...}

3. 声明一个pointcut

    @Pointcut("execution(* com.test.service.impl.*.*(..))")    private void serviceMethod() {    }

4. 声明advice

    @Around("com.test.aop.LogServiceMethodTimeAspect.serviceMethod()")    public Object logMethodRunningTime(ProceedingJoinPoint pjp)            throws Throwable { ...    }

5. 在Junit测试类上引入定义bean的文件:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { “classpath*:/spring/webmvc-config.xml”,
“classpath*:/test-config.xml” })

6. 完整的代码:

src/test/resources/test-config.xml 文件

<?xml version="1.0" encoding="UTF-8"?><beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:beans="http://www.springframework.org/schema/beans"    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"    xmlns:aop="http://www.springframework.org/schema/aop"    xsi:schemaLocation="        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">    <aop:aspectj-autoproxy proxy-target-class="true" />    <beans:bean id="logServiceMethodTimeAspectBean"        class="com.test.aop.LogServiceMethodTimeAspect">    </beans:bean></beans:beans>

com.test.aop.LogServiceMethodTimeAspect文件:

/** * @Title: LogServiceMethodTimeAspect.java * @Package com.test.aop * @Description: 使用AOP统计service方法运行的时间* @author LIUYUEFENG559 * @date 2016年9月7日 下午4:22:23 * @version V1.0*/package com.test.aop;import java.util.concurrent.ConcurrentHashMap;import org.apache.commons.lang3.time.StopWatch;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;/** * @ClassName: LogServiceMethodTimeAspect * @Description: 使用AOP统计service方法运行的时间* @author LIUYUEFENG559 * @date 2016年9月7日 下午4:22:23  */@Aspectpublic class LogServiceMethodTimeAspect {    private static final ConcurrentHashMap<String, Long> methodTimeMap = new ConcurrentHashMap<>();    /**     * @return methodtimemap     */    public static ConcurrentHashMap<String, Long> getMethodtimemap() {        return methodTimeMap;    }    @Pointcut("execution(* com.test.service.impl.*.*(..))")    private void serviceMethod() {    }    @Around("com.test.aop.LogServiceMethodTimeAspect.serviceMethod()")    public Object logMethodRunningTime(ProceedingJoinPoint pjp)            throws Throwable {        // start stopwatch        StopWatch watch = new StopWatch();        watch.start();        Object retVal = pjp.proceed();        // stop stopwatch        watch.stop();        Long time = watch.getTime();        String methodName = pjp.getSignature().getName();        methodTimeMap.putIfAbsent(methodName, time);        // map大小大于1000后清空掉。这么做是担心有人将本类放到生产环境,造成内存泄露        if (methodTimeMap.size() >= 1000) {            methodTimeMap.clear();        }        return retVal;    }}

测试类文件:

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = { "classpath*:/spring/webmvc-config.xml",        "classpath*:/test-config.xml" })public class BaseJunit4Test  {    @Resource    private DataSource dataSource;    /**     * @Title: printServiceMethodsTime     * @Description: 此方法在每个测试方法结束后打印其调用的所有service方法的执行时间    * @throws     */    @After    public void printServiceMethodsTime() {        ConcurrentHashMap<String, Long> methodTimeMap = LogServiceMethodTimeAspect                .getMethodtimemap();        Set<String> methodNameSet = methodTimeMap.keySet();        System.out                .println("----------下面列出调用的service方法的执行时间start--------------");        for (String method : methodNameSet) {            System.out.println("方法 " + method + "执行时间为:"                    + methodTimeMap.get(method) + "毫秒。");        }        System.out.println("----------下面列出调用的service方法的执行时间end--------------");    }}

五、参考链接

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj

0 0