spring mvc启动配置和九大组件使用介绍

2022-07-31,,,,

一,九大组件

1.handlerMapping

用来查找handler的,也就是处理器,对应的就是标签@RequestMapping。也就是说handler,可以是类,也可以是方法.

2.handlerAdapter

我们最原始的servlet处理方式可以知道,当一个请求到达的时候,是封装成request发送到servlet的doService(HttpServletRequest,HttpServletResponse)形式的,所以,要从传统的servlet模式转到spring mvc,这里就需要一个适配器,将对应的url,映射到对应的handler上面

3.handlerExceptionResolver

从名字上看,这个是处理handler异常的组件,此组件的作用是根据异常设置ModelAndView,之后交给渲染方法进行渲染,渲染方法将ModelAndView渲染成页面,返回给客户端。

4.ViewResolver

视图解析器,功能是将string类型的视图名解析为View类型的视图,只有一个resolveViewName()方法。View用来渲染页面,也就是将程序返回的参数和数据填入模板中。

5.RequestToViewNameTranslator

作用是从请求中获取ViewName,因为ViewResolver根据ViewName查找View,如果Handler处理完之后,没有设置View,也没有ViewName,这个时候就需要这个组件中查找ViewName。

6.LocaleResolver

ViewResolver组件的resolveViewName()方法需要两个参数,一个是ViewName,一个就是Locale,Locale的获得就是该组件需要提供的,它从请求中解析出Locale,表示一个区域。中国locale就是zh-CN。

7.ThemeResolver

解析主题,主题就是样式,图片以及他们所形成的显示效果的集合,spring mvc主题对应一个properties文件,里面存放着当前主题相关的所有资源。

8.MultipartResolver

用于处理上传请求,通过将普通的请求包装成MultipartHttpServletRequest来实现,它可以通过getFIle()直接获取文件。简单来说就是封装请求,使其有文件上传的功能

9.FlashMapManager

从名字可以知道,FlashMap的管理者,FlashMap用于重定向时的参数传递,

二,初始化阶段spring

在ssm配置启动web.xml中可以看到如下信息:

springmvc在web.xml中的配置:

 <servlet>
      <servlet-name>springmvc</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:spring/springmvc-context.xml</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
       <servlet-name>springmvc</servlet-name>
       <url-pattern>/</url-pattern>
   </servlet-mapping>

spring在web.xml中的启动配置:

 <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:spring/spring-*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

所以,我们可以知道,这里的启动流程中,其实是有两个线路的,一个是IOC容器的建立,另外一个是web容器的建立,首先我们分析IOC容器是如何初始化的:

ContextLoaderListener类,内容简单,两个构造方法,一个上下文初始化,一个上下文摧毁方法:

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
   public ContextLoaderListener() {
   }
   public ContextLoaderListener(WebApplicationContext context) {
      super(context);
   }
   //Initialize the root web application context.
   public void contextInitialized(ServletContextEvent event) {
      initWebApplicationContext(event.getServletContext());
   }
   // Close the root web application context.
   public void contextDestroyed(ServletContextEvent event) {
      closeWebApplicationContext(event.getServletContext());
      ContextCleanupListener.cleanupAttributes(event.getServletContext());
   }
}

它的继承关系比较简单,实现了ServletContextListener接口,继承了ContextLoader类,由于它实现了该接口,所以当容器启动的时候,会调用ContextLoaderListener类的contextInitialized()方法,进行一些初始化动作。该方法的调用者是StandardContext,它是tomcat体系中很重要的一个类,可以研究一下tomcat。

