从Spring中学到的【1】(spring的认识)

  本篇文章为你整理了从Spring中学到的【1】(spring的认识)的详细内容,包含有spring学到什么程度 spring的认识 spring的dao 从spring到springboot 从Spring中学到的【1】,希望能帮助你了解 从Spring中学到的【1】。

   如果你通过某些培训机构的源码课,就会发现他们的老师在讲源码的时候在类之间、方法之间不停地跳,学员一脸懵逼。因为如果不理解老师讲课的思路,或者是稍微走一下神,就会觉得自己跟不上了。

  其实,问题就在于需要理解源码的基本流程和继承链的这种单一职责原则。

  
最近看了一些 Spring 源码,发现源码分析的文章很多,而底层思想分析的文章比较少,这个系列文章准备总结一下Spring中给我的启示,包括设计模式思想、SOLID设计原则等,涉及一些编程的基本原则,虽然看似简单,实则“小道理、大学问”。
 

  我尽量遇到的问题谈起,再说解决方案,同时至少举两个例子。
 

  这些方法都是基于我遇到的一些实际代码,掌握了基本思想,就可以举一反三。

  让人头晕眼花的跳转

  如果你通过某些培训机构的源码课,就会发现他们的老师在讲源码的时候在类之间、方法之间不停地跳,学员一脸懵逼。因为如果不理解老师讲课的思路,或者是稍微走一下神,就会觉得自己跟不上了。

  其实,问题就在于需要理解源码的基本流程和继承链的这种单一职责原则。

  读懂继承链

  面向对象的特点是封装继承和多态,封装体现在私有方法。

  在Spring中,常见的模式是顶层为接口,然后是抽象类,最后是各个实现类。

  接口负责对外暴露,符合面向接口编程的准则,在更改实现类时,可以减小对于代码的改动,保证了代码依赖于抽象(接口)。.

  抽象类一般使用模板模式,实现了逻辑的组装,把子类中的公用逻辑抽取出来,方便子类编写;模板方法一般为固定的执行链,我们读源码时,可以予以关注。比如常见的AbstractApplicationContext::refresh 方法,AbstractBeanFactory::doGetBean方法。由于接口一般只能暴露方法声明,抽象类可以实现一些状态的getter,settter,这样子类访问这些状态数据只需要调用方法即可。

  实现类通常有多个,比如todo。有些时候当实现类只有一个或者有一个默认实现类时,常常使用default命名,比如DefaultListableBeanFactory. 读源码时可以选择地阅读默认实现。

  Java 只支持单继承,这种语言层面的规范方便了我们阅读源码,一个方法的实现必然在一条继承链中。

  Spring中BeanFactory的默认实现是DefaultListableBeanFactory, 通过继承图可以看出,有多个抽象类,从上到下分别实现了以下的能力:

  别名注册(SimpleAliasRegistry)

  单例注册(DefaultSingletonBeanRegistry)

  FactoryBean 注册(FactoryBeanRegistrySupport)

  bean工厂接口(主要就是getBean相关的一些方法)(AbstractBeanFactory)

  自动装配(AbstractAutowireCapableBeanFactory)

  同时我们还可以看到,基本上每一个抽象类都对应一个实现的接口:

  AliasRegistry ← SimpleAliasRegistry

  SingletonBeanRegistry←DefaultSingletonBeanRegistry

  AbstractBeanFactory ← ConfigurableBeanFactory

  AbstractAutowireCapableBeanFactory←AutowireCapableBeanFactory

  注意到 DefaultListableBeanFactory 实现了ConfigurableListableBeanFactory,这个接口实现了一些简化配置 beanFactory 的方法,是一个常用的基础设施类(接口)。

  实际上,这个设计也是不得已而为之。Java只支持单继承,理想的情况下,DefaultListableBeanFactory 需要继承不同的trait,即单例注册、FactoryBean注册等功能模块,Configurable, Listable, **Capable恰好是这种设计思想的体现。如果最终的DefaultListableBeanFactory写成一个类,一定是巨大的,但是假如我们将BeanFactory不同的特性做拆分的话,就会得到如图所示的看似复杂的接口继承关系。

  这种松散的接口继承关系正是我们需要的,举例来说,autowireCapable 和 hierarchical并没有实际上的联系,一个关注属性注入,另一个则关注bean工厂的层级关系(可以有父工厂)。

  假设每一个松散的接口都有几个或多个实现,不管其是否是抽象类或者具体实现,我们只有通过多继承或者委托模式组装得到DefaultListableBeanFactory。

  这就是矛盾的地方,单一的继承链和松散的接口,其结果就是抽象类具有了一些不必要的功能。比如AbstractAutowireCapableBeanFactory 具有了Configurable、aliasRegistry等能力。这种情况是我们阅读源码是需要注意的。

  Spring通过将单继承链分解为6个类,将DefaultListableBeanFactory 进行了功能拆分,符合开闭原则,每个类也符合单一职责原则的要求。

  通过以上分析,打开DefaultListableBeanFactory 的源码,虽然有2000多行,我们可以清楚地看出类的结构,包括1. 继承链相关:不同抽象方法的实现、未在抽象类中实现的接口方法的实现。2. BeanDefinitionRegistry 3. ConfigurableListableBeanFactory 4. Serializable

  类似如上的分析,AnnotationConfigApplicationContext 的继承链如下:

  DefaultResourceLoader → AbstractApplicationContext → GenericApplicationContext → AnnotationConfigApplicationContext

  每个类实现的功能即其直接实现的接口,有些类通过类名也可以快速得知其实现的功能。不再赘述。

  GenericApplicationContext 实现了BeanDefinitionRegistry,直接委托BeanFactory的实现给DefaultListableBeanFactory,子类包括AnnotationConfigApplicationContext 和mvc容器等。

  在抽象类AbstractApplicationContext可以看到大家耳熟能详的refresh方法。

  ApplicationContext更是重量级,作为应用容器,拥有BeanFactory外的事件广播、国际化、资源读取等能力。

  由于ApplicatoinContext是大接口,是不同功能的最终整合,所以我们看到的接口继承关系并不复杂。

  我们知道MVC模型中具有中央调度器,在SpringMvc中体现为DispatcherServlet,其继承链如图。

  了解过servlet的人都知道HttpServlet具有doGet、doPost等方法,子类重新后所有方法都转发到DispatcherServlet中,doService→doDispatch执行了我们熟知的分发模板逻辑:简单来说就是

  getHandler → getHandlerAdapter → applyPreHandle → **handle** → applyPostHandle → processDispatchResult

  processDispatchResult中包含异常处理,render和afterCompletion

  我们随便选择一个方法,比如初始化mvc容器,其必在继承链上的某个类中进行实现,通过分析源码可以看出:

  initWebApplicationContext在FrameworkServlet中实现。

  GenericServlet暴露init方法。

  HttpServletBean实现了init方法,在init方法中暴露initServletBean方法。

  FrameworkServlet实现initServletBean方法,其中实现了initWebApplicationContext方法。

  虽然执行初始化mvc容器方法需要在继承链上来回跳转,但是其实现了单一职责原则,每一个类负责实现了特定的功能,模板类实现了模板流程,实现类实现具体实现。

  以上就是从Spring中学到的【1】(spring的认识)的详细内容,想要了解更多 从Spring中学到的【1】的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: