一:springmvc运行过程:

1. dispatcherServlet 通过 HandlerMapping 找到controller
2. controller经过后台逻辑处理得到结果集modelandview
3. 视图解析器解析model,渲染view展示页面。

二:springmvc容器是什么:

  很多人喜欢把spring和springmvc混为一谈, 其实它们是完全不同的两个概念。spring主要是创建对像管理依赖的; 而springmvc是一种mvc分层思想将功能模块化,处理用户请求的。而且spring和springmvc是有父子容器关系存在的。子容器可以获取父容器中的对象(可以理解成面向对象中的继承),例如controller通过@Autowired注入spring中的对象, 而如果把controller交给spring来管理,就会出现404异常,因为spring初始化只管理bean,并不会把url和method关联起来。

三:springmvc案例解析:

3.1 自定义springmvc注解@MyController和@MyRequestMapping

3.2 实现过程

public class MyDispatcherServlet extends HttpServlet {// springmvc 容器对象 key:类名id ,value bean对象private ConcurrentHashMap<String, Object> springmvcBeans = new ConcurrentHashMap<String, Object>();// springmvc 容器对象 keya:请求地址 ,vlue 类对象private ConcurrentHashMap<String, Object> urlBeans = new ConcurrentHashMap<String, Object>();// springmvc 容器对象 key:请求地址 ,value 方法名称private ConcurrentHashMap<String, String> handlermapping = new ConcurrentHashMap<String, String>();/*** 容器启动时*/@Overridepublic void init() throws ServletException {// 1. 扫描当前包(及子包)下面的所有类List<Class<?>> classes = ClassUtil.getClasses("org.wulei.controller");try {// 将这些类放到mvc容器this.findClassMVCAnnotation(classes);} catch (Exception e) {}// 将url和方法绑定起来this.headlerMapping();}/* 初始化有controller注解的bean,放入mvc容器。*/public void findClassMVCAnnotation(List<Class<?>> classes)throws ClassNotFoundException, InstantiationException, IllegalAccessException {for (Class<?> classInfo : classes) {// 2. 判断类上是否有加上controller注解MytController myController = classInfo.getDeclaredAnnotation(MytController.class);if (myController != null) {// 默认bean名称是类名小写String beanName = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());// 3. 实例化对象 , 放入mvc容器Object object = ClassUtil.newInstance(classInfo);springmvcBeans.put(beanName, object);}}}/* 通过handlerMapping将url和method关联起来  */public void headlerMapping() {// 4. 遍历mvc的bean容器所有的类for(Map.Entry<String, Object> mvcBean : springmvcBeans.entrySet()) {Object object = mvcBean.getValue();Class<? extends Object> classInfo = object.getClass();// 5. 判断类上面是否有url映射(即:@RequestMapping注解)MyRequestMapping requestMapping = classInfo.getDeclaredAnnotation(MyRequestMapping.class);// 6. 获取类上的url地址String baseUrl = "";if(requestMapping != null) {baseUrl = requestMapping.value();}// 7. 获取方法上的url映射路径Method[] methods = classInfo.getDeclaredMethods();if(methods.length>0) {for(Method method : methods) {MyRequestMapping methodRequestMapping = method.getDeclaredAnnotation(MyRequestMapping.class);if(methodRequestMapping != null) {// 8. 将 classUrl 和 methodUrl 拼接起来,得到完整url路径。String methodUrl = baseUrl+methodRequestMapping.value();// 9. 将url和method绑定起来,存入容器。
                        urlBeans.put(methodUrl, object);handlermapping.put(methodUrl, method.getName());/*** 到上面容器就就初始化完成,可以对外提供服务了。* 现在可以每个方法断点调试看运行过程。*/}}}}}/*** 处理用户请求*/@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1. 获取请求url地址String url = req.getRequestURI();if(StringUtils.isEmpty(url)) return;// 2. 通过url获取容器中的controller对象Object object = urlBeans.get(url);if(object == null) {resp.getWriter().println("ERROR: not found 404 url");return;}// 3. 使用url获取方法.String methodName = handlermapping.get(url);if(StringUtils.isEmpty(methodName)) resp.getWriter().println("ERROR: not found method");// 4. java反射获取被调用的方法String resultPage = null;try {Method method = object.getClass().getMethod(methodName);resultPage = (String) method.invoke(object);resp.getWriter().println(resultPage);} catch (Exception e) {e.printStackTrace();}// 5.调用视图转换器渲染给页面展示this.myResourceViewResolver(resultPage, req, resp);}/*** 视图解析器*/private void myResourceViewResolver(String pageName, HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException {// 根路径String prefix = "/";String suffix = ".jsp";req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}
}

3.3 测试 (正确的url会跳转到相应页面,错误的url会找不到路径)

四:源码解析

4.1 springmvc初始化阶段

