手写springMVC

╰+攻爆jí腚メ 2022-11-16 00:59 256阅读 0赞

参考视频:https://www.bilibili.com/video/BV18K4y1v7f7

代码可以参考本博客,具体编写还是参考视频

本文最后有本人手写springMVC后的代码下载

编写springmvc的目的:

  • 对springMVC原理有进一步的了解
  • 深化java se的高级知识点(注解,反射,xml解析)
  • 通过自定义框架来深化java内功,提高面试通过率
  • 养成规范化,标准化的代码编写习惯和良好的技术文档习惯

一,整备工作

1,Tomcat启动时加载SpringMVC开发的流程是什么?

启动阶段:

  • Tomcatxx加载xxx.war (springmvc: com.xx.xxx.OrderService.class)
  • 创建容器:创建Map iocMap = new HashMap();
  • ScanbasePackage::扫描war下的@Controller,@Service注解的类
  • 实例化:将扫描到的类通过反射实例化,并存入到iocMap容器中
  • 依赖注入:将存在依赖的bean进入注入
  • UrlMapping :http请求路径与Method建立映射关系

运行阶段:

  • 发送http请求,调用servlet的doGet/doPost方法
  • 找到从UrlMapping中找到对应的Method方法对象;
  • 找到Method方法对象后,直接调用
  • 响应返回结果

2,mvc模式回顾:

MVC( 模型-视图-控制器)设计创建 Web 应用程序的模式::

  • Model(模型)是应用程序中用于处理应用程序数据逻辑的部分(dao和service层),通常模型对象负责在数据库中存取数据。
  • View(视图)是应用程序中处理数据显示的部分(jsp和HTML页面),通常视图是依据模型数据创建的。
  • Controller(控制器)是应用程序中处理用户交互的部分(controller层),通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

优点:

  • MVC 分层有助于管理复杂的应用程序,因为您可以在一个时间内专门关注一个方面。例如,您可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。
  • MVC 分层同时也简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。

3,SpringMVC执行的流程

  1. 一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理

  2. DispatcherServlet 接收到请求后, 将根据 请求信息 交给 处理器映射器 (HandlerMapping)

  3. HandlerMapping 根据用户的url请求 查找匹配该url的 Handler,并返回一个执行链

  4. DispatcherServlet 再请求 处理器适配器(HandlerAdapter) 调用相应的 Handler(controller) 进行处理并返回 ModelAndView 给 DispatcherServlet

  5. DispatcherServlet 将 ModelAndView 请求 ViewReslover(视图解析器)解析,返回具体 View

  6. DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)

  7. DispatcherServlet 将页面响应给用户

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDg0MTMxMg_size_16_color_FFFFFF_t_70

二,开始编写

