一、前言【回顾先前的内容】
前面,我们做了两件事。
第一,介绍了Spring框架中,BeanDefinition具备的一些常规属性和属性的含义。
Bean的一些属性总结http://t.csdnimg.cn/590Yv
第二,介绍了Bean资源的加载过程。
BeanDefinitionReader接口,Spring加载Bean的过程http://t.csdnimg.cn/SHu1p
接下来,我们需要去学习剩下的两件事,Bean的加载以及实例化【其实只需要知道Bean如何加载即可,我们BeanDefinition里存储Class需要的类路径信息,很轻松就能实例化】
这一步做完,我们就可以真正地使用Bean了
二、Bean的注册是什么
我们知道,BeanFactory里有一个管理Bean的容器:一个Key为String,Value为BeanDefinition的Map数据结构。
当我们调用getBean获得Bean时,本质就是从Map数据结构里,拿到BeanDefinition,然后将其指向的类实例化成对象,再返回。
假设我们想要一个Bean,名字叫test。
如果这个Map数据结构里没有这个Bean,明显不能得到预期结果。
往Map数据结构里添加String和BeanDefinition的过程,就叫做Bean的注册。【由于BeanFactory里定义了Map数据结构,所以手动注册,本质还是比较简单,只需注意一些验证问题】
注意:Bean的加载,是将Bean定义的资源,统一加载为Resource、Document这样的资源文件,供给访问。
而Bean的注册,则是将BeanDefinition定义,写入Map数据结构,供给实例化的过程。【注:注册这一步,不会实例化Bean。一般来说,实例化是在调用getBean方法时实例化,也有一些加载策略,会先实例化。】
三、与上期Bean的加载的衔接
如果看了上期的Bean加载的文章,我们会发现最后调用了int count = this.registerBeanDefinitions(doc, resource);这个方法。
如果缘着这个方法走,我们最终会发现很多验证、加载Document文档的方法。然而,这些东西对于Spring框架,都被看成输入数据,只要符合某个标准,就可以使用。如果在现在就陷进去,会捋不清楚。
我们只要记住一件事,Bean加载最后的结果,一定是得到一组BeanDefinition数组。
接下来,就从BeanDefinitionRegistry接口开始,理解Bean注册的过程。
四、手动注册Bean的代码
代码在下面,非常长,不过测试代码占了很大一部分,最重要的是理解registerBeanDefinition这个方法。【该方法做的事也不多,就是把BeanDefinition与它的名字对应,并把Bean注册的名字加入Map】(验证、检测忽略)
我们学习一下这段代码的准备、测试部分。
注册前的步骤:
1.实例化BeanDefinition Registry对象,为调用注册方法做准备。
2.实例化BeanDefinition,并设定Bean的一些属性,为手动创建Bean做准备。
注册后的步骤:
1.将Registry对象转化为BeanFactory对象,为拿到Bean的实例化对象做准备
2.拿到实例化对象测试。
3.删除一下Bean的注册进行测试。
public static void main(String[] args) throws Exception{
// 初始化Registry,这么写,是因为经典Factory实现类是Registry的子类
BeanDefinitionRegistry beanDefinitionRegistry = new DefaultListableBeanFactory();
// 初始化Generic Bean,这类Bean在大部分情况下适用
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
// 为Bean指定类对象
genericBeanDefinition.setBeanClass(Class.forName("ldj.createBean.DataEntity"));
// 设定Bean的初始化方法
genericBeanDefinition.setInitMethodName("show");
// -------------注册Bean === 最重要的一步----------------------
beanDefinitionRegistry.registerBeanDefinition("data", genericBeanDefinition);
// 拿到BeanDefinition【注:这个与实体Bean有所差别】
BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition("data");
// 打印出来,看看其信息【为方便测试,开发者一般都会重写toString方法】
System.out.println(beanDefinition);
// 将Registry转化为Bean Factory
BeanFactory beanFactory = (BeanFactory) beanDefinitionRegistry;
// 查看初始化方法,和这个谁先打印
System.out.println("666");
// 尝试一下拿到Bean
DataEntity data = (DataEntity) beanFactory.getBean("data");
// 打印一下看看
System.out.println(data);
System.out.println(data.getName());
// 移除一下试试
beanDefinitionRegistry.removeBeanDefinition("data");
// 发现仍然可以使用data,但是尝试从Bean Factory里拿到名为data的bean,就不行了
System.out.println(data);
}
五、学习BeanDefinitionRegistry继承结构
我们发现,竟然可以用DefaultListableBeanFactory实现类,来赋值给BeanDefinitionRegistry接口。这是因为,Default其实实现类BeanDefinitionRegistry。
BeanDefinitionRegistry是层级很高的接口,其上有一接口AliasRegistry,该接口用处不多,仅是允许用户提供别名。
对于Registry接口,除开非beans包下的子类【有5个context包的子类,继承了Registry接口】,以及已经“废弃”的XmlBeanFactory,它只有2个实现类。
第一个,Simple Bean Definition Registry实现类。
这个类,提供了注册BeanDefinition,和得到BeanDefinition的功能,一般只在调试时使用。
第二个,Default Listable Bean Factory
这个类,同时实现了Bean Factory和Registry接口,有存储、注册、管理Bean的功能。这个类是Spring框架最重要的类之一。
六、结语【意料之外】
这篇文章非常特别,有点水,起笔前我已经会写洋洋洒洒的一堆知识点,真正开始,才发现没有那么多知识可以写。
难道Spring的重点,在于如何解析XML文件、注解或者配置类?然后了解Spring所需的底层资源的结构,并了解它们如何起作用?
我觉得不是,Spring注重单一职责原则,所以该框架的结构也非常清晰。对于这个框架,底层资源的访问,更应该是一种辅助措施。
我认为,Spring的真正结构,几乎都在beans包里,其它包都只是辅助。
也许下一篇源码分析的文章,会描述Spring工厂BeanFactory的继承结构,以及为什么会这么做,不过这篇文章确实比较水,不好意思。
我是蚊子码农,如有补充或者疑问,欢迎在评论区留言。个人的知识体系可能没有那么完善,希望各位多多指正,谢谢大家。