一、 AOP的一些前置知识
1.1什么是Aop
Aop是一种统一处理某一问题的思想,比如验证用户是否登录。
在为使用Aop的时候,我们需要验证的每个类(页面)都有调用验证方法,而使用了Aop后,我们只需要在某处把验证规则配置一下,就可以实现对需要验证的类的登录验证,不用每个类在重复调用验证方法了。
Aop由切面、切点、连接点、通知组成
切面表示我们要统一处理的功能(类)——比如验证用户是否登录
切点则是是否进行Aop拦截的规则(哪些页面不需要进行登录验证,哪些需要,这种规则)
连接点则是具体到哪些页面需要进行拦截(哪些类需要调用登录验证方法)
通知则是验证用户是否登录的那个具体方法实现(代码细节)——》前置通知,后置通知
而AOP是一种思想,而SpringAOP是这个框架对AOP思想的实现(类似IoC和DI)
1.2 AOP的作用
想象一个场景,我们在做后台系统时,除了登录和注册等几个功能不需要做用户登录验证之外,其他几乎所有页面调用的前端控制器(Controller)都需要先验证用户登录的状态,那么这个时候我们要怎么处理呢?
如果不使用AOP,我们之前的处理方式是每个Controller都要写一遍用户登录验证,然而当你的功能越来越多,那么你要写的登录验证也越来越多,就有了很多重复的代码,而且这些方法的代码修改和维护的成本就会很高。
使用AOP,在进入业务代码之前进行统一的一个处理,去验证用户是否登录。
除了统一的用户登录判断之外,AOP还可以实现:
统一日志记录
统一方法执行时间统计
统一的返回格式设置
统一的异常处理
事务的开启和提交
也就是说使用AOP可以扩充多个对象的某个能力,所以AOP可以说是OOP(Object Oriented Programming)面向对象编程的补充和完善。
1.3AOP基础组成
AOP由以下四部分组成:
1.切面(Aspect):定义AOP业务类型(表示当前AOP是做什么的)。
2.连接点(Join Point):有可能调用AOP的地方就叫做一个连接点。
3.切点(Pointcut):定义AOP拦截规则。
4.通知(Advice)【增强方法】:定义什么时候干什么事(代码的实现细节,前后顺序)。
通知定义的是被拦截的方法具体要执行的业务,比如用户登录权限验证方法就是具体要执行的业务。
a) 前置通知:在拦截的目标方法之前执行的通知(事件)
b)后置通知:在拦截的目标方法之后执行的通知(事件)
c)返回之后通知:在拦截的目标方法返回数据之后通知
d)抛出异常之后的通知:在拦截的目标方法抛出异常之后执行的通知
e)环绕通知:在拦截方法执行前后都执行的通知。
AOP整个组成部分的概念如下图所示,以多个页面都要访问用户登录权限为例
二、SpringAOP的实现
2.1添加SpringAOP框架支持
在我们的项目的pom.xml中添加支持
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
Spring AOP 框架, 在创建新项目的时候,搜索不到。
Spring Boot 项目中,有没有内置 AOP 框架。
这个时候,我们就需要借助 中央仓库了https://mvnrepository.com/
细节拓展: Spring AOP 依赖的版本号标签是可以省略的。
虽然 Spring AOP 没有 作为一个常用框架,导致我们引入框架的时候,需要借助 Maven 中央仓库来引入。
但是!Spring Boot 里面,其实有记录 Spring AOP 的 版本关联信息、
它可根据当前项目的环境,自动引入合适版本的 Spring AOP.
2.2定义切面(Aspect)
2.3定义切点(Pointcut)
其中pointcut方法为空方法,其不需要方法体,此方法名就是起到一个“标识”的作用,标识下面的通知方法具体指的是哪个切点。
切点表达式说明
AspectJ支持三种通配符
- *:匹配任意字符,只匹配一个元素(包,类,或方法,方法参数)
- ..:匹配任意字符,可以匹配多个元素,在表示类时,必须和*联合使用
- +:表示按照类型匹配指定类的所有类,必须跟在类名后面,如com.cad.Car+,表示继承该类的所有子类包括本身
表达式示例如下:
execution(* com.example.demo.UserController. *(..)):匹配UserController类里的所有方法。
execution(* com.example.demo.UserController+.*(..)):匹配UserController类的子类包括该类的所有方法
execution(* com.example.demo.*.*(..)):匹配com.example.demo包下的所有类的所有方法
execution(* com.example.demo..*.*(..)):匹配com.example.demo包下、子孙包下所有类的所有方法
execution(* addUser(String,int)):匹配addUser方法,且第一个参数类型是String,第二个参数类型是int