参考某个大神的分解Spring源码,具体的github地址给忘记了。后续补上
加入我自己的理解,如果让你写Spring的BeanFacotory,你是否也会想到这么写?
我们知道,spring就是一个大容器(工厂),里面放置了spring初始化的bean,我们需要需要获得该bean时,直接从spring容器中取就OK了。所以,我们大致描述为:
BeanDefine类表示在spring中怎样定义该bean的。
BeanFactory表示bean工厂,beanDefinitionMap中存放keyàbeanName,valueàBeanDefinition,getBean方法通过beanName获取到该定义的Bean,registerBeanDefinition是通过key,value形式将bean放在工厂中。
但是,该bean需要我们手动实例化,并放在spring容器中。在spring中,bean的实例化是由spring自动管理的,我们只需要提供类的全限定名即可。所以,我们继续改为如下:
BeanDefine:我们继续增加两个属性,beanClassName就是我们需要放在spring bean工厂的类名,spring会通过反射机制实例化该bean
BeanFactory:此外,我们也需要做一些拓展了,将其方法交给实现类AbstractBeanFactory,由AbstractBeanFactory的子类去创建一个bean。。
这时,我们还得考虑另外的问题:类中是有属性的, 按照我们常规的观念:
BeanObject obj = new BeanObject(); obj.setProperties(“abc”);
spring一个典型的功能就是实现IOC(或者说依赖注入DI),我们初始化一个bean时,不仅要实例化该类,还得注入其属性值。
所以,我们继续改造:
BeanDefine:我们增加一个PropertyValues(是一个PropertyValue的List),表示该bean还有若干属性。PropertyValue:keyà属性名,valueà属性值
此时,在AutowireCapableBeanFactory创建一个bean时,分为两步了:先createBeanInstace(通过反射创建bean对象),而后applyPropertyValues(同样通过反射注入属性值到属性里面)。
但是这样,在客户端我们依然要通过PropertyValues设置属性的值。在spring中是通过xml或者注解的方式直接注入。
所以,我们将属性注入改为xml配置。。
这时我们需要新写一个工厂(BeanDefinitionReader)来读取我们的配置文件,从而生成bean。
loadBeanDefinitions(String location)就是很据配置文件的位置(location),读取到registry(是一个Map(beanName,beanDefinition))。其大致流程为:
先用ResourceLoader获取到文件的路径(相对路径哦),并转为InputStream对象。。
而后使用Xml解析器(如:dom4j,sax[这里使用了]),解析了该xml,将xml配置为内容对应到BeanDefinition的ClassName,PropertyValues中去。。
最后再使用BeanFactory生成bean,跟第三步一样。。
可发现,之前我们的注入都是一些常规值。在spring中,我们经常遇到的就是一个bean依赖另外一个bean。比如service层里就是引用dao层。所以,继续改造如下:
对比第4步,基本上没什么大的改动。
增加了一个类BeanReference,保存bean的名name,以及bean对象
此时就需要该两处逻辑了:
1、 XmlBeanDefinitionReader中的processProperty方法,(该方法就是将解析后的xml根据节点属性值传给PropertyValue),我们还需要判断是否是ref属性,如果是则将该属性封装成BeanRefrence,其他同第四步
2、 在AutowireCapableBeanFactory的applyPropertyValues方法中,同样需要判断属性值是否为BeanReference类型,如果是,通过beanReference得到bean,注入到bean属性中。、
我们写个测试:(step01-step04均未写测试方法,你可以看下附件。主要我想着在这里的测试方法基本上能涵括上面所有步骤了)
@Test public void test01() throws Exception{ XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); xmlBeanDefinitionReader.loadBeanDefinitions("li/wei/craftTest/xml/step05.xml");
// 2.初始化BeanFactory并注册bean BeanFactory beanFactory = new AutowireCapableBeanFactory(); for (Map.Entry beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); }
UserService bean = (UserService) beanFactory.getBean("userService"); bean.sayWord(); bean.sayName(); } |
经过上面几步,我们完成了spring的核心内容àBeanFactory。基本上完成了spring的bean工厂生成bean的整个流程。
观察上面的测试方法,基本也没什么问题。但是,你会发现,实例化BeaFactory,怎么加载配置文件,都得需要我们手动指定。为此,我们得考虑让spring去读取我们的资源,自动初始化BeanFactory。。
如果说之前我们完成了spring的心脏(BeanFactory),接下来我们还得为spring造身体àApplicaitonContext
(UML图做了一些调整,删除了一下不太重要的关联。还是基于Step05)
这时,我们就可以将spring加载我们的配置(xml)交给ApplicationContext,并且ApplicationContext加载好配置后,就会调用refresh()方法,该方法会直接调用BeanFactory的 registerBeanDefiniation(),初始化我们的bean工厂。这时我们就直接可以使用getBean()方法直接获取到我们的bean了。。