我们从两个方面了解springmvc执行原理,首先我们去熟悉springmvc执行的过程,然后知道原理后通过手写springmvc去深入了解代码中执行过程。

(一)SpringMVC流程图

(二)SpringMVC流程

1、  用户发送请求至前端控制器DispatcherServlet。

2、  DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3、  处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4、  DispatcherServlet调用HandlerAdapter处理器适配器。

5、  HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

6、  Controller执行完成返回ModelAndView。

7、  HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

8、  DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

9、  ViewReslover解析后返回具体View。

10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

11、 DispatcherServlet响应用户。

(三)SpringMVC核心组件讲解

1、前端控制器DispatcherServlet
作用:接收请求,响应结果,相当于转发器,中央处理器。有了dispatcherServlet减少了其它组件之间的耦合度。
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

2、处理器映射器HandlerMapping
作用:根据请求的url查找Handler
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3、处理器适配器HandlerAdapter
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

4、处理器Handler
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。

5、视图解析器View resolver(不需要工程师开发),由框架提供
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。

6、视图View
View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)

(四)手写springMvc框架思路:

1、配置web.xml,加载自定义的DispatcherServlet。

2、初始化阶段,在DispatcherServlet类中,实现下面几个步骤:

    1. 加载配置类。
    2. 扫描当前项目下的所有文件。
    3. 拿到扫描到的类,通过反射机制,实例化。并且放到ioc容器中。
    4. 初始化path与方法的映射。
    5. 获取请求传入的参数并处理参数通过初始化好的handlerMapping中拿出url对应的方法名,反射调用。

3、运行阶段,每一次请求将会调用doGet或doPost方法,它会根据url请求去HandlerMapping中匹配到对应的Method,然后利用反射机制调用Controller中的url对应的方法,并得到结果返回。

(五)代码阶段:

1.web.xm加载

 <?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" version="2.5">
<display-name>maven_handmvc</display-name>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.zzw.cn.springmvc.dispathcer.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

2.自定义DispatcherServlet

 package com.zzw.cn.springmvc.dispathcer;

 import com.zzw.cn.springmvc.annoation.AnController;
import com.zzw.cn.springmvc.annoation.AnRequestMapping;
import com.zzw.cn.utils.ClassUtils; import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
* @author Simple
* @date 14:34 2019/8/26
* @description 手写springmvc框架流程
* <p>
* 思路:自定义DispatcherServlet
* 1.包扫描获取包下面所有的类
* 2.初始化包下面所有的类
* 3.初始化HandlerMapping方法,将url和方法对应上
* 4.实现HttpServlet 重写dopost方法
*/ public class DispatcherServlet extends HttpServlet { //springmvc 容器存放bean
private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<>(); @Override
public void init(ServletConfig config) {
String packagePath = "com.zzw.cn.springmvc";
//1.进行报扫描获取当前包下面所有的类
List<Class<?>> classes = comscanPackage(packagePath);
try {
//2.初始化springmvcbean
initSpringMvcBean(classes);
} catch (Exception e) {
e.printStackTrace();
}
//3.将请求地址和方法进行映射
initHandMapping(mvcBeans);
} public List<Class<?>> comscanPackage(String packagePath) {
List<Class<?>> classes = ClassUtils.getClasses(packagePath);
return classes;
} /**
* 初始化sprignbean
*
* @param classes
* @throws Exception
*/
public void initSpringMvcBean(List<Class<?>> classes) throws Exception {
if (classes.size() == 0 || null == classes) {
throw new Exception("包扫描后的classes为null");
} for (Class<?> aClass : classes) {
//获取被自定义注解的controller将其初始化到自定义sprignmvc容器中
AnController declaredAnnotation = aClass.getDeclaredAnnotation(AnController.class);
if (declaredAnnotation != null) {
//获取类的名字
String beanid = lowerFirstCapse(aClass.getSimpleName());
//获取对象
Object beanObj = aClass.newInstance();
//放入sprign容器
mvcBeans.put(beanid, beanObj);
}
} } /**
* 初始化HandlerMapping方法
*
* @param mvcBeans
*/
public void initHandMapping(ConcurrentHashMap<String, Object> mvcBeans) {
//遍历springmvc 获取注入的对象值
for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) {
Object objValue = entry.getValue();
Class<?> aClass = objValue.getClass();
//获取当前类 判断是否有自定义的requestMapping注解
String mappingUrl = null;
AnRequestMapping anRequestMapping = aClass.getDeclaredAnnotation(AnRequestMapping.class);
if (anRequestMapping != null) {
mappingUrl = anRequestMapping.value();
}
//获取当前类所有方法,判断方法上是否有注解
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
AnRequestMapping methodDeclaredAnnotation = method.getDeclaredAnnotation(AnRequestMapping.class);
if (methodDeclaredAnnotation != null) {
String methodUrl = methodDeclaredAnnotation.value();
mvcBeanUrl.put(mappingUrl + methodUrl, objValue);
mvcMethodUrl.put(mappingUrl + methodUrl, method.getName());
}
} } } /**
* @param str
* @return 类名首字母小写
*/
public static String lowerFirstCapse(String str) {
char[] chars = str.toCharArray();
chars[0] += 32;
return String.valueOf(chars); } @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doServelt(req, resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} private void doServelt(HttpServletRequest req, HttpServletResponse resp) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ServletException {
//获取请求地址
String requestUrl = req.getRequestURI();
//查找地址所对应bean
Object object = mvcBeanUrl.get(requestUrl);
if (object == null) {
resp.getWriter().println("sorry http is not exit 404");
return;
}
//获取请求的方法
String methodName = mvcMethodUrl.get(requestUrl);
if (methodName == null) {
resp.getWriter().println("sorry method is not exit 404");
return;
}
//通过构反射执行方法
Class<?> aClass = object.getClass();
Method method = aClass.getMethod(methodName);
String invoke = (String) method.invoke(object);
// 获取后缀信息
String suffix = ".jsp";
// 页面目录地址
String prefix = "/";
req.getRequestDispatcher(prefix + invoke + suffix).forward(req, resp);
} @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
} }

