springmvc底层运行原理+源码解析

springmvc运行原理图:(重点)

在这里插入图片描述

一、创建springmvc-demo3

1、IDEA搭建WEB工程
2、web包下创建 WEB-INF 包,并在其之下创建 lib 包并导入 springmvc 开发 jar 包
在这里插入图片描述
3、WEB-INF 包下创建web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<!--配置DispatcherServlet->
	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
		<!--拦截所有>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
</web-app>

4、在src下创建springmvc.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"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

	
	<context:component-scan base-package="com.atguigu.springmvc">
	</context:component-scan>

	<!-- 配置视图解析器 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>	
</beans>

5、修改index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

	<a href="helloworld">Hello World</a>

</body>
</html>

6、创建HelloWorld的controller,注意创建在com.atguigu.springmvc包路径下,因为,之前的springmvc.xml文件中便表明了包的扫描路径
在这里插入图片描述
7、因为springmvc.xml中设置了视图解析器,所以应该在 WEB-INF 的包路径下创建一个views包来存放 jsp 页面,创建success页面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	
	<h4>Success Page</h4>
	
</body>
</html>

8、运行之后
在这里插入图片描述
在这里插入图片描述

二、源码分析

1、第一个流程,即如上代码运行流程(没有配置<mvc:default-servlet-handler/>

在这里插入图片描述

因为没有配置<mvc:default-servlet-handler/> 所以一旦请求地址没有映射,那么就会报如下问题
在这里插入图片描述

2、第二个流程,配置了<mvc:default-servlet-handler/>

在这里插入图片描述

(1)、编写abc.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	
	<h4>Httml Page</h4>
	
</body>
</html>

(2)springmvc.xml配置<mvc:default-servlet-handler/>

<mvc:default-servlet-handler/>

结果:
在这里插入图片描述

3、第三个流程,如果有对应映射呢?(重点)

在这里插入图片描述
代码不改,出现bug
在这里插入图片描述
解决:
添加了<mvc:default-servlet-handler/>后必须同时在其后边添加 <mvc:annotation-driven></mvc:annotation-driven>,不然RequestMapping不起作用。

Debug源码解析

1、DispatcherServlet

 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;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    //1
                    //获取处理器调用件HandlerExecutionChain,该类包含了处理器对象以及和处理器相关 的拦截器,因为调用处理器之前应该调用拦截器的方法,而这个类提供了这些
                    //那么这个方法是如何获取mappedHandler 的呢?请看下方的getHandle方法
                    //如果发的请求是没有经过映射的,那么mappedHandler 也不为空,因为之前我在
                    //springmvc.xml中配置了<mvc:default-servlet-handler/>和<mvc:annotation-driven></mvc:annotation-driven>
                    //如果没有配置这两个节点,那么就会返回空,如果为空,那么就会直接return,返回404,
                    //原因是什么??请看下一个方块“添加default-servlet-handler和annotation-driven之后的区别”
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

					//3
					//获取适配器
                    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()) {
                            String requestUri = urlPathHelper.getRequestUri(request);
                            this.logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
                        }

                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
                    //4
					//调用拦截器的PreHandle
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    try {
                    //5
                    //调用目标方法
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    } finally {
                        if (asyncManager.isConcurrentHandlingStarted()) {
                            return;
                        }

                    }

                    this.applyDefaultViewName(request, mv);
                    //6
                    //调用拦截器的PostHandle
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var28) {
                    dispatchException = var28;
                }
                //7
				//处理视图,具体方法看下方的processDispatchResult方法
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            } catch (Exception var29) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var29);
            } catch (Error var30) {
                this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var30);
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                return;
            } else {
                if (multipartRequestParsed) {
                    this.cleanupMultipart(processedRequest);
                }

            }
        }
    }

//2
//getHandler方法,其实是使用handlerMapping来获取HandlerExecutionChain 的,即handle的映射
//handlerMapping这个对象定义了请求到处理器之间的映射
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Iterator var2 = this.handlerMappings.iterator();

        HandlerExecutionChain handler;
        do {
            if (!var2.hasNext()) {
                return null;
            }

            HandlerMapping hm = (HandlerMapping)var2.next();
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
            }

            handler = hm.getHandler(request);
        } while(handler == null);

        return handler;
    }
    //8
