欢迎大家来到IT世界,在知识的湖畔探索吧!
什么是控制反转?
控制反转(Inversion of Control,缩写为IoC)是软件工程中的一项原则,它将对对象或程序部分的控制转移到容器或框架中。我们最常在面向对象编程的上下文中使用它。
与我们的自定义代码调用库的传统编程相比,IoC 使框架能够控制程序的流程并调用我们的自定义代码。为了实现这一点,框架使用内置附加行为的抽象。如果我们想添加自己的行为,我们需要扩展框架的类或插入我们自己的类。
这种架构的优点是:
- 将任务的执行与其实现分离
- 更容易在不同的实现之间切换
- 程序的更大模块化
- 通过隔离组件或模拟其依赖关系并允许组件通过合约进行通信,从而更轻松地测试程序
我们可以通过各种机制来实现控制反转,例如:策略设计模式、服务定位器模式、工厂模式和依赖注入(DI)。
Spring中的IoC
当大家开始学习spring的时候,都会从如下的示例代码开始:
- 获取一个ClassPathXmlApplicationContext对象
- 通过该对象调用getBean方法获取一个对象
- 然后基于这个对象实现你想要的业务
本文会基于源码(5.1.10版本)给大家讲解spring是如何实现IoC的。
步骤一:创建ClassPathXmlApplicationContext
创建一个新的 ClassPathXmlApplicationContext,从给定的 XML 文件加载定义并自动刷新上下文。
其实这个类里有很多该构造器,在这里我们使用这个方法,获取“spring-config.xml”的路径来初始化上下文。
在上述截图调用本类另一个带三个参数的构造器,如下所示,在这个方法中我们看到有三个方法:
- super:调用父类容器的构造方法为容器设置好Bean资源加载器
- setConfigLocation:获取配置路径,即demo中的“spring-config.xml”
- refresh:加载或刷新配置,这是一个很关键的方法
步骤二:创建AbstractXmlApplicationContext
ApplicationContext 实现的基类,从包含 XmlBeanDefinitionReader 理解的 bean 定义的 XML 文档中绘制配置。
子类只需要实现 getConfigResources 或 getConfigLocations 方法。此外,它们可能会覆盖 getResourceByPath 钩子以特定于环境的方式解释相对路径,或 getResourcePatternResolver 以扩展模式解析。
步骤三:创建AbstractRefreshableConfigApplicationContext
AbstractRefreshableApplicationContext 子类,添加了对指定配置位置的通用处理。
作为基于 XML 的应用程序上下文实现的基类,例如 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,以及 XmlWebApplicationContext。
在这里有一个方法需要注意下“setConfigLocation”,他会根据你传入的参数解析出资源文件的路径。
步骤四:创建AbstractRefreshableApplicationContext
ApplicationContext 实现的基类,它应该支持多次调用 refresh(),每次都创建一个新的内部 bean 工厂实例。通常(但不一定),这样的上下文将由一组配置位置驱动,以从中加载 bean 定义。
子类实现的唯一方法是 loadBeanDefinitions,它在每次刷新时被调用。具体实现应该将 bean 定义加载到给定的 DefaultListableBeanFactory 中,通常委托给一个或多个特定的 bean 定义读取器。
请注意,WebApplicationContexts 有一个类似的基类。AbstractRefreshableWebApplicationContext 提供相同的子类化策略,但另外预实现了 Web 环境的所有上下文功能。
还有一种预定义的方式来接收 Web 上下文的配置位置。这个基类的具体独立子类,以特定的 bean 定义格式读取,是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,它们都派生自通用 AbstractXmlApplicationContext 基类;AnnotationConfigApplicationContext 支持@Configuration-annotated 类作为bean 定义的来源。
在这里有一个工厂对象“beanFactory”,他就是创建bean对象的核心,在讲“DI”的时候我们会着重讲解。
步骤五:创建AbstractApplicationContext
ApplicationContext 接口的抽象实现。不强制要求用于配置的存储类型;简单地实现通用的上下文功能。使用模板方法设计模式,需要具体的子类来实现抽象方法。
与普通的 BeanFactory 相比,ApplicationContext 应该检测其内部 bean 工厂中定义的特殊 bean:因此,此类自动注册 BeanFactoryPostProcessors、BeanPostProcessors 和 ApplicationListeners,它们在上下文中定义为 bean。
MessageSource 也可以作为上下文中的 bean 提供,名称为“messageSource”;否则,消息解析将委托给父上下文。此外,应用程序事件的多播器可以在上下文中作为 ApplicationEventMulticaster 类型的“applicationEventMulticaster”bean 提供;否则,将使用 SimpleApplicationEventMulticaster 类型的默认多播器。
通过扩展 DefaultResourceLoader 实现资源加载。因此,将非 URL 资源路径视为类路径资源(支持包含包路径的完整类路径资源名称,例如“mypackagemyresource.dat”),除非 getResourceByPath 方法在子类中被覆盖。
在这个类中有一个关键的方法“refresh”,这个方法是启动spring容器的核心方法。
在这个构造器中有两个方法和一个静态初始化方法块:
- getResoucePatternResolver:获取ResourcePatternResolver对象来将参数中的配置文件地址解析为Resource实例。
- setParent:设置父类,处理配置环境信息等。
- 静态方法块:避免在 WebLogic 8.1 中关闭应用程序时出现奇怪的类加载器问题关闭了ContextClosedEvent事件。
步骤六:getResoucePatternResolver
获取ResourcePatternResolver对象来将参数中的配置文件地址解析为Resource实例。
步骤七:设置此应用程序上下文的配置位置
设置此应用程序上下文的配置位置。如果未设置,则实现可能会酌情使用默认值。
步骤八:加载或刷新配置
这个方法是IoC的核心方法,在这里会做很多的业务逻辑:
- prepareRefresh:调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
- obtainFreshBeanFactory:告诉子类启动 refreshBeanFactory()方法,Bean 定义资源文件的载入从子类的 refreshBeanFactory()方法启动
- prepareBeanFactory:为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
- postProcessBeanFactory:为容器的某些子类指定特殊的 BeanPost 事件处理器
- invokeBeanFactoryPostProcessors:调用所有注册的 BeanFactoryPostProcessor 的 Bean
- registerBeanPostProcessors:为 BeanFactory 注册 BeanPost 事件处理器.
- initMessageSource:初始化信息源,和国际化相关.
- initApplicationEventMulticaster:初始化容器事件传播器.
- onRefresh:调用子类的某些特殊 Bean 初始化方法
- registerListeners:为事件传播器注册事件监听器.
- finishBeanFactoryInitialization:初始化所有剩余的单例 Bean
- finishRefresh:初始化容器的生命周期事件处理器,并发布容器的生命周期事件
- destroyBeans:销毁已创建的 Bean。
- cancelRefresh:取消 refresh 操作,重置容器的同步标识。
Spring IoC容器载入Bean配置信息从其子类容器的“refreshBeanFactory”方法处理的。
步骤九:刷新子类内部bean工厂
在这里有两个方法:
- refreshBeanFactory:初始化新的bean工厂,在这里使用了委派设计模式,父类定义了抽象方法,子类具体实现。
- getBeanFactory:获取新的bean工厂。
步骤十:刷新bean工厂
此实现执行此上下文的底层 bean 工厂的实际刷新,关闭以前的 bean 工厂(如果有)并为上下文生命周期的下一阶段初始化一个新的 bean 工厂。
在这个方法中我们可以看到有以下方法:
- hasBeanFactory:确定此上下文当前是否包含 bean 工厂,即至少已刷新一次且尚未关闭。
- destroyBeans:销毁当前上下文的所有bean,可以重写销毁步骤。
- closeBeanFactory:释放bean工厂。
- createBeanFactory:创建一个bean工厂,默认实现创建一个DefaultListableBeanFactory对象,可以重写,自定义设置。
- customizeBeanFactory:对bean工厂定制化,例如是否覆盖别名、是否允许bean之间循环引用。可以重写,个性定制。
- loadBeanDefinitions:将 bean 定义加载到给定的 bean 工厂,通常通过委托给一个或多个 bean 定义读取器。
在这个方法中,首先判断是否存在BeanFactory,若存在先销毁所有的bean,并释放bean工厂,接着创建一个DefaultListableBeanFactory工厂,然后通过loadBeanDefinitions进行加载。
步骤十一:载入配置路径
通过 XmlBeanDefinitionReader 加载 bean 定义。
在这个方法中我们可以看到有以下方法:
- XmlBeanDefinitionReader:为给定的 BeanFactory 创建一个新的 XmlBeanDefinitionReader。
- setEnvironment:设置环境
- setResourceLoader:设置资源加载器,因为他的继承类“AbstractApplicationContext”通过扩展 DefaultResourceLoader 实现资源加载,所以本身就是资源加载器
- setEntityResolver:设置要用于解析的 SAX 实体解析器。默认情况下,将使用 ResourceEntityResolver。可以为自定义实体解析覆盖,例如相对于某些特定的基本路径。
- initBeanDefinitionReader:初始化用于加载此上下文的 bean 定义的 资源加载器。默认实现为空。可以在子类中被覆盖,例如用于关闭 XML 验证或使用不同的 XmlBeanDefinitionParser 实现。
- loadBeanDefiniitions:使用给定的 XmlBeanDefinitionReader 加载 bean 定义。bean 工厂的生命周期由 refreshBeanFactory 方法处理;因此这个方法只是应该加载和或注册 bean 定义。
步骤十二:Xml Bean读取器加载Bean配置资源
在这个方法中会基于Xml加载bean配置信息,可以看到以下方法:
- getConfigResources:获取资源位置数组,引用构建此上下文的 XML bean 定义文件。还可以包括位置模式,这将通过 ResourcePatternResolver 得到解决。默认实现返回 null。子类可以覆盖它以提供一组资源位置以从中加载 bean 定义。
- getConfigLocations:获取一个 Resource 对象数组,引用构建此上下文的 XML bean 定义文件。默认实现返回 null。子类可以覆盖它以提供预构建的资源对象而不是位置字符串。
- loadBeanDefinitions:从指定的资源位置加载 bean 定义,这里是加载bean真正实现的方法。
注意,getConfigResources使用了委派模式,具体方法是由ClassPathXmlApplicationContext去实现。所以,我们的demo是以路径去加载bean配置的,所以这块他是为空的,如果在创建上下文的时候参数是class的话,他是有值的。
至此,IoC中关于资源定位的步骤已完毕。
时序图
写在最后
好兄弟可以点赞并关注我的公众号“javaAnswer”,全部都是干货。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/30351.html