实现SpringMVC底层机制(一)

文章目录

    • 1.环境配置
        • 1.创建maven项目
        • 2.创建文件目录
        • 3.导入jar包
    • 2.开发核心控制器
        • 文件目录
        • 1.流程图
        • 2.编写核心控制器SunDispatcherServlet.java
        • 3.类路径下编写spring配置文件sunspringmvc.xml
        • 4.配置中央控制器web.xml
        • 5.配置tomcat,完成测试
          • 1.配置发布方式
          • 2.配置热加载
          • 3.修改SunDispatcherServlet.java
          • 4.完成测试
    • 3.完成客户端/浏览器可以请求控制层
        • 文件目录
        • 1.思路分析
        • 2.编写MonsterController.java
        • 3.自定义注解
          • 1.Controller.java
          • 2.RequestMapping.java
        • 4.自定义容器(1),在tomcat启动时读取配置文件,获取要扫描的包的工作路径
          • 1.SunWebApplicationContext.java
          • 2.修改SunDispatcherServlet.java
          • 3.单元测试,启动tomcat
        • 5.自定义容器(2),在tomcat启动的时候完成对指定包的扫描
          • 1.修改SunWebApplicationContext.java
          • 2.debug测试
        • 6.将自定义容器(3),符合要求的类反射创建对象,放到单例池
          • 1.修改SunWebApplicationContext.java增加方法,添加属性
          • 2.debug查看单例池
        • 7.完成url和控制器方法映射
          • 1.创建映射bean,SunHandler.java
          • 2.修改中央控制器SunWebApplicationContext.java添加方法和属性
          • 3.debug查看映射对象列表
        • 8.完成请求分发到目标方法
          • 1.修改SunDispatcherServlet.java,添加两个方法并在dopost中请求分发
          • 2.单元测试
    • 4.当前阶段完成的功能
        • 1.初始化阶段
        • 2.完成请求分发

1.环境配置

1.创建maven项目

image-20240227084815460

2.创建文件目录

image-20240227085226577

3.导入jar包
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>sun-springmvc</artifactId><packaging>war</packaging><version>1.0-SNAPSHOT</version><name>sun-springmvc Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!--servlet原生api--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><!--在项目打包时不会带上这个jar--><scope>provided</scope></dependency><!--解析xml--><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><!--常用工具类--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.5</version></dependency></dependencies><build><finalName>sun-springmvc</finalName></build>
</project>

2.开发核心控制器

文件目录

image-20240227095105084

1.流程图

image-20240227093109208

2.编写核心控制器SunDispatcherServlet.java
package com.Sun.sunspringmvc.servlet;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 充当中央控制器** @author 孙显圣* @version 1.0*/
public class SunDispatcherServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doGet(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doPost(req, resp);}
}
3.类路径下编写spring配置文件sunspringmvc.xml
4.配置中央控制器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>SunDispatcherServlet</servlet-name><servlet-class>com.Sun.sunspringmvc.servlet.SunDispatcherServlet</servlet-class><!--init—param设置spring配置文件的位置--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:sunspringmvc.xml</param-value></init-param><!--服务器启动时实例化servlet,将其放到容器中,并且调用init方法--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>SunDispatcherServlet</servlet-name><!--拦截所有请求--><url-pattern>/</url-pattern></servlet-mapping></web-app>
5.配置tomcat,完成测试
1.配置发布方式

image-20240227092658306

2.配置热加载

image-20240227092725037

3.修改SunDispatcherServlet.java

image-20240227092846449

4.完成测试

image-20240227092956993

image-20240227093009005

3.完成客户端/浏览器可以请求控制层

文件目录

image-20240227165505347

1.思路分析

image-20240227143426184

