【学习笔记】手写一个简单的 Spring MVC

news2024/10/6 7:31:19

目录

一、什么是Spring MVC ?

Spring 和 Spring MVC 的区别?

Spring MVC 的运行流程?

二、实现步骤

1. DispatcherServlet

1. 创建一个中央分发器

拦截所有请求

测试

2. 接管 IOC 容器

1. 创建配置文件

2. 修改 web.xml 配置文件

3. 启动 IOC 容器

2. HandlerMapping

1. 添加映射

1. 创建 RequestMapping 注解

2. 创建映射Bean

3. 使用 RequestMapping 注解

4. 添加映射

2. 处理映射

1. 创建 ResponseBody 注解

2. 创建一个 HTML 文件

3. 使用 ResponseBody 注解

4. 处理映射

3. 测试


一、什么是Spring MVC ?

Spring MVC 是 Spring 的模块之一,Spring MVC 实现了MVC 的设计思想,并继承了 Servlet API 的WEB 框架。当用户在游览器地址栏上输入 url 后,Spring MVC就可以处理用户的请求

Spring 和 Spring MVC 的区别?

Spring 是一个框架,这个框架由不同的模块组成,其中一个模块 就是Spring MVC,Spring 核心是IOC 控制反转,IOC 容器负责对象的创建和依赖注入。

Spring MVC 是基于 MVC 设计来开发web 应用,Spring MVC 将前端发送的请求分发给适当的控制器 Controller,然后根据结果选择合适的视图进行渲染返回

Spring MVC 的运行流程?

  1. 用户发送HTTP请求
  2. 请求到达服务器后,Spring MVC 的中央分发器拦截请求
  3. 中央分发器根据 请求的路径找到对应的 HandlerMapping,确定由哪个 Controller 控制器处理
  4. HandlerMaping 根据请求信息映射到对应的 Controller ,然后返回给中央分发器
  5. 中央分发器把请求交给对应的Controller 控制器,Controller 是Spring MVC的一个组件,它负责处理请求以及响应结果
  6. Controller 控制器调用合适的业务层或 Mapper 层获取数据
  7. Controller 把数据封装成一个ModelAndView对象,然后返回给中央分发器
  8. 中央分发器把ModelAndView对象传给 ViewResolver
  9. ViewResolver 根据视图名称解析出一个 View 对象,然后返回给中央分发器
  10. 中央分发器把 view 对象渲染出来返回给客户端

在 手写 Spring IOC 的基础上再进行扩展,手写一个 Spring MVC

二、实现步骤

1. DispatcherServlet

1. 创建一个中央分发器

package com.shao.MVC;

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;


public class DispatcherServlet extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("dispatcherServlet 初始化");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("dispatcherServlet 开始执行任务了");
    }

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

}

拦截所有请求

在 Tomcat 的 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>dispatcherServlet</servlet-name>
        <servlet-class>com.shao.MVC.DispatcherServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

测试

在地址栏随便输入一个请求,中央分发器会进行拦截,然后执行初始化和相关方法,初始化只会执行一次

2. 接管 IOC 容器

在 手写 spring IOC 的时候,为了方便测试是在 Servlet 的 doGet 方法中初始化 IOC 容器,现在改为在中央分发器初始化的时候启动 IOC 容器

1. 创建配置文件

在配置文件中配置扫描包路径,然后启动中央分发器的时候把配置文件传过去

2. 修改 web.xml 配置文件

3. 启动 IOC 容器

在中央分发器的初始化方法中启动 IOC 容器

package com.shao.MVC;

import com.shao.IOC.ApplicationContext;

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.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Properties;


public class DispatcherServlet extends HttpServlet {

    // 存储 IOC 容器
    private ApplicationContext applicationContext;
    private Properties Prop = new Properties();

    /**
     * 初始化
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("dispatcherServlet 初始化");
        // 获取传过来的配置文件
        String configLocation = config.getInitParameter("contextConfigLocation");
        String fileName = configLocation.replace("classpath:", "");
        // 调用 loadConfig 方法,传入配置文件名,返回扫描包路径
        String packagePath = loadConfig(fileName);

        try {
            // 启动 IOC 容器
            applicationContext = new ApplicationContext(packagePath);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("dispatcherServlet 开始执行任务了");
    }

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

    /**
     * 加载配置文件,解析配置文件,返回扫描包路径
     */
    public String loadConfig(String path) {

        // 以流的方式加载配置文件
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(path);

        String basePackage = "";
        try {
            // 解析配置文件中的属性,以键值对的方式存储到 Prop 中
            Prop.load(resourceAsStream);
            basePackage = Prop.getProperty("basePackage");

        } catch (IOException e) {
            e.printStackTrace();
        }
        return basePackage;
    }

}

