spring中基于注解使用AOP

本文内容:spring中如何使用注解实现面向切面编程,以及如何使用自定义注解。html


一个场景

好比用户登陆,每一个请求发起以前都会判断用户是否登陆,若是每一个请求都去判断一次,那就重复地作了不少事情,只要是有重复的地方,就有优化的空间。如今就把重复的地方抽取出来,暂且称之为 " 拦截器 ",而后每次请求以前就先通过" 拦截器 ",这个编程的思想就能够称之为面向切面编程。AOP(Aspect Oriented Program)java

最典型的应用就是事务管理和权限验证,还有日志统计,下文中的案例就是接口执行时间的统计。spring

spring中使用AOP(基于注解)

不得不说注解是个很巧妙的设计,使用不多量的信息描述数据,这类数据称之为元数据,描述数据的数据。关于注解的理解,这里有个传送门:http://www.importnew.com/10294.html编程

下面的案例是在springBoot中进行的,直观地感觉一下如何使用注解完成AOP。springboot

@Service
public class UserService {

    public void getUser() {
        //To do something
        System.out.println("getUser() has been called");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

切面是这样定义的:性能

@Component
@Aspect
public class LoggerAspect {
    /**
     * getUser()执行以前执行
     */
    @Before("execution(* com.springboot.demo.service.UserService.getUser(..))")
    public void callBefore() {
        System.out.println("before call method");
        System.out.println("begin........................");
    }

    /**
     * getUser()执行以后执行
     */
    @After("execution(* com.springboot.demo.service.UserService.getUser(..))")
    public void callAfter() {
        System.out.println("after call method");
        System.out.println("end..............................");
    }
}

来个单元测试验证一下:单元测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void getUserTest() {
        userService.getUser();
    }
}

具体案例

假若有如下的业务场景: UserService业务类中有个getUser()这个方法,如今想统计一下这个方法的执行时间,可能须要测试这个接口的性能。一般作法是方法开始时获取系统当前时间,而后方法结束时获取当前时间,最后 excuteTime=endTime-startTime。测试

若是如今不只是这个方法须要统计,还有getUserByName()、getUserById()须要统计,上述的方法明显很笨了。优化

使用AOP怎么解决? 抽取公共部分为一个切面,方法执行前记录时间,而后执行目标方法,最后,目标方法执行完成以后再获取一次系统时间。设计

具体实现以下:在LoggerAspect中再写一个方法,记录getUser()方法的执行时间。

/**
     * 记录执行时间
     * @param point 切点
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.springboot.demo.service.UserService.getUser(..))")
    public Object getMethodExecuteTime(ProceedingJoinPoint point) throws Throwable {
        System.out.println("---------------getMethodExecuteTime------------------");
        long startTime = System.currentTimeMillis();
        //调用目标方法
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTime;
        System.out.println("executeTime=" + executeTime + "------------------");

        return result;
    }

@Around将目标方法再次封装,控制了它的调用时机,以此来记录getUser()的执行时间。可是好像并无达到记录UserService中的多个方法的执行时间的目的。

@Around("execution(* com.springboot.demo.service.UserService.getUser(..))")

其中指定了切点是getUser()这个方法,这里的表达式很丰富,能够设置为:

* com.springboot.demo.service.UserService.*(..)

表示UserService中的每个方法都是切点,甚至能够是这样:

* com.springboot.demo.service..(..)

表示service包下的全部类的全部方法都是切点,可是这样很明显不够灵活,若是能自定义地控制就更好了。

自定义一个注解

若是用一个注解标注某个方法须要记录其执行时间,岂不是更加优雅。

/**
 * @Description 标注某个方法须要记录执行时间
 * @Author YaoQi
 * @Date 2018/7/6 15:51
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented

public @interface Logger {
    String value() default "";
}

注解是用来描述数据的,上面的这个注解的意思是:这个注解将做用于方法,而且在运行时有效。可是这样只是标注了,如何读取这个标注的信息?

在LoggerAspect中加入这一个方法:

/**
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.springboot.demo.annotation.Logger)")
    public Object getMethodExecuteTimeForLogger(ProceedingJoinPoint point) throws Throwable {
        System.out.println("---------------getMethodExecuteTime------------------");
        long startTime = System.currentTimeMillis();
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTime;
        System.out.println("executeTime=" + executeTime + "------------------");

        return result;
    }

哪一个方法须要记录执行时间就将@Logger放在对应的方法上:

@Logger
    public void getUser() {
        System.out.println("getUser() has been called");
    }
相关文章
相关标签/搜索