2.编写MonsterController.java
package com.Sun.controller;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*** @author 孙显圣* @version 1.0*/
public class MonsterController {public void listMonster(HttpServletRequest request, HttpServletResponse response) {//设置mine类型response.setContentType("text/html;charset=utf-8");try {PrintWriter writer = response.getWriter();writer.write("<h1>妖怪列表信息</h1>");} catch (IOException e) {throw new RuntimeException(e);}}
}
3.自定义注解
1.Controller.java
package com.Sun.sunspringmvc.annotation;import java.lang.annotation.*;/*** 用于标识一个Controller** @author 孙显圣* @version 1.0*/
@Target(ElementType.TYPE) //作用于类型
@Retention(RetentionPolicy.RUNTIME) //作用范围
@Documented
public @interface Controller {
}
2.RequestMapping.java
package com.Sun.sunspringmvc.annotation;import java.lang.annotation.*;/*** 用于指定映射路径** @author 孙显圣* @version 1.0*/
@Target(ElementType.METHOD) //作用于方法
@Retention(RetentionPolicy.RUNTIME) //作用范围
@Documented
public @interface RequestMapping {
}
4.自定义容器(1),在tomcat启动时读取配置文件,获取要扫描的包的工作路径
1.SunWebApplicationContext.java
package com.Sun.sunspringmvc.context;import com.Sun.sunspringmvc.xml.XmlParser;import java.net.URL;
import java.util.ArrayList;
import java.util.List;/*** @author 孙显圣* @version 1.0*/
public class SunWebApplicationContext {//存放所有要扫描的包下的class文件的全路径private List<String> classFullPathList = new ArrayList<String>();//初始化容器public void init() {//读取spring配置文件,获取要扫描的包的信息String basePage = XmlParser.getBasePage("sunspringmvc.xml");//完成对指定包的扫描scanPage(basePage);}//创建方法,完成对指定包的扫描,获取所有class文件的全路径public void scanPage(String packFullName) {//将包的全类名中的点替换为斜杠String packPath = packFullName.replaceAll("\\.", "/");//通过类加载器来获取这个包的工作路径,就是获取工作路径下的类路径下的文件路径URL resource = SunWebApplicationContext.class.getClassLoader().getResource(packPath);System.out.println(resource);}
}
2.修改SunDispatcherServlet.java
package com.Sun.sunspringmvc.servlet;import com.Sun.sunspringmvc.context.SunWebApplicationContext;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;/*** 充当中央控制器** @author 孙显圣* @version 1.0*/
public class SunDispatcherServlet extends HttpServlet {@Overridepublic void init(ServletConfig config) throws ServletException {//初始化容器SunWebApplicationContext sunWebApplicationContext = new SunWebApplicationContext();sunWebApplicationContext.init();}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doGet");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doPost");}
}
3.单元测试,启动tomcat

image-20240227133426603

5.自定义容器(2),在tomcat启动的时候完成对指定包的扫描
1.修改SunWebApplicationContext.java
package com.Sun.sunspringmvc.context;import com.Sun.sunspringmvc.xml.XmlParser;import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;/*** @author 孙显圣* @version 1.0*/
public class SunWebApplicationContext {//存放所有要扫描的包下的class文件的全路径private List<String> classFullPathList = new ArrayList<String>();//初始化容器public void init() {//读取spring配置文件,获取要扫描的包的信息String basePage = XmlParser.getBasePage("sunspringmvc.xml");//初始化容器//根据逗号进行分割,得到多个要扫描的包的全路径,遍历将里面的class文件全路径放到列表中String[] split = basePage.split(",");for (String packPath : split) {scanPage(packPath);}}//创建方法,完成对指定包的扫描,获取所有class文件的全路径public void scanPage(String packFullName) {//将包的全类名中的点替换为斜杠String packPath = packFullName.replaceAll("\\.", "/");//通过类加载器来获取这个包的工作路径,就是获取工作路径下的类路径下的文件路径URL url = SunWebApplicationContext.class.getClassLoader().getResource(packPath);//得到路径String file = url.getFile();//根据这个文件夹来创建一个file对象,从而遍历里面所有的class文件得到所有class文件的全路径File packDirectory = new File(file);if (packDirectory.isDirectory()) {//如果是文件夹则列出里面的所有文件对象File[] files = packDirectory.listFiles();//遍历这些文件对象,实际上就那个包下的所有class文件对象for (File classFile : files) {//如果这里的文件对象还是文件夹,则进行递归扫描if (classFile.isDirectory()) {scanPage(packFullName + "." + classFile.getName());} else {//如果这里的文件对象指的都是文件,则将其放到classFullPathList中//得到当前文件的全类名 = 包的全路径 + class文件的名字去掉.classString classFullPath = packFullName + "." + classFile.getName().replaceAll(".class", "");//放到列表中classFullPathList.add(classFullPath);}}}}}
2.debug测试

image-20240227143135578

6.将自定义容器(3),符合要求的类反射创建对象,放到单例池
1.修改SunWebApplicationContext.java增加方法,添加属性

image-20240227145800640