ContextLoaderListener主要代码功能在父类ContextLoader中,如下面的initWebApplicationContext()方法:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
   try {
      if (this.context == null) {
         //创建根上下文 【入】
         this.context = createWebApplicationContext(servletContext);
      }
      //如果web上下文 为可配置的web上下文
      if (this.context instanceof ConfigurableWebApplicationContext) {
         //转型为需要产生的IOC容器
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
         if (!cwac.isActive()) {
            if (cwac.getParent() == null) {
               //载入 根上下文的双亲上下文
               ApplicationContext parent = loadParentContext(servletContext);
               cwac.setParent(parent);
            }
            configureAndRefreshWebApplicationContext(cwac, servletContext);
         }
      }
      //设置标志位,把spring的web上下文,放到servletContext中,
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
      //获取当前线程的上下文 加载器
      ClassLoader ccl = Thread.currentThread().getContextClassLoader();
      if (ccl == ContextLoader.class.getClassLoader()) { //如果上下文加载器和当前类加载器是同一个,
         currentContext = this.context;
      }//如果不是同一个,则保存起来
      else if (ccl != null) {
         currentContextPerThread.put(ccl, this.context);
      }
      return this.context;
   }
}

创建容器,保存到当前类的字段:private WebApplicationContext context;  ,重点是如何创建的该容器,跟踪createWebApplicationContext()方法,依然位于ContextLoader类中:

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
   //判断使用什么样的类作为web容器中的ioc容器,也就是查找webApplicationContext的实现类
   Class<?> contextClass = determineContextClass(sc);
   //根据报错信息,这个类,一定是实现了ConfigurableWeb...接口的!
   if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {}
    //通过BeanUtils工具类进行实例化,和getBean()底层实例化手法相同
   return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

方法中重点内容是第一个方法determineContextClass()方法:

protected Class<?> determineContextClass(ServletContext servletContext) {
   //从servletContext中(web.xml)获取 信息     :contextClass
   String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
   if (contextClassName != null) { //如果web.xml中配置了信息,则直接使用,反射创建
         return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
   }
   else { //为空,使用默认的实现类:XmlWebApplicationContext
      contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
         return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
    }
}

我们的web.xml中没有定义,所以走默认配置,默认配置是从本类的一个字段中获取,该字段存放的信息,是通过该类的static{}今天代码块初始化的:

static {
      ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
      defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

该文件位于spring-web模块中的web.context文件夹下面:ContextLoader.properties文件中,存放的唯一一条信息:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

所以默认实现的容器是XmlWebApplicationContext,看一下继承图:

拿到类的全路径,通过反射,实例化了本对象,回到initWebApplicationContext()方法中,获得实例化对象后,保存到了ContextLoader的context字段中,由于该对象是才初始化的,所以active=false,parent=null,所以会进入loadParentContext()方法,但是现在在spring 5.0中这个方法直接返回null.我们就跳过,

来到最后一个方法configureAndRefreshWebApplicationContext()方法:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
....
   //把sc 设置到spring的web上下文中
   wac.setServletContext(sc);
   //获取web.xml中配置的 spring.web容器的配置文件路径
   String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
   if (configLocationParam != null) {
      wac.setConfigLocation(configLocationParam);
   }
   ConfigurableEnvironment env = wac.getEnvironment();
   if (env instanceof ConfigurableWebEnvironment) {
      ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
   }
   customizeContext(sc, wac);
   //调用上下文的刷新方法,开始bean的加载自动注入,初始化
   wac.refresh();
}

整体流程总结:

参考链接:https://blog.csdn.net/zknxx/article/details/73060866(很详细)

https://www.imooc.com/article/19606(慕课)

二,mvc初始化

分析springmvc的启动,与类DispatcherServlet有关,看它的继承图:

httpServlet是jdk自带的,,第一层父类HttpServletBean类重写了init()方法,它既然是一个servlet,那么就要遵守servlet的生命周期,会有实例化,初始化,接收请求,处理请求,销毁等流程,这里从init()方法开始:

public final void init() throws ServletException {
   // Set bean properties from init parameters.  该类在读取web.xml中配置的信息,封装到了pvs中,和依赖注入手法相同
   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
   if (!pvs.isEmpty()) {
      try {
         //封装自己称为一个Bean,进行依赖注入,将web.xml中配置的字段信息,注入到该类中
         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
         // 简单的 上下文封装,该类结构简单
         ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
         bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
         initBeanWrapper(bw); //【空方法】
         //为DispatcherServlet中的属性 赋值  比如web.xml中的配置属性,在这里赋值
         bw.setPropertyValues(pvs, true);  //依赖注入,一样的手法
      }
   }
   initServletBean(); //【入】
}

