本文内容
1. 设计
在 Spring 中有一个 Event 事件功能,它可以通过 事件的定义、发布、监听,来完成一些自定义的动作。
例如在项目中定义一个用户注册事件,当用户完成注册时,通过事件监听给用户发送优惠卷和短信通知等。使用 事件 功能就能很好的 将基本功能(注册)和对应的策略服务(发送优惠卷、短信通知等)分离开来,降低系统的耦合。当后续再扩展注册服务时(比如添加实名认证、判断用户属性等),就不会影响到依赖于注册成功后执行的动作。
在 Spring 中有一个 Event 事件功能,它可以通过 事件的定义、发布、监听,来完成一些自定义的动作。
例如在项目中定义一个用户注册事件,当用户完成注册时,通过事件监听给用户发送优惠卷和短信通知等。使用 事件 功能就能很好的 将基本功能(注册)和对应的策略服务(发送优惠卷、短信通知等)分离开来,降低系统的耦合。当后续再扩展注册服务时(比如添加实名认证、判断用户属性等),就不会影响到依赖于注册成功后执行的动作。
目前我们的 Spring IOC 容器已经趋于完整,但还有一些可以扩展的地方。例如,我们业务中可能需要 不同作用域的 Bean 对象,但目前我们只支持单例 Bean,因此可以再添加对原型 Bean(prototype bean)的支持。
此外,交给 Spring 管理的 Bean 对象,不一定都是我们用类创建出来的。比如在 MyBatis 这样的 ORM 框架中,我们并没有手动去创建任何操作数据库的 Bean 对象。所以我们就需要 把复杂且以代理方式动态变化的对象,也注册到 Bean 容器中。
在上一章中,我们对 Spring 框架添加了初始化/销毁方法的扩展,使得框架的功能更强大,现在的框架所提供的功能有:Bean 对象的定义和注册、在操作 Bean 过程中执行的 BeanFactoryPostProcessor、BeanPostProcessor、InitializingBean、DisposableBean 处理,以及在 XML 中新增的配置处理。
现在,我们希望能够 获取 Spring 容器中的内部对象,比如获取其中的 BeanFactory、BeanName、BeanClassLoader、ApplicationContext 等,获取后就可以进一步对框架进行扩展使用。
当我们把某个类创建的 Bean 对象交给 Spring 容器管理后,该类对象就可以被赋予更多的使用能力。例如在上一章中,我们就给类对象添加了 对 BeanDefinition 未实例化前的属性信息的修改能力,以及 初始化过程中的前置处理和后置处理,让 Bean 对象实例化前后可以进行修改或替换。
这些额外能力的实现,都可以让我们对现有工程中的类对象做相应的 扩展处理。
在上一章的设计中,将类和属性都配置到 XML 文件中,完成了通过 XML 配置文件的方式进行类的实例化操作及依赖属性注入。
我们在测试时,是使用 DefaultListableBeanFactory
和 XmlBeanDefinitonReader
的实例化对象来进行初始化 BeanFactory,读取配置文件以及注册 Bean。但这其实是 提供给 Spring 框架的,不可能把这些暴露给用户。而且目前也 不能对 Class 类、Bean 对象进行扩展操作。
在上一章中,我们可以通过单元测试进行手动操作 Bean 对象的定义、注册和属性填充,以及最终获取对象调用方法。但是,实际上有个问题,在注册多个 Bean 时,如果依赖的属性很多,那么需要将这些属性一个一个地封装成 PropertyValue,再添加进PropertyValues,然后才进行属性填充,无疑增加了很多代码量。
所以这个章节,我们就用配置文件的方式,将所有的 Bean,以及 Bean 所依赖的属性都配置到 XML 文件。所以就需要一个 能解析 XML 配置文件的模块,将文件中的信息解析出来,然后由这个模块进行 PropertyValues 的处理,接着进行 Bean 的注册,此时就可以顺带把依赖的属性 PropertyValues 也传递进去。
在上一章的设计中,按照是否带有参数的构造函数实现了不同的类实例化策略。但是并 没有考虑类中是否有属性,如果类包含属性,那么 在实例化时就需要填充属性信息,这样创建的对象才是完整的。
举个例子,如果在 UserService 类中使用到了 userDao(private UserDao userDao
),那么 在实例化 UserService 时,也需要将 UserDao 实例化,得到 userDao 对象(依赖对象),将其注入进 UserService 类,填充为 UserService 类的属性。
在第02章的设计中,虽然扩充了 Bean 容器的功能,将类的实例化交给了容器来处理,但是并 没有考虑到带有有参构造器的类的实例化,因此这章我们来进行改造,使其具备获取有参 Bean 的能力。
具体的,在 BeanFactory 中添加 Object getBean(String name, Object... args)
接口,即可在获取 Bean 的时候把构造函数的入参信息传递进去,从而能实例化有参数的 Bean 对象。
上面创建的 Bean 容器比较简单,接下来我们需要提供一个 单例 Bean 缓存(因为缓存中保存一个实例 Bean 即可,所以采用单例),在获取 Bean 的时候,如果缓冲中存在就直接返回,否则才需要创建 Bean。
另外,在上面的设计中,BeanDefinition 中的 bean 变量是 Object 类型,那么在初始化阶段就要实例化这个 Bean。所以我们考虑 将 bean 变量定义成 Class 类型,这样 在初始化阶段就可以只传一个类信息,转而把 Bean 的实例化也交给容器来做。
实现一个最简单的 Bean 容器,由于我们需要通过 Bean 的名字来获取该 Bean 对象,所以使用 Map 进行映射最好不过了。
我们把 Bean 对象单独放在一个 BeanDefinition 类中,再定义一个 Bean 工厂 BeanFacotry 来存取 Bean。