3.ClassUtils工具类封装

 package com.zzw.cn.utils;

 import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; public class ClassUtils { /**
* 从包package中获取所有的Class
*
* @param pack
* @return
*/
public static List<Class<?>> getClasses(String packageName) { // 第一个class类的集合
List<Class<?>> classes = new ArrayList<Class<?>>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} return classes;
} /**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
List<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
@Override
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}

4.自定义注解类AnController

 package com.zzw.cn.springmvc.annoation;

 import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* @author Simple
* @date 14:06 2019/8/27
* @description
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnController {
}

5.自定义注解类AnRequestMapping

 package com.zzw.cn.springmvc.annoation;

 import java.lang.annotation.*;

 /**
* @author Simple
* @date 14:07 2019/8/27
* @description
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AnRequestMapping {
String value() default "";
}

6.HelloWorld类

 package com.zzw.cn.springmvc.controller;

 import com.zzw.cn.springmvc.annoation.AnController;
import com.zzw.cn.springmvc.annoation.AnRequestMapping; /**
* @author Simple
* @date 15:15 2019/8/27
* @description
*/
@AnController
@AnRequestMapping(value = "/hello")
public class HelloWorld {
@AnRequestMapping("/method")
public String method(){
return "index";
}
}

7.index.jsp

 <html>
<body>
<h2>Hello World!</h2>
</body>
</html>

8.访问地址:http://localhost:8080/hello/method

9.成功结果:

10.错误结果

现在代码已经完成了,也许开始你对很多东西不理解,但是没关系可以当作了解,多敲敲。

