spring AOP 注解开发

原文来自搬砖工,如需转载请注明出处

1、简介

AOP主要包括通知、切点和连接点等术语,介绍以下:
1.通知(advice)
通知定义了切面是甚么和什么时候调用,什么时候调用包括以下几种

Before 在方法调用之前调用通知
After 在方法完成以后调用通知,不管方法履行是不是成功
After-returning 在方法成功履行以后调用通知
After-throwing 在方法抛出异常后调用通知
Around 通知包裹了被通知方法,在被通知方法调用之前和调用以后履行自定义的行动

2.切点(pointcut)

通知定义了切面的甚么和什么时候,切点定义了何处,切点的定义会匹配通知所要织入的1个或多个连接点,
我们通常使用明确的类的方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称来指定这些切点。
3.连接点(JoinPoint)
连接点是在利用履行进程中能够插入切面的1个点,这个点可以是调用方法时,抛出异常时,乃至是修改1个字段时,
切面代码可以利用这些连接点插入到利用的正常流程中,并添加新的行动,如日志、安全、事务、缓存等。

joinPoint.getSignature().getDeclaringTypeName() 可以获得到调用方法的类名(包括包名),
joinPoint.getSignature().getName()    可以获得方法名,
Arrays.toString(joinPoint.getArgs())   得到的是方法调用的参数列表,
joinPoint.proceed()   可以得到方法的返回结果

2、注解开发

声明1个切面,只需要在类名上添加@Aspect属性便可,具体的连接点,我们用@pointcut和@Before、@After等标注。
在声明前 我们需要依赖配置pom

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.6.11</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.11</version>
</dependency>
例子:
package com.ganji.demo.service.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Service;
/**
 * Created by admin on 2015/9/2.
 */
@Aspect
@Service
public class XmlAopDemoUserLog {
// 配置切点 及要传的参数  
    @pointcut(execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..)) && args(id))
    public void pointcut(int id)
    {
    }
// 配置连接点 方法开始履行时通知
    @Before(pointcut(id))
    public void beforeLog(int id) {
        System.out.println(开始履行前置通知  日志记录:+id);
    }
//    方法履行完后通知
    @After(pointcut(id))
    public void afterLog(int id) {
        System.out.println(开始履行后置通知 日志记录:+id);
    }
//    履行成功后通知
    @AfterReturning(pointcut(id))
    public void afterReturningLog(int id) {
        System.out.println(方法成功履行后通知 日志记录:+id);
    }
//    抛出异常后通知
    @AfterThrowing(pointcut(id))
    public void afterThrowingLog(int id) {
        System.out.println(方法抛出异常后履行通知 日志记录+id);
    }
//    环绕通知
    @Around(pointcut(id))
    public Object aroundLog(ProceedingJoinPoint joinpoint,int id) {
        Object result = null;
        try {
            System.out.println(环绕通知开始 日志记录+id);
            long start = System.currentTimeMillis();
            //有返回参数 则需返回值
            result =  joinpoint.proceed();
            long end = System.currentTimeMillis();
            System.out.println(总共履行时长 + (end - start) +  毫秒);
            System.out.println(环绕通知结束 日志记录);
        } catch (Throwable t) {
            System.out.println(出现毛病);
        }
        return result;
    }
}

3、AOP切面中的同步问题

在WebLogAspect切面中,分别通过dobefore和doAfterReturning两个独立函数实现了切点头部和切点返回后履行的内容
若我们想统计要求的处理时间,就需要在dobefore处记录时间,并在doAfterReturning处通过当前时间与开始处记录的时间计算
得到要求处理的消耗时间
那末我们是不是可以在WebLogAspect切面中定义1个成员变量来给dobefore和doAfterReturning1起访问呢?是不是会有同步问题呢?
的确,直接在这里定义基本类型会有同步问题,所以我们可以引入ThreadLocal对象,像下面这样进行记录:

@Aspect
@Component
public class WebLogAspect {
    private Logger logger = Logger.getLogger(getClass());
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    @pointcut(execution(public * com.juzi.web..*.*(..)))
    public void webLog(){}
    @Before(webLog())
    public void dobefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());
        // 省略日志记录内容
    }
    @AfterReturning(returning = ret,pointcut = webLog())
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完要求,返回内容
        logger.info(RESPONSE :  + ret);
        logger.info(SPEND TIME :  + (System.currentTimeMillis() - startTime.get()));
    }
}

4、AOP切面的优先级

由于通过AOP实现,程序得到了很好的解耦,但是也会带来1些问题,比如:我们可能会对Web层做多个切面,校验用户
校验头信息等等,这个时候常常会碰到切面的处理顺序问题。

所以,我们需要定义每一个切面的优先级,我们需要@Order(i)注解来标识切面的优先级。i的值越小,优先级越高。
假定我们还有1个切面是CheckNameAspect用来校验name必须为didi,我们为其设置@Order(10),
而上文中WebLogAspect设置为@Order(5),所以WebLogAspect有更高的优先级,这个时候履行顺序是这样的:

在@Before中优先履行@Order(5)的内容,再履行@Order(10)的内容
在@After和@AfterReturning中优先履行@Order(10)的内容,再履行@Order(5)的内容
所以我们可以这模样总结:

在切入点前的操作,按order的值由小到大履行
在切入点后的操作,按order的值由大到小履行

相关文章

统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
前言 之前做了微信登录,所以总结一下微信授权登录并获取用户...
FastAdmin是我第一个接触的后台管理系统框架。FastAdmin是一...
之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...