初识AOP
简而言之的了解下什么是AOP:AOP(Aspect Oriented Programming)称为面向切面编程,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程(这里尤指Spring AOP)。
为什么使用AOP?回答这类的问题,就从它最常使用的场景入手。
比如我现在有一段校验的代码,判断数据的有效性,好多地方都会用到,于是,为了保证功能的正确性,每个使用到的地方都加上这段代码。
重复性代码太不美观和高效了,于是将校验逻辑单独提取出去,这样就好多了。
这样虽然有了一些切面编程的意思,但是如果现在业务代码已经写完了,只是想补充上这段校验逻辑,如果以这种方式去实现,那势必要改动这个类,加上这段校验。而AOP的实现是将校验逻辑独立出去,运行时在业务实现方法前插入校验逻辑,其表现为下图的样子。
这样的好处显而易见的,它不会破坏原有的代码逻辑,也无需改动业务代码,可以使业务方法更专注于业务实现。
AOP的基本概念
关于Spring AOP,有很多概念,先来梳理一下:
- Joint point(连接点):程序执行过程中明确的点,一般是方法的调用。被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。它自身还可以嵌套其它 joint point。简单点理解就是可以认为一个方法的执行就是一个连接点,它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。
- Pointcut(切点):表示一组 joint point,在程序中主要体现为书写切入点表达式,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
- Advice(增强):Advice 定义了在 Pointcut 里面定义的具体要增强的操作,包括before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕),以此区别在何处增强。
- Aspect(切面): 通常是一个类,类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
- Target(目标对象):织入 Advice 的目标对象.。
- Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程,在编译时(例如使用AspectJ编译器)、加载时或运行时完成。
- AOP Proxy(AOP代理):AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
使用AOP的准备工作
必要的jar包还是需要引入的,最简单的,就是直接将spring-boot-starter-aop引入就好了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>
通过Java启用@AspectJ支持@Configuration,需要在AppConfig类上添加@EnableAspectJAutoProxy 注释:
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
当然,也可以使用xml配置,需要加入aop:aspectj-autoproxy:
<aop:aspectj-autoproxy/>
AOP Demo
趁热打铁,来写个例子
先准备一个实现类,在StudyService 接口中定义getLearningontents()方法
@Component
public class StudyServiceImpl implements StudyService {
@Override
public String getLearningontents() {
System.out.println("方法执行中...");
return "aop";
}
}
想要实现切面,就要有一个类,引入@Aspect让其生效。接下来,定义一个切入点,并指定在什么位置增强,于是有了下面的代码:
@Component
@Aspect
public class StudyAspect {
@Pointcut("execution(* com.study.ioc.service.*.*(..))")
public void studyTransfer(){};
@Before("studyTransfer()")
public void before(){
System.out.println("执行前...");
}
@After("studyTransfer()")
public void after(){
System.out.println("执行后...");
}
}
execution(* com.study.ioc.service..(…))是一种切入点表达式,表示service包中定义的任何方法都执行。下面的@Before和@After指明对于哪个切入点进行通知。
在App类中测试一下:
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
StudyService studyService = ac.getBean(StudyService.class);
System.out.println(studyService.getLearningontents());
}
}
执行结果: 执行前...
方法执行中...
执行后...
aop
到此,一个简单的AOP实现就做好了。
AOP的基础理论和基本使用就介绍到这,有关Spring对AOP的实现部分且看后续