springmvc加载过程及源码分析

什么是springmvc

springmvc是基于的spring的一个web层框架。

 这张图是spring的架构体系,从中可以看出springmvc和struts一样都是属于一个web层框架,是spring框架的一部分。

springmvc和mvc有什么区别

mvc是一种设计模式,而springmvc是一个表现层框架。springmvc可以说是对mvc设计模式的一种很好的实现。

springmvc加载过程

用户发起请求到前端控制器(DispatcherServlet)
前端控制器将请求转发给处理映射器(HandlerMapping)
处理器映射器(HandlerMapping)根据请求的url查找到对应的handler,并返回一个(HandlerExecutionChain,HandlerExecutionChain包括找到的hander和interceptor(有))给前端控制器(DispatcherServlet)
前端控制器调用处理器适配器(HandlerAdapter)执行handler
执行handler完毕后返回一个ModelAndView给前端控制前(DispatcherServlet)
前端控制器调用视图解析器,请求视图解析,视图解析完后返回view对象给前端控制器
前端控制器进行视图渲染,视图渲染就是将模型数据(在ModelAndView对象中)填充到request域中。
前端控制器向用户响应结果。
在分析源码之前先看下DispatcherServlet的继承体系


通过这个继承体系可以知道DispatcherServlet本质就是一个servlet,关于servlet的知识可以参考《servlet简介》,servlet入口是init方法,我们看下HttpServletBean中这个init方法源码,从源码中可以看出这个init方法用了final修饰说明这个方法是不能被子类继承的,由此可以HttpServletBean中的init方法就是sevlet的入口。

 public final void init() throws ServletException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
        }
 
        PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
                //模板方法,做一些初始化的工作,可以在子类中实现
                this.initBeanWrapper(bw);
                //将初始化的值设置到dispatchsevlet中(contextConfigLocation)
                bw.setPropertyValues(pvs, true);
            } catch (BeansException var4) {
                if (this.logger.isErrorEnabled()) {
                    this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
                }
 
                throw var4;
            }
        }
 
        this.initServletBean();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
        }
 
    }


ServletConfigPropertyValues是HttpServletBean的内部静态内,在构造过程会通过传入的servletconfig对象将web.xml的配置的参数设置到ServletConfigPropertyValues的内部。


 通过BeanWrapper来构建DispatcherServlet。BeanWrapper是spring提供的一个来操作javabean的属性的工具,使用它可以直接修改javabean 的属性,用法如下

package com.linewell.springmvc.pojo;
 
public class Book {
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}
 
package com.linewell.springmvc.pojo;
 
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.PropertyValue;
 
public class BeanWrapperTest {
    public static void main(String[] args) {
        Book book = new Book();
        BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(book);
        beanWrapper.setPropertyValue("name","MySql从删库到跑路");
        System.out.println(book.getName());
        PropertyValue value = new PropertyValue("name", "java从入门到放弃");
        beanWrapper.setPropertyValue(value);
        System.out.println(book.getName());
    }
}
打印结果:

再往下看这里有一个执行了一个initServletBean方法,

initServletBean方法里面什么都没有

这说明子类中应该是对它进行了实现,我们看下它的子类FrameworkServlet,果然在代码里面找到了initServletBean方法,源码如下:

 protected final void initServletBean() throws ServletException {
        this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
        }
 
        long startTime = System.currentTimeMillis();
 
        try {
            this.webApplicationContext = this.initWebApplicationContext();
            this.initFrameworkServlet();
        } catch (RuntimeException | ServletException var5) {
            this.logger.error("Context initialization failed", var5);
            throw var5;
        }
 
        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");
        }
 
    }
其中最重要的两行代码

this.initWebApplicationContext()表示初始化WebApplicationContext属性,WebApplicationContext是继承ApplicationContext接口的接口,该属性也是ApplicationContext容器的上下文。

 this.initFrameworkServlet()未做任何处理,主要是由子类继承来做一些操作。

进入initWebApplicationContext方法,源码如下:

