转载

跟我猜spring-boot:第一优化-bean的加载

本篇是《跟我猜Spring-Boot》系列第四篇,至于为什么有这个,前面已经啰嗦太多,不作缀述。本篇文章所有代码的改动都基于 https://github.com/yfge/mini-boot/archive/'article-03'.tar.gz 上一篇,我们已经实现了基本的HttpServer,但是大家也看到了,功能虽然实现了,但是代码很丑,主要因为:

  1. Application.loadBean 太复杂,太长了,重复的代码很多!

  2. 我已经有了ApplicationContext来管理Bean,为什么还要把bean包裹在UrlMap里一层一层传递?

  3. ServerRunnable那个类实现的实在丑了(丑出天际!!)有没有办法优雅一点?

那么,在继续给我们的mini-boot增加功能之前,我们先把这几个问题解决一下,免得以后的坑越来越多。

那么针对上面的1,2,3 咱们开始一样一样的梳理。

优化 Application.loadBean

我们现在的代码里, Application.loadBean 是这样的:

注意这里的2,3,4:我们所有的做法,其实都是:

查找特定注解并执行特定的操作

即,这里面注解和操作是有关联的,那么在 Spring 对外的特性中,这种关联是怎么实现的?通过查找一些官方的资料和技术blog,我们知道,如果需要扩展Spring的注解,可以通过实现  BeanPostProcessor 这个接口来实现。其定义如下:

看这个注解,与我们之前猜测的的 Annotation处理类 的方式并不一致,细想一下,其实是可以理解的,因为在每一步操作我们其实处理的是一组  Annotation 比如  RestController 和  RequestMapping 我们是放在一起处理的。

同时,注意这个是对bean进行处理的,那么这样,我们原有的逻辑变成了

  1. 查找所有的bean定义

  2. 查到所有的beanPostPorocessor的实现

  3. 对每个bean定义

    • 创建bean

    • 执行每个beanPostProcessor的postProcessBeforeInitialization

    • 执行postConstruct的方法

    • 执行每个beanPostProcessor的postProcessAfterInitialization

这个逻辑看似完整,但又来了新问题:

  1. beanPostPorocessor要在什么时间创建?同时又怎么进行获取?

  2. beanPostPorocessor是否也是一个bean?

  3. 如果它是bean那么怎么进行创建?

按照约定,我们目前只是去实现spring的对外特性,那么先把这几个操作按照 目前够用 的方式处理一下。于是,我们的顺序变成:

  1. 查到所有的bean定义--创建bean,同时如果他是一个beanPostProccessor 就保存到一个列表里

  2. 对bean遍历:

    • 进行依赖注入

    • 执行每个Processor的postProcessBeforeInitialization

    • 执行postConstruct的方法

    • 执行每个Processor的postProcessAfterInitialization

同时,我们注意到这两个方法都是返回了相应的object,也就是说,我们把对应的bean进行替换的。

于是,我们 loadBean 变成了这种:

同时,注意,因为将创建和注意集为一个步骤了,所以我们将这步实现提取为 createAndInitBean

这里,我们是用了递归的,即创建一个bean之后,先将其加入beanMap中,然后再对其中的field进行注入,使用递归的方法在于:

当两个bean A和B,如果A依赖B时,使用递归可以保证B比A先创建,从而注入成功

当然,再好的方式可以先遍历所有bean的定义信息,根据bean互相依赖的关系去生成图,然后使用用像图的遍历依次生成相应的bean.

处理 ApplicationContext

我在不定的写这个文章,不停的对spring官方文档去“猜”他的实现时,发现有这么一段话:

The org.springframework.beans and org.springframework.context packages are the basis for Spring Framework’s IoC container. The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory. It adds:

  • Easier integration with Spring’s AOP features

  • Message resource handling (for use in internationalization)

  • Event publication

  • Application-layer specific contexts such as the WebApplicationContext for use in web applications.

    In short, the BeanFactory provides the configuration framework and basic functionality, and the ApplicationContext adds more enterprise-specific functionality. The ApplicationContext is a complete superset of the BeanFactory and is used exclusively in this chapter in descriptions of Spring’s IoC container. For more information on using the BeanFactory instead of the ApplicationContext, see The BeanFactory.