1,创建一个maven工程。导入依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>SpringMVC</groupId>
  6. <artifactId>SpringMVC</artifactId>
  7. <version>1.0-SNAPSHOT</version>
  8. <packaging>war</packaging>
  9. <name>SpringMVC Maven Webapp</name>
  10. <!-- FIXME change it to the project's website -->
  11. <url>http://www.example.com</url>
  12. <properties>
  13. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  14. <maven.compiler.source>10.0.1</maven.compiler.source>
  15. <maven.compiler.target>10.0.1</maven.compiler.target>
  16. </properties>
  17. <dependencies>
  18. <dependency>
  19. <groupId>junit</groupId>
  20. <artifactId>junit</artifactId>
  21. <version>4.12</version>
  22. <scope>test</scope>
  23. </dependency>
  24. <!--springMvc是对servlet的进一步封装-->
  25. <dependency>
  26. <groupId>javax.servlet</groupId>
  27. <artifactId>servlet-api</artifactId>
  28. <version>2.5</version>
  29. <scope>provided</scope>
  30. </dependency>
  31. <!--需要对配置文件进行解析-->
  32. <dependency>
  33. <groupId>dom4j</groupId>
  34. <artifactId>dom4j</artifactId>
  35. <version>1.6.1</version>
  36. </dependency>
  37. <!-- 导入一些工具类-->
  38. <dependency>
  39. <groupId>org.apache.commons</groupId>
  40. <artifactId>commons-lang3</artifactId>
  41. <version>3.5</version>
  42. </dependency>
  43. <!--为了不写get set方法引入lombok-->
  44. <dependency>
  45. <groupId>org.projectlombok</groupId>
  46. <artifactId>lombok</artifactId>
  47. <version>1.18.18</version>
  48. <scope>provided</scope>
  49. </dependency>
  50. <!--使用RequestBody注解时,需要把字符串bean对象转成json集合-->
  51. <dependency>
  52. <groupId>com.fasterxml.jackson.core</groupId>
  53. <artifactId>jackson-databind</artifactId>
  54. <version>2.8.1</version>
  55. </dependency>
  56. </dependencies>
  57. <build>
  58. <finalName>SpringMVC</finalName>
  59. <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
  60. <plugins>
  61. <plugin>
  62. <artifactId>maven-clean-plugin</artifactId>
  63. <version>3.1.0</version>
  64. </plugin>
  65. <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
  66. <plugin>
  67. <artifactId>maven-resources-plugin</artifactId>
  68. <version>3.0.2</version>
  69. </plugin>
  70. <plugin>
  71. <artifactId>maven-compiler-plugin</artifactId>
  72. <version>3.8.0</version>
  73. </plugin>
  74. <plugin>
  75. <artifactId>maven-surefire-plugin</artifactId>
  76. <version>2.22.1</version>
  77. </plugin>
  78. <plugin>
  79. <artifactId>maven-war-plugin</artifactId>
  80. <version>3.2.2</version>
  81. </plugin>
  82. <plugin>
  83. <artifactId>maven-install-plugin</artifactId>
  84. <version>2.5.2</version>
  85. </plugin>
  86. <plugin>
  87. <artifactId>maven-deploy-plugin</artifactId>
  88. <version>2.8.2</version>
  89. </plugin>
  90. </plugins>
  91. </pluginManagement>
  92. <plugins>
  93. <plugin>
  94. <groupId>org.apache.maven.plugins</groupId>
  95. <artifactId>maven-compiler-plugin</artifactId>
  96. <version>3.7.0</version>
  97. <configuration>
  98. <source>10.0.1</source>
  99. <target>10.0.1</target>
  100. <compilerArgs>
  101. <arg>-parameters</arg>
  102. </compilerArgs>
  103. <encoding>UTF-8</encoding>
  104. </configuration>
  105. </plugin>
  106. </plugins>
  107. </build>
  108. </project>

2,完善目录结构,

  1. web.xml文件//用来告诉tomcat服务器,启动服务器时要调用前端控制器的位置,和springmvc.xml文件的位置,一些初始化操作,所有访问tomcat的请求都要经过前端控制器
  2. springmvc.xml文件//配置前端控制器要扫描的包,该包下的对象要注入到spring的ioc容器中
  3. pojo层:user类 //用来存放数据
  4. service层:UserService接口 UserServiceImpl实现类 //用来写业务功能
  5. controller层:UserController类 //用来处理请求
  6. 注解接口:@service @controller @RequestMapping @RequestParam @AutoWired//这些注解只是一个标记的作用,存储value参数的作用
  7. 前端控制器:DispatcherServlet类
  8. Spring容器:WebApplicationContext类
  9. 处理器类:MyHandler类 //用来存放 (请求url,对应控制器对象controller,处理url请求的方法method)
  10. xml文件解析类:XMLParse类//用来对springmvc.xml文件进行解析,得到要扫描的包
  11. 自定义异常:ContextException类//包扫描不到自定义抛出自定义异常提醒自己

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDg0MTMxMg_size_16_color_FFFFFF_t_70 1

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDg0MTMxMg_size_16_color_FFFFFF_t_70 2

3,具体代码的编写和解释

1,web.xml文件:用来告诉tomcat服务器,启动服务器时要调用前端控制器的位置,和springmvc.xml文件的位置,一些初始化操作,所有访问tomcat的请求都要经过前端控制器

  1. <!DOCTYPE web-app PUBLIC
  2. "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  3. "http://java.sun.com/dtd/web-app_2_3.dtd" >
  4. <web-app>
  5. <display-name>Archetype Created Web Application</display-name>
  6. <!-- 把我们刚刚编写的DispatcherServlet进行配置,告诉服务器它的位置-->
  7. <servlet>
  8. <servlet-name>DispatcherServlet</servlet-name>
  9. <servlet-class>com.springMVC.servlet.DispatcherServlet</servlet-class>
  10. <init-param>
  11. <!-- springmvc的配置文件-->
  12. <param-name>contextConfigLocation</param-name>
  13. <param-value>classpath:springmvc.xml</param-value>
  14. </init-param>
  15. <!--web服务器一旦启动,servlet就实例化创建对象,进行初始化-->
  16. <load-on-startup>1</load-on-startup>
  17. </servlet>
  18. <!-- 我们访问的任何请求都会进入DispatcherServlet-->
  19. <servlet-mapping>
  20. <servlet-name>DispatcherServlet</servlet-name>
  21. <url-pattern>/</url-pattern>
  22. </servlet-mapping>
  23. </web-app>

