1.1 执行流程图
1.2 执行过程
- 前端发送Http请求到DispatcherServlet;
- DispatcherServlet收到请求调用HandlerMapping处理映射器,处理映射器根据请求的url找到具体的处理器,生成处理器对象以及处理器拦截器(如果有则生成),一并返回给DispatcherServlet;
- DispatcherServlet通过HandlerAdapter处理器适配器调用处理器;
- 执行处理器(Controlle,也叫后端控制器),返回ModelAndView(数据模型和视图名称);
- HandlerAdapter将ModelAndView传给DispatcherServlet;
- DispatcherServlet将ModelAndView传给ViewResolver视图解析器,解析后返回具体的View;
- DispatcherServlet对View进行渲染,将数据模型填充到View中;
- DispatcherServlet响应用户;
-
读取配置文件
SpringMVC本质上是一个Servlet,为了读取web.xml配置,这里用到了ServletConfig 这个类,它代表当前Servlet在web.xml中的配置信息,通过 [^ config.getInitParameter("contextConfigLocation");//读取启动参数],读取application.properties。
<init-param> <param-name>contextConfigLocation</param-name> <param-value>application.properties</param-value> </init-param>
-
初始化阶段
@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对应上) iniHandlerMapping(); }
-
运行阶段
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception { if(handlerMapping.isEmpty()){ return; } String url = req.getRequestURI(); String contextPath = req.getContextPath(); url = url.replace(contextPath,"").replaceAll("/+","/"); if(!this.handlerMapping.containsKey(url)){ resp.getWriter().write("404 No 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("HttpServletRequest".equals(requestParam)){ //参数类型已明确,强转类型是 paramValues[i] = req; continue; } if("HttpServletResponse".equals(requestParam)){ paramValues[i] = resp; continue; } if("String".equals(requestParam)){ for (Entry<String, String[]> param : parameterMap.entrySet()) { String valuse = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ","); paramValues[i] = valuse; } } } //利用反射机制调用 try { method.invoke(this.controllerMap.get(url),paramValues);//第一个参数为method所对应的实例,在IOC容器中 }catch (Exception e){ e.printStackTrace(); }
https://github.com/gnng/MySpringMVC
-
Maven的依赖范围provided
<dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <!-- <scope>xxx</scope>依赖范围 * compile,缺省值,适用于所有阶段,会随着项目一起发布。 * provided,类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet.jar。 * runtime,只在运行时使用,如JDBC驱动,适用运行和测试阶段。 * test,只在测试时使用,用于编译和运行测试代码。不会随项目发布。 * system,类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它。 --> <scope>provided</scope> </dependency> </dependencies>
-
Java元注解
/** * java中元注解有四个: @Retention @Target @Document @Inherited; * @Retention:注解的保留位置 * @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含 * @Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得, * @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 * * @Target:注解的作用目标 * @Target(ElementType.TYPE) //接口、类、枚举、注解 * @Target(ElementType.FIELD) //字段、枚举的常量 * @Target(ElementType.METHOD) //方法 * @Target(ElementType.PARAMETER) //方法参数 * @Target(ElementType.CONSTRUCTOR) //构造函数 * @Target(ElementType.LOCAL_VARIABLE)//局部变量 * @Target(ElementType.ANNOTATION_TYPE)//注解 * @Target(ElementType.PACKAGE) ///包 * * @Document:说明该注解将被包含在javadoc中 * * @Inherited:说明子类可以继承父类中的该注解 */