一.为什么用IOC

IOC控制反转 ,目的为了让我们编写出更加松耦合,更加优雅的程序。传统的程序需要我们在类的内部显示的创建依赖对象。从而导致类与类之间的耦合度过高。,将对象的创建,查找依赖,以及生命周期的控制权交给了 Ioc 容器。对象之间
耦合较松,更加灵活。

二.IOC的实现原理

IOC实现的基础是工厂模式和反射机制

但是对于传统的工厂模式,如果增加新的水果类,就需要改动工厂类,采用反射的方式进行解决。

public static IFruit getInstance(String className) {
IFruit fruit = null;
try {
fruit = (IFruit) Class.forName(className).newInstance();//Class 类 的 forName 方法构造类的实例
//forName 返回与给定字符串名称的类或接口相关联的 类对象。
//newInstance 创建由此类对象表示的类的新实例。 该类被实例化为一个具有空参数列表的new表达式。 如果类尚未初始化,则初始化该类
}catch (Exception e) { e.printStackTrace();
}
return fruit; }
public static void main(String[] args) {
IFruit obj = null;
obj =
FruitFactoryByReflection.getInstance("com.bryantchang.reflectionTest.Ap
ple");
obj.eat();
}

但是我们不可能没错都知道完整的包名和类型啊!
这时候创建一个存放 bean 的资源文件

public class BeanResourceOps {
public static Properties getBeanProperties() throws IOException {
//Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。 
Properties beanProperty = new Properties();
File beanResourceFile = new
File("/Users/TheBlindM/Desktop/work/meituan/codes/test_codes/personal_
growth/testJava/src/main/resources/fruit.properties");
if (beanResourceFile.exists()) {
beanProperty.load(new FileInputStream(beanResourceFile));
}else {
beanProperty.setProperty("apple",
"com.bryantchang.reflectionTest.Apple");
beanProperty.setProperty("orange",
"com.bryantchang.reflectionTest.Orange");
beanProperty.store(new FileOutputStream(beanResourceFile),
"fruit bean infos");
}
return beanProperty; } }

调用

public static void main(String[] args) throws IOException {
IFruit obj = null;
Properties fruitProperties = BeanResourceOps.getBeanProperties();
obj =
FruitFactoryByReflection.getInstance(fruitProperties.getProperty("apple
"));
obj.eat();
}

三.IOC容器初始化流程

接下来简要概括一下流程,具体源码可以去
IOC源码分析
1.Resource定位
创建 spring 容器时,通常要访问 XML 配置文件,除此之外还可以通过访问文件类型、
二进制流等方式访问资源,还有当需要网络上的资源时可以通过访问 URL,Spring 把这些
文件统称为 Resource。

FileSystemResource:以文件的绝对路径方式进行访问资源,效果类似于 Java 中的 File;
ClassPathResourcee:以类路径的方式访问资源,效果类似于

this.getClass().getResource("/").getPath();
2.通过返回的 resource 对象,进行 BeanDefinition 的载入
BeanDefinition 相当于一个数据结构,这个数据结构的生成过程是根据定位的
resource 资源对象中的 bean 而来的,这些 bean 在 Spirng IoC 容器内部表示成了的
BeanDefintion 这样的数据结构,IoC 容器对 bean 的管理和依赖注入的实现都是通过操
作 BeanDefinition 来进行的。
3.将 BeanDefiniton 注册到容器中
最终 Bean 配置会被解析成 BeanDefinition 并与 beanName,Alias 一同封装到
BeanDefinitionHolder 类中, 之后 beanFactory.registerBeanDefinition(beanName,
bdHolder.getBeanDefinition()),注册到
DefaultListableBeanFactory.beanDefinitionMap (ConcurrentHashMap)中。之后客户端如果要获取 Bean 对象,
Spring 容器会根据注册的 BeanDefinition 信息进行实例化。

四.详细初始化流程

在这里插入图片描述
1.)AbstractApplicationContext#refresh:此方法定义为模板方法(将部分方法延迟到子类实现),包含Spring容器启动的一系列工作。这里我们直接看obtainFreshBeanFactory方法,这是IoC容器初始化的入口。

2.)AbstractApplicationContext#obtainFreshBeanFactory:调用refreshBeanFactory和getBeanFactory方法,重新获取一个新的BeanFactory实例。

3.)AbstractRefreshableApplicationContext#refreshBeanFactory:销毁原BeanFactory,新建DefaultListableBeanFactory实例,并对其进行一些初始化工作。

4.)AbstractXmlApplicationContext#loadBeanDefinitions:上一步创建BeanFactory实例之后,通过该BeanFactory创建XmlBeanDefinitionReader实例,也就是ApplicationContext上下文将BeanDefinition的定位加载工作交付到了XmlBeanDefinitionReader。继续跟到重载的同名方法loadBeanDefinitions(XmlBeanDefinitionReader reader),这一步完成了将部分XML文件资源封装为Resource对象,而用户以XML文件路径传入的BeanDefinition资源文件也会在AbstractBeanDefinitionReader#loadBeanDefinitions(String…locations)逐一封装为Resource对象,并进行进一步的处理。

以上 1.) ~ 4.)步完成了Resource定位的工作。

5.)继续跟踪到XmlBeanDefinitionReader#loadBeanDefinitions:这一步主要是通过Resource对象获取XML文件的输入流,继续执行doLoadBeanDefinitions。因为是XML文件格式组织的BeanDefinition,因此AbstractBeanDefinitionReader需要委托到其子类XmlBeanDefinitionReader。

6.)XmlBeanDefinitionReader#doLoadBeanDefinitions:通过上一步获取到的输入流和Resource对象,获取Document对象。

7.)XmlBeanDefinitionReader#registerBeanDefinitions:初始化DefaultBeanDefinitionDocumentReader对象,将上一步得到的Document对象交由DefaultBeanDefinitionDocumentReader对象解析,并统计解析的BeanDefinition个数。

8.)DefaultBeanDefinitionDocumentReader#registerBeanDefinitions:获取Document对象的根元素,执行doRegisterBeanDefinitions。

9.)继续跟到DefaultBeanDefinitionDocumentReader#parseBeanDefinitions:初始化BeanDefinitionParserDelegate实例,递归解析Document。DefaultBeanDefinitionDocumentReader负责读入Document对象,实际将Document解析为BeanDefinition的却是BeanDefinitionParserDelegate对象。

10.)继续跟到DefaultBeanDefinitionDocumentReader#processBeanDefinition:传入Document对象的单个Element元素,将其交由上一步的BeanDefinitionParserDelegate对象解析,解析后得到BeanDefinitionHolder对象。解析过程较为复杂,有兴趣可以自行跟踪。

以上 5.) ~10.)步完成了BeanDefinition的载入工作,得到BeanDefinitionHolder对象。

11.)顺着DefaultBeanDefinitionDocumentReader#processBeanDefinition继续往下看,跟到BeanDefinitionReaderUtils#

registerBeanDefinition。传入上一步的BeanDefinitionHolder对象,将BeanDefinition注册到IoC容器中。

以上11.)完成的是向IoC容器注册这些BeanDefinition,BeanFactory是以Map的结构组织这些BeanDefinition的。可以在DefaultListableBeanFactory中看到此Map的定义。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
以上就是Spring IoC容器的整个启动过程,这里完成的仅仅是BeanDefinition的载入和注册,Javabean之间的依赖关系并不会在初始化的时候完成!
————————————————

借鉴了该博客的部分内容 https://blog.csdn.net/sinat_34596644/article/details/80394209

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