2,springmvc.xml文件:配置前端控制器要扫描的包,该包下的对象要注入到spring的ioc容器中

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <beans>
  3. <!-- 开启使用注解类扫描的jar包-->
  4. <component-scan base-package="com.bruce.service,com.bruce.controller"></component-scan>
  5. </beans>

3,pojo层:user类,用来存放数据

  1. package com.bruce.POJO;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @AllArgsConstructor
  7. @NoArgsConstructor
  8. public class User {
  9. private Integer id;
  10. private String name;
  11. private String password;
  12. }

4,service层:UserService接口 UserServiceImpl实现类 //用来写业务功能

UserService接口:

  1. package com.bruce.service;
  2. import com.bruce.POJO.User;
  3. import java.util.List;
  4. public interface UserService {
  5. List<User> findUser(String name);
  6. String getUserMassage(String name);
  7. }

UserServiceImpl实现类:

  1. package com.bruce.service;
  2. import com.bruce.POJO.User;
  3. import com.springMVC.annotation.Service;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. @Service
  7. public class UserServiceImpl implements UserService {
  8. @Override
  9. public List<User> findUser(String name) {
  10. System.out.println("查询的参数是:"+name);
  11. //模拟一些数据
  12. List<User> users=new ArrayList<User>();
  13. users.add(new User(1,"建江","123"));
  14. users.add(new User(2,"小米","111"));
  15. return users;
  16. }
  17. @Override
  18. public String getUserMassage(String name) {
  19. return "我是getUserMassage方法: "+name;
  20. }
  21. }

5,controller层:UserController类 //用来处理请求

  1. package com.bruce.controller;
  2. import com.bruce.POJO.User;
  3. import com.bruce.service.UserService;
  4. import com.bruce.service.UserServiceImpl;
  5. import com.springMVC.annotation.*;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.io.IOException;
  9. import java.io.PrintWriter;
  10. import java.util.List;
  11. @Controller
  12. public class UserController {
  13. @AutoWired
  14. private UserServiceImpl userServiceImpl;
  15. @RequestMapping("/user/query")
  16. public void findUser(HttpServletRequest request, HttpServletResponse response,
  17. String name){
  18. System.out.println(userServiceImpl);
  19. response.setContentType("text/html;charset=utf-8");//处理响应的中文乱码
  20. List<User> users = userServiceImpl.findUser(name);
  21. try {
  22. PrintWriter out = response.getWriter();
  23. out.print("<h1>SpringMVC容器"+name+"</h1>");
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }finally {
  27. System.out.println("我执行了");
  28. }
  29. }
  30. /**
  31. * 返回到一个新的页面
  32. * @param request
  33. * @param response
  34. * @param name
  35. * @return
  36. */
  37. @RequestMapping("/user/query1")
  38. public String findUser1(HttpServletRequest request, HttpServletResponse response, String name){
  39. response.setContentType("text/html;charset=utf-8");//处理响应的中文乱码
  40. String userMassage = userServiceImpl.getUserMassage(name);
  41. request.setAttribute("userMassage",userMassage);
  42. //转发到user.jsp
  43. return "forward:/user.jsp";
  44. }
  45. /**
  46. * 返回一个json数据
  47. */
  48. @RequestMapping("/user/query2")
  49. @RequestBody
  50. public List<User> findUser2(HttpServletRequest request, HttpServletResponse response, String name){
  51. List<User> user = userServiceImpl.findUser(name);
  52. return user;
  53. }
  54. }

6,注解接口:@service @controller @RequestMapping @RequestParam @AutoWired//这些注解只是一个标记的作用,存储value参数的作用

@service:(创建过程和从句接口过程类似,选择创建注解Annotation)

  1. package com.springMVC.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.*;
  4. /**
  5. * 自定义注解
  6. */
  7. @Target(ElementType.TYPE)//元注解。TYPE表示该这个自定义注解的作用范围是在类上
  8. @Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
  9. @Documented//生成文档注释,可以不用加
  10. public @interface Service{
  11. String value() default "";//添加service注解的参数value
  12. }

@controller:

  1. package com.springMVC.annotation;
  2. import java.lang.annotation.*;
  3. /**
  4. * 自定义注解
  5. */
  6. @Target(ElementType.TYPE)//元注解。TYPE表示该这个自定义注解的作用范围是在类上
  7. @Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
  8. @Documented//生成文档注释,可以不用加
  9. public @interface Controller {
  10. String value() default "";//添加service注解的参数value
  11. }

