SpringAOP详解

1. SoringAOP

1.1 什么是AOP?

AOP(Aspect Oriented Programming):⾯向切⾯编程,它是⼀种思想,它是对某⼀类事情的集中处理.

1.2 AOP的作用

就比如说使用csdn时,首先要经过登录页面验证用户信息,然后进入网站后,无论访问任何页面都需要验证你是否登录了,这个时候就需要使用AOP思想,在某处统一配置一下,就可以处理所有需要判断用户是否登录的方法,就这就不需要在每个方法中都实现用户登录判断.

对于那些需要功能统一且使用次数非常之多的功能,可以考虑使用AOP来统一处理了,比如:统一的异常处理,统一的登录拦截,统一的返回格式,统一的日志记录…

1.3 AOP的组成

1.3.1 切面

切⾯(Aspect)由切点(Pointcut)和通知(Advice)组成,它既包含了横切逻辑的定义,也包括了连接点的定义。
通俗来说:AOP是统一事物处理,这个统一处理事物的功能就是切面,

1.3.2 连接点

应⽤执⾏过程中能够插⼊切⾯的⼀个点,这个点可以是⽅法调⽤时,抛出异常时,甚⾄修改字段时。切⾯代码可以利⽤这些点插⼊到应⽤的正常流程之中,并添加新的⾏为.
简单来理解就是:能够可能触发AOP(拦截方法的点),.就是连接点

1.3.3 切点

Pointcut 的作⽤就是提供⼀组规则(使⽤ AspectJ pointcut expression language 来描述)来匹配 Join Point,给满⾜规则的 Join Point 添加 Advice.
通俗说就是:定义AOP 拦截的规则

1.3.4 通知

定义了切⾯是什么,何时使⽤,其描述了切⾯要完成的⼯作,还解决何时执⾏这个⼯作的问题。
简单来说:就是AOP执行的时机和执行的方法.

Spring 切⾯类中,可以在⽅法上使⽤以下注解,会设置⽅法为通知⽅法,在满⾜条件后会通知本
⽅法进⾏调⽤:

  • 前置通知使⽤ @Before:通知⽅法会在⽬标⽅法调⽤之前执⾏。
  • 后置通知使⽤ @After:通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤
  • 返回之后通知使⽤ @AfterReturning:通知⽅法会在⽬标⽅法返回后调⽤。
  • 抛异常后通知使⽤ @AfterThrowing:通知⽅法会在⽬标⽅法抛出异常后调⽤。
  • 环绕通知使⽤ @Around:通知包裹了被通知的⽅法,在被通知的⽅法通知之前和调⽤之后执⾏⾃定义的⾏为

AOP整个概念图如下:
在这里插入图片描述

2. SpringAOP 实现

2.1 添加AOP引用

在pom.xml中添加如下代码:

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

2.2 代码演示

流程:

  • 创建SpringBoot框架
  • 实现切面
  • 实现切点
  • 定义通知

Controller层代码:

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
//@RequestMapping("/user")
public class UserController {

    @RequestMapping("/sayhi")
    public String sayHi() {
        System.out.println("sayHi 方法:被执行了。");
        return "你好,世界。";
    }

    @RequestMapping("/sayhello")
    public String sayHello() throws InterruptedException {
        Thread.sleep(250);
        System.out.println("sayHello 方法:被执行了。");
        //测试抛出异常
//        int num = 10 / 0;
        return "世界,你好。";
    }
}

AOP配置代码:

package com.example.demo.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Aspect    //此处表示这个类是一个切面
@Component//定义切面
public class UserAspect {