    //编写方法,将符合要求的类反射创建对象,并封装到单例池中public void executeInstance(){//遍历所有全类名for (String classPath : classFullPathList) {try {//反射Class<?> aClass = Class.forName(classPath);//判断是否有Controller注解if (aClass.isAnnotationPresent(Controller.class)) {//有注解,当他是单例的,反射创建bean对象,放到单例池中,默认首字母小写//获取类名首字母小写String name = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);//放到单例池中singleObjects.put(name, aClass.newInstance());}} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}
2.debug查看单例池

image-20240227145600884

7.完成url和控制器方法映射
1.创建映射bean,SunHandler.java
package com.Sun.sunspringmvc.handler;import java.lang.reflect.Method;/*** 用于存放有注解的类的映射信息** @author 孙显圣* @version 1.0*/
public class SunHandler {private String url; //映射的urlprivate Object controller; //controller对象private Method method; //方法对象,用于反射调用方法public SunHandler(String url, Object controller, Method method) {this.url = url;this.controller = controller;this.method = method;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public Object getController() {return controller;}public void setController(Object controller) {this.controller = controller;}public Method getMethod() {return method;}public void setMethod(Method method) {this.method = method;}@Overridepublic String toString() {return "SunHandler{" +"url='" + url + '\'' +", controller=" + controller +", method=" + method +'}';}
}
2.修改中央控制器SunWebApplicationContext.java添加方法和属性

image-20240227155457879