4.1.1 我们在用springmvc的时候, 首先会配置 dispatcherServlet,它会拦截符合规则的url请求。

  <servlet><servlet-name>WEB</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 指定springmvc扫描文件 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:config/spring-*.xml</param-value></init-param><!-- 优先级 --><load-on-startup>1</load-on-startup></servlet><servlet-mapping><!-- 拦截规则 --><servlet-name>WEB</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping>

4.1.2 可以看出 dispatcherServlet 最终还是继承于httpservlet, 说明它也会满足servlet的工作原理。

  Servlet 生命周期:
    Servlet 加载—>实例化—>服务—>销毁。
    init():在Servlet的生命周期中,仅执行一次init()方法。负责初始化Servlet对象。
    service():负责响应客户的请求。每次请求都会调用相应的doGet/doPost功能。
    destroy():仅执行一次,在服务器端停止且卸载Servlet时执行该方法,负责释放占用的资源。

4.1.3 HttpServletBean 重写了 httpservlet 的 init()方法, 通过initServletBean() 方法来初始化bean, 具体实现在子类FrameworkServlet中。

    /*** Map config parameters onto bean properties of this servlet, and* invoke subclass initialization.* @throws ServletException if bean properties are invalid (or required* properties are missing), or if subclass initialization fails.*/@Overridepublic final void init() throws ServletException {if (logger.isDebugEnabled()) {logger.debug("Initializing servlet '" + getServletName() + "'");}// 初始化DespatcherService的配置参数try {PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);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) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);throw ex;}// 让子类来初始化bean
        initServletBean();if (logger.isDebugEnabled()) {logger.debug("Servlet '" + getServletName() + "' configured successfully");}}

4.1.4 FrameworkServlet重写了initServletBean(),并在这个方法中调用了initWebApplicationContext()方法

protected WebApplicationContext initWebApplicationContext() {// 获取 ContextLoaderListener 初始化WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(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) {// 将 Spring 的容器设为 SpringMVC 容器的父容器
                    cwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {wac = findWebApplicationContext();}if (wac == null) {wac = createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// dispatcherServlet 重写这个方法
        onRefresh(wac);}if (this.publishContext) {// 发布这个容器到 ServletContext 中String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;
}

4.1.5 dispatcherServlet 重写onRefresh(),调用initHandlerMappings将url和method关联起来

    /*** Initialize the HandlerMappings used by this class.* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,* we default to BeanNameUrlHandlerMapping.*/private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;if (this.detectAllHandlerMappings) {// 获取HandlerMapping实例Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());// 排序OrderComparator.sort(this.handlerMappings);}}else {try {HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later.
            }}// Ensure we have at least one HandlerMapping, by registering// a default HandlerMapping if no other mappings are found.if (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isDebugEnabled()) {logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");}}}

4.2 springmvc运行阶段

4.2.1 上面已经说过每次请求都会执行servlet的service()方法,FrameworkServlet重写了这个方法。

    /*** Override the parent class implementation in order to intercept PATCH* requests.*/@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 获取请求方式例如  get / postString method = request.getMethod();if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {processRequest(request, response);}else {super.service(request, response);}}

4.2.2 然后会根据请求方式去httpsrevlet中调用具体的方法。

    protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String method = req.getMethod();// get请求if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {// servlet doesn't support if-modified-since, no reason// to go through further expensive logic
                doGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);} catch (IllegalArgumentException iae) {// Invalid date header - proceed as if none was setifModifiedSince = -1;}if (ifModifiedSince < (lastModified / 1000 * 1000)) {maybeSetLastModified(resp, lastModified);doGet(req, resp);} else {resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);}}// head请求} else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);// post请求} else if (method.equals(METHOD_POST)) {doPost(req, resp);// put请求} else if (method.equals(METHOD_PUT)) {doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {doOptions(req,resp);} else if (method.equals(METHOD_TRACE)) {doTrace(req,resp);} else {// 以上都不满足, 说明请求方式有问题。String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[1];errArgs[0] = method;errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}}

4.2.3 FrameworkServlet重写了这些方法,并最终都是通过 processRequest( ) 方法来处理请求的。

    @Overrideprotected final void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response);}/*** Delegate POST requests to {@link #processRequest}.* @see #doService*/@Overrideprotected final void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response);}/*** Delegate PUT requests to {@link #processRequest}.* @see #doService*/@Overrideprotected final void doPut(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response);}

    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());// 初始化 ContextHolders
        initContextHolders(request, localeContext, requestAttributes);try {// 最终处理是dispatcherServlet 中的doService()方法调用 doDispatch()来处理的
            doService(request, response);}

