一、SpringAOP
1、基本概念
- SpringAOP是面向切面编程,是一种技术,是spring的框架下的技术之一
- 作用:在程序运行期间,不修改源码对已有方法进行增强(底层原理是动态代理技术)通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用
- 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、 在AOP 这种思想还没有出现的时候,我们解决 切面的问题思路无非有以下两种:
-
方式一:通过静态方法实现(缺点:需要修改源码,后期不好维护)把需要添加的代码抽取到一个地方,然后在需要添加那些方法中引用;
-
方式二:通过继承方案来解决(缺点:需要修改源码,继承关系复杂,后期不好维护),抽取共性代码到父类, 子类在需要的位置,调用父类方法。(抽取代码过程中就修改了源码)
其实AOP 字面直译过来是面向切面编程,其实通俗一点它就是对我们的具体某个方法进行了增强而已。在之前我们对某个方法进行增强无非是两种手段 ,一种是装饰者模式 、 一种是代理模式 (静态代理 & 动态代理) 。 AOP 的底层使用的是动态代理方式
3.AOP主要术语
- JoinPoint: 连接点
类里面哪些方法可以被增强,这些方法称为连接点. 在spring的AOP中,指的是所有现有的方法。 - Pointcut: 切入点
在类里面可以有很多方法被增强,但是实际开发中,我们只对具体的某几个方法而已,那么这些实际增强的方法就称之为切入点 - Advice: 通知/增强
增强的逻辑、称为增强,比如给某个切入点(方法) 扩展了校验权限的功能,那么这个校验权限即可称之为增强 或者是通知
通知分为:
前置通知: 在原来方法之前执行.
后置通知: 在原来方法之后执行. 特点: 可以得到被增强方法的返回值
环绕通知:在方法之前和方法之后执行. 特点:可以阻止目标方法执行
异常通知: 目标方法出现异常执行. 如果方法没有异常,不会执行. 特点:可以获得异常的信息
最终通知: 指的是无论是否有异常,总是被执行的 - Aspect: 切面
切入点和通知的结合(在切面类编写通知的逻辑(前置还是后置),按照规则在切面类中编写哪些通知需要跟哪些切点联系)
二、代码的实现
切面类(编写切入规则和通知规则,有点像动态代理的处理器类)
客户端(客户端中有切点,就是实际需要被增强的方法)
- 切面类
@Aspect
@Component
public class MyAspect {
/**
* 定义需要拦截的东西,这里介绍两个常用的表达式:一个是使用 execution(),另一个是使用 annotation()。
* 1.以 execution(* com.mutest.controller..*.*(..))) 表达式为例:
* 第一个 * 号的位置:表示返回值类型,* 表示所有类型。
* 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,在本例中指 com.mutest.controller包、子包下所有类的方法。
* 第二个 * 号的位置:表示类名,* 表示所有类。
* *(..):这个星号表示方法名,* 表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
* 2.annotation() 方式是针对某个注解来定义切点,比如我们对具有 @PostMapping 注解的方法做切面,可以如下定义切面,也可以自定义注解
*
*/
//AopController类下的save方法需要被增强
@Before("execution(* com.itan.springAOP..AopController.save(..))")
public void pointCut() {
System.out.println("执行了execution的拦截方法");
}
//切入点的方法:所有被PostMapping注解修饰的方法都会被advice通知
@Before("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public void checkPrivilege() {
System.out.println("get请求权限校验了");
}
//对标记有自定义注解PermissionAnnotation的方法进行增强
@Before("@annotation(com.itan.springAOP.PermissionAnnotation)")
public void checkPrivilege01() {
System.out.println("PermissionAnnotation注解方法校验了");
}
- 客户端类
@RestController
@RequestMapping("/aop")
public class AopController {
@Autowired
private AopService aopService;
@GetMapping("/save")
public String save() {
String s = aopService.save();
return s;
}
@PostMapping("/update")
public String update() {
return "更新成功";
}
@PostMapping("/permission")
@PermissionAnnotation()
public String permission() {
return "自定义注解";
}
}
- 自定义注解
/**
* 自定义注解()
* 1.元注解:@Target指明此注解用在哪个位置,如果不写默认可以用在任何位置上
* TYPE:用在类,接口上
* FIELD:用在成员变量上
* METHOD:用在方法上
* PARAMETER:用在参数上
* CONSTRUCTOR:用在构造方法上
* LOCAL_VARIABLE:用在局部变量上
* 2.元注解:@Retention定义注解生命周期
* SOURCE:注解只存在于Java源代码中,编译生成的字节码文件中就不存在了
* CLASS:注解存在于Java源代码、编译以后的字节码文件中,运行的时候内存中没有,默认值
* RUNTIME:注解存在于Java源代码中、编译以后的字节码文件中、运行时内存中,程序可以通过反射获取该注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionAnnotation {
}
- 小结:
可以看出,AOP比动态代理更加简洁,切面类+客户端就可以,到底需要有继承接口的委托者类还是没有继承接口的委托者类,这些不用管;直接在切面类中告诉spring,哪些类哪些方法那些注解标记的方法需要被增强就行