    //定义切点
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))") //此处说明切点的定义范围是UserController类下的所有方法
    public void pointCut(){
    }

    //定义前置通知
    @Before("pointCut()")
    public void doBefore(){
        System.out.println("执行了前置通知");
    }

    //定义后置通知
    @After("pointCut()")
    public void doAfter() {
        System.out.println("后置通知:被执行了。");
    }

    //返回之后的通知
    @AfterReturning("pointCut()")
    public void doAfterReturning() {
        System.out.println("执行了 AfterReturning 方法");
    }

    //抛出异常之后的通知
    @AfterThrowing("pointCut()")
    public void doAfterThrowing() {
        System.out.println("执行了 AfterThrowing 方法");
    }


    //针对环绕通知的计算方法所需的时间
    @Around("pointCut()")
    public Object Around(ProceedingJoinPoint proceedingJoinPoint){

        Object o=null;
        System.out.println("环绕方法开始了");
        StopWatch stopWatch=new StopWatch();//用这个类来实现计算方法使用的时间
        try {
            stopWatch.start();
            o=proceedingJoinPoint.proceed();
            stopWatch.stop();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕方法执行了");
        System.out.println(proceedingJoinPoint.getSignature().getDeclaringTypeName()+"."+//次方法为获取到路劲和类名
                proceedingJoinPoint.getSignature().getName() +"使用时间为:" +  //次方法为获取到方法名
                stopWatch.getTotalTimeMillis()+"ms");    //次方法获取总的时间
        return o;

    }
}

结果如下,几种通知的区别很容易看出来.
在这里插入图片描述

3. SpringAOP 实现原理

Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的⽀持局限于⽅法级别的拦截
Spring AOP ⽀持 JDK Proxy 和 CGLIB ⽅式实现动态代理。默认情况下,实现了接⼝的类,使⽤AOP 会基于 JDK ⽣成代理类,没有实现接⼝的类,会基于 CGLIB ⽣成代理类。

在这里插入图片描述
织入:代理的生成时机

织⼊是把切⾯应⽤到⽬标对象并创建新的代理对象的过程,切⾯在指定的连接点被织⼊到⽬标对象中.
在⽬标对象的⽣命周期⾥有多个点可以进⾏织⼊:
编译期:切⾯在⽬标类编译时被织⼊。这种⽅式需要特殊的编译器。AspectJ的织⼊编译器就是以这种⽅式织⼊切⾯的。
类加载器:切⾯在⽬标类加载到JVM时被织⼊。这种⽅式需要特殊的类加载器(ClassLoader),它可以在⽬标类被引⼊应⽤之前增强该⽬标类的字节码.AspectJ5的加载时织⼊(load-time weaving.LTW)就⽀持以这种⽅式织⼊切⾯。
运⾏期:切⾯在应⽤运⾏的某⼀时刻被织⼊。⼀般情况下,在织⼊切⾯时,AOP容器会为⽬标对象态创建⼀个代理对象。SpringAOP就是以这种⽅式织⼊切⾯的。

动态代理:

此种实现在设计模式上称为动态代理模式,在实现的技术⼿段上,都是在 class 代码运⾏期,动态的织⼊字节码。
我们学习 Spring 框架中的AOP,主要基于两种⽅式:JDK 及 CGLIB 的⽅式。这两种⽅式的代理⽬标都是被代理类中的⽅法,在运⾏期,动态的织⼊字节码⽣成代理类。

  • CGLIB是Java中的动态代理框架,主要作⽤就是根据⽬标类和⽅法,动态⽣成代理类.
  • Java中的动态代理框架,⼏乎都是依赖字节码框架(如 ASM,Javassist 等)实现的.
  • 字节码框架是直接操作 class 字节码的框架。可以加载已有的class字节码⽂件信息,修改部分信息,或动态⽣成⼀个 class.

3.1JDK和CGLIB 的区别

  1. JDK 实现,要求被代理类必须实现接⼝,之后是通过 InvocationHandler 及 Proxy,在运⾏时动态的在内存中⽣成了代理类对象,该代理对象是通过实现同样的接⼝实现(类似静态代理接⼝实现的⽅式),只是该代理类是在运⾏期时,动态的织⼊统⼀的业务逻辑字节码来完成。
  2. CGLIB 实现,被代理类可以不实现接⼝,是通过继承被代理类,在运⾏时动态的⽣成代理类对象。

4. 总结

AOP 是对某⽅⾯能⼒的统⼀实现,它是⼀种实现思想,Spring AOP 是对 AOP 的具体实现,SpringAOP 可通过 AspectJ(注解)的⽅式来实现 AOP 的功能,Spring AOP 的实现步骤是:

  1. 添加 AOP 框架⽀持。
  2. 定义切⾯和切点。
  3. 定义通知。
    Spring AOP 是通过动态代理的⽅式,在运⾏期将 AOP 代码织⼊到程序中的,它的实现⽅式有种:JDK Proxy 和 CGLIB
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值