@RequestMapping:

  1. package com.springMVC.annotation;
  2. import java.lang.annotation.*;
  3. /**
  4. * 自定义注解
  5. */
  6. @Target(ElementType.METHOD)//元注解。METHOD表示该这个自定义注解的作用范围是在方法上
  7. @Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
  8. @Documented//生成文档注释,可以不用加
  9. public @interface RequestMapping {
  10. String value() default "";//添加service注解的参数value
  11. }

@RequestParam:

  1. package com.springMVC.annotation;
  2. import java.lang.annotation.*;
  3. /**
  4. * 自定义注解
  5. */
  6. @Target(ElementType.PARAMETER)//元注解。PARAMETER表示该这个自定义注解的作用范围是在参数上
  7. @Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
  8. @Documented//生成文档注释,可以不用加
  9. public @interface RequestParam {
  10. String value() default "";//添加service注解的参数value
  11. }

@AutoWired

  1. package com.springMVC.annotation;
  2. import java.lang.annotation.*;
  3. /**
  4. * 自定义注解
  5. */
  6. @Target(ElementType.FIELD)//元注解。FIELD表示该这个自定义注解的作用范围是在字段上
  7. @Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
  8. @Documented//生成文档注释,可以不用加
  9. public @interface AutoWired {
  10. String value() default "";//添加service注解的参数value
  11. }

7,前端控制器:DispatcherServlet类:这个类功能比较复杂(做好准备)

