Spring之IOC、AOP
在学习Spring的时候,常常听见IOC、AOP,这两个东西其实就是Spring的精髓所在,特别是IOC和AOP,下面就开始讲讲IOC和AOP到底是什么
IOC(Inversion of control)
英文翻译过来就是控制反转。是一种思想即在Java中将对象的创建以及管理问题。
将控制反转进行拆分进行理解:
- 控制:对象的创建以及管理
- 反转:控制权交给外部环境(IOC容器)
为什么要把控制权交给外部环境?
假如现在有一个对象A依赖了对象B,在普通的Java程序中,是这样写的
class A {
private B b;
private C c;
// ..
public A() {
// 注入A依赖的对象
this.b = new B();
this.c = new C();
// ..
}
}
class B {
//属性...
}
class C {
//属性...
}
有什么问题?如果一个对象所依赖的属性非常多,对象的构造器的代码非常的多(所依赖的对象需要我们一一去new),耦合性非常强
有没有一种方法可以简化创建对象的流程,或者干脆就不需要我们去关注创建对象的过程
那就是IOC,IOC可以让我们不用去关注对象的创建以及管理,你只需要将一个对象使用配置文件或者注解标注;Spring会自动将这个对象放入一个容器(实际上就是一个Map<key, value>)管理起来;当你在一个另一个被Spring管理起来的对象中依赖了这个对象,Spring会帮助你自动注入依赖;
示例:
@Component
@Data
class A {
@Resource
private B b;
@Resource
private C c;
}
@Component
class B {
//属性...
}
@Component
class C {
//属性...
}
可以看到代码立即简洁了;我们不再需要关注对象的创建过程以及管理,并且随着对象依赖的对象越来越多,IOC的优势会更加明显;
IOC总结
IOC是一种设计模式,它将对象的创建和管理交给了框架或容器,而不是由应用程序自己进行管理。在IOC中,对象的依赖关系由容器来维护,而不是在代码中显式硬编码。
AOP(Aspect oriented programming)
翻译为中文是 “面向切面编程”。在软件开发中,AOP 是一种编程范式,它与传统的 “面向对象编程”(OOP)相辅相成。AOP 的核心思想是将应用程序的横切关注点(cross-cutting concerns)从主业务逻辑中分离出来,以便更好地组织和管理这些横切关注点
其中的专业术语(觉得很晦涩难懂的可以先看下面的内容,再回来理解)
- 横切关注点(cross-cutting concerns) :多个类或对象中的公共行为(如日志记录、事务管理、权限控制、接口限流等等)。
- 切面(Aspect):对横切关注点进行封装的类,一个切面是一个类。切面可以定义多个通知,用来实现具体的功能。
- 连接点(JoinPoint):连接点是方法调用或者方法执行时的某个特定时刻(如方法调用、异常抛出等)。
- 通知(Advice):通知就是切面在某个连接点要执行的操作。通知有五种类型,分别是前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around)。前四种通知都是在目标方法的前后执行,而环绕通知可以控制目标方法的执行过程。
- 切点(Pointcut):一个切点是一个表达式,它用来匹配哪些连接点需要被切面所增强。切点可以通过注解、正则表达式、逻辑运算等方式来定义。比如
execution(* com.xyz.service..*(..))
匹配com.xyz.service
包及其子包下的类或接口。 - 织入(Weaving):织入是将切面和目标对象连接起来的过程,也就是将通知应用到切点匹配的连接点上。常见的织入时机有两种,分别是编译期织入(AspectJ)和运行期织入(AspectJ)。
有什么好处?
假如我们需要在一些业务方法执行的前后进行例如日志记录、事务管理、权限控制、接口限流等,正常要怎么做?
不使用AOP
public class MyBusinessClass {
public void doSomething() {
// 接口限流
System.out.println("接口限流");
// 业务逻辑
System.out.println("业务逻辑1正在执行...");
// 横切关注点:记录日志
System.out.println("记录日志:业务逻辑执行完成...");
}
public void doSomething2() {
// 接口限流
System.out.println("接口限流");
// 业务逻辑
System.out.println("业务逻辑2正在执行...");
// 横切关注点:记录日志
System.out.println("记录日志:业务逻辑执行完成...");
}
public void doSomething3() {
// 接口限流
System.out.println("接口限流");
// 业务逻辑
System.out.println("业务逻辑3正在执行...");
// 横切关注点:记录日志
System.out.println("记录日志:业务逻辑执行完成...");
}
}
使用AOP
public class MyBusinessClass {
public void doSomething() {
// 业务逻辑
System.out.println("业务逻辑正在执行...");
}
public void doSomething() {
// 业务逻辑
System.out.println("业务逻辑正在执行...");
}
}
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
@Before("execution(* com.example.MyBusinessClass.*())")
public void beforeDoSomething() {
// 横切关注点:接口限流
System.out.println("接口限流");
}
@After("execution(* com.example.MyBusinessClass.*())")
public void AfterDoSomething() {
// 横切关注点:记录日志
System.out.println("在业务逻辑执行之前执行横切关注点(after advice):记录日志");
}
}
好处
可以看出使用AOP后
-
可读性提高:使用AOP后,业务代码更加简洁了,不再混杂着横切关注点的代码,提高了可读性
-
维护性提高:如果需要修改横切关注点(例如日志记录),在使用AOP的情况下只需修改切面代码,而不需要修改业务逻辑代码。这降低了修改的风险并提高了维护性
-
代码复用性增加:通俗来讲,就是不需要写那么多的重复代码了
应用场景
- 日志记录:自定义日志记录注解,利用 AOP,一行代码即可实现日志记录。
- 性能统计:利用 AOP 在目标方法的执行前后统计方法的执行时间,方便优化和分析。
- 事务管理:
@Transactional
注解可以让 Spring 为我们进行事务管理比如回滚异常操作,免去了重复的事务管理逻辑。@Transactional
注解就是基于 AOP 实现的。 - 权限控制:利用 AOP 在目标方法执行前判断用户是否具备所需要的权限,如果具备,就执行目标方法,否则就不执行。例如,SpringSecurity 利用
@PreAuthorize
注解一行代码即可自定义权限校验。 - 接口限流:利用 AOP 在目标方法执行前通过具体的限流算法和实现对请求进行限流处理。
- 缓存管理:利用 AOP 在目标方法执行前后进行缓存的读取和更新。
AOP的实现方式
AOP的实现方式主要有两种:编译时(AspectJ)和运行时(Spring AOP)。以下是对两种方式的简要介绍:
- 编译时实现(AspectJ):
- AspectJ语言: AspectJ是一个独立的编程语言,支持AOP编程范式。它通过特定的关键字和语法扩展Java语言,使得程序员可以直接在AspectJ语言中定义切面和通知。
- 编译过程: 在编译时,AspectJ编译器会将AspectJ语言的代码编译成Java字节码。这个编译过程会将切面织入到目标程序中,生成一个包含横切关注点的增强类。
- 运行时实现(Spring AOP):
- 动态代理: Spring AOP使用了JDK动态代理和CGLIB动态代理两种技术来实现运行时的横切关注点织入。
- JDK动态代理: 当目标对象实现了接口时,Spring AOP会使用JDK动态代理。它通过创建实现了相同接口的代理对象,并在代理对象中添加横切关注点的逻辑。
- CGLIB动态代理: 当目标对象没有实现接口时,Spring AOP会使用CGLIB动态代理。它通过创建目标对象的子类,并在子类中添加横切关注点的逻辑。
无论是编译时还是运行时实现,AOP的核心思想都是通过在连接点上织入切面的代码,从而实现横切关注点的功能。
参考链接:https://javaguide.cn/system-design/framework/spring/ioc-and-aop.html