4.2.4 doDispatch() 方法的主要过程是通过 HandlerMapping 获取 Handler,再找到用于执行它的 HandlerAdapter,执行 Handler 后得到 ModelAndView

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);// 获取处理请求的具体方法mappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// 获取处理请求的适配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);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;}// 得到modelandviewmv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(request, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}// 处理请求结果
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Error err) {triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

4.2.5 跳转页面

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,HandlerExecutionChain mappedHandler, ModelAndView mv, 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);}}

转载于:https://www.cnblogs.com/wlwl/p/10136814.html

SpringMVC源码解析相关推荐

  1. 筑基期第一式:SpringMVC源码解析

    文章目录 SpringMVC源码解析 SPI机制 案例 SpringMVC中SPI的使用 初始化IOC容器与九大组件 初始化容器 初始化九大组件 小结 SpringMVC如何处理一个请求 doDisp ...

  2. SpringMVC源码解析(四)——请求处理

    2019独角兽企业重金招聘Python工程师标准>>> 前言 这一篇,将着手介绍一次请求的处理.用到了 HandlerMapping.HandlerAdapter 知识,如果遇到不是 ...

  3. SpringMVC源码解析与思考

    首先要知道servletContext与servletConfig servletContext是web应用级别,是jvm进程级别:servletConfig是servlet服务级别,是线程级别. 定 ...

  4. SpringMVC源码解析HandlerMethod

    被 RequestMapping 注解封印的方法模型类. 封装了关于处理器方法信息的方法和bean类 . 提供了对方法参数,方法返回值,方法注释等方便地访问入口. 该类可以使用bean实例或具有bea ...

  5. SpringMVC源码解析之Last-Modified缓存机制

    Spring MVC 支持HTTP协议的 Last-Modified 缓存机制. 支持上次修改的HTTP请求,以方便内容缓存. 相同的合同作为Servlet API中的getLastModified方 ...

  6. SpringMVC源码解析 - HandlerAdapter - HandlerMethodArgumentResolver

    HandlerMethodArgumentResolver主要负责执行handler前参数准备工作. 看个例子,红色部分的id初始化,填充值就是它干的活: 1 @RequestMapping(valu ...

  7. clickhouse原理解析与开发实战 pdf_重识SSM,“超高频面试点+源码解析+实战PDF”,一次性干掉全拿走...

    重识SSM,"超高频面试点"+"源码解析"+"实战PDF",一次性干掉全拿走!! 01 超高频面试点知识篇 1.1 Spring超高频面试点 ...

  8. yolov3之pytorch源码解析_springmvc源码架构解析之view

    说在前面 前期回顾 sharding-jdbc源码解析 更新完毕 spring源码解析 更新完毕 spring-mvc源码解析 更新完毕 spring-tx源码解析 更新完毕 spring-boot源 ...

  9. tns03505 无法解析名称_SpringBootWeb源码解析SpringMVC自动配置

    SpringMVC自动配置 在 Spring Boot 中引入了 spring-boot-starter-web 依赖,并完成了 DispatcherServlet 的自动配置之后,便会通过 WebM ...

最新文章

  1. linux sed 小数点,每天进步一点点——linux——sed
  2. 安装GitLab,Jenkins,及自动化上线
  3. Node Express4.x 片段视图 partials
  4. Windows 8操作技巧之快捷键大全
  5. 4.设计包(design package)
  6. flv怎么转换成html5,怎么转asf格式 如何将flv格式转换成asf格式?
  7. java 实现 excel normsdist_Excel函数NormSDist和NormSInv的VB实现
  8. win11/ win10 C盘扩容教程
  9. 数学用计算机开方,开方(数学术语)_百度百科
  10. 【5 于博士Cadence SPB15.7 快速入门视频】allegro的测量工具的使用
  11. Android 架构组件之 Room
  12. RabbitMQ的安装与配置
  13. L1-054 福到了 (15 分)C语言
  14. EHIGH恒高:大话UWB技术之TDOA与TOF两种技术方案对比
  15. PHP判断ip是否在指定IP段内(微信支付回调IP段验证)
  16. 直坐标机械手的优缺点都有哪些?
  17. LayUI可选择可输入下拉框
  18. 【华为机试真题 Python】素数之积
  19. 不知道什么时候开始,35 岁突然成了 IT 人职业生涯的终结年龄
  20. Himall商城插件内部成员\获取已安装的插件信息\获取指定的插件信息

热门文章

  1. DOM(Document object madle) 文档对象模型: 元素节点 文本节点 属性节点
  2. webpack - vue Component 从入门到放弃(三)
  3. HTML基础笔记-02
  4. 使用Linq读取资源文件
  5. 详解基于CentOS6.2下DNS主从复制搭建与部署
  6. 偷懒日志 - 自动生成代码 - 第二步 生成POJO
  7. PL/Sql循序渐进全面学习教程(下)(ZT)
  8. centos6.9安装Tomcat8.0.36
  9. MYSQL常用命令(转载)
  10. mac下安装apc并且使用