手写 Spring MVC

不多说,简历装 X 必备。不过练好还是需要求一定的思维能力。

一、整体思路

思路要熟练背下来

1)配置阶段

  • 配置 web.xml: XDispatchServlet
  • 设定 init-param: contextConfigLocation = classpath:application.xml
  • 设定 url-pattern: /*
  • 配置 Annotation: @XController @XService @XAutowired @XRequestMapping

2)初始化阶段

  • IOC:

    • 调用 init() 方法: 加载配置文件
    • IOC 容器初始化: Map<String, Object>
    • 扫描相关的类: scan-package="com.xiaopengwei"
    • 创建实例化并保存到容器: 同过反射机制将类实例化放入 IOC 容器中
  • DI:
    • 进行 DI 操作: 扫描 IOC 容器中的实例,给没有赋值的属性自动赋值
  • MVC:
    • 初始化 HandlerMapping: 将一个 URL 和一个 Method 进行一对一的关联映射 Map<String, Method>

3)运行阶段

  • 调用 doGet() / doPost() 方法: Web 容器调用 doGet() / doPost() 方法,获得 request/response 对象
  • 匹配 HandleMapping: 从 request 对象中获得用户输入的 url,找到其对应的 Method
  • 反射调用 method.invoker(): 利用反射调用方法并返回结果
  • response.getWrite().write(): 将返回结果输出到浏览器

二、源代码

项目结构:

源代码:

(1)在 pom.xml 引入一个 jar 包

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>

(2)web.xml 文件:

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>xmvc</servlet-name>
<servlet-class>com.xiaopengwei.xspring.servlet.XDispatchServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--you can't use classpath*: -->
<param-value>application.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>xmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

(3)application.properties 文件:

scan-package=com.xiaopengwei

(4)自定义注解 XAutowired:

package com.xiaopengwei.xspring.annotation;
import java.lang.annotation.*;
/**
* <p>
*
* @author XiaoPengwei
* @since 2019-07-19
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XAutowired {
String value() default "";
}

(5)自定义注解 XController:

package com.xiaopengwei.xspring.annotation;
import java.lang.annotation.*;
/**
* <p>
*
* @author XiaoPengwei
* @since 2019-07-19
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XController {
String value() default "";
}

(6)自定义注解 XRequestMapping:

package com.xiaopengwei.xspring.annotation;
import java.lang.annotation.*;
/**
* <p>
*
* @author XiaoPengwei
* @since 2019-07-19
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XRequestMapping {
String value() default "";
}

(7)自定义注解 XService:

package com.xiaopengwei.xspring.annotation;
import java.lang.annotation.*;
/**
* <p>
*
* @author XiaoPengwei
* @since 2019-07-19
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XService {
String value() default "";
}

(8)核心 XDispatchServlet:

package com.xiaopengwei.xspring.servlet;
import com.xiaopengwei.xspring.annotation.XAutowired;
import com.xiaopengwei.xspring.annotation.XController;
import com.xiaopengwei.xspring.annotation.XRequestMapping;
import com.xiaopengwei.xspring.annotation.XService;
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.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*; /**
* <p>
* XSpring
*
* @author XiaoPengwei
* @since 2019-07-19
*/
public class XDispatchServlet extends HttpServlet { /**
* 属性配置文件
*/
private Properties contextConfig = new Properties(); private List<String> classNameList = new ArrayList<>(); /**
* IOC 容器
*/
Map<String, Object> iocMap = new HashMap<String, Object>(); Map<String, Method> handlerMapping = new HashMap<String, Method>(); @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //7、运行阶段
try {
doDispatch(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 Exception Detail:\n" + Arrays.toString(e.getStackTrace()));
} } /**
* 7、运行阶段,进行拦截,匹配
*
* @param req 请求
* @param resp 响应
*/
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException { String url = req.getRequestURI(); String contextPath = req.getContextPath(); url = url.replaceAll(contextPath, "").replaceAll("/+", "/"); System.out.println("[INFO-7] request url-->" + url); if (!this.handlerMapping.containsKey(url)) {
try {
resp.getWriter().write("404 NOT FOUND!!");
return;
} catch (IOException e) {
e.printStackTrace();
}
} Method method = this.handlerMapping.get(url); System.out.println("[INFO-7] method-->" + method); String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName()); System.out.println("[INFO-7] iocMap.get(beanName)->" + iocMap.get(beanName)); // 第一个参数是获取方法,后面是参数,多个参数直接加,按顺序对应
method.invoke(iocMap.get(beanName), req, resp); System.out.println("[INFO-7] method.invoke put {" + iocMap.get(beanName) + "}.");
} @Override
public void init(ServletConfig servletConfig) throws ServletException { //1、加载配置文件
doLoadConfig(servletConfig.getInitParameter("contextConfigLocation")); //2、扫描相关的类
doScanner(contextConfig.getProperty("scan-package")); //3、初始化 IOC 容器,将所有相关的类实例保存到 IOC 容器中
doInstance(); //4、依赖注入
doAutowired(); //5、初始化 HandlerMapping
initHandlerMapping(); System.out.println("XSpring FrameWork is init."); //6、打印数据
doTestPrintData();
} /**
* 6、打印数据
*/
private void doTestPrintData() { System.out.println("[INFO-6]----data------------------------"); System.out.println("contextConfig.propertyNames()-->" + contextConfig.propertyNames()); System.out.println("[classNameList]-->");
for (String str : classNameList) {
System.out.println(str);
} System.out.println("[iocMap]-->");
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
System.out.println(entry);
} System.out.println("[handlerMapping]-->");
for (Map.Entry<String, Method> entry : handlerMapping.entrySet()) {
System.out.println(entry);
} System.out.println("[INFO-6]----done-----------------------"); System.out.println("====启动成功====");
System.out.println("测试地址:http://localhost:8080/test/query?username=xiaopengwei");
System.out.println("测试地址:http://localhost:8080/test/listClassName");
} /**
* 5、初始化 HandlerMapping
*/
private void initHandlerMapping() { if (iocMap.isEmpty()) {
return;
} for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
Class<?> clazz = entry.getValue().getClass(); if (!clazz.isAnnotationPresent(XController.class)) {
continue;
} String baseUrl = ""; if (clazz.isAnnotationPresent(XRequestMapping.class)) {
XRequestMapping xRequestMapping = clazz.getAnnotation(XRequestMapping.class);
baseUrl = xRequestMapping.value();
} for (Method method : clazz.getMethods()) {
if (!method.isAnnotationPresent(XRequestMapping.class)) {
continue;
} XRequestMapping xRequestMapping = method.getAnnotation(XRequestMapping.class); String url = ("/" + baseUrl + "/" + xRequestMapping.value()).replaceAll("/+", "/"); handlerMapping.put(url, method); System.out.println("[INFO-5] handlerMapping put {" + url + "} - {" + method + "}."); }
} } /**
* 4、依赖注入
*/
private void doAutowired() {
if (iocMap.isEmpty()) {
return;
} for (Map.Entry<String, Object> entry : iocMap.entrySet()) { Field[] fields = entry.getValue().getClass().getDeclaredFields(); for (Field field : fields) {
if (!field.isAnnotationPresent(XAutowired.class)) {
continue;
} System.out.println("[INFO-4] Existence XAutowired."); // 获取注解对应的类
XAutowired xAutowired = field.getAnnotation(XAutowired.class);
String beanName = xAutowired.value().trim(); // 获取 XAutowired 注解的值
if ("".equals(beanName)) {
System.out.println("[INFO] xAutowired.value() is null");
beanName = field.getType().getName();
} // 只要加了注解,都要加载,不管是 private 还是 protect
field.setAccessible(true); try {
field.set(entry.getValue(), iocMap.get(beanName)); System.out.println("[INFO-4] field set {" + entry.getValue() + "} - {" + iocMap.get(beanName) + "}.");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
} /**
* 3、初始化 IOC 容器,将所有相关的类实例保存到 IOC 容器中
*/
private void doInstance() {
if (classNameList.isEmpty()) {
return;
} try {
for (String className : classNameList) { Class<?> clazz = Class.forName(className); if (clazz.isAnnotationPresent(XController.class)) {
String beanName = toLowerFirstCase(clazz.getSimpleName());
Object instance = clazz.newInstance(); // 保存在 ioc 容器
iocMap.put(beanName, instance);
System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap."); } else if (clazz.isAnnotationPresent(XService.class)) { String beanName = toLowerFirstCase(clazz.getSimpleName()); // 如果注解包含自定义名称
XService xService = clazz.getAnnotation(XService.class);
if (!"".equals(xService.value())) {
beanName = xService.value();
} Object instance = clazz.newInstance();
iocMap.put(beanName, instance);
System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap."); // 找类的接口
for (Class<?> i : clazz.getInterfaces()) {
if (iocMap.containsKey(i.getName())) {
throw new Exception("The Bean Name Is Exist.");
} iocMap.put(i.getName(), instance);
System.out.println("[INFO-3] {" + i.getName() + "} has been saved in iocMap.");
}
} }
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 获取类的首字母小写的名称
*
* @param className ClassName
* @return java.lang.String
*/
private String toLowerFirstCase(String className) {
char[] charArray = className.toCharArray();
charArray[0] += 32;
return String.valueOf(charArray);
} /**
* 2、扫描相关的类
*
* @param scanPackage properties --> scan-package
*/
private void doScanner(String scanPackage) { // package's . ==> /
URL resourcePath = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/")); if (resourcePath == null) {
return;
} File classPath = new File(resourcePath.getFile()); for (File file : classPath.listFiles()) { if (file.isDirectory()) { System.out.println("[INFO-2] {" + file.getName() + "} is a directory."); // 子目录递归
doScanner(scanPackage + "." + file.getName()); } else { if (!file.getName().endsWith(".class")) {
System.out.println("[INFO-2] {" + file.getName() + "} is not a class file.");
continue;
} String className = (scanPackage + "." + file.getName()).replace(".class", ""); // 保存在内容
classNameList.add(className); System.out.println("[INFO-2] {" + className + "} has been saved in classNameList.");
}
}
} /**
* 1、加载配置文件
*
* @param contextConfigLocation web.xml --> servlet/init-param
*/
private void doLoadConfig(String contextConfigLocation) { InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation); try {
// 保存在内存
contextConfig.load(inputStream); System.out.println("[INFO-1] property file has been saved in contextConfig.");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

(9)示例:TestController:

package com.xiaopengwei.demo.xcontroller;
import com.xiaopengwei.demo.xservice.ITestXService;
import com.xiaopengwei.xspring.annotation.XAutowired;
import com.xiaopengwei.xspring.annotation.XController;
import com.xiaopengwei.xspring.annotation.XRequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List; /**
* <p>
* 前置控制器
*
* @author XiaoPengwei
* @since 2019-07-19
*/
@XController
@XRequestMapping("/test")
public class TestController { @XAutowired
ITestXService testXService; /**
* 测试方法 /test/query
*
* @param req 请求体
* @param resp 响应体
*/
@XRequestMapping("/query")
public void query(HttpServletRequest req, HttpServletResponse resp) { if (req.getParameter("username") == null) {
try {
resp.getWriter().write("param username is null");
} catch (IOException e) {
e.printStackTrace();
}
} else { String paramName = req.getParameter("username");
try {
resp.getWriter().write("param username is " + paramName);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("[INFO-req] New request param username-->" + paramName);
}
} /**
* 测试方法 /test/listClassName
*
* @param req 请求体
* @param resp 响应体
*/
@XRequestMapping("/listClassName")
public void listClassName(HttpServletRequest req, HttpServletResponse resp) {
String str = testXService.listClassName();
System.out.println("testXService----------=-=-=>" + str);
try {
resp.getWriter().write(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}

(10)示例接口:ITestXService:

package com.xiaopengwei.demo.xservice;
/**
* <p>
* 接口
*
* @author XiaoPengwei
* @since 2019-07-19
*/
public interface ITestXService {
String listClassName();
}

(11)示例实现类 TestXServiceImpl:

package com.xiaopengwei.demo.xservice.impl;
import com.xiaopengwei.demo.xservice.ITestXService;
import com.xiaopengwei.xspring.annotation.XService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List; /**
* <p>
* 业务实现类
*
* @author XiaoPengwei
* @since 2019-07-19
*/
@XService
public class TestXServiceImpl implements ITestXService { @Override
public String listClassName() { // 假装来自数据库
return "123456TestXServiceImpl";
}
}

(12)测试:

配置 Tomcat 后,访问:

http://localhost:8080/test/query?username=xiaopengwei

http://localhost:8080/test/listClassName

手写 Spring的更多相关文章

  1. 一个老程序员是如何手写Spring MVC的

    人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...

  2. 【Spring】手写Spring MVC

    Spring MVC原理 Spring的MVC框架主要由DispatcherServlet.处理器映射.处理器(控制器).视图解析器.视图组成. 完整的Spring MVC处理 流程如下: Sprin ...

  3. 我是这样手写 Spring 的(麻雀虽小五脏俱全)

    人见人爱的 Spring 已然不仅仅只是一个框架了.如今,Spring 已然成为了一个生态.但深入了解 Spring 的却寥寥无几.这里,我带大家一起来看看,我是如何手写 Spring 的.我将结合对 ...

  4. 《四 spring源码》利用TransactionManager手写spring的aop

    事务控制分类 编程式事务控制          自己手动控制事务,就叫做编程式事务控制. Jdbc代码: Conn.setAutoCommite(false);  // 设置手动控制事务 Hibern ...

  5. 手写Spring框架,加深对Spring工作机制的理解!

    在我们的日常工作中,经常会用到Spring.Spring Boot.Spring Cloud.Struts.Mybatis.Hibernate等开源框架,有了这些框架的诞生,平时的开发工作量也是变得越 ...

  6. 手写Spring+demo+思路

    我在学习Spring的时候,感觉Spring是很难的,通过学习后,发现Spring没有那么难,只有你去学习了,你才会发现,你才会进步 1.手写Spring思路: 分为配置.初始化.运行三个阶段如下图 ...

  7. 我是这样手写Spring的,麻雀虽小五脏俱全

    人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...

  8. Spring事务原理分析--手写Spring事务

    一.基本概念和原理 1.Spring事务 基于AOP环绕通知和异常通知的 2.Spring事务分为编程式事务.声明事务.编程事务包括注解方式和扫包方式(xml) Spring事务底层使用编程事务(自己 ...

  9. 手写Spring MVC

    闲及无聊 又打开了CSDN开始看一看有什么先进的可以学习的相关帖子,这时看到了一位大神写的简历装X必备,手写Spring MVC. 我想这个东西还是有一点意思的 就拜读了一下大佬的博客 通读了一遍相关 ...

随机推荐

  1. Ceph OpenSSL

    Ceph OpenSSL 1. SSL介绍 SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信 ...

  2. Cocos2d-x 3.X Qt MinGW版本编译运行

    自Cocos2d-x 3.X引入了C++ 11特性,在Windows平台上的支持就仅限VS 2012,其实还可以尝试MinGW版本,GitHub上有MinGW版本的Qt Creator工程. 地址:h ...

  3. Ring3下无驱动移除winlogon.exe进程ctrl+alt+del,win+u,win+l三个系统热键,非屏蔽热键(子类化SAS 窗口)

    随手而作,纯粹技术研究,没什么实际意义. 打开xuetr,正常情况下.winlogon.exe注册了三个热键.ctrl+alt+del,win+u,win+l三个. 这三个键用SetWindowsHo ...

  4. 我们检测到您的浏览器不兼容传输层安全协议 (TLS) 1.1 或更高版本,此协议在 Internet 上用于确保您通信的安全性。

    早上使用.Net WebClient类采集亚马逊数据,返回http 400 Bad Request错误,内容里面有“我们检测到您的浏览器不兼容传输层安全协议 (TLS) 1.1 或更高版本,此协议在 ...

  5. ASP.NET Core 通过 Microsoft.DotNet.Watcher.Tools 实现热部署

    之前开发前端的时候,webpack 会有热更新工具,在修改了代码之后,自动将代码编译,实时展现到页面上,给开发带来了极大的方便. Java也可以通过第三方插件JRebel实现热部署,不用频繁的重启To ...

  6. Qt单元测试浅析

    Qt单元测试框架,使用于基于Qt的应用程序和库,先从一个简单的demo工程说起吧. 我们可以通过QtCreator来创建一个简单的Qt单元测试工程,夏天到了,这个demo工程的名字就叫Summer好了 ...

  7. SYN5307型数字式电秒表

    SYN5307型数字式电秒表 产品概述 SYN5307型数字式电秒表是由西安同步电子科技有限公司精心设计生产的一款多功能,高精度电秒表.该仪器采用高精度石英晶振作为测量基准,测量准确度高于一般的电秒表 ...

  8. 由django请求生命周期延伸出的知识点大总结

    django项目搭建见: https://www.cnblogs.com/dongxixi/p/10981577.html django请求生命周期图: 由浏览器发起请求开始 知识点1: 浏览器与服务 ...

  9. Arm架构下VUE环境的安装

    最近因为项目需要在arm环境下搭建vue环境,网上有基于Linux的 教程,路径略有不同,现整理如下 1.安装文件下载 1.下载地址:http://nodejs.cn/download/ 2.选择一个 ...

  10. tensorflow学习笔记-bili莫烦

    bilibili莫烦tensorflow视频教程学习笔记 1.初次使用Tensorflow实现一元线性回归 # 屏蔽警告 import os os.environ[' import numpy as ...