欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-15 17:47:33
一,九大组件1.handlerMapping 用来查找handler的,也就是处理器,对应的就是标签@RequestMapping。也就是说handler,可以是类,也可以是方法.2.handlerAdapter 我们最原始的servlet处理方式可以知道,当一个请求到达的时候,是封装成request发送到servlet的doService(HttpServletRequest,HttpServletResponse)形式的,所以,要从传统的servlet模式转到spring m......

一,九大组件

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,看一下继承图:

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

拿到类的全路径,通过反射,实例化了本对象,回到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();
}

整体流程总结:

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


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

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

二,mvc初始化

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

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

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