IOC
所谓的IOC(inversion of control),就是控制反转的意思。何为控制反转?
在传统的程序设计中,应用程序代码通常控制着对象的创建和管理。例如,一个对象需要依赖其他对象,那么它会直接new出来对象。这样的设计通常被称为 "控制流程"。
而在IOC 中,控制关系发生了反转。控制权被转移到Spring容器中,容器负责创建和管理对象,并在需要的时候将它们注入到应用程序中。
所以,原来这个对象的控制权在我们的代码中,我们自己new的对象,在Spring中,应用程序不再控制对象的创建,而是被动地接受由容器注入的对象。
我们拿代码来举个例子:
下面是一个没有IOC的例子
class A {}
class B {// B需要将A的实例new出来,也就是我们说的控制private A a = new A();public void use() {System.out.print(a);}}
当有了IOC之后
@Component // 说明A自己控制自己,把自己初始化出来,注入给了容器
class A {}class B {// B不需要控制a,直接使用。如果A没有把自己注入给容器,B就不能使用@Resourceprivate A a;public void use() {System.out.print(a);}}
也就是说,没有Spring的话,我们要使用的对象,需要我们自己创建,而有了Spring的IOC之后,对象由IOC容器创建并管理,我们只需要在想要使用的时候从容器中获取就行了。
值得说明的是,IOC只是一种思想和理念,可以有不同的实现方式。
IOC的优点
使用IOC,有最少三个好处:
- 使用者不用关心引用bean的实现细节,譬如对于
B b = new A(c,d,e,f);
来说,如果B要使用A,那还要把c,d,e,f多个类全都感知一遍,这显然是非常麻烦且不合理的
- 不用创建多个相同的bean导致浪费,仍然是:
A b = new A();
A z = new A();
如果B和Z都引用了A,那么B和Z就可能new 两个A实例,实际上,我们只需要一个就好了。
- Bean的修改使用方无需感知。同样是上面的例子,假如说BeanA需要修改,如果没有IOC的话,所有引用到A的其他bean都需要感知这个逻辑,并且做对应的修改。但是如果使用了IOC,其他bean就完全不用感知到
Spring的IOC
对于Spring的IOC来说,它是IOC思想的一种实现方式。在容器启动的时候,它会根据每个bean的要求,将bean注入到SpringContainer中。如果有其他bean需要使用,就直接从容器中获取即可,如下图所示:
扩展知识
IOC是如何实现的?
使用Spring的IOC容器能力,非常简单,如下代码所示:
ApplicationContext context= new AnnotationConfigApplicationContext("cn.wxxlamp.spring.ioc");
Bean bean = context.getBean(Bean.class);
bean.use();
从上面的代码中,我们也能看出来Spring的IOC是如何实现的:
- 从配置元数据中获取要DI的业务POJO(这里的配置元数据包括xml,注解,configuration类等)
- 将业务POJO形成BeanDefinition注入到Spring Container中
- 使用方通过ApplicationContext从Spring Container直接获取即可。如下图所示:
AOP
AOP(Aspect-Oriented Programming),即面向切面编程,用人话说就是把公共的逻辑抽出来,让开发者可以更专注于业务逻辑开发。
和IOC一样,AOP也指的是一种思想。AOP思想是OOP(Object-Oriented Programming)的补充。OOP是面向类和对象的,但是AOP则是面向不同切面的。一个切面可以横跨多个类和对象去操作,极大的丰富了开发者的使用方式,提高了开发效率。
譬如,一个订单的创建,可能需要以下步骤:
- 权限校验
- 事务管理
- 创建订单
- 日志打印
如果使用AOP思想,我们就可以把这四步当成四个“切面”,让业务人员专注开发第三个切面,其他三个切面则是基础的通用逻辑,统一交给AOP封装和管理。
Spring AOP有如下概念(列举下,不用刻意记):
术语 | 翻译 | 释义 |
Aspect | 切面 | 切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。 |
PointCut | 切入点 | 切入点是对连接点进行拦截的条件定义,决定通知应该作用于截哪些方法。(充当where角色,即在哪里做) |
Advice | 通知 | 通知定义了通过切入点拦截后,应该在连接点做什么,是切面的具体行为。(充当what角色,即做什么) |
Target | 目标对象 | 目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。 |
JoinPoint | 连接点 | 连接点是程序在运行时的执行点,这个点可以是正在执行的方法,或者是正在抛出的异常。因为Spring只支持方法类型的连接点,所以在Spring中连接点就是运行时刻被拦截到的方法。连接点由两个信息确定:
|
Weaving | 织入 | 织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。 |
对于通知类型来说:
Before Advice | 连接点执行前执行的逻辑 |
After returning advice | 连接点正常执行(未抛出异常)后执行的逻辑 |
After throwing advice | 连接点抛出异常后执行的逻辑 |
After finally advice | 无论连接点是正常执行还是抛出异常,在连接点执行完毕后执行的逻辑 |
Around advice | 该通知可以非常灵活的在方法调用前后执行特定的逻辑 |