SpringBoot学习-(九)SpringBoot中的AOP

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依赖


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-aopartifactId>
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
@Component
public 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的值由大到小执行

你可能感兴趣的:(springboot,aop编程,SpringBoot)