    //初始化映射对象列表,获取映射对象并且将其放到映射列表中private void initHandlerMapping() {//判断单例池是否为空if (sunWebApplicationContext.singleObjects.isEmpty()) {return;}//取出单例池里的所有对象for (Map.Entry<String, Object> entry : sunWebApplicationContext.singleObjects.entrySet()) {//反射Class<?> aClass = entry.getValue().getClass();//判断是否有colltroller注解if (aClass.isAnnotationPresent(Controller.class)) {//反射获取所有方法对象Method[] declaredMethods = aClass.getDeclaredMethods();//判断方法里是否有requestmapping注解for (Method declaredMethod : declaredMethods) {if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {//获取这个方法的注解信息String url = declaredMethod.getAnnotation(RequestMapping.class).value();//将信息封装到映射bean对象中SunHandler sunHandler = new SunHandler(url, entry.getValue(), declaredMethod);//添加到列表中handlers.add(sunHandler);}}}}}
3.debug查看映射对象列表

8.完成请求分发到目标方法
1.修改SunDispatcherServlet.java,添加两个方法并在dopost中请求分发
package com.Sun.sunspringmvc.servlet;import com.Sun.sunspringmvc.annotation.Controller;
import com.Sun.sunspringmvc.annotation.RequestMapping;
import com.Sun.sunspringmvc.context.SunWebApplicationContext;
import com.Sun.sunspringmvc.handler.SunHandler;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.ArrayList;
import java.util.List;
import java.util.Map;/*** 充当中央控制器** @author 孙显圣* @version 1.0*/
public class SunDispatcherServlet extends HttpServlet {//存放所有的映射关系private List<SunHandler> handlers = new ArrayList<SunHandler>();private SunWebApplicationContext sunWebApplicationContext = null;@Overridepublic void init(ServletConfig config) throws ServletException {//初始化容器sunWebApplicationContext = new SunWebApplicationContext();sunWebApplicationContext.init();//初始化映射列表initHandlerMapping();System.out.println("ss");}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//请求分发executeDispatch(req, resp);}//初始化映射对象列表,获取映射对象并且将其放到映射列表中private void initHandlerMapping() {//判断单例池是否为空if (sunWebApplicationContext.singleObjects.isEmpty()) {return;}//取出单例池里的所有对象for (Map.Entry<String, Object> entry : sunWebApplicationContext.singleObjects.entrySet()) {//反射Class<?> aClass = entry.getValue().getClass();//判断是否有colltroller注解if (aClass.isAnnotationPresent(Controller.class)) {//反射获取所有方法对象Method[] declaredMethods = aClass.getDeclaredMethods();//判断方法里是否有requestmapping注解for (Method declaredMethod : declaredMethods) {if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {//获取这个方法的注解信息String url = declaredMethod.getAnnotation(RequestMapping.class).value();//将信息封装到映射bean对象中SunHandler sunHandler = new SunHandler(url, entry.getValue(), declaredMethod);//添加到列表中handlers.add(sunHandler);}}}}}//根据请求对象得到映射对象private SunHandler getSunHandler(HttpServletRequest request) {//获取uri: /sun-springmvc/list/monsterString requestURI = request.getRequestURI();String contextPath = request.getServletContext().getContextPath();//遍历映射对象列表,查看列表中是否有这个urifor (SunHandler handler : handlers) {//这里拼接一个上下文路径if ((contextPath + "/" + handler.getUrl()).equals(requestURI)) {//返回这个映射对象return handler;}}return null;}//请求分发private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {//获取映射对象SunHandler sunHandler = getSunHandler(request);//映射对象不等于空则反射调用controller的方法if (sunHandler != null) {try {sunHandler.getMethod().invoke(sunHandler.getController(), request, response);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);}} else {//当映射对象是空的时候,返回404try {response.getWriter().write("<h1>404 not found!</h1>");} catch (IOException e) {throw new RuntimeException(e);}}}
}
2.单元测试

image-20240227164234686

image-20240227164244515

image-20240227164254432

4.当前阶段完成的功能

1.初始化阶段
  • tomcat服务器启动,自动装载中央控制器(servlet),调用init方法
  • 初始化spring容器
    • 创建spring容器实例,调用init方法
    • 读取spring配置文件,得到要扫描的包的工作路径
    • 扫描指定的包,获取所有class文件的全路径
    • 扫描所有class文件,将包含Controller注解的类反射创建对象放到单例池中(这里假设都是单例的)
  • 初始化映射对象列表
    • 扫描所有单例池中的对象
    • 反射获取这个对象对应类的所有方法,如果方法包含RequestMapping注解,则将这个对象,url,Method对象封装到映射对象中,并且添加到映射对象列表
2.完成请求分发
  • 根据请求对象得到映射对象
    • 获取请求的uri
    • 遍历对象映射列表查看是否有匹配的映射对象,如果有则返回映射对象
  • 请求分发
    • 首先根据请求对象得到映射对象
    • 如果得到了就反射调用方法
    • 没有得到则返回404

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/651682.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu查看opencveigen

ubuntu查看opencv&eigen&cmake版本的方法 eigen eigen版本号在/usr/include/eigen3/Eigen/src/Core/util/Macros.h文件中&#xff0c;下图代表版本3.3.7 opencv版本 pkg-config --modversion opencv4也可能最后的字符串是opencv2&#xff0c;opencv

采购数据分析驾驶舱分享,照着它抄作业

今天我们来看一张采购管理驾驶舱。这是一张充分运用了多种数据可视化图表、智能分析功能&#xff0c;从物料和供应商的角度全面分析采购情况的BI数据可视化报表&#xff0c;主要分为三个部分&#xff0c;接下来就分部分来了解一下。 第一部分&#xff1a;关键指标计算及颜色预…

Xcode for Mac:强大易用的集成开发环境

Xcode for Mac是一款专为苹果开发者打造的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它集成了代码编辑器、编译器、调试器等一系列开发工具&#xff0c;让开发者能够在同一界面内完成应用的开发、测试和调试工作。 Xcode for Mac v15.2正式版下载 Xcode支持多种编程…

大模型咨询培训老师叶梓:利用知识图谱和Llama-Index增强大模型应用

大模型&#xff08;LLMs&#xff09;在自然语言处理领域取得了显著成就&#xff0c;但它们有时会产生不准确或不一致的信息&#xff0c;这种现象被称为“幻觉”。为了提高LLMs的准确性和可靠性&#xff0c;可以借助外部知识源&#xff0c;如知识图谱。那么我们如何通过Llama-In…

Spark AQE 导致的 Driver OOM问题

背景 最近在做Spark 3.1 升级 Spark 3.5的过程中&#xff0c;遇到了一批SQL在运行的过程中 Driver OOM的情况&#xff0c;排查到是AQE开启导致的问题&#xff0c;再次分析记录一下&#xff0c;顺便了解一下Spark中指标的事件处理情况 结论 SQLAppStatusListener 类在内存中存…

【Jenkins】持续集成与交付 (一):深入理解什么是持续集成?

【Jenkins】持续集成与交付 (一):深入理解什么是持续集成? 1、软件开发生命周期与持续集成2、 持续集成的流程3、持续集成的好处4、Jenkins的应用实践5、结语💖The Begin💖点点关注,收藏不迷路💖 1、软件开发生命周期与持续集成 软件开发生命周期(SDLC)是指软件从…

【leetcode面试经典150题】75. 二叉树展开为链表(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

国产麒麟系统下打包electron+vue项目(AppImage、deb)

需要用到的一些依赖包、安装包以及更详细的打包方法word以及麒麟官网给出的文档都已放网盘&#xff0c;链接在文章最后&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&a…

OpenHarmony ArkUI 实战开发—ETS 装饰器解读

前言 最近利用空闲时间在学习华为方舟开发框架&#xff08;简称&#xff1a;ArkUI&#xff09;的ets开发&#xff0c;发现在ets语言中装饰器的有着非常重要的作用&#xff0c;在写每一个自定义组件时都需要用到它&#xff0c;看到装饰器这个字眼&#xff0c;想起之前学过的设计…

数据分析:转录组分析-kallisto或salmon的RNA-seq流程

RNA-seq pipeline through kallisto or salmon kallisto 和salmon相比含有hisat2和STAR等软的RNA-seq流程而言&#xff0c;速度更快&#xff0c;这是因为该软件基于转录组序列reference&#xff08;也即是cDNA序列&#xff09;并且基于k mer比对原理。通常如果想研究RNA-seq过…

Feign负载均衡

Feign负载均衡 概念总结 工程构建Feign通过接口的方法调用Rest服务&#xff08;之前是Ribbon——RestTemplate&#xff09; 概念 官网解释: http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign Feign是一个声明式WebService客户端。使用Feign能让…

RabbitMQ发布确认和消息回退(6)

概念 发布确认原理 生产者将信道设置成 confirm 模式&#xff0c;一旦信道进入 confirm 模式&#xff0c;所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始)&#xff0c;一旦消息被投递到所有匹配的队列之后&#xff0c;broker就会发送一个确认给生产者(包含消…