(二)springMvc原理和手写springMvc框架的更多相关文章

  1. 《四 spring源码》手写springmvc

    手写SpringMVC思路 1.web.xml加载  为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...

  2. 手写SpringMVC框架(二)-------结构开发设计

    续接前文, 手写SpringMVC框架(一)项目搭建 本节我们来开始手写SpringMVC框架的第二阶段:结构开发设计. 新建一个空的springmvc.properties, 里面写我们要扫描的包名 ...

  3. 手写SpringMVC 框架

    手写SpringMVC框架 细嗅蔷薇 心有猛虎 背景:Spring 想必大家都听说过,可能现在更多流行的是Spring Boot 和Spring Cloud 框架:但是SpringMVC 作为一款实现 ...

  4. 手写SpringMVC框架(三)-------具体方法的实现

    续接前文 手写SpringMVC框架(二)结构开发设计 本节我们来开始具体方法的代码实现. doLoadConfig()方法的开发 思路:我们需要将contextConfigLocation路径读取过 ...

  5. 手写DAO框架(二)-开发前的最后准备

    -------前篇:手写DAO框架(一)-从“1”开始 --------- 前言:前篇主要介绍了写此框架的动机,把主要功能点大致介绍了一下.此篇文章主要介绍开发前最后的一些准备.主要包括一些基础知识点 ...

  6. 手写MVC框架(二)-代码实现和使用示例

    --------上一篇:手写MVC框架(一)-再出发----- 背景 书接上文,之前整理了实现MVC框架需要写哪些东西.这周粗看了一下,感觉也没多少工作量,所以就计划一天时间来完成.周末的时间,哪会那 ...

  7. 手写MQ框架(二)-服务端实现

    一.起航 书接上文->手写MQ框架(一)-准备启程 本着从无到有,从有到优的原则,所以计划先通过web实现功能,然后再优化改写为socket的形式. 1.关于技术选型 web框架使用了之前写的g ...

  8. 重学 Java 设计模式:实战中介者模式「按照Mybaits原理手写ORM框架,给JDBC方式操作数据库增加中介者场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 同龄人的差距是从什么时候拉开的 同样的幼儿园.同样的小学.一样 ...

  9. 手写MVC框架(一)-再出发

    背景 前段时间把之前写的DAO框架(手写DAO框架(一)-从“1”开始)整理了一下,重构了一版.整理过程中看以前写的代码,只是为了了解实现,只是为了实现,代码写的有点粗糙.既然已经整理了DAO框架,索 ...

随机推荐

  1. LinkedHashMap的特殊之处

    一.前言 乍眼一看会怀疑或者问LinkedHashMap与HashMap有什么区别? 它有什么与众不同之处?  由于前面已经有两篇文章分析了HashMap,今天就看看LinkedHashMap.(基于 ...

  2. Pow共识算法

    谈到哈希算法,每个程序员都不陌生,但是谈到比特币共识算法PoW,如果没有接触过的技术人员可能觉得应该会很复杂,毕竟全球的比特币节点数量如此庞大,达成共识的算法应该不会很简单.但其实如果你已掌握哈希算法 ...

  3. Extjs的文件上传问题

    最近做一个ExtJs4.0的文件上传.发现在没有添加 xtype:filefield,   时提交数据form的数据,修改form都能提交,而且返回正常.但是当加入xtype:filefield后,返 ...

  4. SWT 注意事项

    一:GridData (1) 将 GridData 的 widthHint 设置为0,可以解决控件大小会随着这其默认值长度大小而改变的问题.

  5. spark shuffle写操作之SortShuffleWriter

    提出问题 1. spark shuffle的预聚合操作是如何做的,其中底层的数据结构是什么?在数据写入到内存中有预聚合,在读溢出文件合并到最终的文件时是否也有预聚合操作? 2. shuffle数据的排 ...

  6. HTML5 Device Access (设备访问)

    camera api (含图片预览) 参考地址 主要为利用input type=file, accept="image/*" 进行处理 图片预览方式(两种) const file ...

  7. Java虚拟机(一)-Java内存区域

    通过看深入理解java虚拟机这本书,大致总结一些笔记,或者提出一些问题,希望大家深入交流学习,第一次写博客,大家多多支持 Java虚拟机对于很多Java开发人员每天都在用,但是大部分人初学者对这些并不 ...

  8. ssh的执行流畅

    SSH运行流程 1. 服务器启动,创建Struts2的Filter控制器,创建Spring容器对象. 实例化Struts2控制器时,加载struts.xml,struts-default.xml,de ...

  9. Python连载30-多线程之进程&线程&线程使用举例

    一.多线程 1.我们的环境 (1)xubuntu 16.04(2)anaconda(3)pycharm(4)python 3.6 2.程序:一堆代码以文本的形式存入一个文档 3.进程:程序运行的一个状 ...

  10. soap天气查询

    public class MainActivity extends AppCompatActivity { private TextView tvContent; @Override protecte ...