注解:执行的流程

  1. /**
  2. * 初始化:
  3. * 去web.xml文件中得到springmvc.xml配置文件的名字,传给spring容器,进行初始化
  4. * spring容器初始化过程:
  5. * spring容器会调用xml解析器,对springmvc.xml文件进行解析,得到要扫描的包,把包下面的java文件名存到classNameList中
  6. * 遍历classNameList中的每个类,判断他们是否有@controller和@Service注解,如果有,就把它们的该类的名字,和实例化对象存到iocMap中
  7. * 遍历IocMap集合中的实例化对象,得到它的属性集合,遍历属性,判断属性上是否有@AutoWired注解,如果有就获取它的类型名
  8. * 根据类型名去IOCMap容器找到对应的实例化对象,进行赋值(对象的注入)
  9. *
  10. * 初始化请求映射:
  11. * 如IOCMap容器中得到实例化的Controller类
  12. * 获取到这个类的所有方法,遍历方法集合
  13. * 判断方法上是否有RequestMapping注解,如果有就得到它的value值也就是请求路径url
  14. * 把url,method,controller三个传给Handler进行初始化(记录三者的关系,由此可以根据请求路径定位到该方法)
  15. * 把Handler存到handlerList
  16. *
  17. * 处理请求:
  18. * 根据url来获取对应的处理器类,判断它是否为空,为空报404
  19. * 不为空,根据Handler找到该url要调用的方法,遍历方法的参数,把参数存到数组Params中,
  20. * 存储参数时需要进行判断,是HttpServletRequest类型的还是HttpServletResponse类型的,存放在对应位置
  21. * 如果是String类型的,进需要判断请求参数,与方法参数是否一样,如果一样才能调用方法,还需要找到有RequestParam注解
  22. * 判断它的value值是否与请求参数的名称一致,不一致不能调用处理请求的方法。
  23. *
  24. * 判断返回值类型是不是String类型的
  25. * 如果是String类型,可能要跳转的jsp页面
  26. * 如果该方法上有RequestBody注解,返回的是json集合
  27. * 需要使用Jackson工具类,对bean对象进行转化成json类型后,以输出流的形式发送到浏览器
  28. *
  29. * @throws ServletException
  30. */
  31. package com.springMVC.servlet;
  32. import com.fasterxml.jackson.databind.ObjectMapper;
  33. import com.springMVC.annotation.Controller;
  34. import com.springMVC.annotation.RequestBody;
  35. import com.springMVC.annotation.RequestMapping;
  36. import com.springMVC.annotation.RequestParam;
  37. import com.springMVC.context.WebApplicationContext;
  38. import com.springMVC.exception.ContextException;
  39. import com.springMVC.handler.MyHandler;
  40. import javax.servlet.ServletException;
  41. import javax.servlet.http.HttpServlet;
  42. import javax.servlet.http.HttpServletRequest;
  43. import javax.servlet.http.HttpServletResponse;
  44. import java.io.IOException;
  45. import java.io.PrintWriter;
  46. import java.lang.reflect.Field;
  47. import java.lang.reflect.Method;
  48. import java.lang.reflect.Parameter;
  49. import java.util.ArrayList;
  50. import java.util.List;
  51. import java.util.Map;
  52. /**
  53. * DispatcherServlet是SpringMVC的核心控制器
  54. */
  55. public class DispatcherServlet extends HttpServlet {
  56. private WebApplicationContext webApplicationContext;
  57. //存储url和对象方法的映射关系
  58. List<MyHandler> handlerList=new ArrayList<MyHandler>();
  59. //需要重写HttpServlet的两个方法
  60. @Override
  61. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  62. //super.doGet(req, resp);
  63. this.doPost(req,resp);
  64. }
  65. @Override
  66. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  67. //super.doPost(req, resp);
  68. //请求的分发处理
  69. executeDispatch(req,resp);
  70. }
  71. /**
  72. * 初始化
  73. * @throws ServletException
  74. */
  75. @Override
  76. public void init() throws ServletException {
  77. //1,Servlet初始化的时候,去web.xml文件读取初始化的参数contextConfigLocation的值:classpath:springmvc.xml
  78. String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
  79. //2,创建spring容器:将值classpath:springmvc.xml传进去,创建spring的容器
  80. webApplicationContext=new WebApplicationContext(contextConfigLocation);
  81. //初始化spring容器
  82. webApplicationContext.refresh();
  83. //4,初始化请求映射:/user/query------》Controller----->method-----param
  84. initHandlerMapping();
  85. System.out.println("请求地址和控制器方法的映射关系:"+handlerList);
  86. //super.init();
  87. }
  88. /**
  89. * 初始化请求映射
  90. */
  91. void initHandlerMapping(){
  92. //判断iocMap中是否有bean对象
  93. if(webApplicationContext.iocMap.isEmpty()){
  94. throw new ContextException("spring容器为空");
  95. }
  96. for (Map.Entry<String,Object> entry:webApplicationContext.iocMap.entrySet()){
  97. Class<?> clazz =entry.getValue().getClass();
  98. if (clazz.isAnnotationPresent(Controller.class)){
  99. Method[] methods = clazz.getDeclaredMethods();
  100. for (Method method:methods){
  101. if (method.isAnnotationPresent(RequestMapping.class)){
  102. RequestMapping requestMappingAnnotation = method.getAnnotation(RequestMapping.class);
  103. String url = requestMappingAnnotation.value();
  104. MyHandler myHandler=new MyHandler(url,entry.getValue(),method);
  105. handlerList.add(myHandler);
  106. }
  107. }
  108. }
  109. }
  110. }
  111. /**
  112. * 请求分发
  113. */
  114. void executeDispatch(HttpServletRequest req, HttpServletResponse resp){
  115. MyHandler handler = getHandler(req);
  116. try {
  117. if (handler==null){
  118. resp.getWriter().print("<h1> 404 not found</h1>");
  119. }else {
  120. Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
  121. Object []params=new Object[parameterTypes.length];//定义一个参数数组,来存放请求的参数
  122. for (int i=0;i<parameterTypes.length;i++){
  123. Class<?> parameterType=parameterTypes[i];
  124. if ("HttpServletRequest".equals(parameterType.getSimpleName())){
  125. params[i]=req;
  126. }else if ("HttpServletResponse".equals(parameterType.getSimpleName())){
  127. params[i]=resp;
  128. }
  129. }
  130. //获取请求中的参数集合
  131. Map<String,String[]> parameterMap = req.getParameterMap();
  132. for (Map.Entry<String,String []> entry:parameterMap.entrySet()){
  133. String value = entry.getValue()[0];
  134. String name = entry.getKey();
  135. //写死了,默认第三个参数是name
  136. System.out.println("请求的参数是:"+name+"---->"+value);
  137. int index = hasRequestParam(handler.getMethod(), name);//把该处理器的方法,和参数key传进去
  138. if (index!=-1){//如果找到下标
  139. params[index]=value;
  140. }else {
  141. List<String> names = getParameterNames(handler.getMethod());
  142. System.out.println(names);//[request, response, name]
  143. for(int i=0;i<names.size();i++){
  144. if (name.equals(names.get(i))){//如果请求中的参数和方法中参数列表中的名字相同就进行赋值
  145. params[i]=value;
  146. break;
  147. }
  148. }
  149. }
  150. //params[2] =value;
  151. }
  152. //调用控制器中的方法,得到它的返回值用来转发到对应的页面
  153. Object result = handler.getMethod().invoke(handler.getController(), params);
  154. if (result instanceof String){
  155. //跳转到jsp页面
  156. String viewName=(String)result;
  157. if (viewName.contains(":")){//说明是forward:/user.jsp语法
  158. String viewType=viewName.split(":")[0];
  159. String viewPage=viewName.split(":")[1];
  160. if (viewType.equals("forward")){
  161. //转发
  162. req.getRequestDispatcher(viewPage).forward(req,resp);
  163. }else {
  164. //重定向
  165. resp.sendRedirect(viewPage);
  166. }
  167. }else {
  168. //默认是转发
  169. req.getRequestDispatcher(viewName).forward(req,resp);
  170. }
  171. }
  172. else {
  173. //返回json数据
  174. Method method=handler.getMethod();
  175. if (method.isAnnotationPresent(RequestBody.class)){
  176. //调用返回值用Json转化的工具类,把它转化成json字符串
  177. ObjectMapper objectMapper=new ObjectMapper();
  178. String json = objectMapper.writeValueAsString(result);
  179. resp.setContentType("text/html;charset=utf-8");//返回数据解决中文乱码
  180. PrintWriter out = resp.getWriter();//把response请求写入输出流
  181. out.print(json);//将json数据显示在浏览器
  182. out.flush();//情况流中的数据
  183. out.close();//关闭输出流
  184. }
  185. }
  186. }
  187. } catch (Exception e) {
  188. e.printStackTrace();
  189. }
  190. }
  191. /**
  192. * 获取请求对应的控制器Handler
  193. */
  194. MyHandler getHandler(HttpServletRequest req){
  195. // /user/query路径
  196. String requestURI = req.getRequestURI();
  197. for (MyHandler handler:handlerList){//遍历处理器列表,对比有没有相同url的处理器,有就返回
  198. if (handler.getUrl().equals(requestURI)){
  199. return handler;
  200. }
  201. }
  202. return null;
  203. }
  204. /**
  205. * 判断控制器的参数是否有,RequestMapping注解,并且比较对应的value值是否相等,如果相等返回下标
  206. */
  207. public int hasRequestParam(Method method,String name){
  208. Parameter[] parameters = method.getParameters();
  209. for (int i=0;i<parameters.length;i++){
  210. Parameter parameter=parameters[i];
  211. boolean b = parameter.isAnnotationPresent(RequestParam.class);//参数是否有RequestParam注解
  212. if (b){
  213. RequestParam requestParam = parameter.getAnnotation(RequestParam.class);//得到RequestParam注解,判断value值
  214. String value = requestParam.value();
  215. if (name.equals(value)){//如果传入的参数name和value值相同就返回参数的下标位置
  216. return i;
  217. }
  218. }
  219. }
  220. return -1;//没有找到
  221. }
  222. /**
  223. * 获取参数的名字列表,需要在pom.xml文件配置maven编译插件,依赖于jdk1.8
  224. */
  225. public List<String> getParameterNames(Method method){
  226. List<String> list=new ArrayList<String>();//存放参数
  227. Parameter[] parameters = method.getParameters();
  228. for (Parameter parameter:parameters){
  229. list.add(parameter.getName());
  230. }
  231. return list;
  232. }
  233. }