2. HandlerMapping

HandlerMapping 根据注解的值映射到对应的 Controller 和方法

将 url 和 controller 里面的方法进行映射,存到 HashMap 中,key 是 url ,value 是一个对象,这个对象有 url 对应的 controller 对象和对应的方法

1. 添加映射

1. 创建 RequestMapping 注解

2. 创建映射Bean

package com.shao.MVC;


import java.lang.reflect.Method;

public class RequestMappingBean {
    /**
     * controller 对象
     */
    private Object controller;
    /**
     * controller 的方法
     */
    private Method method;

    public RequestMappingBean(Object controller, Method method) {
        this.controller = controller;
        this.method = method;
    }

    /**
     * 获取
     *
     * @return controller
     */
    public Object getController() {
        return controller;
    }

    /**
     * 设置
     *
     * @param controller
     */
    public void setController(Object controller) {
        this.controller = controller;
    }

    /**
     * 获取
     *
     * @return method
     */
    public Method getMethod() {
        return method;
    }

    /**
     * 设置
     *
     * @param method
     */
    public void setMethod(Method method) {
        this.method = method;
    }

    public String toString() {
        return "RequestMappingBean{controller = " + controller + ", method = " + method + "}";
    }
}

3. 使用 RequestMapping 注解

4. 添加映射
package com.shao.MVC;

import com.shao.Annotation.Controller;
import com.shao.Annotation.RequestMapping;
import com.shao.IOC.ApplicationContext;

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.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Properties;


public class DispatcherServlet extends HttpServlet {

    // 存储 IOC 容器
    private ApplicationContext applicationContext;
    private Properties Prop = new Properties();

    private HashMap<String, RequestMappingBean> mappingMap = new HashMap<>();


    /**
     * 初始化
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("dispatcherServlet 初始化");
        // 获取传过来的配置文件
        String configLocation = config.getInitParameter("contextConfigLocation");
        String fileName = configLocation.replace("classpath:", "");
        // 调用 loadConfig 方法,传入配置文件名,返回扫描包路径
        String packagePath = loadConfig(fileName);

        try {
            // 启动 IOC 容器
            applicationContext = new ApplicationContext(packagePath);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        // 添加映射
        AddRequestMapping();
        
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("dispatcherServlet 开始执行任务了");
    }

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

    /**
     * 加载配置文件,解析配置文件,返回扫描包路径
     */
    public String loadConfig(String path) {

        // 以流的方式加载配置文件
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(path);

        String basePackage = "";
        try {
            // 解析配置文件中的属性,以键值对的方式存储到 Prop 中
            Prop.load(resourceAsStream);
            basePackage = Prop.getProperty("basePackage");

        } catch (IOException e) {
            e.printStackTrace();
        }
        return basePackage;
    }

    /**
     * 添加映射
     * 1. 从 IOC 容器中获取所有带 RequestMapping 注解的 Controller 对象
     * 2. 获取 Controller 对象中带 RequestMapping 注解的方法
     * 3. 将 Controller 对象和方法封装为 RequestMappingBean 对象
     * 4. 构建映射关系,key 是 url,value 是 映射Bean 对象,包括 Controller 对象和方法
     */

    public void AddRequestMapping() {
        // 获取 IOC 容器的 Bean Map
        HashMap<String, Object> beanMap = applicationContext.getBeanMap();

        for (Object bean : beanMap.values()) {
            // 获取 bean 的 Class 对象
            Class<?> aClass = bean.getClass();

            // 判断是否有 @Controller 注解
            if (!aClass.isAnnotationPresent(Controller.class)) {
                continue;
            }

            // 判断是否有 @RequestMapping 注解
            if (!aClass.isAnnotationPresent(RequestMapping.class)) {
                continue;
            }

            // 获取类的 @RequestMapping 注解的值
            String basePath = aClass.getAnnotation(RequestMapping.class).value();

            // 获取 Controller 对象中的所有方法
            Method[] methods = aClass.getDeclaredMethods();
            for (Method method : methods) {

                // 判断方法上有没有带 @RequestMapping 注解
                if (!method.isAnnotationPresent(RequestMapping.class)) {
                    continue;
                }
                String path = method.getAnnotation(RequestMapping.class).value();

                // 封装为 映射Bean 对象
                RequestMappingBean mappingBean = new RequestMappingBean(bean, method);

                // 构建映射,添加到 Map 中
                mappingMap.put(basePath + path, mappingBean);
            }
        }

        System.out.println("映射添加完成");
        System.out.println(mappingMap);

    }

}

