SpringBoot学习-(九)SpringBoot中的AOP

来源:互联网 发布:js cookie 跨域 编辑:程序博客网 时间:2024/06/08 05:08

AOP相关概念:

  • Target(目标对象):需要被代理增强的对象
  • Proxy(代理对象):目标对象被AOP 织入 增强/通知后,产生的对象.
  • Joinpoint(连接点):指那些被拦截到的点.在Spring中,这些点指方法(因为Spring只支持方法类型的连接点).
  • Pointcut(切入点):指需要(配置)被增强的Joinpoint.
  • Advice(通知/增强):指拦截到Joinpoint后要做的操作.通知分为前置通知/后置通知/异常通知/最终通知/环绕通知等.
  • Aspect(切面):切入点和通知的结合。
  • Weaving(织入):指把增强/通知应用到目标对象来创建代理对象的过程(Spring采用动态代理织入,AspectJ采用编译期织入和类装载期织入).
  • Introduction(引入增强):一种特殊通知,在不修改类代码的前提下,可以在运行期为类动态地添加一些Method/Field(不常用).

AOP相关注解:

  • @Aspect注解将一个java类定义为切面类
  • @Pointcut定义一个切入点,可以是一个规则表达式
  • @Before在切入点开始处切入内容
  • @After在切入点结尾处切入内容
  • @AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
  • @AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑
  • @Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容

1.添加pom依赖

<!-- springboot aop --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-aop</artifactId></dependency>

starter中默认添加了@EnableAspectJAutoProxy

2.编写切面类

package com.ahut.aop;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;/** *  * @ClassName: ActionAspect * @Description:控制层切面类 * @author cheng * @date 2017年9月25日 上午11:05:38 */@Aspect@Componentpublic class ActionAspect {    /**     *      * @Title: actionAspect     * @Description:切入点表达式     */    @Pointcut("execution(public * com.ahut.action.*.*(..))")    public void actionAspect() {    }    /**     *      * @Title: beforeMethod     * @Description:目标方法执行之前调用     */    @Before("actionAspect()")    public void beforeMethod() {        System.out.println("目标方法执行之前调用。。。");    }    /**     *      * @Title: afterMethod     * @Description:目标方法返回或者抛出异常之后调用     */    @After("actionAspect()")    public void afterMethod() {        System.out.println("目标方法返回或者抛出异常之后调用。。。");    }    /**     *      * @Title: afterReturningMethod     * @Description:目标方法返回之后调用,抛出异常时不调用     */    @AfterReturning("actionAspect()")    public void afterReturningMethod() {        System.out.println("目标方法返回之后调用,抛出异常时不调用。。。");    }    /**     *      * @Title: afterThrowingMethod     * @Description:目标方法抛出异常之后调用,正常返回时不调用     */    @AfterThrowing("actionAspect()")    public void afterThrowingMethod() {        System.out.println("目标方法抛出异常之后调用,正常返回时不调用。。。");    }    /**     *      * @Title: aroundMethod     * @Description: 环绕通知     * @param joinPoint     * @return     * @throws Throwable     */    @Around("actionAspect()")    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {        System.out.println("环绕在目标方法之前。。。");        // 访问目标方法名称        String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();        // 访问目标方法的参数        Object[] paramsArr = joinPoint.getArgs();        // 执行目标方法        Object returnObj = joinPoint.proceed(paramsArr);        System.out.println("执行的方法:" + methodName);        System.out.println("环绕在目标方法之后。。。");        return returnObj;    }}

3.执行结果

环绕在目标方法之前。。。目标方法执行之前调用。。。执行的方法:com.ahut.action.GoodsTypeAction.getGoodsTypeList环绕在目标方法之后。。。目标方法返回或者抛出异常之后调用。。。目标方法返回之后调用,抛出异常时不调用。。。

JoinPoint详解

AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象,如果是环绕增强时,使用org.aspectj.lang.ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口。任何一个增强方法都可以通过将第一个入参声明为JoinPoint访问到连接点上下文的信息。我们先来了解一下这两个接口的主要方法:

1)JoinPoint

  • java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
  • Signature getSignature() :获取连接点的方法签名对象;
  • java.lang.Object getTarget() :获取连接点所在的目标对象;
  • java.lang.Object getThis() :获取代理对象本身;

2)ProceedingJoinPoint

ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
- java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
- java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。

常用的几个:

//获取目标方法所在类名称String className = joinPoint.getSignature().getDeclaringTypeName()//获取目标方法名称String methodName = joinPoint.getSignature().getName();//获取目标方法完整名称String fullMethodName = className + "." + methodName;//获取目标方法参数Object[] paramsArray = joinPoint.getArgs();//以原来的参数列表执行目标方法Object returnObject = joinPoint.proceed();//以自定义参数列表执行目标方法Object returnObject = joinPoint.proceed(paramsArray);

关于多个切面的执行顺序

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

所以要定义每个切面的优先级,我们需要@Order(i)-(直接添加在切面类上)注解来标识切面的优先级。

i值越小,优先级越高。

假设我们还有一个切面是CheckNameAspect用来校验name必须为jack,我们为其设置@Order(10),而上文中WebLogAspect设置为@Order(5),所以WebLogAspect有更高的优先级,这个时候执行顺序是这样的:

在@Before中优先执行@Order(5)的内容,再执行@Order(10)的内容
在@After和@AfterReturning中优先执行@Order(10)的内容,再执行@Order(5)的内容

创建两个相似的ActionAspect, 代码执行结果:

环绕在目标方法之前。。。111目标方法执行之前调用。。。111环绕在目标方法之前。。。222目标方法执行之前调用。。。222//切入点环绕在目标方法之后。。。222目标方法返回或者抛出异常之后调用。。。222目标方法返回之后调用,抛出异常时不调用。。。222环绕在目标方法之后。。。111目标方法返回或者抛出异常之后调用。。。111目标方法返回之后调用,抛出异常时不调用。。。111

所以我们可以这样子总结:

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