Spring Boot 2.0 goes GA

  经过 17 个月的酝酿,来自 215 个不同开发者,超过 6800 个 commits 之后,终于迎来了一个全新的 2.0 版本现在如期而至!
  可以通过 repo.spring.ioMaven Central 下载使用。
  而此次发布距离 1.0 版本已经过去了4个年头,这也是第一个开始全面支持 Spring Framework 5.0 。

  主要包括以下新特性:
1、支持 Java 9,最低支持 Java 8
2、支持 Reactive Web 编程
3、对 Reactive Spring Data 提供自动配置项,包括 Cassandra/MongoDB/Couchbase/Redis
4、内嵌 Netty 高性能网络框架
5、内嵌容器 Tomcat/Undertow/Jetty 开始支持 HTTP/2
6、提供 Kotlin 语言支持
7、提供 Jersey 和 WebFlux 支持
8、提供全新的 Micrometer 统计支持
9、提供 Quartz 定时任务支持
10、安全配置优化

  更多新特性了解,可以浏览这里:release notes,老版本迁移看这里:migration guide,还有熟悉的start bootstrap:start.spring.io

Spring MVC之工作流程

  一直以来都是用Spring MVC做业务开发,却很少去了解内部的一些细节,虽然网上与天盖地的源码解读,然而却无多大印象,加上版本变化之快,不妨重读一次。这里以spring-webmvc 4.3.4为例,也正是花图所使用的版本,下面详细来看看。

  DispatcherServlet继承自FrameworkServlet,而FrameworkServlet又继承自HttpServletBean并实现了ApplicationContextAware,通过这样的一个分层结构,可以更清晰的知道处理流程。

  第一部分:初始化

  初始化分两个部分:一个是监听器部分:负责Root WebApplicationContext的初始化,包含通过XmlBeanDefinitionReader获取配置文件,进行加载,通过RequestMappingHandlerMapping扫描出带有RequestMapping注解的Controller,通过RequestMappingHandlerAdapter注册Controller,等等。

  另一部分是FrameworkServlet的初始化:抽象类HttpServletBean继承自HttpServlet,实现了父类的init()方法,获取init-param中的配置,对DispatcherServlet进行数据绑定。然后调用FrameworkServlet.initServletBean(),初始化webApplicationContext,并调用DispatcherServlet.onRefresh(),来完成初始化。

initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);

  从Bean中取出相应的Resolver,若没配置则取默认,默认的设置在DispatcherServlet.properties。

  第二部分:接收请求

  对于Java Web而言,都是通过web.xml中的Servlet来分配请求,一般的都会将”/”根请求配置为DispatcherServlet,这样所有的前端请求都会路由到Spring MVC。

  抽象类FrameworkServlet对Servlet的几个基本Http method方法进行了代理,统一通过processRequest()进行处理,并最终到达DispatcherServlet中的doDispatch()。

  doDispatch()中先检查request是否是multipart request,如果是,则用一个multipartResolver来包装request,判断依据是contentType是否是”multipart/”开头。

  找到HandlerExecutionChain用来处理当前的请求,方法是从已注册的mappingRegistry中找到最匹配请求URL路径的handlerMethod,这个即我们最熟悉的Controller,放在HandlerExecutionChain中。若HandlerExecutionChain为空,则没有匹配到任何处理流程,发送404响应,并报非常熟悉的一个日志:”No mapping found for HTTP request with URI XXX”。

  找到HandlerAdapter用来处理请求,从已加载的handlerAdapters中遍历出一个支持该Handler的HandlerAdapter,本例中的Handler即Controller。

  检查Http method是否为GET或者HEAD,如果是,进行last-modified验证,符合条件的发送304响应,并更新Last-Modified相关参数值。

  检查拦截器配置,遍历执行applyPreHandle(),并触发triggerAfterCompletion(),若有返回false的,则本次请求结束。

  一路顺利到此可以调用Controller中的方法了。

  对于Controller返回时ModelAndView没设置view的,给一个默认view。

  接着倒着遍历执行过滤器applyPostHandle(),执行完一个完整的拦截器链。

  接下来是进行ModelAndView的渲染,将ModelMap转化到request.setAttribute()中,并RequestDispatcher.include() or forward()到JSP中。

  最终完成本次请求。