8,Spring容器:WebApplicationContext类

  1. package com.springMVC.context;
  2. import com.springMVC.annotation.AutoWired;
  3. import com.springMVC.annotation.Controller;
  4. import com.springMVC.annotation.Service;
  5. import com.springMVC.exception.ContextException;
  6. import com.xml.XmlParser;
  7. import java.io.File;
  8. import java.lang.reflect.Field;
  9. import java.net.URL;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. import java.util.Map;
  13. import java.util.concurrent.ConcurrentHashMap;
  14. /**
  15. * spring容器
  16. */
  17. public class WebApplicationContext {
  18. private String contextConfigLocation;//内容是classpath:springmvc.xml
  19. List<String> classNameList=new ArrayList<String>();//存放要扫描的包
  20. //spring的ioc容器,用来存放bean对象
  21. public Map<String,Object> iocMap=new ConcurrentHashMap<String,Object>();
  22. public WebApplicationContext(String contextConfigLocation) {
  23. this.contextConfigLocation = contextConfigLocation;
  24. }
  25. /**
  26. * 初始化spring容器
  27. */
  28. public void refresh(){
  29. //1,解析spring.xml文件,使用dom4j
  30. //把springmvc.xml传到getBasePackage方法中,该方法会实例化springmvc.xml对象,把对应要扫描包的值取出来
  31. //使用basePackage的值是com.bruce.service,com.bruce.controller
  32. String basePackage = XmlParser.getBasePackage(contextConfigLocation.split(":")[1]);
  33. String []basePackages=basePackage.split(",");//把它们分割开来
  34. if (basePackage.length()>0){//如果有值,分割里面的报,进行初始化
  35. for (String pack:basePackages){
  36. executeScanPackage(pack);//这个方法在下面,获取包的路径存到list中
  37. }
  38. }
  39. System.out.println("扫描后内容是:"+classNameList);
  40. //实例化spring容器中的bean
  41. executeInstance();
  42. System.out.println("IOCMap容器中的对象"+iocMap);
  43. //实现spring容器中对象的注入
  44. executeAutoWired();
  45. }
  46. /**
  47. * 根据配置文件中的路径信息,扫描对应的包,把这些包的路径存到list集合中
  48. */
  49. public void executeScanPackage(String pack){
  50. URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
  51. String path=url.getFile();
  52. File dir=new File(path);
  53. for (File f:dir.listFiles()){
  54. if (f.isDirectory()){
  55. //是一个文件目录
  56. executeScanPackage(pack+"."+f.getName());//继续进入这个文件夹中寻找文件
  57. }
  58. else {
  59. //是一个文件,获取它的全路径,把它存到list集合中
  60. String className=pack+"."+f.getName().replaceAll(".class","");
  61. classNameList.add(className);
  62. }
  63. }
  64. }
  65. /**
  66. * 实例化spring容器中的bean对象
  67. * 主要是service层和controller层的类
  68. */
  69. public void executeInstance(){
  70. if(classNameList.size()==0){
  71. //没有扫描到要实例化的类,抛出自定义的异常
  72. throw new ContextException("没有实例化的class");
  73. }
  74. for (String className:classNameList){
  75. try {
  76. Class<?> clazz=Class.forName(className);
  77. if (clazz.isAnnotationPresent(Controller.class)){
  78. //判断该类是否有@Controller 注解,如果有说明他是控制层的类
  79. //得到这个类的名字,第一个字母转小写。作为这个类对象的名字
  80. String beanName=clazz.getSimpleName().substring(0,1).toLowerCase()+ clazz.getSimpleName().substring(1);
  81. iocMap.put(beanName,clazz.newInstance());//把该对象的名字和实例存到ioc容器中
  82. }
  83. else if (clazz.isAnnotationPresent(Service.class)){
  84. //如果有@Controller 注解,说明他是业务层的类
  85. Service serviceAnnotation=clazz.getAnnotation(Service.class);//得到@Service
  86. String beanName =serviceAnnotation.value();//得到里面的value值
  87. if("".equals(beanName)){//如果我们没有使用value值去设置对象的名字
  88. Class<?> []interfaces= clazz.getInterfaces();//就得到该类实现接口的名字
  89. for (Class<?> c1:interfaces){//遍历接口,把接口名作为该类对象的名字,和该类的实例一起存到ioc容器中
  90. String beanName1=clazz.getSimpleName().substring(0,1).toLowerCase()+ clazz.getSimpleName().substring(1);
  91. iocMap.put(beanName1,clazz.newInstance());
  92. }
  93. }else {
  94. iocMap.put(beanName,clazz.newInstance());//把value作为对象名字,与对象实例存入ioc集合
  95. }
  96. }
  97. } catch (Exception e) {
  98. e.printStackTrace();
  99. }
  100. }
  101. }
  102. /**
  103. *实现spring容器中的对象的依赖注入:userService
  104. */
  105. public void executeAutoWired(){
  106. try {
  107. if(iocMap.isEmpty()){//如果容器为空
  108. throw new ContextException("没有找到初始化的bean对象");
  109. }
  110. for (Map.Entry<String,Object> entry:iocMap.entrySet()){
  111. String key = entry.getKey();
  112. Object bean = entry.getValue();//对象
  113. Field[] declaredFields = bean.getClass().getDeclaredFields();//获取bean对象上的所有字段
  114. for (Field declaredField:declaredFields){//遍历所有字段
  115. if (declaredField.isAnnotationPresent(AutoWired.class)){//判断该字段上是否有AutoWired注解
  116. AutoWired autoWiredAnnotation = declaredField.getAnnotation(AutoWired.class);//把这个AutoWired注解取出来
  117. String beanName=autoWiredAnnotation.value();//看看该注解上的value值有没有被赋值
  118. if ("".equals(beanName)){//如果value没有被赋值
  119. Class<?> type=declaredField.getType();//得到type的名称:UserService
  120. //把第一个字母变小写后赋值给beanName
  121. beanName=type.getSimpleName().substring(0,1).toLowerCase()+ type.getSimpleName().substring(1);
  122. }
  123. System.out.println("bean:"+bean);
  124. System.out.println("beanName:"+iocMap.get(beanName));
  125. declaredField.setAccessible(true);//暴力反射,如果该对象是私有的,就需要开启它的访问权限
  126. //属性注入,调用反射给属性注入值
  127. //注入失败,接口名,与实现类对象名不一致(因为存在IOCMap中的key是实现类的名,不是接口名)
  128. declaredField.set(bean,iocMap.get(beanName));
  129. }
  130. }
  131. }
  132. }catch (Exception e){
  133. e.printStackTrace();
  134. }
  135. }
  136. }