第一点是pvs,属性值的获取,通过内部类的构造方法,进行读取,(配置在web.xml中的属性值)

public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) {
   Enumeration<String> paramNames = config.getInitParameterNames();
   while (paramNames.hasMoreElements()) {
      String property = paramNames.nextElement();
      Object value = config.getInitParameter(property);
      addPropertyValue(new PropertyValue(property, value)); //【添加】
   }
}

准备好属性值之后,将DispatcherServlet封装成一个bean,然后使用spring DI的代码,进行依赖注入,将属性值注入到该bean中。

之后重点是iniServletBean()方法,该类是委派模式,在第二层FrameworkServlet类中:

protected final void initServletBean() throws ServletException {
    this.webApplicationContext = initWebApplicationContext(); //初始化Ioc容器,最终调用refresh()方法
    initFrameworkServlet(); //空方法
}

初始化web容器:

protected WebApplicationContext initWebApplicationContext() {
   //通过工具类,获取ContextLoaderListener中创建的webApplicationContext容器
   WebApplicationContext rootContext =
         WebApplicationContextUtils.getWebApplicationContext(getServletContext());
   WebApplicationContext wac = null;
   //如果容器 已经存在,则直接使用已经存在的
   if (this.webApplicationContext != null) {
      wac = this.webApplicationContext;
      if (wac instanceof ConfigurableWebApplicationContext) {
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
         if (!cwac.isActive()) { //激活
            if (cwac.getParent() == null) {
               cwac.setParent(rootContext); //将listener中创建的容器,设置为父容器
            }
            configureAndRefreshWebApplicationContext(cwac);    //内部就是调用refresh()方法
         }
      }
   }
   if (wac == null) {
      wac = findWebApplicationContext(); //则从servletContext中查找配置的webApplicationContext
   }
   if (wac == null) {
      wac = createWebApplicationContext(rootContext); //没有就直接创建 默认的同样是xmlWeb..
   }
   //=======  以上都是在创建 wac ===================
   if (!this.refreshEventReceived) { //如果还没有调用refresh()方法,则调用onRefresh()方法
      synchronized (this.onRefreshMonitor) {
         onRefresh(wac);
      }
   }
   if (this.publishContext) {
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
    
   }
   return wac;
}

这里的容器创建,和listener的模式一样,同样是先去查找有没有指定的实现类,如果没有,就直接create一个默认的容器实现类:

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
   Class<?> contextClass = getContextClass();  //默认的XmlWebApplicationContext 类
   ConfigurableWebApplicationContext wac =
         (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    //属性设置
   wac.setEnvironment(getEnvironment());
   wac.setParent(parent);
   String configLocation = getContextConfigLocation();
   if (configLocation != null) {
      wac.setConfigLocation(configLocation);
   }
    //设置,然后调用refresh()方法
   configureAndRefreshWebApplicationContext(wac);
   return wac;
}

最后调用onRefresh()方法,在子类DispatcherServlet类中,开启了初始化

protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
   //1.多文件上传的组件
   initMultipartResolver(context);
   //2.初始化本地语言环境
   initLocaleResolver(context);
   //3.初始化 模板处理器
   initThemeResolver(context);
   //4.初始化handlerMapping  url和controller的关系建立
   initHandlerMappings(context);
   //5.初始化 参数适配器
   initHandlerAdapters(context);
   //6.初始化 异常拦截器
   initHandlerExceptionResolvers(context);
   //7.初始化 视图预处理器
   initRequestToViewNameTranslator(context);
   //8.初始化 视图转换器
   initViewResolvers(context);
   //9.初始化 flashMap管理器
   initFlashMapManager(context);
}

参考链接:https://blog.csdn.net/zknxx/article/details/73073468

本文地址:https://blog.csdn.net/W1427259949/article/details/107891865

《spring mvc启动配置和九大组件使用介绍.doc》

下载本文的Word格式文档,以方便收藏与打印。