zoukankan      html  css  js  c++  java
  • springmvc核心转发器(中央控制器)DispatcherServlet分析

    springmvc采用最常见的前端控制器模式(Front Controller Pattern)

    是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。
    springmvc中所有请求都会到达DiapatcherServlet,具体由 doDispatch(HttpServletRequest request, HttpServletResponse response)方法进行分发,

    分发到具体的controller层。首先我们来看看DispatcherServlet继承关系.

                                                                                                                            dispacherservlet的继承关系

              HttpServlet继承了HttpServlet,并重写了init  方法:

     @Override

    public final void init() throws ServletException {
    if (logger.isDebugEnabled()) {
    logger.debug("Initializing servlet '" + getServletName() + "'");
    }

    // Set bean properties from init parameters.
    try {
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);

    <servlet>

    <servlet-name>dispatcherServlet</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <!-- 指定SpringMVC框架的配置文件的路径和名称 -->
    <param-value>classpath*:conf/mvc/mvc-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>

    //构造ServletConfigPropertyValues过程中会查找web.xml配置,读取nit-param中的配置,并设置到ServletConfigPropertyValues中
    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);//BeanWrapper 是spring内部实例化bean的方法,使用BeanWrapper实例化DispatcherServlet
    ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
    bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));//注册响应的属性编辑器(资源加载器),当类型是Resource的用ResourceEditor进行类型转换

    initBeanWrapper(bw);//初始化bean

    bw.setPropertyValues(pvs, true);//进行属性赋值

    }
    catch (BeansException ex) {
    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
    throw ex;
    }

    // Let subclasses do whatever initialization they like. //让子类做一写具体的处理
    initServletBean();

    if (logger.isDebugEnabled()) {
    logger.debug("Servlet '" + getServletName() + "' configured successfully");
    }
    }

    比如上面这段配置,传递了contextConfigLocation参数,之后构造BeanWrapper,这里使用BeanWrapper,有2个理由:1. contextConfigLocation属性在FrameworkServlet中定义,HttpServletBean中未定义 2. 利用Spring的注入特性,只需要调用setPropertyValues方法就可将contextConfigLocation属性设置到对应实例中,也就是以依赖注入的方式初始化属性。然后设置DispatcherServlet中的contextConfigLocation属性(FrameworkServlet中定义)为web.xml中读取的contextConfigLocation参数,该参数用于构造SpringMVC容器上下文。

    下面看下FrameworkServlet这个类,FrameworkServlet继承自HttpServletBean。

    首先来看下该类覆写的initServletBean方法:

    /**
    * Overridden method of {@link HttpServletBean}, invoked after any bean properties
    * have been set. Creates this servlet's WebApplicationContext.
    */
    @Override
    protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
    this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
    this.webApplicationContext = initWebApplicationContext();//将servlet属性与spring上下文绑定,webApplicationContext作为上下文环境。
    initFrameworkServlet();//不做任何处理,DispatcherServlet也不做任何处理
    }
    catch (ServletException ex) {
    this.logger.error("Context initialization failed", ex);
    throw ex;
    }
    catch (RuntimeException ex) {
    this.logger.error("Context initialization failed", ex);
    throw ex;
    }

    if (this.logger.isInfoEnabled()) {
    long elapsedTime = System.currentTimeMillis() - startTime;
    this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
    elapsedTime + " ms");
    }
    }

    来看看initWebApplicationContext具体代码实现:                           

     

    protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());//获取根上下文,一般放在ServletContext上下文中,

    键 WebApplicationContext.class.getName() + ".ROOT",值webApplicationContext
    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();//contextAttribute以这个属性名去servletContext中查找springmvc上下文。

    //org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcherServlet attrname作为键名保存在servletContext上下文中
    }
    if (wac == null) {
    // No context instance is defined for this servlet -> create a local one
    wac = createWebApplicationContext(rootContext);//创建WebApplicationContext上下文,并将相关配置加入其中
    }

    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.
    onRefresh(wac); //模板方法在DispatcherServlet中具体实现,加载一些初始化设置。
    }

    if (this.publishContext) {
    // Publish the context as a servlet context attribute.
    String attrName = getServletContextAttributeName();

    getServletContext().setAttribute(attrName, wac);//将先创建的上下文放到ServletContext中去。
    if (this.logger.isDebugEnabled()) {
    this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
    "' as ServletContext attribute with name [" + attrName + "]");
    }
    }

    return wac;
    }

    这里的根上下文是web.xml中配置的ContextLoaderListener监听器中根据contextConfigLocation路径生成的上下文

    <!--加载spring上下文对象 -->
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:conf/cpic/root-context.xml</param-value>
    </context-param>
    <!--default enviroment for setting -->
    <context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>development</param-value>
    </context-param>

    比如这段配置文件中根据classpath :conf/applicationContext.xml下的xml文件生成的根上下文。

    最后看下DispatcherServlet。

    DispatcherServlet覆写了FrameworkServlet中的onRefresh方法:

    /**
    * This implementation calls {@link #initStrategies}.
    */
    @Override
    protected void onRefresh(ApplicationContext context) {
    initStrategies(context);//初始化各种策略的接口
    }

    /**
    * Initialize the strategy objects that this servlet uses.
    * <p>May be overridden in subclasses in order to initialize further strategy objects.
    */
    protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
    }

    //很明显,initStrategies方法内部会初始化各个策略接口的实现类。

    总结一下各个Servlet的作用:

    1. HttpServletBean

      主要做一些初始化的工作,将web.xml中配置的参数设置到Servlet中。比如servlet标签的子标签init-param标签中配置的参数。

    2. FrameworkServlet

      将Servlet与Spring容器上下文关联。其实也就是初始化FrameworkServlet的属性webApplicationContext,这个属性代表SpringMVC上下文,它有个父类上下文,既web.xml中配置的ContextLoaderListener监听器初始化的容器上下文。

    3. DispatcherServlet 

      初始化各个功能的实现类。比如异常处理、视图处理、请求映射处理等。

  • 相关阅读:
    【转】Android开发中Handler的使用
    【转】关于微信开发者平台移动应用获取签名解决问题
    AndroidStudio开发工具快捷键
    进程与线程
    【转】Git常用命令
    Java中内存空间的分配及回收
    【转】Github入门教程
    周记
    本周工作内容及感想
    总结
  • 原文地址:https://www.cnblogs.com/caibixiang123/p/8580692.html
Copyright © 2011-2022 走看看