当前位置: 首页 > news >正文

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

文章目录

    • 1.创建Controller和自定义注解
        • 1.目录
        • 2.MonsterController.java 怪物控制器
        • 3.Controller.java 自定义Controller注解
        • 4.RequestMapping.java 自定义RequestMapping注解
    • 2.dom4j解析sunspringmvc.xml
        • 1.XMLParser.java
        • 2.sunspringmvc.xml
        • 3.XMLParserTest.java
        • 4.结果
    • 3.开发自己的Spring容器(简易版,所有实例均为单例)
        • 1.目录
        • 2.创建几个空的Controler和Service用于测试扫描
        • 3.XMLParser.java 升级,解析spring配置文件,扫描所有component-scan元素,获取所有base-package属性值
        • 4.sunspringmvc.xml 指定要扫描的基本包以及子包的类
        • 5.SunWebApplicationContext.java 扫描所有包以及子包,并将有Controller注解的实例化到容器中
        • 6.SunDispatcherServlet.java 中央控制器重写init方法,初始化容器
        • 7.测试
    • 4.初始化映射器
        • 1.目录
        • 2.修改一下controller和service的包路径
        • 3.在TestConroller.java中加@Controller注解,测试没有方法的控制器
        • 4.HandlerMapping.java 映射器实体类
        • 5.SunHandler.java 扫描IOC容器,初始化映射器
        • 6.SunDispatcherServlet.java 调用SunHandler初始化映射器
        • 7.测试
    • 5.完成请求分发到目标方法
        • 1.目录
        • 2.SunDispatcherServlet.java
          • 1.获取请求路径,匹配映射器实体
          • 2.请求分发
          • 3.重写doGet和doPost方法,调用executeDispatch方法,统一处理请求
          • 4.完整代码
        • 3.测试

1.创建Controller和自定义注解

1.目录

CleanShot 2024-08-06 at 16.14.18@2x

