代码参考牛客网大佬,何人听我楚狂声:https://github.com/CN-GuoZiyang/My-Spring-IOC/tree/82967670e52fe66ad55a6b2a539dbb4d48b46805
代码目录分5大部分:
- context
- factory
- entity
- io
- reader
已实现功能:基本属性注入,引用依赖注入,singleton和prototype模式注入。
第一阶段:要实现基于XML的配置文件注入,第一步想到的就是怎么把xml文件里面的内容给拿出来,这部分涉及到三个目录的内容,每个目录的功能如下。
1.entity目录
此目录下定义了4种实体类,也就是4种数据结构,核心的数据结构是BeanDefinition实体类。
2.io目录
此目录的功能是通过.xml文件名去获取.xml文件的绝对路径名,然后建立InputStream。
3.reader目录
此目录功能是通过io目录建立的InputStream将xml文件的内容拿到一个Document对象里面,这里用的是jdk自带的类DocumentBuilderFactory和DocumentBuilder,拿到代表xml文件内容的Docunment对象后就对Document对象从文件根递归解析,把每个bean的定义和配置信息拿到我们定义的Definition对象里面,包括beanName,beanClassName,propertyValues,singleton。最后返回一个Map<String, BeanDefinition>,key是bean的名字,value就是bean的信息(一个Definition对象),最后关闭输入流inputStream.close()。
第二阶段:拿到我们所有的bean的信息后我们怎么生成bean对象呢?这就是第二阶段的内容,这次我们从上往下分析:我们在Spring框架中通过.xml文件去拿到Spring容器的时候,都要通过一个ClassPathXmlApplicationContext(xx.xml)的有参构造来返回一个应用上下文ApplicationContext,他是Spring的入口,然后我们通过这个应用上下去调applicationContext.getBean("beanName")就能得到我们的bean对象,所以context目录的功能就出来了:
4.context目录
此目录定义一个ApplicationContext接口,在接口中定义getBean方法,给出了获取bean对象的方法,而getBean的方法实现,我们用ClassPathXmlApplicationContext这个类来实现,我们在这个类里面把第一阶段产生的一个Map全部转移到BeanFactory里面的Map中去,也就是把每个bean的名字和对应的Bean的信息都传给BeanFactory,然后对象怎么产生,我们定义在BeanFactory中。
注意,在 Spring 中,ApplicationContext 实现 BeanFactory 接口的方式,是在 ApplicationContext 对象的内部,保存了一个 BeanFactory 对象的实例,实质上类似一种代理模式。所以我们通过applicationContext.getBean去获取Bean,实际上是beanFactory.getBean去获取Bean,applicationContext.getBean封装了beanFactory.getBean。
5.factory目录
我们用ConcurrntHashMap存储关于Bean的Name和Bean的信息以保证并发安全。
我们根据Spring框架的2行常见代码来解析factory的内容:
第一行是获取Spring IOC容器,也就是建立BeanFactory工厂,本质工作就是检查BeanFactory里面存储Bean信息的Map,看哪个Bean是singleton模式,singleton模式的Bean在这个阶段就直接生成,然后存储到Map里面去,下次要用就直接返回就行了。核心代码是在ClassPathXmlApplicationContext类里面调用了prepareBeanFactory(beanFactory),这行代码的本质就是调用BeanFactory里面的doCreateBean(),循环遍历来创建singleton模式的Bean对象。
第二行代码就是从Spring IOC容器里面get Bean对象,本质也是调用BeanFactory的getBean方法,如果singleton模式就直接返回,不是就去创建。
创建Bean的本质是通过反射来创建的,对象的创建依赖:
Object bean = beanDefinition.getBeanClass().newInstance();
而对象的属性的赋值依赖:
Field field = bean.getClass().getDeclaredField(propertyValue.getName());