SpringMVC中的DispatcherServlet结构和初始化详解

 更新时间:2024年01月05日 08:34:36   作者:it_lihongmin  
这篇文章主要介绍了SpringMVC中的DispatcherServlet结构和初始化详解,SpringMVC中Spring容器的关系是通过监听方式启动的,那么Spring与Servlet的Web容器(如:Tomcat、jetty)的关系则是通过DispatcherServlet进行关联,需要的朋友可以参考下

前言

SpringMVC中Spring容器的关系是通过监听方式启动的。

那么Spring(或者说SpringMVC)与Servlet的Web容器(如:Tomcat、jetty)的关系则是通过DispatcherServlet进行关联。

在Web容器看,DispatcherServlet就是同一普通的Servlet,从Spring看是与Web的关联,会将所有的请求转发到该控制器

1、DispatcherServlet结构

从层级能看出其顶层实现了Servlet和ServletConfig接口,由于Web容器只认识Servlet,而Spring只认识Bean,所以DispatcherServlet既是请求转发和返回视图解析控制中心,更是web与Spring的适配器,从其父子结构层级就能看出,慢慢的从Servlet适配到Bean的过程。

1)、GenericServlet

GenericServlet实现了Servlet、ServletConfig接口,也实现了所有未实现的方法。主要是config相关,init和service方法。

2)、HttpServlet

HttpServlet主要定义了Http协议相关的请求方法,以及Last-ModifiedIf-Modified-Since相关参数的处理。

3)、HttpServletBean

HttpServletBean除了继承自HttpServlet,还实现了EnvironmentCapable和EnvironmentAware接口,处理Spring Environment相关,之前分析过。

4)、FrameworkServlet

FrameworkServlet实现了接口ApplicationContextAware,注入了ApplicationContext,以及contextConfigLocation配置。

5)、DispatcherServlet

DispatcherServlet作为整个MVC的控制器,那么请求该转发到哪个Controller,返回的model会该使用哪个视图返回(视图解析器进行解析)。

都需要在这里进行完成,所以初始化时需要使用到Spring MVC的九大件。

初始化时使用了策略模式初始化九大件

2、DispatcherServlet初始化(九大件的加载和初始化)

web容器启动时会加载配置的DispatcherServlet,则会调用其init方法,在GenericServlet中实现,如下:

@Override
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

设置了ServletConfig值,并且调用了自定义无参空方法init,在子类HttpServletBean中实现。

public final void init() throws ServletException {
    // 解析init-param参数,并封装成ServletConfigPropertyValues
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            throw ex;
        }
    }
 
    // Let subclasses do whatever initialization they like.
    initServletBean();
}

在初始化方法之前,允许init-param通过addRequiredProperty方法添加到requiredProperties属性中的配置,以Bean的形式进行加载。主要的方法初始化方法是调用本类的空方法initServletBean,在下层子类FrameworkServlet中实现

@Override
protected final void initServletBean() throws ServletException {
    // 省略日志打印和,try catch部分的代码
    this.webApplicationContext = initWebApplicationContext();
    // 留给子类实现,子类DispatcherServlet没有进行实现
    initFrameworkServlet();
}
protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
 
    if (this.webApplicationContext != null) {
        // A context instance was injected at construction time -> use it
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent -> set
                    // the root application context (if any; may be null) as the parent
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        // No context instance was injected at construction time -> see if one
        // has been registered in the servlet context. If one exists, it is assumed
        // that the parent context (if any) has already been set and that the
        // user has performed any initialization such as setting the context id
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        // 确保Spring容器创建并且唯一,所以无论ContextLoaderListener是否启动和refresh
        wac = createWebApplicationContext(rootContext);
    }
 
    if (!this.refreshEventReceived) {
        // Either the context is not a ConfigurableApplicationContext with refresh
        // support or the context injected at construction time had already been
        // refreshed -> trigger initial onRefresh manually here.
        synchronized (this.onRefreshMonitor) {
            onRefresh(wac);
        }
    }
 
    if (this.publishContext) {
        // 将Spring的WebApplicationContext容器,设置到Web容器中
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }
 
    return wac;
}

无论监听是否启动Spring容器执行refresh方法,这里都会进行检查启动。

并将启动的Spring容器,设置到web容器中,所以当启动完成后两个容器中就是你中有我,我中有你的状态。

Spring MVC的初始化会同步锁synchronized(onRefreshMonitor)保证下初始化,在子类层级DispatcherServlet中完成:

@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}

