码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Java-Web手写MVC框架


    文件结构

    1、编写Web.xml

    将所有 .do 后缀的请求全都映射到DispatchServlet 中,在DispatchServlet 跳转的时候将配置文件名传递过去,配置文件的编写在后面, 因为测试类还没写。

    1. "1.0" encoding="UTF-8"?>
    2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    5. version="4.0">
    6. <servlet>
    7. <servlet-name>DispatchServletservlet-name>
    8. <servlet-class>com.ae.DispatchServletservlet-class>
    9. <init-param>
    10. <param-name>contentConfigLocationparam-name>
    11. <param-value>application.propertiesparam-value>
    12. init-param>
    13. <load-on-startup>0load-on-startup>
    14. servlet>
    15. <servlet-mapping>
    16. <servlet-name>DispatchServletservlet-name>
    17. <url-pattern>*.dourl-pattern>
    18. servlet-mapping>
    19. web-app>

    2、自定义两个注解

    ResponseBody 以及 ResponseView 内容如下,用于区分不同类型的请求。

    3、编写ResponseType枚举类

    主要作用就是给你自定义的类定义两个不同的类型,之后有其他类型可以直接在这里面进行添加。

    4、编写DispatchServlet

    该Servlet在初始化的时候会将配置文件的Input流获取,然后传递给HandlerMapping进行加载,结果就行,将所有请求与对应的类和方法建立映射关系。

    Service方法进行的时候,应为已经在初始化的时候将所有的url与方法建立了映射关系,所以可以直接可以获取对应的方法,然后执行对应的方法,并且获取返回的结果。

    1. public class DispatchServlet extends HttpServlet {
    2. @Override
    3. public void init(ServletConfig config) throws ServletException {
    4. // 首先获取对应的配置文件的名字
    5. String path = config.getInitParameter("contentConfigLocation");
    6. // 获取资源配置文件的读取流InputStream
    7. InputStream is = DispatchServlet.class.getClassLoader().getResourceAsStream(path);
    8. // 将读取流InputStream传递给HandlerMapping,将所有键值进行加载
    9. HandlerMapping.load(is);
    10. }
    11. @Override
    12. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
    13. //1.获取用户请求的uri
    14. String uri = req.getRequestURI();
    15. // 根据请求的uri获取对应的映射内容
    16. HandlerMapping.MVCMapping mapping = HandlerMapping.get(uri);
    17. // 为空则不存在该请求
    18. if(mapping == null) {
    19. resp.sendError(404, "MVC映射地址不存在" + uri);
    20. return ;
    21. }
    22. // 获取请求对应的类对象
    23. Object obj = mapping.getObj();
    24. // 获取请求对应的方法
    25. Method method = mapping.getMethod();
    26. // 用于存取请求结果
    27. Object result = null;
    28. try {
    29. // 执行对应的方法返回结果
    30. result = method.invoke(obj, req, resp);
    31. } catch (IllegalAccessException e) {
    32. throw new RuntimeException(e);
    33. } catch (InvocationTargetException e) {
    34. throw new RuntimeException(e);
    35. }
    36. switch(mapping.getType()){ // 根据对应的内容进行不同的操作
    37. case TEXT: // 返回的文本则直接写入
    38. resp.getWriter().write((String)result);
    39. break;
    40. case VIEW: // 返回的页面文件名则进行跳转
    41. resp.sendRedirect((String) result);
    42. break;
    43. }
    44. }
    45. }

    5、HandlerMapping的编写

    作用是将配置文件进行加载读取,并且将请求与对应的类的方法建立映射关系,并且存储。

    1. public class HandlerMapping {
    2. // 记录每一个用过的注解值,防止请求地址使用重复
    3. private static Map data = new HashMap();
    4. public static MVCMapping get(String uri) {
    5. return data.get(uri);
    6. }
    7. public static void load(InputStream is) {
    8. Properties properties = new Properties();
    9. try {
    10. // 加载配置文件中的内容
    11. properties.load(is);
    12. // 获取配置文件中描述的一个个的类
    13. Collection values = properties.values();
    14. for(Object cla : values) {
    15. // 获取配置文件中第一个类文件名
    16. String className = (String)cla;
    17. try {
    18. // 获取对应的类文件
    19. Class c = Class.forName(className);
    20. // 创建这个类的对象
    21. Object o = c.getConstructor().newInstance();
    22. // 获取这个类的所有方法
    23. Method[] ms = c.getMethods();
    24. // 枚举类中每个方法
    25. for(Method m : ms) {
    26. // 获取当前方法中所有的注解
    27. Annotation[] an = m.getAnnotations();
    28. if(an != null) {
    29. // 枚举当前方法中所有的注解
    30. for(Annotation annotation : an) {
    31. if(annotation instanceof ResponseBody) {
    32. // 说明此方法用于返回字符串给客户端
    33. MVCMapping mapping = new MVCMapping(o, m, ResponseType.TEXT);
    34. Object object = data.put(((ResponseBody) annotation).value(), mapping);
    35. if(object != null) {
    36. // 存在了重复的请求地址
    37. throw new RuntimeException("请求地址重复:" + ((ResponseBody) annotation).value());
    38. }
    39. } else if(annotation instanceof ResponseView) {
    40. // 说明此方法用于返回界面给客户端
    41. MVCMapping mapping = new MVCMapping(o, m, ResponseType.VIEW);
    42. Object object = data.put(((ResponseView) annotation).value(), mapping);
    43. if(object != null) {
    44. // 存在了重复的请求地址
    45. throw new RuntimeException("请求地址重复:" + ((ResponseView) annotation).value());
    46. }
    47. }
    48. }
    49. }
    50. }
    51. } catch (Exception e) {
    52. throw new RuntimeException(e);
    53. }
    54. }
    55. } catch (IOException e) {
    56. throw new RuntimeException(e);
    57. }
    58. }
    59. /**
    60. * 映射对象,每一个对象都封装一个对象,用于处理请求
    61. */
    62. public static class MVCMapping {
    63. private Object obj;
    64. private Method method;
    65. private ResponseType type;
    66. public Method getMethod() {
    67. return method;
    68. }
    69. public void setMethod(Method method) {
    70. this.method = method;
    71. }
    72. public ResponseType getType() {
    73. return type;
    74. }
    75. public void setType(ResponseType type) {
    76. this.type = type;
    77. }
    78. public MVCMapping() {
    79. }
    80. public MVCMapping(Object obj, Method method, ResponseType type) {
    81. this.obj = obj;
    82. this.method = method;
    83. this.type = type;
    84. }
    85. public Object getObj() {
    86. return obj;
    87. }
    88. public void setObj(Object obj) {
    89. this.obj = obj;
    90. }
    91. }
    92. }
    93. 6、编写测试类以及添加配置文件

      以上已经写好了,测试一下就行了。建立两个测试类。Test1Controller, Test2Controller

      然后将两个类的路径添加到配置文件中。这里只需要将所有请求类的路径加进行就行,等号左边的名字随便填就行。​​

      ​​​注意资源配置文件跟数据库druid.properties文件问题一样,建在resources文件下,否则无法加载,老版本的项目需要建立在源文件目录下即可。

      Test1Controller

      success.jsp随便写几个内容就行,进行区分。注意建立在Webapp目录下

      1. public class Test1Controller {
      2. @ResponseBody("/login.do") // 测试返回文本内容的请求
      3. public String login(HttpServletRequest request, HttpServletResponse response) {
      4. return "login success";
      5. }
      6. @ResponseView("/register.do") // 测试返回文件名以及跳转页面的请求
      7. public String register(HttpServletRequest request, HttpServletResponse response) {
      8. return "success.jsp";
      9. }
      10. }

      Test2Controller

      xxx.html也随便谢谢进行区分即可,注意建立在Webapp目录下

      1. public class Test2Controller {
      2. @ResponseBody("/test1.do") // 测试返回文本的请求
      3. public String test1(HttpServletRequest request, HttpServletResponse response) {
      4. return "ha ha ha";
      5. }
      6. @ResponseView("/test2") // 测试不加后缀是否能使用
      7. public String test2(HttpServletRequest request, HttpServletResponse response) {
      8. return "hei hei hei";
      9. }
      10. @ResponseView("/test3.do") // 测试返回页面文件名以及跳转的请求
      11. public String test3(HttpServletRequest request, HttpServletResponse response) {
      12. return "xxx.html";
      13. }
      14. }

    94. 相关阅读:
      『可道云』内网穿透牛刀小试,会敲键盘就能搭建的私有云网盘
      Thinkphp漏洞远程代码执行漏洞事件分析报告
      《代码大全2》第4章 关键的“构建“决策
      田忌赛马Java
      测试老鸟总结,性能测试三大核心指标详解,并发/TPS/响应时间...
      QPushButton 样式使用示例(以及按钮setmenu添加下拉菜单的方法)
      导师详解:多比特信号的CDC处理方式之异步FIFO
      C/C++语言100题练习计划 76——汉诺塔问题(递归实现)
      如何从 Android 中恢复误删除的照片
      UTF-8字符集成为Java 18默认字符集?发布周期将至,Java 18现身
    95. 原文地址:https://blog.csdn.net/qq_64468032/article/details/136635275
      • 最新文章
      • 攻防演习之三天拿下官网站群
        数据安全治理学习——前期安全规划和安全管理体系建设
        企业安全 | 企业内一次钓鱼演练准备过程
        内网渗透测试 | Kerberos协议及其部分攻击手法
        0day的产生 | 不懂代码的"代码审计"
        安装scrcpy-client模块av模块异常,环境问题解决方案
        leetcode hot100【LeetCode 279. 完全平方数】java实现
        OpenWrt下安装Mosquitto
        AnatoMask论文汇总
        【AI日记】24.11.01 LangChain、openai api和github copilot
      • 热门文章
      • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
        奉劝各位学弟学妹们,该打造你的技术影响力了!
        五年了,我在 CSDN 的两个一百万。
        Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
        面试官都震惊,你这网络基础可以啊!
        你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
        心情不好的时候,用 Python 画棵樱花树送给自己吧
        通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
        13 万字 C 语言从入门到精通保姆级教程2021 年版
        10行代码集2000张美女图,Python爬虫120例,再上征途
      Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
      正则表达式工具 cron表达式工具 密码生成工具

      京公网安备 11010502049817号