实现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 {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    @Override
    protected 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 {
    @Override
    public void init(ServletConfig config) throws ServletException {
        //初始化容器
        SunWebApplicationContext sunWebApplicationContext = new SunWebApplicationContext();
        sunWebApplicationContext.init();

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet");
    }

    @Override
    protected 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文件的名字去掉.class
                    String 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查看单例池

上一篇:Group Query Attention (GQA) 机制详解以及手动实现计算


下一篇:实验七 智能手机互联网程序设计(微信程序方向)实验报告