//处理视图方法processDispatchResult
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {//9、异常处理
            if (exception instanceof ModelAndViewDefiningException) {
                this.logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException)exception).getModelAndView();
            } else {
                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                //如果发生异常就调用processHandlerException方法,具体方法在下方
                mv = this.processHandlerException(request, response, handler, exception);
                errorView = mv != null;
            }
        }

        if (mv != null && !mv.wasCleared()) {
        	//11、渲染视图,如何渲染?代码render在下方
            this.render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        } else if (this.logger.isDebugEnabled()) {
            this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
        }

        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
               //19、调用拦截器的AfterCompletion,这是最后一步
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }

        }
    }

//10
//处理异常
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ModelAndView exMv = null;
        //靠调用异常解析器handlerExceptionResolvers来处理
        Iterator var6 = this.handlerExceptionResolvers.iterator();

        while(var6.hasNext()) {
            HandlerExceptionResolver handlerExceptionResolver = (HandlerExceptionResolver)var6.next();
            exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
            if (exMv != null) {
                break;
            }
        }

        if (exMv != null) {
            if (exMv.isEmpty()) {
                return null;
            } else {
                if (!exMv.hasView()) {
                    exMv.setViewName(this.getDefaultViewName(request));
                }

                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
                }

                WebUtils.exposeErrorRequestAttributes(request, ex, this.getServletName());
                return exMv;
            }
        } else {
            throw ex;
        }
    }

//12
//渲染视图的方法
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Locale locale = this.localeResolver.resolveLocale(request);
        response.setLocale(locale);
        View view;
        if (mv.isReference()) {
        //13、
        //找到view对象,方法resolveViewName在下方
            view = this.resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");
            }
        } else {
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + this.getServletName() + "'");
            }
        }

        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'");
        }

        try {
        	//15,真正渲染视图的方法,应该去view.class中然后找到rendce的实现类AbstractView类,操作如下面方块“AbjectView类所在地”
            view.render(mv.getModelInternal(), request, response);
        } catch (Exception var7) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'", var7);
            }

            throw var7;
        }
    }
//14、resolveViewName方法,寻找合适的视图解析器
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
        Iterator var5 = this.viewResolvers.iterator();
		//遍历viewResolvers,寻找合适的视图解析器
        View view;
        do {
            if (!var5.hasNext()) {
                return null;
            }

            ViewResolver viewResolver = (ViewResolver)var5.next();
            view = viewResolver.resolveViewName(viewName, locale);
        } while(view == null);

        return view;
    }

//AbstractView类中render方法
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes);
        }

        Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);
        this.prepareResponse(request, response);
        //16、找到InternalResourceView实现类,地址在下方方块“renderMergedOutputModel实现类InternalResourceView所在地”中,代码如下
        this.renderMergedOutputModel(mergedModel, request, response);
    }
//17、InternalResourceView的实现类
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest requestToExpose = this.getRequestToExpose(request);
        this.exposeModelAsRequestAttributes(model, requestToExpose);
        this.exposeHelpers(requestToExpose);
        String dispatcherPath = this.prepareForRendering(requestToExpose, response);
        RequestDispatcher rd = this.getRequestDispatcher(requestToExpose, dispatcherPath);
        if (rd == null) {
            throw new ServletException("Could not get RequestDispatcher for [" + this.getUrl() + "]: Check that the corresponding file exists within your web application archive!");
        } else {
            if (this.useInclude(requestToExpose, response)) {
                response.setContentType(this.getContentType());
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Including resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");
                }

                rd.include(requestToExpose, response);
            } else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Forwarding to resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");
                }
				//18、转发
                rd.forward(requestToExpose, response);
            }

        }
    }

添加default-servlet-handler和annotation-driven之后的区别

没有添加<mvc:default-servlet-handler/> <mvc:annotation-driven></mvc:annotation-driven>之前
在运行到mappedHandler = this.getHandler(processedRequest);的时候,只能找到如下红框中的HandleMapping。在getHandler方法中就会返回null,最后就会返回404页面。
在这里插入图片描述
如果加上这两个节点又会怎么样?
那么返回值不为空,加上这两个节点后就会出现下图红框中的三个HandleMapping
在这里插入图片描述
其中RequestMappingHandleMapping是搞requestMapping的映射的
SimpleUrlHandleMapping是搞静态请求的,如上当进行到mappedHandler = this.getHandler(processedRequest);的时候,会遍历红框中的HandleMapping,上两个HanddleMapping都会返回空,因为原来的请求没有映射,但是到SimpleUrlHandleMapping不会,因为将其默认为了静态资源请求。
在这里插入图片描述
如果请求有映射,那么
在这里插入图片描述

AbstractView类所在地

在这里插入图片描述

renderMergedOutputModel实现类InternalResourceView所在地

在这里插入图片描述

断点实现一遍图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
即是解析视图内容返回视图

在这里插入图片描述
debug页面
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值