所有文章

https://www.cnblogs.com/lay2017/p/11775787.html

正文

springboot mvc自动配置的时候,获得了DispatcherServlet和DispatcherServletRegistrationBean。DispatcherServletRegistrationBean将DispatcherServlet给注册到了ServletContext当中。

注册到ServletContext中的Servlet将会触发其init方法,从而进行Servlet的初始化。本文将从Servlet的init方法开始,看看触发MVC各个组件初始化的代码

DispatcherServlet类图

我们先看看DispatcherServlet的类图

根据类图可以看到两部分的设计,第一部分是Servlet到HttpServlet,也就是Servlet容器相关的内部设计。第二部分是Spring在HttpServlet的基础上扩展了框架相关的内容,而最终DispatcherServlet将扩展springMVC的内容。

GenericServlet

我们跟进GenericServlet的init方法,看看它的实现

  1. @Override
  2. public void init(ServletConfig config) throws ServletException {
  3. this.config = config;
  4. this.init();
  5. }

继续跟进init方法

  1. public void init() throws ServletException {
  2. // NOOP by default
  3. }

没有实现逻辑,供子类去选择实现

HttpServletBean

httpServlet么有实现init方法,由HttpServletBean这个spring实现的类来扩展。跟进HttpServletBean的init方法

  1. @Override
  2. public final void init() throws ServletException {
  3.  
  4. // Set bean properties from init parameters.
  5. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
  6. if (!pvs.isEmpty()) {
  7. try {
  8. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
  9. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
  10. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
  11. initBeanWrapper(bw);
  12. bw.setPropertyValues(pvs, true);
  13. }
  14. catch (BeansException ex) {
  15. if (logger.isErrorEnabled()) {
  16. logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
  17. }
  18. throw ex;
  19. }
  20. }
  21.  
  22. // Let subclasses do whatever initialization they like.
  23. initServletBean();
  24. }

HttpServletBean也是扩展了一个initServletBean接口来供子类实现

FrameworkServlet

跟进FrameworkServlet的initServletBean的方法

  1. @Override
  2. protected final void initServletBean() throws ServletException {
  3. getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
  4. if (logger.isInfoEnabled()) {
  5. logger.info("Initializing Servlet '" + getServletName() + "'");
  6. }
  7. long startTime = System.currentTimeMillis();
  8.  
  9. try {
  10. this.webApplicationContext = initWebApplicationContext();
  11. initFrameworkServlet();
  12. }
  13. catch (ServletException | RuntimeException ex) {
  14. logger.error("Context initialization failed", ex);
  15. throw ex;
  16. }
  17.  
  18. // ...
  19. }

initFrameworkServlet是一个空实现,核心逻辑落到了initWebApplicationContext中,跟进它

  1. protected WebApplicationContext initWebApplicationContext() {
  2. WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  3. WebApplicationContext wac = null;
  4.  
  5. if (this.webApplicationContext != null) {
  6. //...
  7. }
  8. if (wac == null) {
  9. wac = findWebApplicationContext();
  10. }
  11. if (wac == null) {
  12. wac = createWebApplicationContext(rootContext);
  13. }
  14.  
  15. if (!this.refreshEventReceived) {
  16. synchronized (this.onRefreshMonitor) {
  17. onRefresh(wac);
  18. }
  19. }
  20.  
  21. if (this.publishContext) {
  22. String attrName = getServletContextAttributeName();
  23. getServletContext().setAttribute(attrName, wac);
  24. }
  25.  
  26. return wac;
  27. }

Springboot在启动过程中创建了ApplicationContext,这里将公用同一个ApplicationContext。

onRefresh方法提供了一个空实现,供子类去做初始化实现

  1. protected void onRefresh(ApplicationContext context) {
  2. // For subclasses: do nothing by default.
  3. }

DispatcherServlet

跟进DispatcherServlet的onRefresh方法

  1. @Override
  2. protected void onRefresh(ApplicationContext context) {
  3. initStrategies(context);
  4. }

继续跟进initStrategies

  1. protected void initStrategies(ApplicationContext context) {
  2. initMultipartResolver(context);
  3. initLocaleResolver(context);
  4. initThemeResolver(context);
  5. initHandlerMappings(context);
  6. initHandlerAdapters(context);
  7. initHandlerExceptionResolvers(context);
  8. initRequestToViewNameTranslator(context);
  9. initViewResolvers(context);
  10. initFlashMapManager(context);
  11. }

我们看到,initStrategies包含了各种MVC组件的初始化方法

组件初始化