使用策略模式初始化九大件,为后续请求调用,视图解析等做准备。

protected void initStrategies(ApplicationContext context) {
    // 文件上传
    initMultipartResolver(context);
    // i18n相关解析器
    initLocaleResolver(context);
    // 主题解析器(一种主题就是一类网页风格)
    initThemeResolver(context);
    // 请求映射
    initHandlerMappings(context);
    // 适配器,后面请求转发的Controller会专门分析
    initHandlerAdapters(context);
    // 操作异常的解析处理(中途发送异常该调整到哪里)
    initHandlerExceptionResolvers(context);
    // 请求的Controller没有返回View时,该如何解析
    initRequestToViewNameTranslator(context);
    // 视图解析器(ModelAndView中的view字符串怎么进行解析)
    initViewResolvers(context);
    // 请求属性(参数)管理器,主要用于重定向等情况时获取属性,比如post重定向到get
    initFlashMapManager(context);
}

九大件的处理方式大致相同,只是定义的Bean名称和调用getBean的类型不同而已,比如上传文件,如下:

private void initMultipartResolver(ApplicationContext context) {
    // 省略日志打印和try catch的代码
    this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
}

主要看初始化的什么类型,是在静态代码块的策略模式中加载初始化(DispatcherServlet.properties),如下:

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
 
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
 
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping
 
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter
 
 
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
 
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
 
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
 
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

到此这篇关于SpringMVC中的DispatcherServlet结构和初始化详解的文章就介绍到这了,更多相关DispatcherServlet结构和初始化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java使用Optional实现优雅避免空指针异常

    Java使用Optional实现优雅避免空指针异常

    空指针异常(NullPointerException)可以说是Java程序员最容易遇到的问题了。为了解决这个问题,Java 8 版本中推出了 Optional 类,本文就来讲讲如何使用Optional实现优雅避免空指针异常吧
    2023-03-03
  • 一文搞懂Spring循环依赖的原理

    一文搞懂Spring循环依赖的原理

    这篇文章将用实例来为大家详细介绍@Autowired解决循环依赖的原理,文中的示例代码讲解详细,对我们学习Spring有一定帮助,感兴趣的可以学习一下
    2022-07-07
  • SpringBoot Web开发之系统任务启动与路径映射和框架整合

    SpringBoot Web开发之系统任务启动与路径映射和框架整合

    这篇文章主要介绍了SpringBoot Web开发中的系统任务启动与路径映射和Servlet、Filter、Listener框架整合,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • java信号量控制线程打印顺序的示例分享

    java信号量控制线程打印顺序的示例分享

    这篇文章主要介绍了java信号量控制线程打印顺序的示例,如ABCABC这样输出线程,大家参考使用吧
    2014-01-01
  • 一个注解搞定Spring Security基于Oauth2的SSO单点登录功能

    一个注解搞定Spring Security基于Oauth2的SSO单点登录功能

    本文主要介绍 同域 和 跨域 两种不同场景单点登录的实现原理,并使用 Spring Security 来实现一个最简单的跨域 SSO客户端。对Spring Security基于Oauth2的SSO单点登录功能感兴趣的朋友一起看看吧
    2021-09-09
  • Java8函数式接口的基础学习教程

    Java8函数式接口的基础学习教程

    这篇文章主要给大家介绍了关于Java8函数式接口基础学习的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Jpa Specification如何实现and和or同时使用查询

    Jpa Specification如何实现and和or同时使用查询

    这篇文章主要介绍了Jpa Specification如何实现and和or同时使用查询,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • SpringBoot详细讲解断言机制原理

    SpringBoot详细讲解断言机制原理

    断言Assertion是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是org.junit.jupiter.api.Assertions的静态方法。检查业务逻辑返回的数据是否合理。所有的测试运行结束以后,会有一个详细的测试报告
    2022-06-06
  • 解决Spring Security中AuthenticationEntryPoint不生效相关问题

    解决Spring Security中AuthenticationEntryPoint不生效相关问题

    这篇文章主要介绍了解决Spring Security中AuthenticationEntryPoint不生效相关问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • SpringBoot项目启动执行任务的多种方法小结

    SpringBoot项目启动执行任务的多种方法小结

    这篇文章主要介绍了SpringBoot项目启动执行任务的多种方法小结,本文给大家分享的这几种方法经常会被用到,当我们的项目启动后需要调用对应的方法,用来项目的初始化等,本文通过示例代码讲解的非常详细,需要的朋友参考下吧
    2023-07-07

最新评论