什么是IOC控制翻转?
Spring作为常用框架,面试的时候经常会有面试官问,Spring IOC了解吗?作为面试者回答:这个我知道,Spring IOC控制翻转,就是把实例化的对象交给Spring容器来实始化。 面试官继续问:哪里说说IOC控制翻转有什么作用(优点),是怎么实现的?作为面试者:enmmmm…这个时候就很尴尬了。
任何事情不能局限于表面,需要有求知意识,尽自己能力和理解去探求真相,当然这个也不是一蹴而就,是一个循序渐进的过程,重要的是在这条路上需要不断求索。
了解Bean是如何在Spring中配置的
Bean默认是配置在applicationContext.xml文件中,那么我们首先需要明白Spring是如何加载配置文件的,请看博文:你不知道系列–Spring是如何加载配置文件
通过博文我们会了解到Spring配置文件是如何实现加载的,Spring配置文件实例化的流程,通过依赖Servlet的创建和销毁,来实现Soring配置文件加载和销毁,其实ContextLoaderListener是web组件,真正实例化的是Tomcat,Tomcat在启动加载web.xml实例化并识别Lintenter配置,Spirng是维护了ApplicationContext对象(容器对象),通过ApplicationContext对象读取配置文件。
了解Bean是如何在Spring中实例化
在了解Spring如何加载配置文件和读取Bean后,那么Bean是如何进行实例化的,以及Bean的生命周期流程是什么,请看博文:spring–Bean生命周期。
通过博文我们会了解到通过Spring维护的applicationContext对象(容器对象),可以通过getBean()来获取指定Bean对象,是不是很神奇?,不仅如此,如果知道Bean的生命周期,我们还能再Bean实例化的各个步骤中进行动态处理。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
}
容器对象ApplicationContext
ApplicationContext接口继承了很多接口,这些接口我们可以将其分为五类:
- MessageSource,主要用于国际化
- ApplicationEventPublisher,提供了事件发布功能
- EnvironmentCapable,可以获取容器当前运行的环境
- ResourceLoader,主要用于加载资源文件
- BeanFactory,负责配置、创建、管理Bean,IOC功能的实现主要就依赖于该接口子类实现。
具体的接口功能,大家有兴趣可以了解,推荐博文:https://blog.csdn.net/qq_41907991/article/details/104890350
应该如何理解Spring核心思想IOC控制翻转:
对象的创建交给外部容器完成,这个就做控制反转。
- Spring使用控制反转来实现对象不用在程序中写死
- 控制反转解决对象处理问题【把对象交给别人创建】
如何理解DI,是如何处理对象之间的依赖问题:
- Spring使用依赖注入来实现对象之间的依赖关系
- 在创建完对象之后,对象的关系处理就是依赖注入
IOC和DI关系
IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI(Dependency Injection),即依赖注入。
DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI依赖注入
- Spring使用依赖注入来实现对象之间的依赖关系(实际开发中我们常用的@Autowired注解标注在对象属性上,实际就是提供了对象属性的Set方法)
- 在创建完对象之后,对象的关系处理就是依赖注入
Srping如何处理Bean的属性注入
- 有参构造注入
- set方法注入
总结:
所谓 IOC ,就是由 Spring IOC 容器来负责对象的生命周期和对象之间的关系。
来段代码看看没有IOC容器进行属性注入
- 定义一个水果类接口
public interface Fruit {
public void get();
}
- 实现水果的类,具体水果
public class Apple implements Fruit {
public void get() {
// TODO Auto-generated method stub
System.out.println("拿到苹果!!!");
}
}
public class Banana implements Fruit {
public void get() {
// TODO Auto-generated method stub
System.out.println("拿到香蕉!!");
}
}
- Service业务层,这里主要是体现业务逻辑
(体现组件之间的依赖关系,对象属性的注入)
public class UserService {
private Fruit fruit = new Apple();
public void getName() {
fruit.get();
}
}
- 模拟用户类调用
public class User {
public static void main(String[] args) {
UserService us = new UserService();
us.getName();
}
}
思考:
其实这一段代码没什么问题,控制会输出"拿到苹果!!",这也符合我们代码需求,但是水果的实现不止一种,如果我们需要香蕉呢?这个时候就需要改动Service代码,把苹果换成香蕉,那么每次更换都需要重新new水果对象,这样一点都不灵活,其实我们需要思考一个问题?我们每次用到自己依赖的对象真的需要自己去创建吗?我们知道,我们在UserService依赖的Fruit对象其实并不是依赖该对象本身,而是依赖它所提供的服务(不同水果对象,提供不同的水果),只要在我们需要它的时候,它能够及时提供服务即可,至于它是我们主动去创建的还是别人送给我们的,其实并不是那么重要。再说了,相比于自己千辛万苦去创建它还要负责初始化、管理使用、销毁等方法而言,直接有人送过来是不是显得更加好呢?
IOC就是给我们送东西过来的人,或者理解称为中介,需要什么直接找到IOC容器,用户需要苹果,并不需要直接创建苹果,而是开放程序接口,由IOC容器进行注入。
IOC的思想最核心的地方在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。
再来段代码看看IOC容器,通过set属性注入后的变化
- 在Service业务层依赖水果类,我们给水果类提供了Set方法进行属性注入
public class UserService {
private Fruit fruit;
public void getName() {
fruit.get();
}
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
}
- 模拟用户调用,这时我们需要什么水果权限,就给到用户来决定(控制权限翻转)。
public class User {
public static void main(String[] args) {
UserService us = new UserService();
us.setFruit(new Banana());
us.getName();
}
}
最后总结一下使用IOC的好处:
- 不用自己组装,拿来就用。
- 享受单例的好处,效率高,不浪费空间。
- 便于单元测试,方便切换mock组件。
- 便于进行AOP操作,对于使用者是透明的。
- 统一配置,便于修改。
Spring IOC容器和DI(依赖注入的)是相辅相成的关系,通过组件或对象的注入实现对象创建,不需要用户亲自管理组件或对象的组合装配,这些都交给IOC容器进行管理,需要什么从资源管理的第三方IOC容器获取,用户只需要专注自己的业务逻辑。
希望帮助大家更熟悉Spring IOC的原理和意义,请多多支持我的博客!