springmvc源码分析原理及简单实现
2018-04-03 15:31
651 查看
现在我主要分四步走。
一、我们先来看下springmvc的配置与启动流程。
这是spring与springmvc框架的web应用的web.xml简单配置。虽然这篇博客讲的是springmvc但是,它的启动流程还是离不开Spring的,所以我先简单提下Spring的启动流程,后期会写另一篇博客详细的说一说Spring的,请大家见谅。
首先我们看到在这个web.xml中配置了<listener>标签,里面配置了一个监听器类ContextLoaderListener。
我们可以看到这个类实现了ServletContextListener这个监听器接口 ,这个接口是web容器自带的,它是用于监听容器的启动与销毁的,并给实现了这个接口的监听器发送通知。所以web容器启动的时候会先触发这个ContextLoaderListener监听器,这个监听器会执行它的 initWebApplicationContext的方法,方法名称即是其含义。方法中首先创建了WebApplicationContext,配置并且刷新实例化整个SpringApplicationContext中的Bean。因此,如果我们的Bean配置出错的话,在容器启动的时候,会抛异常出来的。
然后会把创建的好的WebApplicationContext放到ServletContext容器中,以key为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 存储这个WebApplication 到servlet容器中。并在后续springMVC的初始化中会用到。
二、下面我们开始来讲一讲SpringMVC部分的初始化。
从配置的web.xml中我们可以看到整个web应用就配置了一个Servlet,也就是DispatcherServlet。我们可以看到 DispatcherServlet这个类继承了这么多的抽象类,其实是用了一种模板方法的设计模式, 公共部分的统一自己实现,变化的部分抽象,交给子类去实现。我就随便举个HttpServletBean类的例子,大家看一下。
这里HttpServletBean重写了init()方法,使的Servlet不再关心init-param部分的赋值,让servlet更关注于自身Bean初始化的实现,并实现SpringMVC自己的容器。这里我们需要注意:在Spring的配置文件中root-context.xml 我们扫描注解的时候不需要扫描@Controller注解,需要用exclude把这个注解排除在外,不让Spring去扫描它。但是我们在SpringMVC的配置文件servlet-context.xml中扫描包的时候是需要声明扫描Controller注解的,需要用到 include,例如:<context:component-scan base-package="com.nn.web.controller"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
这个 use-default-filters = "false"最好添加,默认为true,因为 springmvc 配置文件中不添加use-default-filters = "false"的话,spring会扫描的包下的@repository,@service的bean,可能会导致一些问题。这个尤其在springmvc+spring+hibernate等集成时最容易出问题的,最典型的错误就是:事务不起作用。 现在我们来说说DispatcherServlet继承的另外一个抽象类 FrameworkServlet,它主要的作用是把Spring的ApplicationContext上下文设置成SpringMVC上下文的parent。我在网上找到一张图,大家可以看一看。
三、DispatchServlet的工作流程
因为它是个Servlet ,所以它肯定会有doService()方法,我们先从doService()开始看起。主要是做了两件事,一个是放很多属性或者全局变量到request中,第二个调用doDispatch()。我们现在简单说下doDispatch 做了什么事。页面来了请求,通过request和URL来遍历 HandlerMapping 找到对应的handler ,并给这个handler加上拦截器,形成一个处理链对象 HandlerExecutionChain。再通过handler得到对应类型的handlerAdapter适配器(通过遍历),handlerAdapter调用handler,也就是去执行对应controller方法,返回一个 ModelAndView 给handlerAdapter适配器。然后Dispatch执行视图解析器。进行数据的解析和渲染视图。我在网上找了两张图,大家可以看看。一个是时序图
一个是流转图
这样大家应该清楚了。
四、下面我们来简单的自己实现SpringMVC的功能
这个是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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <servlet> <servlet-name>MySpringMVC</servlet-name> <servlet-class>com.vicoqi.servlet.MyDispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>application.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>MySpringMVC</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>这个是MyDispatcherServlet中的 init 方法。
@Override public void init(ServletConfig config) throws ServletException { //1.加载配置文件 doLoadConfig(config.getInitParameter("contextConfigLocation")); //2.初始化所有相关联的类,扫描用户设定的包下面所有的类 doScanner(properties.getProperty("scanPackage")); //3.拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中(k-v beanName-bean) beanName默认是首字母小写 doInstance(); //4.初始化HandlerMapping(将url和method对应上) initHandlerMapping(); }这个是doDispatch 方法
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception { if(handlerMapping.isEmpty()){ return; } String url =req.getRequestURI(); String contextPath = req.getContextPath(); //拼接url并把多个/替换成一个 url=url.replace(contextPath, "").replaceAll("/+", "/"); if(!this.handlerMapping.containsKey(url)){ resp.getWriter().write("404 NOT FOUND!"); return; } Method method =this.handlerMapping.get(url); //获取方法的参数列表 Class<?>[] parameterTypes = method.getParameterTypes(); //获取请求的参数 Map<String, String[]> parameterMap = req.getParameterMap(); //保存参数值 Object [] paramValues= new Object[parameterTypes.length]; //方法的参数列表 for (int i = 0; i<parameterTypes.length; i++){ //根据参数名称,做某些处理 String requestParam = parameterTypes[i].getSimpleName(); if (requestParam.equals("HttpServletRequest")){ //参数类型已明确,这边强转类型 paramValues[i]=req; continue; } if (requestParam.equals("HttpServletResponse")){ paramValues[i]=resp; continue; } if(requestParam.equals("String")){ for (Entry<String, String[]> param : parameterMap.entrySet()) { String value =Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ","); paramValues[i]=value; } } } //利用反射机制来调用 try { method.invoke(this.controllerMap.get(url), paramValues);//obj是method所对应的实例 在ioc容器中 } catch (Exception e) { e.printStackTrace(); } }项目很多不严谨,但是只给提供一下思路,请大家见谅了。但是注释写的都很详细。希望可以帮助大家我可以给大家我github地址,大家去下和运行,试着体会。这个项目是没有问题的我运行的是好的。如果大家认为可以,请给一下博客赞和github上star,谢谢。github地址:https://github.com/vicoqi/SpringMVC-Simple-realize.git
相关文章推荐
- VS平台账号注册机--源码--详细文档--分析-原理-实现
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- 设计有穷自动机DFA实现C++简单程序的词法分析、扫描(编译原理实验) 推荐
- 如何使用androidpn实现android手机消息推送(简单的源码分析)
- 也谈WEB打印(二):简单的分析一下IE的打印原理并实现
- 加壳原理与简单实现加壳(delphi源码)
- 如何使用androidpn实现android手机消息推送(简单的源码分析)
- mahout实现的模糊K-Means聚类算法原理和源码分析
- 【转】【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- kallsyms实现原理源码分析
- PHP的MVC模式实现原理分析(一相简单的MVC框架范例)
- 也谈WEB打印(二):简单的分析一下IE的打印原理并实现简单的打印和预览
- 如何使用androidpn实现android手机消息推送(简单的源码分析)
- Mangos源码分析(3):服务器结构探讨之简单的世界服实现
- ORM,ASP.NET中ORM学习,ASP.NET中ORM学习心得,WEB2.0中ORM实现原理,Asp.net简单ORM示例源码详细讲解,Asp.net2.0:如何使用ObjectDataSource(配合ORM )
- Mangos源码分析(3):服务器结构探讨之简单的世界服实现
- Mangos源码分析(3):服务器结构探讨之简单的世界服实现
- 加壳原理与简单实现加壳(delphi源码)
- 控制反转(IOC)的简单实现及原理分析
- 【JfaceTextFramework学习笔记之四】TextViewer实现原理简单分析