protected WebApplicationContext initWebApplicationContext() {
        //创建父容器,并将父容器和子容器做一个关联
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.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);
                    }
 
                    this.configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
 
        if (wac == null) {
 
        //以ContextAttribute为key在ApplicationContext获取WebApplicationContext,一般不会设置,所以这里一般为空
            wac = this.findWebApplicationContext();
        }
 
        if (wac == null) {
        //创建WebApplicationContext对象,并把根上下文设置为WebApplicationContext的父上下文
            wac = this.createWebApplicationContext(rootContext);
        }
 
        if (!this.refreshEventReceived) {
            //WebApplicationContext创建成功后,就会进行onRefresh操作
            this.onRefresh(wac);
        }
 
        if (this.publishContext) {
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
            }
        }
 
        return wac;
    }
接下来我们看下这个onRefresh方法,这个方法在其子类(DispatchServlet)中实现源码如下

protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }
方法中只有一个方法,进入到这个initStrategies中,源码如下

protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
这个时候springmvc就开始去加载对应的一些模块中主要的组件,比如initMultipartResolver用来springmvc处理文件的上传,initLocaleResolver(context)用来处理国际话语言相关的一些操作(目前为止没有接触到。。。。)initThemeResolver().这个是用来处理一些有关动态更换样式的支持(主题)。好像也没有使用过。。。initHandlerMappings()这个很重要处理我们经常听到的有关url和controller的映射关系,initHandlerAdapters()处理映射有关的适配相关。initHandlerExceptionResolvers(context);springmvc有关异常的处理。initRequestToViewNameTranslator(context)处理请求到视图名称的一个转换。initViewResolvers()处理视图。初始化操作到这里基本已经结束。

DispatcherServlet处理请求过程

前面提到DispatcherServlet本质就是一个servlet,那么处理的请求的方法就是service方法,而DispatcherServlet中并没有这个方法,我们看下其父类(FrameworkServlet),在父类找到了这个service方法

我们进入这个processRequest方法,在里面可以看到这么一句

这个方法在FrameworkServlet是一个抽象的方法

protected abstract void doService(HttpServletRequest httpservletrequest,
            HttpServletResponse httpservletresponse) throws Exception;
这说明其子类中肯定对它进行了重写,我们看下它的子类也就是DispatcherServlet中的这个方法,在这个doService方法找到了这么一个方法

源码如下:

 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 
        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;
 
                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
 
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        }
 
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
 
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
 
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
 
                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
 
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }
 
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }
 
        }
    }
这个方法才是真正处理我们请求的方法。下面通过一个例子结合源码来分析springmvc的执行流程

使用配置的方式来加载controller

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 
 
  <servlet>
    <servlet-name>dispatch</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatch</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>
        
spring-mvc.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="/demo1" class="com.linewell.springmvc.Demo1.BeanController"></bean>
 
</beans>
controller如下

package com.linewell.springmvc.Demo1;
 
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class BeanController implements Controller {
 
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        System.out.println("BeanController run。。。。。。。");
        return null;
    }
}
断点位置

这一步根据请求的url获取handler,进入方法里面

 @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            Iterator var2 = this.handlerMappings.iterator();
 
            while(var2.hasNext()) {
                HandlerMapping hm = (HandlerMapping)var2.next();
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
                }
 
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
 
        return null;
    }
 

这里有两个HandlerMapping,它会根据你配置的方法来决定使用哪一个 HandlerMapping,通过HandlerMapping来查找Handler并返回一个HandlerExcutionChain(执行链,包括handler和interceptor)对象, 我们看下这个HandlerExcutionChain对象

由此可以看出返回的HandlerExcutionChain中包含一个处理器和拦截器,返回HandlerExcutionChain给前端控制器之后,前端控制器会调用
处理器适配器,如下图

进入getHandlerAdpter方法,源码如下

这里有三个拦截器,通过进一步的分析


 由此看出SimpleControllerHandlerAdapter这个适配器会匹配上,最终由这个适配器来执行handler并返回一个ModelAndView。

当使用注解的方式来加载controller时,处理器映射器使用的是RequestMappingHandlerMapping

处理器适配器使用的是RequestMappingHandlerAdapter


--------------------- 
作者:偏花逐流水 
来源:CSDN 
原文:https://blog.csdn.net/mp252119282/article/details/83056336 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/shixiansen6535/article/details/83145248
今日推荐