2. 处理映射

1. 创建 ResponseBody 注解

2. 创建一个 HTML 文件

3. 使用 ResponseBody 注解

4. 处理映射

为了方便测试,只处理了 GET 方法的映射

package com.shao.MVC;

import com.alibaba.fastjson2.JSON;
import com.shao.Annotation.Controller;
import com.shao.Annotation.RequestMapping;
import com.shao.Annotation.ResponseBody;
import com.shao.IOC.ApplicationContext;

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.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Properties;


public class DispatcherServlet extends HttpServlet {

    // 存储 IOC 容器
    private ApplicationContext applicationContext;
    private Properties Prop = new Properties();

    private HashMap<String, RequestMappingBean> mappingMap = new HashMap<>();


    /**
     * 初始化
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("dispatcherServlet 初始化");
        // 获取传过来的配置文件
        String configLocation = config.getInitParameter("contextConfigLocation");
        String fileName = configLocation.replace("classpath:", "");
        // 调用 loadConfig 方法,传入配置文件名,返回扫描包路径
        String packagePath = loadConfig(fileName);

        try {
            // 启动 IOC 容器
            applicationContext = new ApplicationContext(packagePath);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        // 添加映射
        AddRequestMapping();

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("dispatcherServlet 开始执行任务了");

        // 处理请求
        HandlerMapping(req, resp);

    }

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

    /**
     * 加载配置文件,解析配置文件,返回扫描包路径
     */
    public String loadConfig(String path) {

        // 以流的方式加载配置文件
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(path);

        String basePackage = "";
        try {
            // 解析配置文件中的属性,以键值对的方式存储到 Prop 中
            Prop.load(resourceAsStream);
            basePackage = Prop.getProperty("basePackage");

        } catch (IOException e) {
            e.printStackTrace();
        }
        return basePackage;
    }

    /**
     * 添加映射
     * 1. 从 IOC 容器中获取所有带 RequestMapping 注解的 Controller 对象
     * 2. 获取 Controller 对象中带 RequestMapping 注解的方法
     * 3. 将 Controller 对象和方法封装为 RequestMappingBean 对象
     * 4. 构建映射关系,key 是 url,value 是 映射Bean 对象,包括 Controller 对象和方法
     */

    public void AddRequestMapping() {
        // 获取 IOC 容器的 Bean Map
        HashMap<String, Object> beanMap = applicationContext.getBeanMap();

        for (Object bean : beanMap.values()) {
            // 获取 bean 的 Class 对象
            Class<?> aClass = bean.getClass();

            // 判断是否有 @Controller 注解
            if (!aClass.isAnnotationPresent(Controller.class)) {
                continue;
            }

            // 判断是否有 @RequestMapping 注解
            if (!aClass.isAnnotationPresent(RequestMapping.class)) {
                continue;
            }

            // 获取类的 @RequestMapping 注解的值
            String basePath = aClass.getAnnotation(RequestMapping.class).value();

            // 获取 Controller 对象中的所有方法
            Method[] methods = aClass.getDeclaredMethods();
            for (Method method : methods) {

                // 判断方法上有没有带 @RequestMapping 注解
                if (!method.isAnnotationPresent(RequestMapping.class)) {
                    continue;
                }
                String path = method.getAnnotation(RequestMapping.class).value();

                // 封装为 映射Bean 对象
                RequestMappingBean mappingBean = new RequestMappingBean(bean, method);

                // 构建映射,添加到 Map 中
                mappingMap.put(basePath + path, mappingBean);
            }
        }

        System.out.println("映射添加完成");
        System.out.println(mappingMap);

    }