9,处理器类:MyHandler类 //用来存放 (请求url,对应控制器对象controller,处理url请求的方法method)

  1. package com.springMVC.handler;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import java.lang.reflect.Method;
  6. @AllArgsConstructor
  7. @NoArgsConstructor
  8. @Data
  9. public class MyHandler {
  10. private String url;
  11. private Object controller;
  12. private Method method;
  13. }

10,xml文件解析类:XMLParse类//用来对springmvc.xml文件进行解析,得到要扫描的包

  1. package com.xml;
  2. import org.dom4j.Attribute;
  3. import org.dom4j.Document;
  4. import org.dom4j.DocumentException;
  5. import org.dom4j.Element;
  6. import org.dom4j.io.SAXReader;
  7. import java.io.InputStream;
  8. /**
  9. * 解析springMVC.xml
  10. */
  11. public class XmlParser {
  12. public static String getBasePackage(String xml){
  13. SAXReader saxReader=new SAXReader();
  14. InputStream inputStream = XmlParser.class.getClassLoader().getResourceAsStream(xml);
  15. //xml文档对象
  16. try {
  17. Document document = saxReader.read(inputStream);
  18. //获取到根节点
  19. Element rootElement = document.getRootElement();
  20. //找到扫描包的配置节点component-scan
  21. Element componentScan = rootElement.element("component-scan");
  22. //去取出来属性为base-package的值
  23. Attribute attribute = componentScan.attribute("base-package");
  24. String basePackage = attribute.getText();
  25. return basePackage;//包得到的com.bruce.service返回
  26. } catch (DocumentException e) {
  27. }
  28. return "";
  29. }
  30. }