即,bean的创建和维护是由ApplicationContext来做的。所以,我们进一步的操作是将loadBean变成了 ApplicationContext 的一个实例方法。

即我们现在的 Application.run 变成了:

至于 ApplicationContext 更改,基本都是代码的复制和粘贴,这里就不贴上来凑字数了。

有一个有意思的地方在于,注意到在Spring中,我们其实是会经常在各种地方注入 ApplicationContext 的,所以,在  ApplicationContext 的构造器中,我们把当前实例也加到了beanMap中。

这里有一个 不完美的地方 ,或是说 ,即,我们没有用我们通用的处理bean的方式来处理  ApplicationContext , 我们知道,一个好的编译器是应该具有自举能力的,同时,我认为一个完美的框架也应该有类似的的能力。即,我们应该可以用通用的处理bean的方式来处理  ApplicationContext ,显然,目前还没有做到。

虽然不知道spring内部的实现机理 ,但我觉得各位看官可以仔细琢磨一下,这是一个很有意思的话题。

处理 ServerRunnable

到现在为止,我们已经成功的将 beanPostProcessor 这个接口的处理机制实现了,那么,再优化原来的服务器实现机制,就变得好玩很多:

  1. 在已经有了  RestController 这一系列注解的基础上,我们定义一个  RestApiPostProcessor 来处理这些注解。

  2. 同时,我们可以将服务的启用和监听封装成一个bean来进行处理。

  3. 更酷的是,因为注意到httpServerStart是非阻塞的,那么我们可以极端的服务的启动加上  PostConstruct 注解。这样,整个加载就在 神不知鬼不觉 间完成了。

这样,我们先定义我们自己的Server封装类 BootServer :

可以看到,因为我们有了注入和初始化的机制,bootServer的实现变得非常简单粗暴,它什么也不做,就负责在PostConstruct时启动服务。

那么,对应注入的 ServerHandler 的实现就变成了如下的机制:

是的,我们只让他来维护地址的映射工作。

有了这两个类以后,我们的 RestApiPostProcessor 就变得顺理成章了。

是的,全是注入,因为它是一个Service,所以Handler也是注入的!

好了,我们现在终于要处理ServerRunnable这个丑丑的类了。我们觉得他丑,主要是因为我们在这里做了大量的重复性的工作,那么,从优化的角度,先将Http返回的信息封装成一个简单的类:

这样,我们的ServerRunnable的Run方法就变成如下:

同时,这个抽象出来的generateResponseData是如下形式:

OK ,这样,虽然这个类还是丑一些,但是,已经比之前好看太多了。

总结

作为本系列的第四篇,我们在本次的优化中。

  1. 改进了bean的加载机制

  2. 改进了serverRunnable的结构。

最重要的是,我们成功的抽象出来了 BeanPostProcessor 这个接口,通过这个接口,我们再进行扩展功能时就变成了:

  1. 定义Annotation

  2. 实现相应的BeanPostProcessor

而不用对加载的过程进行改动和处理,并且在这种机制下,我们的bean管理初步有了 生命周期 的概念。

虽然离真正的Spring还差非常远,但是我们已经有了一个 目前够用的机制了

其他

不给源码的分享都是耍流氓!

所以,我们的项目地址是:https://github.com/yfge/mini-boot 由于,随着文章的发布,本代码会不停的更新,所以,本章的tag是 article-04

参考

  1. Customizing Beans by Using a BeanPostProcessor

  2. sring-annotation-how-it-works

  3. JSR 250:Common Annotions for JavaPlatform

关于老拐瘦

  • 散养程序猿,野生架构狮

  • 二流搬砖工,三流摄影师

  • 假正经真逗比,装文艺实二逼

所以,这么一个公众号里,会有代码,有段子,有美图,有鸡汤,反正,乱七八遭的,没准碰上哪个刚好就烦到您了呢

啥也不说,扫码关注吧

跟我猜spring-boot:第一优化-bean的加载

原文  http://mp.weixin.qq.com/s?__biz=MzI3OTI5MzU3Mw==&mid=2247483867&idx=1&sn=12e59641f0fd96665a760e97d229334d
正文到此结束
Loading...