    /**
     * 处理请求,根据 url 找到对应的映射对象,调用对应的方法,返回结果
     */
    public void HandlerMapping(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        // 获取请求的路径,这个请求路径中有项目名称
        String requestURI = req.getRequestURI();
        // 获取项目名
        String contextPath = req.getContextPath();
        // 去掉项目名
        String url = requestURI.replace(contextPath, "");

        // 获取 url 对应的映射对象
        RequestMappingBean mappingBean = mappingMap.get(url);
        Object controller = mappingBean.getController();
        Method method = mappingBean.getMethod();
        Object res = null;
        try {
            // 调用映射对象中的方法
            res = method.invoke(controller);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 判断方法是否有返回内容
        if (res == null) {
            return;
        }

        // 判断方法是否有 @ResponseBody 注解
        if (method.isAnnotationPresent(ResponseBody.class)) {
            resp.setContentType("application/json;charset=utf-8");

            // 响应数据
            resp.getWriter().write(JSON.toJSONString(res));

        } else {

            // 获取编译后的项目根目录
            String path = this.getClass().getClassLoader().getResource("../../").getPath();

            // 路径前面有一个 /,比如: /D:/xxx,需要去掉,然后拼接静态资源名称
            String filePath = path.substring(1) + res;

            try {
                // 解码,如果路径有空格或者中文,会出现 16 进制的字符
                filePath = URLDecoder.decode(filePath, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            // 获取静态资源内容
            byte[] fileContents = StaticResourceHandler.getFileContents(filePath);
            // 获取文件媒体类型
            String mimeType = StaticResourceHandler.getFileMimeType(filePath);
            resp.setContentType(mimeType + ";charset=utf-8");

            // 响应内容
            resp.getWriter().write(new String(fileContents));
        }
    }
}

3. 测试

如果显示乱码可以添加以下命令试一下

-Dfile.encoding=UTF-8

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vSAN03:vSAN故障处理、节点维护、删除节点、关闭/重启/删除vSAN集群

目录 vSAN故障处理单节点维护从vSAN集群中永久删除节点关闭vSAN集群重启vSAN集群删除vSAN集群 vSAN故障处理 级别状态处理可能原因活动正常无无缺失vSAN 检测到临时组件故障&#xff0c;且其中的组件可以恢复并还原其工作状态&#xff0c;则该组件将处于“缺失”状态。&#x…

分析JS Crash(进程崩溃)

一、JS Crash异常检测能力 1、JS Crash日志规格 以下是进程崩溃日志信息中对应字段解释。 Build info:XXX-XXXX X.X.X.XX(XXXXXXXX) <- 版本信息 Module name:com.example.myapplication <- 模块名 Version:1.0.0 <- 版本号 Pid:579 <- 进程号 Uid:0 <- 用户ID…

【Qt】Qt学习笔记(一):Qt界面初识

Qt 是一个跨平台应用程序和 UI 开发框架。使用 Qt 您只需一次性开发应用程序&#xff0c;无须重新编写源代码&#xff0c;便可跨不同桌面和嵌入式操作系统部署这些应用程序。Qt Creator是跨平台的Qt集成开发环境。 创建项目 Qt的一些界面&#xff0c;初学时一般选择Qt Widgets …

VirtualBox虚拟机连接宿主机并能够上网(小白向)

现存问题 windows系统主要使用vmare和virtualbox两种虚拟机&#xff0c;virtualbox相对于vmare更加轻便&#xff0c;但少有博客能够详细说明使用virtualbox的教程。踩了网上的坑后&#xff0c;决定写一篇文章介绍virtualbox虚拟机上网的流程。 需求 1. virtualbox虚拟机与宿主机…

Linux 文件 IO 管理(第四讲:软硬链接和动静态库)

Linux 文件 IO 管理&#xff08;第四讲&#xff1a;软硬链接和动静态库&#xff09; 软硬链接操作与现象软链接硬链接 解释软链接硬链接作用 动静态库初识静态库怎么做库&#xff08;开发角度&#xff09;怎么用库&#xff08;使用角度&#xff09;安装当前目录直接使用 动态库…

JavaWeb程序设计(第四版)习题参考答案

JavaWeb程序设计&#xff08;第四版&#xff09;习题参考答案 目录 模块1 习题参考答案 模块2 习题参考答案 模块3 习题参考答案 模块4 习题参考答案 模块5 习题参考答案 模块6 习题参考答案 模块7 习题参考答案 模块8 习题参考答案 模块1 习题参考答案 选择题 1 .A …

WaveletGPT:基于小波的多尺度表征增强大型语言模型训练效率

斯坦福大学的研究人员首次将小波理论应用于大型语言模型&#xff0c;提出了WaveletGPT&#xff0c;通过在Transformer解码器层中添加多尺度滤波器&#xff0c;加速了模型训练速度&#xff0c;并在文本、音频和音乐等多个领域取得了显著的性能提升。 论文介绍 大型语言模型 (L…

求和问题题解

减 sort拍后就A了 #include <bits/stdc.h> #include <cstring> using namespace std; typedef long long ll; ll n,a[400005],cnt0,b[400005]; bool cmp(long long x,long long y){return x>y; } int main () {cin>>n;for(int i1;i<n;i){cin>>…

查缺补漏----同步,异步,半同步,分离式通信

目录 1.同步通信&#xff08;同步定时方式&#xff09; 2.异步通信 3.半同步通信 4.分离式通信 1.同步通信&#xff08;同步定时方式&#xff09; 同步通信方式&#xff08;比如SPI&#xff09;&#xff0c;是把许多字符组成一个信息组&#xff0c;这样&#xff0c;字符可…

Springboot 整合 durid

文章目录 Springboot 整合 druiddruid的优势配置参数使用整合 Druid配置数据源配置参数绑定配置参数配置监控页面配置拦截器 Springboot 整合 druid druid的优势 可以很好的监控 DB 池连接 和 SQL 的执行情况可以给数据库密码加密可以很方便的编写JDBC插件 配置参数 使用 整…

数据结构之树(4)

摘要&#xff1a;本篇主要讲哈夫曼树、并查集、二叉排序树、平衡二叉树等&#xff0c;非常非常非常重要&#xff01;&#xff01;&#xff01; 一、哈夫曼树 基于霍夫曼树&#xff0c;利用霍夫曼编码进行通信可以大大提高信道利用率&#xff0c;缩短信息传输时间&#xff0c;…

Android2024.2.1升级错误

提示 Gradle 版本不兼容&#xff0c;升级后就报错了 。 1.gradle安装包镜像 distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists //distributionUrlhttps\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionUrlhttps://mirrors.cloud.tencen…

Koa2项目实战1(项目搭建)

前言 在正式开始之前&#xff0c;需要先知道用到的东西&#xff1a; koa&#xff1a;Koa 是一个基于 Node.js 的 Web 应用框架&#xff0c;非常适合开发API服务&#xff0c;可以与前端框架&#xff08;如 Vue.js、React.js&#xff09;结合使用&#xff0c;实现前后端分离的开…

第八篇:磁盘管理(1)

目录 6.1分区 6.1.1基本分区 6.1.1.1磁盘的相关知识 6.1.1.1.1基础知识 6.1.1.1.2命名 1.对于串口硬盘&#xff1a;/dev/sda、/dev/sdb、/dev/sdc......往后都是一个字母一个字母的累加 2.对于并口硬盘&#xff1a;/dev/hda其余相同 6.1.1.1.3磁盘的分区方式 方式1&am…

c++_ 多态

目录 一.多态 1.1多态(polymorphism)的概念 1.2实现多态还有两个必须重要条件&#xff1a; 1.3 重载 和 虚函数的重写/覆盖 和 隐藏 的比对 1.4 协变(了解) 1.5 析构函数的重写 1.6 override 和final关键字 二.纯虚函数和抽象类 三. 多态的原理 3.1虚函数表指针 3.…

黑马JavaWeb开发跟学(十一)SpringBootWeb案例

黑马JavaWeb开发跟学十一.SpringBootWeb案例 SpringBootWeb案例1. 新增员工1.1 需求1.2 接口文档1.3 思路分析1.4 功能开发1.5 功能测试1.6 前后端联调 2. 文件上传2.1 简介2.2 本地存储2.3 阿里云OSS2.3.1 准备2.3.2 入门2.3.3 集成 3. 修改员工3.1 查询回显3.1.1 接口文档3.1…

性能测试笔记2-总

安装路径&#xff1a;先装jdk,后装JMeter 安装JDK&#xff1a; 下载JDK – 安装JDK – 配置环境变量 – 验证 安装Jmeter&#xff1a; 下载Jmeter – 安装Jmeter – 配置环境变量 – 启动验证 注意点&#xff1a; 下载JDK时&#xff0c;注意电脑操作系统是32位/64位 下载…

力扣 简单 110.平衡二叉树

文章目录 题目介绍解法 题目介绍 解法 平衡二叉树:任意节点的左子树和右子树的高度之差的绝对值不超过 1 //利用递归方法自顶向下判断以每个节点为根节点的左右子树的最大深度是否大于1 class Solution {public boolean isBalanced(TreeNode root) {if(root null){return tr…

Uniapp API

1.uni.showToast 显示消息提示框 unishowToast({ obj参数 }) 2.uni.showLoading 显示 loading 提示框, 需主动调用 uni.hideLoading 才能关闭提示框。 3.uni.showModal 显示模态弹窗&#xff0c;可以只有一个确定按钮&#xff0c;也可以同时有确定和取消按钮。类似于一个A…

windows上安装python环境

前言 最近电脑重装了系统&#xff0c;需要重新安装python环境 &#xff0c;因此记录一下 1.下载 打开python官网下载&#xff0c;下载链接&#xff1a;https://www.python.org/downloads/windows/ 点击下载 &#xff0c;我这里使用64位操作系统(大部分电脑)&#xff0c;根据…