我们打开initMultipartResolver看看

  1. private void initMultipartResolver(ApplicationContext context) {
  2. try {
  3. this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
  4. // ...
  5. catch (NoSuchBeanDefinitionException ex) {
  6. // Default is no multipart resolver.
  7. this.multipartResolver = null;
  8. // ...
  9. }
  10. }

其实就是从Bean工厂当中获取对应的Bean对象。multipartResolver默认可能为空

再打开initViewResolvers看看

  1. private void initViewResolvers(ApplicationContext context) {
  2. this.viewResolvers = null;
  3.  
  4. if (this.detectAllViewResolvers) {
  5. Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
  6. if (!matchingBeans.isEmpty()) {
  7. this.viewResolvers = new ArrayList<>(matchingBeans.values());
  8. AnnotationAwareOrderComparator.sort(this.viewResolvers);
  9. }
  10. } else {
  11. try {
  12. ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
  13. this.viewResolvers = Collections.singletonList(vr);
  14. }
  15. catch (NoSuchBeanDefinitionException ex) {
  16. // Ignore, we'll add a default ViewResolver later.
  17. }
  18. }
  19.  
  20. if (this.viewResolvers == null) {
  21. this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
  22. }
  23. }

一样的是从Bean工厂当中获取Bean,只不过这里是获取到一个集合,且如果没有将会获取默认的策略。

其它的MVC组件初始化类似,这里不每个打开看了。

总结

DispatcherServlet作为一个Servlet的实现,在Servlet被调用init方法以后最终将会调用DispatcherServlet的initStrategies方法,该方法将会初始化各个组件。

初始化组件基本就是把各个Bean对象从BeanFactory中拿出来组合到DispatcherServlet中,供后续使用。

springboot mvc自动配置(三)初始化mvc的组件的更多相关文章

  1. springboot mvc自动配置(目录)

    对于长时间基于spring框架做web开发的我们,springmvc几乎成为了开发普通web项目的标配.本系列文章基于快速启动的springboot,将从源码角度一点点了解springboot中mvc ...

  2. 全网最深分析SpringBoot MVC自动配置失效的原因

    前言 本来没有计划这一篇文章的,只是在看完SpringBoot核心原理后,突然想到之前开发中遇到的MVC自动失效的问题,虽然网上有很多文章以及官方文档都说明了原因,但还是想亲自看一看,本以为很简单的事 ...

  3. springboot mvc自动配置(一)自动配置DispatcherServlet和DispatcherServletRegistry

    所有文章 https://www.cnblogs.com/lay2017/p/11775787.html 正文 springboot的自动配置基于SPI机制,实现自动配置的核心要点就是添加一个自动配置 ...

  4. Springboot MVC 自动配置

    Springboot MVC 自动配置 官方文档阅读 https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#w ...

  5. spring-boot spring-MVC自动配置

    Spring MVC auto-configuration Spring Boot 自动配置好了SpringMVC 以下是SpringBoot对SpringMVC的默认配置:==(WebMvcAuto ...

  6. 关于SpringBoot的自动配置和启动过程

    一.简介 Spring Boot简化了Spring应用的开发,采用约定大于配置的思想,去繁从简,很方便就能构建一个独立的.产品级别的应用. 1.传统J2EE开发的缺点 开发笨重.配置繁多复杂.开发效率 ...

  7. 面试题: SpringBoot 的自动配置原理

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 3.Spring Boot 的自动配置原理 package com.mmall; import org. ...

  8. SpringBoot的自动配置

    1.根据条件来装配bean,SpringBoot的自动配置,根据条件进行自动配置. 首先创建一个接口,如下所示: package com.bie.encoding; /** * * @Descript ...

  9. springboot(六)自动配置原理和@Conditional

    官方参考的配置属性:https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#common-appl ...

随机推荐

  1. LoadRunne遇到的一些问题FAQ(持续更新...)

    1.LR11破解完成,添加License失败,报错License security violation Loadrunner11破解成功后,用deletelicense.exe工具把License全删 ...

  2. <JavaScript>调用apply报错:CreateListFromArrayLike called on non-object;

    Function.apply(obj, args)方法能接收两个参数 obj:这个对象将代替Function类里this对象 args:这个是数组,它将作为参数传给Function(args--> ...

  3. C# 可扩展编程MEF学习

    http://www.cnblogs.com/yunfeifei/p/3991330.html

  4. C#实现简单的 Ping 的功能,用于测试网络是否已经联通

    /// <summary> /// 是否能 Ping 通指定的主机 /// </summary> /// <param name="ip">ip ...

  5. spark简单文件配置

    cd /usr/local/spark/spark-2.2.1-bin-hadoop2.7/conf cp slaves.template slaves cp spark-env.sh.templat ...

  6. 关于jmeter+ant+jenkins性能自动化将测试结果文件jtl转换成html文件遇到的问题。

    1.ant自身缺陷,返回结果中有特殊字符,乱码字符,无法识别,jtl文件转换时报错. 2.jtl文件过大转换成html文件时出现内存溢出. 针对以上情况:可考虑使用BeenShell Sampler: ...

  7. 【Leetcode_easy】1103. Distribute Candies to People

    problem 1103. Distribute Candies to People solution:没看明白代码... class Solution { public: vector<int ...

  8. 01.轮播图之三 : collectionView 轮播

    个人觉得 collection view 做轮播是最方便的,设置下flowlayout 其他不会有很大的变动,没有什么逻辑的代码 let's begin…… 创建自定义的view .h 声明文件 @i ...

  9. golang 日志模块(log)

    log 日志 log 模块可以自定义log 对象, 也可以使用log默认对象的日志方法 func New 创建log对象 func New(out io.Writer, prefix string, ...

  10. MySQL的注入总结

    0x01 MySQL 5.0以上和MySQL 5.0以下版本的区别 MySQL5.0以上版本存在一个叫information_schema的数据库,它存储着数据库的所有信息,其中保存着关于MySQL服 ...