11,自定义异常:ContextException类//包扫描不到自定义抛出自定义异常提醒自己

  1. package com.springMVC.exception;
  2. public class ContextException extends RuntimeException {
  3. public ContextException(String message) {
  4. super(message);
  5. }
  6. public ContextException(Throwable cause) {
  7. super(cause);
  8. }
  9. @Override
  10. public String getMessage() {
  11. return super.getMessage();
  12. }
  13. }

12,跳转后的页面user.jsp

  1. <%--
  2. Created by IntelliJ IDEA.
  3. User: jianjiang
  4. Date: 2021/4/4
  5. Time: 11:12
  6. To change this template use File | Settings | File Templates.
  7. --%>
  8. <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
  9. <html>
  10. <head>
  11. <title>跳转页面</title>
  12. </head>
  13. <body>
  14. <h1>我是user.jsp页面</h1>
  15. <h1>${requestScope.userMassage}</h1>
  16. </body>
  17. </html>

手写springmvc下载地址:

链接:https://pan.baidu.com/s/1OaRKjBIRhZrK2IjVVS8yRg 提取码:110b 复制这段内容后打开百度网盘手机App,操作更方便哦

发表评论

表情:
评论列表 (有 0 条评论,256人围观)

还没有评论,来说两句吧...

相关阅读

    相关 迷你SpringMVC框架

    前言 本文总结自慕课网同名课程。 学习如何使用Spring,SpringMVC是很快的,但是在往后使用的过程中难免会想探究一下框架背后的原理是什么,本文将通过讲解如何手

    相关 SpringMVC框架

    引言 在分析springMVC框架之前,我们根据我们对整个框架的流程分析,先来手写一个简易版的springMVC框架, 这样我们在看源码的时候会更清晰,毕竟框架源码还是非

    相关 SpringMVC-思路篇

        此篇文章主要是讲解如何手写SpringMVC的思路,其中会涉及到一部分的代码来辅助来说明思路,希望对想手写SpringMVC,而无从下手的朋友有些帮助。 一、先来一份