基础[后端] Spring AOP面向切面

160 阅读4分钟

什么是AOP

AOP被称为面向切面的程序设计或面向方面程序设计,是计算机科学中的一种编程思想。旨在于将交叉关注点与业务主体的核心关注点分离,以提高代码的模块化程度,通过在现有代码基础上增加额外的通告机制,能够被声明为切入点的代码块进行统一的管理与装饰。

面向方面编程是面向对象的延申与拓展。若称它与面向对象编程思想并列倒是有点夸大其词了。

个人觉得AOP更类似于模板方法模式的产物,或者说模板方法模式是AOP的产物,通过组织特定流程而实现代码的拦截执行操作。

代理的应用场景

AOP因其思想特性决定了它主要是处理那些需要大量重复编码的流程化编程,通过织入的方式对重复流程进行统一处理,降低了代码的维护难度提升了编码的效率。

  • 统一的日志记录
  • 统一的方法执行时间统计
  • 统一的返回格式设置
  • 统一的异常处理
  • 事务的开启提交回滚等

AOP的组成

在Spring AOP中沿用了Aspectj的概念,具体AspectJ中AOP的组成包括

术语中文概念
Aspect切面切入点和通知的集合,一般单独作为一个类,切入点和通知共同定义了关于切面的全部内容,它是什么时候,在何时何处完成的任务。
JoinPoint连接点指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法
Advice通知指拦截到JoinPoint之后要做的事情,即对切入点的增强
Pointcut切入点指要对哪些Joinpoint进行拦截,即被拦截的连接点
Proxy代理生成的代理对象
Weaving织入把增强代码应用到目标上,生成代理对象的过程
Target目标代理的目标对象

Spring AOP中的注解

Spring AOP中主要提供了五种通知,分别是前置通知,后置通知,返回通知,异常通知,环绕通知。这五种通知作用方式和触发时机如下所示

image.png

同时提供了@Aspect切面标识和@Pountcute注解标识织入点

@Aspect

声明标识类为切面类

@Pointcute

声明织入点,一般标识那些方法或类、包需要增强

@Pointcute("execution(* function(..))")
@Pointcute("within(..)")
@Pointcute("@annotation(..)")
@Pointcute("bean(..)")
private void pointcute(){}

execution

表示匹配方法签名,* 表示任意返回值,..表示任意参数

@Pointcute("execution(* function(..))")@Pointcute("execution(* testControlller(..))") // 指定方法

within

表示指定的类或包下的方法 是Spring特有的

@Pointcute("within(..)")@Pointcute("within(com.tset.controller)")// 对controller包进行标记

@annotation

该方式是spring特有的,表示对IOC容器中的Bean进行增强

@Pointcute("@annotation(..)")@Pointcute("@annotation(com.test.annotation.MyAnnotation)") // 表示对注解标识的类方法进行标记

bean

该方式是spring特有的,表示对IOC容器中的Bean进行增强

@Pointcute("bean(idOrNameOfBean)")@PointCute("bean(testController)") // 对controller层进行标记

JoinPoint对象

该对象是提供了连接点处的可用状态和有关它的静态信息的反射访问对象

Object[] getArgs() //                   返回此连接点处(目标方法)的参数,目标方法无参数时,返回空数组
Signature getSignature() //             返回连接点处的签名。
Object getTarget() //                   返回目标对象
Object getThis() //                     返回当前正在执行的对象
StaticPart getStaticPart() //           返回一个封装此连接点的静态部分的对象。
SourceLocation getSourceLocation() //   返回与连接点对应的源位置
String toLongString() //                返回连接点的扩展字符串表示形式。
String toShortString() //               返回连接点的缩写字符串表示形式。
String getKind() //                     返回表示连接点类型的字符串
Object proceed() //                     进入下一个拦截器,该方法是ProceedingJoinPoint提供的next方法

Spring boot中AOP的使用

Spring boot提供的面向切面能力是在spring-boot-startr-aop依赖包中实现的,我们需要引入相关依赖才能开始编码

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

引入相关依赖后我们需要在启动类上开启AOP代理,一般AOP自动装配类中默认启动了

@SpringBootApplication
@EnableAspectJAutoProxy
public class TestApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(TestApplication.class);
    }
}

开启启用Aspectj注解后我们需要编写织入类,如下所示


@Aspect //              声明本类为切面类
@Order(value = 999) //    指定切入顺序
@Component //           将本类交给spring容器管理
public class MyAspect {
    // 表示对com.test.controller包下的方法进行代理
    @Pointcut(value = "within(com.test.controller..*)")
    private  void aspectPoint(){}

    @Before(value = "aspectPoint()")
    private void before(){
        System.out.println("before");
    }
    @After(value = "aspectPoint()")
    private void after(){
        System.out.println("after");
    }
    @AfterReturning(value = "aspectPoint()")
    private void returning(){
        System.out.println("returning");
    }
    @AfterThrowing(value = "aspectPoint()")
    private void throwing(){
        System.out.println("throwing");
    }

    @Around(value = "aspectPoint()")
    private void around(ProceedingJoinPoint joinPoint)throws Throwable{
        System.out.println("around");
        try {
            joinPoint.proceed(joinPoint.getArgs());
        } catch (Exception e) {
            System.out.println(e);
        }
        System.out.println("afteraround");
    }
}

demo

通过@Before打印方法的入参

@Before()
public void before(JoinPoint point){
    System.out.Println("arguments args:"+ Arrays.toString(point.getArgs()));
}

通过@AfterReturning打印方法的出参

@AfterReturning(pointcut="",returning="returnval")
public void afterReturining(Object returnval){
    System.out.Println("return args:" + returnVal);
}