2.MonsterController.java 怪物控制器
package com.sunxiansheng.springmvc.controller;import com.sunxiansheng.springmvc.annotation.Controller;
import com.sunxiansheng.springmvc.annotation.RequestMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*** Description: 怪物控制器* @Author sun* @Create 2024/8/6 16:04* @Version 1.0*/
@Controller
public class MonsterController {// 日志private static final Logger log = LoggerFactory.getLogger(MonsterController.class);/*** 查询怪物列表* @param request* @param response*/@RequestMapping(url = "/list/monster")public void listMonster(HttpServletRequest request, HttpServletResponse response) {// 设置响应类型response.setContentType("text/html;charset=utf-8");try {// 输出怪物列表PrintWriter writer = response.getWriter();writer.write("<h1>monster1, monster2, monster3</h1>");log.info("MonsterController listMonster: monster1, monster2, monster3");} catch (IOException e) {throw new RuntimeException(e);}}}
3.Controller.java 自定义Controller注解
package com.sunxiansheng.springmvc.annotation;import java.lang.annotation.*;/*** Description: 自定义Controller注解* @Author sun* @Create 2024/8/6 16:09* @Version 1.0*/
@Retention(value = RetentionPolicy.RUNTIME) // 运行时生效
@Target(value = ElementType.TYPE) // 作用在类上
@Documented // 生成文档
public @interface Controller {String name() default "";}
4.RequestMapping.java 自定义RequestMapping注解
package com.sunxiansheng.springmvc.annotation;import java.lang.annotation.*;/*** Description: 自定义RequestMapping注解* @Author sun* @Create 2024/8/6 16:12* @Version 1.0*/
@Retention(value = RetentionPolicy.RUNTIME) // 运行时生效
@Target(value = {ElementType.TYPE, ElementType.METHOD}) // 作用在类型和方法上
@Documented // 生成文档
public @interface RequestMapping {String url() default "";}

2.dom4j解析sunspringmvc.xml

1.XMLParser.java
package com.sunxiansheng.springmvc.xml;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;/*** Description: 解析spring配置文件* @Author sun* @Create 2024/8/6 16:22* @Version 1.0*/
public class XMLParser {public static String getBasePackage(String xmlPath) {SAXReader saxReader = new SAXReader();// 读取xml文件:获取类路径下的资源流InputStream resourceAsStream = XMLParser.class.getClassLoader().getResourceAsStream(xmlPath);try {// 获取文档Document document = saxReader.read(resourceAsStream);// 获取根元素Element rootElement = document.getRootElement();// 获取根元素下的component-scan元素Element componentScanElement = rootElement.element("component-scan");// 返回component-scan元素的base-package属性值return componentScanElement.attributeValue("base-package");} catch (Exception e) {throw new RuntimeException(e);}}}
2.sunspringmvc.xml
<?xml version="1.0" encoding="utf-8" ?>
<beans><!-- 指定要扫描的基本包以及子包的类 --><component-scan base-package="com.sunxiansheng.springmvc.controller" />
</beans>
3.XMLParserTest.java
package com.sunxiansheng.springmvc.xml;import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** Description:* @Author sun* @Create 2024/8/6 16:41* @Version 1.0*/
public class XMLParserTest {private static final Logger log = LoggerFactory.getLogger(XMLParserTest.class);@Testpublic void readXML() {String basePackage = XMLParser.getBasePackage("sunspringmvc.xml");log.info("basePackage: {}", basePackage);}
}
4.结果

CleanShot 2024-08-06 at 16.46.20@2x

3.开发自己的Spring容器(简易版,所有实例均为单例)

1.目录

CleanShot 2024-08-07 at 13.58.29@2x

2.创建几个空的Controler和Service用于测试扫描

CleanShot 2024-08-07 at 13.58.58@2x

3.XMLParser.java 升级,解析spring配置文件,扫描所有component-scan元素,获取所有base-package属性值
package com.sunxiansheng.springmvc.xml;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;/*** Description: 解析spring配置文件,扫描所有component-scan元素,获取所有base-package属性值* @Author sun* @Create 2024/8/6 16:22* @Version 1.0*/
public class XMLParser {public static List<String> getBasePackages(String xmlPath) {List<String> basePackages = new ArrayList<>();SAXReader saxReader = new SAXReader();// 获取类路径下的资源流InputStream resourceAsStream = XMLParser.class.getClassLoader().getResourceAsStream(xmlPath);if (resourceAsStream == null) {throw new RuntimeException("Cannot find the specified XML file: " + xmlPath);}try {// 获取文档Document document = saxReader.read(resourceAsStream);// 获取根元素Element rootElement = document.getRootElement();// 获取所有的 component-scan 元素List<Element> componentScanElements = rootElement.elements("component-scan");// 遍历所有 component-scan 元素,获取 base-package 属性值for (Element element : componentScanElements) {String basePackage = element.attributeValue("base-package");if (basePackage != null && !basePackage.trim().isEmpty()) {basePackages.add(basePackage);}}} catch (Exception e) {throw new RuntimeException("Failed to parse XML file: " + xmlPath, e);}return basePackages;}
}
4.sunspringmvc.xml 指定要扫描的基本包以及子包的类
<?xml version="1.0" encoding="utf-8" ?>
<beans><!-- 指定要扫描的基本包以及子包的类 --><component-scan base-package="com.sunxiansheng.springmvc.controller" /><component-scan base-package="com.sunxiansheng.springmvc.service" />
</beans>
5.SunWebApplicationContext.java 扫描所有包以及子包,并将有Controller注解的实例化到容器中
package com.sunxiansheng.springmvc.context;import com.sunxiansheng.springmvc.annotation.Controller;
import com.sunxiansheng.springmvc.xml.XMLParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;/*** Description: 自己的Spring容器** @Author sun* @Create 2024/8/7 12:35* @Version 1.0*/
public class SunWebApplicationContext {// 日志private static final Logger log = LoggerFactory.getLogger(SunWebApplicationContext.class);/*** 扫描的包以及子包的类的全路径*/private List<String> classFullPathList = new ArrayList<>();/*** IOC容器(全是单例的,这里不考虑多例的了)*/public ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();/*** Tomcat启动时初始化Spring容器*/public void init() {// 1.解析spring配置文件,获取要扫描的包List<String> basePackages = XMLParser.getBasePackages("sunspringmvc.xml");log.info("Tomcat启动</load-on-startup>,init方法被调用,解析spring配置文件,获取要扫描的basePackages: {}" ,basePackages);// 2.扫描指定的包if (!basePackages.isEmpty()) {for (String basePackage : basePackages) {scanPackage(basePackage);}}log.info("扫描的包以及子包的类的全路径列表:{}", classFullPathList);// 3.将有特定注解的类实例化,放入IOC容器executeInstance();log.info("IOC容器:{}", ioc);}/*** 扫描指定的包* @param path*/public void scanPackage(String path) {// 使用类加载器获取指定路径下的资源,原来的格式为:com.sunxiansheng.springmvc.controller需要替换为com/sunxiansheng/springmvc/controllerString scanPath = path.replace(".", "/");// 获取类加载器ClassLoader classLoader = SunWebApplicationContext.class.getClassLoader();// 获取指定路径下的资源URL resource = classLoader.getResource(scanPath);// 获取这个文件夹的File对象File dir = new File(resource.getFile());// 递归获取这个文件夹下的所有文件for (File file : dir.listFiles()) {if (file.isDirectory()) {// 如果是文件夹,递归调用scanPackage(path + "." + file.getName());} else {// 如果是class文件,获取全路径:com.sunxiansheng.springmvc.controller.MonsterController(注意这里是没有.class后缀的)if (file.getName().endsWith(".class")) {String classFullPath = path + "." + file.getName().replace(".class", "");classFullPathList.add(classFullPath);}}}}/*** 反射所有的类,将有特定注解的类实例化,放入IOC容器*/public void executeInstance() {if (classFullPathList.isEmpty()) {return;}// 遍历所有的类classFullPathList.forEach(classFullPath -> {try {// 获取类对象Class<?> clazz = Class.forName(classFullPath);// 判断是否有特定注解if (clazz.isAnnotationPresent(Controller.class)) {// 实例化Object instance = clazz.getDeclaredConstructor().newInstance();// 将实例化的对象放入IOC容器// 获取注解Controller controller = clazz.getAnnotation(Controller.class);// 判断注解的name属性是否为空,如果为空,就获取类名,并且首字母小写if ("".equals(controller.name())) {ioc.put(toLowerFirstWord(clazz.getSimpleName()), instance);} else {ioc.put(controller.name(), instance);}} else {//  其他注解的处理。。。。。。}} catch (Exception e) {e.printStackTrace();}});}/*** 辅助方法,如果传进来的第一个名字是空,就获取第二个Class对象参数的类名,并且首字母小写*/private String toLowerFirstWord(String simpleName) {char[] chars = simpleName.toCharArray();// 之所以加32,是因为大写字母的ASCII码比小写字母的大32chars[0] += 32;return String.valueOf(chars);}}
6.SunDispatcherServlet.java 中央控制器重写init方法,初始化容器
package com.sunxiansheng.springmvc.servlet;import com.sunxiansheng.springmvc.context.SunWebApplicationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** Description: 中央控制器(本质是一个Servlet)* @Author sun* @Create 2024/8/6 15:33* @Version 1.0*/
public class SunDispatcherServlet extends HttpServlet {// 日志private static final Logger log = LoggerFactory.getLogger(SunDispatcherServlet.class);/*** 初始化方法(在web.xml中配置过<load-on-startup> ,只要Tomcat启动,就会加载,如果没有配置则会在第一次请求到中央控制器时启动)* @throws ServletException*/@Overridepublic void init() throws ServletException {// 初始化Spring容器SunWebApplicationContext sunWebApplicationContext = new SunWebApplicationContext();sunWebApplicationContext.init();}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {log.info("SunDispatcherServlet doGet");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {log.info("SunDispatcherServlet doPost");}}
7.测试

CleanShot 2024-08-07 at 14.03.13@2x

4.初始化映射器

1.目录

CleanShot 2024-08-07 at 14.59.00@2x

2.修改一下controller和service的包路径

CleanShot 2024-08-07 at 14.59.58@2x

3.在TestConroller.java中加@Controller注解,测试没有方法的控制器

CleanShot 2024-08-07 at 15.00.23@2x

4.HandlerMapping.java 映射器实体类
package com.sunxiansheng.springmvc.handler;import java.lang.reflect.Method;/*** Description: 映射器实体类** @Author sun* @Create 2024/8/7 14:10* @Version 1.0*/
public class HandlerMapping {/*** 请求路径*/private String url;/*** 控制器*/private Object controller;/*** 方法*/private 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 "HandlerMapping{" +"url='" + url + '\'' +", controller=" + controller +", method=" + method +'}';}
}
5.SunHandler.java 扫描IOC容器,初始化映射器
package com.sunxiansheng.springmvc.handler;import com.sunxiansheng.springmvc.annotation.Controller;
import com.sunxiansheng.springmvc.annotation.RequestMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;/*** Description: 映射器** @Author sun* @Create 2024/8/7 14:20* @Version 1.0*/
public class SunHandler {/*** 日志*/private static final Logger log = LoggerFactory.getLogger(SunHandler.class);/*** 映射器列表*/private static final List<HandlerMapping> HANDLER_MAPPING_LIST = new ArrayList<>();/*** 扫描IOC容器,初始化映射器*/public void initHandlerMapping(ConcurrentHashMap<String, Object> ioc) {if (ioc.isEmpty()) {log.error("IOC容器为空,映射器初始化失败");return;}// 遍历IOC容器ioc.forEach((beanName, beanObject) -> {// 获取Class对象Class<?> clazz = beanObject.getClass();// 判断是否有Controller注解if (clazz.isAnnotationPresent(Controller.class)) {// 扫描所有的方法Method[] declaredMethods = clazz.getDeclaredMethods();if (declaredMethods.length == 0) {log.error("类{}没有方法", clazz.getName());} else {// 类有方法,则遍历方法扫描RequestMapping注解for (Method method : declaredMethods) {if (method.isAnnotationPresent(RequestMapping.class)) {// 获取RequestMapping注解RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);// 获取注解的值String url = requestMapping.url();// 封装映射器HandlerMapping handlerMapping = new HandlerMapping();handlerMapping.setUrl(url);handlerMapping.setController(beanObject);handlerMapping.setMethod(method);HANDLER_MAPPING_LIST.add(handlerMapping);}}}}});// 日志log.info("映射器初始化完成:{}", HANDLER_MAPPING_LIST);}
}
6.SunDispatcherServlet.java 调用SunHandler初始化映射器

CleanShot 2024-08-07 at 15.02.40@2x

7.测试

CleanShot 2024-08-07 at 15.03.33@2x

5.完成请求分发到目标方法

1.目录

CleanShot 2024-08-07 at 15.30.04@2x

2.SunDispatcherServlet.java
1.获取请求路径,匹配映射器实体
    /*** 获取请求路径,匹配映射器实体** @param request* @return*/private HandlerMapping getHandlerMapping(HttpServletRequest request) {// 获取请求路径String requestURI = request.getRequestURI();// 遍历映射器列表for (HandlerMapping handlerMapping : SunHandler.HANDLER_MAPPING_LIST) {if (requestURI.equals(handlerMapping.getUrl())) {// 匹配到映射器log.info("匹配到映射器:{}", handlerMapping);return handlerMapping;}}// 如果没有找到映射器,就返回nullreturn null;}
2.请求分发
    /*** 请求分发** @param request* @param response*/private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {// 获取请求路径,匹配映射器实体HandlerMapping handlerMapping = getHandlerMapping(request);// 如果没有找到映射器,就返回404if (handlerMapping == null) {try {response.getWriter().write("404 NOT FOUND");} catch (Exception e) {throw new RuntimeException(e);}return;}// 匹配成功,调用目标方法Object controller = handlerMapping.getController();Method method = handlerMapping.getMethod();try {method.invoke(controller, request, response);} catch (Exception e) {throw new RuntimeException(e);}}
3.重写doGet和doPost方法,调用executeDispatch方法,统一处理请求

CleanShot 2024-08-07 at 15.31.40@2x

4.完整代码
package com.sunxiansheng.springmvc.servlet;import com.sunxiansheng.springmvc.context.SunWebApplicationContext;
import com.sunxiansheng.springmvc.handler.HandlerMapping;
import com.sunxiansheng.springmvc.handler.SunHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;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.Method;/*** Description: 中央控制器(本质是一个Servlet)** @Author sun* @Create 2024/8/6 15:33* @Version 1.0*/
public class SunDispatcherServlet extends HttpServlet {/*** 日志*/private static final Logger log = LoggerFactory.getLogger(SunDispatcherServlet.class);/*** IOC容器*/private SunWebApplicationContext sunWebApplicationContext = null;/*** 初始化方法(在web.xml中配置过<load-on-startup> ,只要Tomcat启动,就会加载,如果没有配置则会在第一次请求到中央控制器时启动)** @throws ServletException*/@Overridepublic void init() throws ServletException {// 1.初始化Spring容器sunWebApplicationContext = new SunWebApplicationContext();sunWebApplicationContext.init();// 2.初始化映射器SunHandler sunHandler = new SunHandler();sunHandler.initHandlerMapping(sunWebApplicationContext.ioc);}/*** 请求分发** @param request* @param response*/private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {// 获取请求路径,匹配映射器实体HandlerMapping handlerMapping = getHandlerMapping(request);// 如果没有找到映射器,就返回404if (handlerMapping == null) {try {response.getWriter().write("404 NOT FOUND");} catch (Exception e) {throw new RuntimeException(e);}return;}// 匹配成功,调用目标方法Object controller = handlerMapping.getController();Method method = handlerMapping.getMethod();try {method.invoke(controller, request, response);} catch (Exception e) {throw new RuntimeException(e);}}/*** 获取请求路径,匹配映射器实体** @param request* @return*/private HandlerMapping getHandlerMapping(HttpServletRequest request) {// 获取请求路径String requestURI = request.getRequestURI();// 遍历映射器列表for (HandlerMapping handlerMapping : SunHandler.HANDLER_MAPPING_LIST) {if (requestURI.equals(handlerMapping.getUrl())) {// 匹配到映射器log.info("匹配到映射器:{}", handlerMapping);return handlerMapping;}}// 如果没有找到映射器,就返回nullreturn null;}// 重写doGet和doPost方法,调用executeDispatch方法,统一处理请求@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {executeDispatch(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {executeDispatch(req, resp);}}
3.测试

CleanShot 2024-08-07 at 15.33.43@2x

CleanShot 2024-08-07 at 15.33.49@2x


http://www.mrgr.cn/news/10350.html

相关文章:

  • 我的sql我做主!Mysql 的集群架构详解之组从复制、半同步模式、MGR、Mysql路由和MHA管理集群组
  • 8.26算法训练
  • PHP酒店宾馆民宿预订系统小程序源码
  • 力扣2025.分割数组的最多方案数
  • linux内核链表
  • Three 物体(四)
  • Python编码系列—Python中的HTTPS与加密技术:构建安全的网络通信
  • 使用HTML实现贪吃蛇游戏
  • 为什么制造企业智能化升级需要MES管理系统
  • 【Material-UI】Radio Group中的独立单选按钮详解
  • JavaScript 手写仿instanceof
  • Blazor开发框架Known-V2.0.9
  • 如何用Python Django构建二手房房价预测与知识图谱系统?
  • 磁场强度H和磁感应强度B,磁化强度M和磁极化强度J
  • HarmonyOS--认证服务-操作步骤
  • 2024.8.22(Docker)
  • Ubuntu 20.04安装中文输入法
  • 探索Unity3D URP后处理在UI控件Image上的应用
  • el-input按回车 界面自动刷新
  • 【ORACLE】Oracle 表空间查询