1.1 gradle搭建源码调试环境
1)搭建gradle环境
4个步骤
1、File-New-Module
抉择java和web
2、填写包信息
3、存储门路
2)减少起步依赖
依赖的我的项目,间接复制粘贴下来
1、对spring的依赖
2、对MVC的依赖
3、对Tomcat插件的依赖
build.gradle
group 'com.spring.test'version '5.0.2.RELEASE'apply plugin: 'java'apply plugin: 'war'apply plugin: 'com.bmuschko.tomcat' //tomcat: 插件// tomcat: 以下配置会在第一次启动时下载插件二进制文件//在我的项目根目录中执行gradle tomcatRunbuildscript { repositories { jcenter() } dependencies { classpath 'com.bmuschko:gradle-tomcat-plugin:2.5' }}// 配置阿里源allprojects { repositories { maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} }}dependencies { testCompile group: 'org.testng', name: 'testng', version: '6.14.3' runtime 'javax.servlet:jstl:1.1.2' // Servlet容器必须 compile(project(':spring-context')) compile(project(':spring-web')) compile(project(':spring-webmvc')) // tomcat: 将Tomcat运行时库增加到配置tomcat中: (此处为Tomcat9) def tomcatVersion = '9.0.1' tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}", "org.apache.tomcat.embed:tomcat-embed-logging-juli:9.0.0.M6", "org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}"}// tomcat: 一些协定设置(留神,这里必须加上,不然会抛tomcat的异样,仅限tomcat9)tomcat { httpProtocol = 'org.apache.coyote.http11.Http11Nio2Protocol' ajpProtocol = 'org.apache.coyote.ajp.AjpNio2Protocol'}// UTF-8tasks.withType(JavaCompile) { options.encoding = "UTF-8"}
3)MVC代码编写
前提:
减少WEB-INF目录和Web.xml
1、关上File - Proect Structrue
2、选中方才的mvc我的项目,开展,选中web gradle , 到左边 点击加号
3、确认门路
spring-mvc-test\src\main\webapp\WEB-INF\web.xml
WEB-INF和xml创立结束
webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- Spring MVC配置 --> <servlet> <servlet-name>mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:mvc-servlet.xml</param-value> <!--<param-value>/WEB-INF/mvc-servlet.xml</param-value>--> </init-param> <!-- load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()办法) --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app>
resources/mvc-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解扫描 --> <context:component-scan base-package="com.spring.mvc.test"/> <!-- 视图解析器对象 --> <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <!--<property name = "prefix" value="/WEB-INF/"></property>--> <property name="suffix" value=".jsp"/> </bean> <!-- 开启SpringMVC框架注解的反对 --> <mvc:annotation-driven/> <!--动态资源(js、image等)的拜访--> <mvc:default-servlet-handler/></beans>
webapp/index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html> <head> <title>SpringMvc源码深刻分析</title> </head> <body> Gradle构建Spring MVC例子.... </body></html>
MvcController.java
package com.spring.mvc.test;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.ModelAndView;@Controllerpublic class MvcController { @RequestMapping("/index") public ModelAndView getModeAndView() { //创立一个模型视图对象 ModelAndView mav = new ModelAndView("index"); return mav; } @RequestMapping("/text") @ResponseBody public String text() { return "Text..."; }}
4)启动MVC我的项目
两种启动形式
形式一:外挂启动
idea环境里面启动(我的项目根目录下运行 gradle + task name)
Task Name | Depends On | Type | Description |
---|---|---|---|
tomcatRun | - | TomcatRun | 启动Tomcat实例并将Web应用程序部署到该实例。 |
tomcatRunWar | - | TomcatRunWar | 启动Tomcat实例并将WAR部署 |
tomcatStop | - | TomcatStop | 进行Tomcat实例 |
tomcatJasper | - | TomcatJasper | 运行JSP编译器并应用Jasper将JSP页面转换为Java源代码。 |
在我的项目根目录中执行gradle tomcatRun
#动Tomcat实例并将Web应用程序部署到该实例gradle tomcatRun#进行Tomcat实例gradle tomcatStop
控制台失常输入
形式二:集成到idea中启动
设置
即可点击运行
运行胜利
形式三:
idea左边找到gradle的task,间接双击,这个爽~
拜访MVC我的项目
留神:spring-test-mvc是我的项目的名称
http://localhost:8080/spring-test-mvc/index
成果如下
5)源码调试配置
idea里的调试
简略,debug模式启动tomcat即可
近程调试模式
重要
想要近程debug,须要应用下面的办法二,因为debug启动须要设置gradle的环境变量,
即运行gradle命令的时候插入一些参数命令。
减少debug参数,对外裸露5005端口,即监听5005端口。
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
在配置Remote;监听5005端口
点击+号,创立Remote;默认配置即可
最初一步
1、先运行tomcat
2、再运行remote
http://localhost:8080/spring-test-mvc/index
打上断点试试!
包含咱们之前ioc里的bean创立等中央,轻易打。
1.2 MVC工作原理和继承关系
1)MVC底层工作原理
指标:意识SpringMVC的工作原理(对照源码),如何找到对应的Controller,进行页面渲染的
步骤:11步
源头:http://localhost:8080/spring-...
SpringMVC工作原理
1、DispatcherServlet(前端控制器) 是个servlet,负责接管Request 并将Request 转发给对应的解决组件。
2、 HanlerMapping (处理器映射器)是SpringMVC 中实现url 到Controller 映射的组件。DispatcherServlet 从HandlerMapping 查找解决Request 的Controller,
3、HanlerMapping 返回一个执行器链(url 到Controller 映射的组件)给DispatcherServlet
4、DispatcherServlet申请处理器适配器HandlerAdapter
5、处理器适配器HandlerAdapter去拜访咱们的handler(controller)
6、handler(controller)返回ModelAndView给处理器适配器HandlerAdapter
7、处理器适配器HandlerAdapter返回ModelAndView给DispatcherServlet
8、DispatcherServlet申请ViewResolver视图解析器
9、ViewResolver视图解析器返回view给DispatcherServlet
10、DispatcherServlet申请view做页面解析和渲染
11、view将渲染好的数据返回给DS,DS将渲染好的字符流给client,看到了页面!
2)MVC外围类继承关系
指标:简略意识MVC的继承关系
tips
不要求记住
DispatcherServlet 前端总控制器(webmvc源码)
FrameworkServlet (webmvc源码)
HttpServletBean 是的一个简略扩大类((webmvc源码)
HttpServlet(servlet API , 曾经来到了spring mvc的管制范畴)
1.3 Spring MVC源码深刻分析
引言:以后源码解说思路1、断点调试2、流程图对照3、继承关系对照
1.3.1 MVC启动阶段
留神,这个阶段没法debug,咱们从servlet标准去间接看源码
上面的申请阶段,再具体debug申请链路的残缺过程
web.xml回顾
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- Spring MVC配置 --> <servlet> <servlet-name>mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:mvc-servlet.xml</param-value> <!--<param-value>/WEB-INF/mvc-servlet.xml</param-value>--> </init-param> <!-- load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()办法) --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!--初始化Spring icC容器--> <!--<context-param>--> <!--<param-name>contextConfigLocation</param-name>--> <!--默认的门路是/WEB-INF/applicationontext.xml,上面多个xml应用,宰割--> <!--<param-value>classpath: applicationContext-ZH.xml</param-value>--> <!--</context-param>--> <!--要应用Spring的IoC容器--> <!--<listener>--> <!--<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>--> <!--</listener>--></web-app>
从下面的配置,咱们能够看出,web.xml中的DS是一个servlet,那就从java web的servlet标准说起
下面类关系,咱们说过,springmvc的领域里,最顶层的是 HttpServletBean 继承的 规范 HttpServlet
1、ioC Bean初始化
org.springframework.web.servlet.HttpServletBean#init
2、9大组件初始化(ioC)
org.springframework.web.servlet.HttpServletBean#init
启动:servlet标准,init办法被容器调用
在servlet实例化后,被容器调用一次init办法,所以启动咱们找到mvc里的父类从init看起
总结:办法调用关系(伪代码)
HttpServletBean{ init(){ protected initServletBean(); }}FrameworkServlet extends HttpServletBean{ @Override initServletBean(){ initWebApplicationContext(){ WebApplicationContext wac = createWebApplicationContext(rootContext); protected onRefresh(wac); } }}DispatcherServlet extends FrameworkServlet{ onRefresh(wac){ initStrategies(wac){ //多文件上传的组件 initMultipartResolver(context); //初始化本地语言环境 initLocaleResolver(context); //初始化模板处理器 initThemeResolver(context); //初始化处理器映射器 initHandlerMappings(context); //初始化处理器适配器 initHandlerAdapters(context); //初始化异样拦截器 initHandlerExceptionResolvers(context); //初始化视图预处理器 initRequestToViewNameTranslator(context); //初始化视图转换器 initViewResolvers(context); //FlashMap 管理器 initFlashMapManager(context); } }}
1.3.2 MVC申请阶段
需要:咱们在浏览器输出http://localhost:8080/spring-...,背地到底做了哪些事件
指标:MVC如何通过一个url就能找到咱们的controller,并返回数据
1、断点调试
2、流程图对照
3、继承关系对照
流程图解:
规范Servlet(回顾tomcat源码里,容器最初调的是wrapper的 service 办法)
伪代码
interface Servlet{ service() // 1 , 规范servlet标准的入口}HttpServlet implements Servlet{ public service(ServletRequest req, ServletResponse res){ //转成 HttpServletRequest protected service(req,res); // 2 } protected service(HttpServletRequest req, HttpServletResponse resp){ if(isGet){ protected doGet() // 4 } } protected void doGet(HttpServletRequest req, HttpServletResponse resp); // 5}//spring mvcFrameworkServlet extends HttpServlet{ @Override service(){ super.service(); // 3 } protected void doGet(HttpServletRequest req, HttpServletResponse resp){ processRequest(request, response){ protected doService(request, response); // 6 } }}DispatcherServlet extends FrameWorkServlet{ protected doService(request, response); // 7 , here!}
代码查找的门路:
tips:
spring mvc的 FrameworkServlet ,这是咱们源码跟踪的入口
我的项目启动
拜访
http://localhost:8080/spring-test-mvc/index
上图的初始化流程在源码中是怎么流转的呢?
入口:开启申请的大门
org.springframework.web.servlet.FrameworkServlet:
java web规范通知咱们,request的get会交给规范 HttpServlet的doGet办法
而这个类FrameworkServlet,是HttpServlet的子类,笼罩了上述的doGet,
所以,申请进入spring的第一入口,就在这里!!!
1)org.springframework.web.servlet.FrameworkServlet#doGet
调用到了org.springframework.web.servlet.FrameworkServlet#doGet
//get申请调用 @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
2)org.springframework.web.servlet.FrameworkServlet#processRequest
// 重点关注:doService protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { //重点查看,跳到DispatcherServlet 类中(子类重写) doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } } publishRequestHandledEvent(request, response, startTime, failureCause); } }
3)org.springframework.web.servlet.DispatcherServlet#doService
//重写父类 //重点关注 doDispatch(request, response); @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); } try { //重点关注 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
进入外围
4)org.springframework.web.servlet.DispatcherServlet#doDispatch
// Spring MVC的最外围代码 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { //创立视图对象 ModelAndView mv = null; Exception dispatchException = null; try { //申请查看,是否文件上传申请(二进制申请) processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request);// 依据以后的申请去拿一个Handler.这个Handler其实就是咱们的控制器,进入!!!!! mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 处理器适配器,9大组件初始化 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); //get办法为true boolean isGet = "GET".equals(method); //method为get if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { 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; } // 执行咱们的业务控制器办法,com.spring.mvc.test.MvcController.getModeAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } //视图解析器 applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } //视图渲染 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
5)org.springframework.web.servlet.DispatcherServlet#getHandler
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //不止一个,比方BeanNameHandlerMapping、SimpleUrlHandlerMapping,还有咱们须要的RequestHandlerMapping //在9个组件初始化的时候赋值 if (this.handlerMappings != null) { for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } //这个就是执行器链 HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } } return null; }
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { return ha; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
6) 调用业务Controller
// 执行咱们的业务控制器办法,com.spring.mvc.test.MvcController.getModeAndViewmv = ha.handle(processedRequest, response, mappedHandler.getHandler());
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInterna
@Override protected boolean supportsInternal(HandlerMethod handlerMethod) { return true; } @Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }
7)org.springframework.web.servlet.DispatcherServlet#processDispatchResult
//1、申请视图解析器,解析成view //2、执行页面渲染 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; //如果异样不为空 if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } //视图渲染,响应视图 if (mv != null && !mv.wasCleared()) { //执行渲染 render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }
如果本文对您有帮忙,欢送
关注
和点赞
`,您的反对是我保持创作的能源。转载请注明出处!