【JavaEE】spring boot快速上手

SpringBoot快速上手

文章目录

  • SpringBoot快速上手
    • Maven
      • 会出现的一个官方bug
      • 创建完项目之后
      • 常用的的三个功能
      • 依赖管理
      • Maven仓库
        • 中央仓库
        • 本地仓库
        • 国内源配置
        • 私服
    • springboot项目创建
      • 什么是spring
      • spring boot项目的创建
      • Hello World
        • web服务器
    • SpringMVC
      • 什么是SpringWebMVC
        • 什么是MVC
      • SpringMVC
      • 学习Spring web mvc
        • 建立连接
          • @RequestMapping
      • 请求
        • 指定请求方式
        • 请求单个参数
        • 请求多个参数
        • 传递对象
        • 参数重命名
          • 设置参数为非必传的
        • 传递数组
        • 传递集合
        • 传递`JSON`数据
          • JSON与Javascript的关系
          • JSON 优点
        • 获取URL中的参数
        • 上传文件
        • 获取Cookie/Session
          • Cookie
          • Session
          • Cookie和Session的区别
          • 传统方式获取Cookie
          • SpringBoot获取Cookie
          • 传统方式获取Session
          • SpringBoot获取Session
        • 获取Header
          • 传统获取Header
          • springboot方式获取Header
      • 响应
        • 返回静态页面
          • `@RestContraller` 和 `@Controller`的区别
          • 路径问题
          • 一个项目部署多个服务
        • 返回数据@ResponseBody
        • 返回HTML片段
        • 返回JSON
        • 设置状态码
        • 设置Header
          • 设置Content-Type
          • 设置其他Header
  • 案例
    • 加法计算器
      • 前端代码
      • 后端代码
        • 接口定义
    • 用户登录
      • 前端代码
      • 后端代码
    • 留言板
      • 前端代码
      • 后端代码
        • lombok工具包介绍
    • 图书管理系统
      • 定义接口
      • MOCK
      • 应用分层
        • 命名规范
        • MVC和三层架构之间的关系
  • SpringMVC小结
    • 什么是springmvc
    • @RequestMapping
    • 请求
    • 响应
    • 注解总结
    • Cookie和Session

学习流程介绍:

  1. spring boot
  2. springmvc
  3. spring framework
  4. mybatis
  5. spring 源码

Maven

项目管理工具,idea中将他嵌入进来了

  1. 项目构建、打包
  2. 依赖管理

会出现的一个官方bug

就是当你创建maven项目的时候会卡死

那么怎么办呢?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你只能先关闭原来项目,再重新去创建maven项目。

创建完项目之后

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建完maven项目之后,idea右边会出现一个maven的框

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

那么右键选择

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

就可以解决这个问题.

常用的的三个功能

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

clean:清理class文件(也就是清理缓存)

package:打包

install:在工作中常用于发布包到本地

依赖管理

通过poe.xml

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

依赖已经进来了.

  1. 会将当前的依赖引入到当前项目里面

  2. Maven Helper

    插件,可以查看依赖之间的关系

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Maven仓库

仓库:

  1. 本地仓库
  2. 远程仓库
    1. 中央仓库
    2. 私有仓库
中央仓库

中央仓库

中央仓库查询会有一定的滞后性

本地仓库

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

需要自己配置噢

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

国内源配置

找到setting.xml文件,在 mirrors 节点上,添加内容如下:

<mirror><id>aliyunmaven</id><mirrorOf>central</mirrorOf><name>阿⾥云公共仓库</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url></mirror>

然后再设置好新项目的setting.建议找一个存储空间大的盘,像我一样设置(如上图).因为随着时间推移,外卖做的项目的数量变多,本地仓库中的setting文件的占用的内存也会越来越多.

还有就是建议命名不要用中文!

私服

企业开发中一些代码具有一定的私密性,所以企业会建立自己的私服(需要账号密码)


外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

springboot项目创建

建议是申请教育版本的ideaa或者专业版的idea噢~

如果是社区版的idea,那么就需要你去下载spring 插件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

别直接点击Install,除非你钱多~

插件地址

什么是spring

spring是一个非常好用的框架,快 简单 安全

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

spring是一个家族产品,在面试中提到spring,大多是指spring家族.

spring boot的诞生就是未来简化spring程序开发的


spring boot项目的创建

  1. 需要注意的是现在idea2023创建spring项目的时候,只有jdk17 jdk21,可以选择,这里我的解决办法是替换项目的源头,我们只知道IDEA页面创建Spring项目,其实是访问spring initializr去创建项目。故我们可以通过阿里云国服去间接创建Spring项目。将https://start.spring.io/或者http://start.springboot.io/替换为 https://start.aliyun.com/.即可解决这个问题.

  2. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  3. 选择springboot版本,选择2.X版本,因为2.X使用的是JDK8,也不要选择snapshot版本.(不稳定版本)

    snapshot 程序有问题的话,可以修改,重新发包

    非 snapshot 是不能修改的,若需要修改,只能改版本号

  4. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  5. 在这里插入图片描述

  6. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    此处的测试代码,和测试人员无关,是开发人员的测试代码,

    开发人员先进行简单测试,测试完成之后再提交给测试人员.单元测试代码

  7. 项目启动

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Hello World

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建HelloController

@RestController
public class HelloController {@RequestMapping("/hello")public String hello(){return "hello, SpringBoot";}
}

然后再网页中输入localhost:8080/hello

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

web服务器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

常⻅的Web服务器有: Apache,Nginx, IIS, Tomcat, Jboss

SpringBoot 内置了Tomcat服务器, ⽆需配置即可直接运⾏

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Tocmat默认端⼝号是8080, 所以我们程序访问时的端⼝号也是8080


SpringMVC

本小节将会了解到的:

  1. 学习常见的Spring MVC Web注解
  2. 了解SPring MVC来完成基础功能开发
  3. 了解MVC和三层架构的设计模式
  4. 掌握企业开发的命名规范

什么是SpringWebMVC

Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring框架中。它的正式名称“Spring Web MVC”来自其源模块的名称(Spring-webmvc),但它通常被称为"SpringMVC".

什么是MVC

Model模型:是应⽤程序的主体部分,⽤来处理程序中数据逻辑的部分

View视图:指在应⽤程序中专⻔⽤来与浏览器进⾏交互,展⽰数据的资源

Controller控制器:可以理解为⼀个分发器,⽤来决定对于视图发来的请求,需要⽤哪⼀个模型来处理,以及处理完后需要跳回到哪⼀个视图。即⽤来连接视图和模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

MVC是一种思想,而Spring MVCMVC的一种实现


SpringMVC

SpringMVC项目的创建和上面创建SpringBoot项目一样.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不过目前推崇的前后端分离已经不需要View层了,于是乎:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

学习Spring web mvc

  1. 建立连接
  2. 请求
  3. 响应
建立连接
@RequestMapping

类注解、方法注解

作用范围:

  1. 当一个类有多个注解的时候,这些注解没有先后顺序

    类路径+方法路径

    @RequestMapping("/user")// /可加可不加 但是我建议你加
    @RestController
    public class UserController {@RequestMapping("/hello")public String hello(){return "hello";}
    }

    此时访问路径:[127.0.0.1:8080/user/hello](http://127.0.0.1:8080/user/hello)

  2. 方法上

    @RestController
    public class UserController {@RequestMapping("/hello")public String hello(){return "hello";}
    }
    

请求方式是Get还是Post

  1. get通过浏览器访问的方式为get
  2. post

请求

指定请求方式
@RequestMapping("/user")
@RestController
public class UserController {@RequestMapping(value = "/hello",method = RequestMethod.GET)// 注解里,双引号的值会赋给"value"这个属性// 只有一个属性的时候,且属性名为value,可以省略public String hello(){return "hello";}
}
请求单个参数
@RequestMapping("/r1")public String r1(String name){return "接受到参数 name:" + name;}@RequestMapping("/r2")public String r2(int age){return "接受到参数 age:" + age;}
请求多个参数
@RequestMapping("/r3")public String r3(String name,Integer age){return "name:"+name+" "+"age:"+age;// 参数请求 顺序先后不分}
传递对象
// 创建userInfo
public class UserInfo {private Integer id;private String name;private Integer age;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "UserInfo{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}
}
@RequestMapping("/r4")public String r4(UserInfo user){return user.toString();}
参数重命名

@RequestParam

@RequestMapping("/r5")public String r4(@RequestParam("name") String username, Integer age){return "username: " + username+ ", age: " + age;}

@RequestParam(“name”) 从请求中获取 name 的参数,并且赋值给 username 参数 且默认这个参数是必传的

设置参数为非必传的

我们先看一下RequestParam

public @interface RequestParam {@AliasFor("name")String value() default "";@AliasFor("value")String name() default "";boolean required() default true;String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}

于是乎我们可以:将required设置为false

@RequestMapping("/r5")public String r5(@RequestParam(value = "name", required = false) String username, Integer age){return "username: " + username+ ", age: " + age;}
传递数组
@RequestMapping("/r7")public String r7(String[]arr){return Arrays.toString(arr);}
传递集合
@RequestMapping("/r8")public String r8(@RequestParam("list") List<String> list){return list.toString();}

在Spring MVC中,@RequestParam 注解用于从请求中提取参数值。@RequestParam("list") List<String> list 表示从请求中获取名为 “list” 的参数,并将其绑定到一个 List<String> 类型的变量 list 上。

这种方式常用于接收前端传递的多个相同类型的参数,例如,前端通过 URL 或者表单提交将多个字符串参数传递给后端。通过将它们绑定到一个 List<String> 类型的参数上,你可以轻松地处理多个相同类型的参数。

请求 URL 如下:

/r8?list=value1&list=value2&list=value3

通过上述的 @RequestParam("list") List<String> list,Spring 将自动将这些值绑定到一个列表中,你可以在方法体内使用这个 list 参数来访问传递的多个值。

总的来说,这是一种方便的方式,用于处理请求中包含多个相同类型参数的场景。

在Web开发中,有时候我们需要从前端接收一组相同类型的参数。使用 List<String> 类型的参数绑定可以方便地处理这种情况,而不需要为每个参数定义一个独立的变量。

考虑以下情景:

  1. 表单提交: 当用户通过表单提交多个相同类型的数据时,可以将这些数据绑定到一个 List<String> 中。例如,一个多选框(Checkbox)的多个选项。

  2. URL参数: 当通过URL传递多个相同类型的参数时,使用List<String> 可以更清晰地表达意图。例如,/r8?list=value1&list=value2&list=value3

  3. RESTful风格的请求: 在RESTful风格的API中,有时需要从请求体或路径中接收多个相同类型的数据。

绑定到 List<String> 的好处包括:

  • 代码简洁: 不需要为每个参数定义一个变量,通过一个 List 就可以容纳所有的值。

  • 可扩展性: 如果前端需要传递更多的相同类型的参数,代码不需要做太多修改。

  • 清晰明了: 通过命名为 list,表达了这是一组相同类型的数据。

传递JSON数据
JSON与Javascript的关系

没有关系, 只是语法相似, js开发者能更快的上⼿⽽已, 但是他的语法本⾝⽐较简单, 所以也很好学

JSON 优点
  1. 简单易⽤: 语法简单,易于理解和编写,可以快速地进⾏数据交换

  2. 跨平台⽀持: JSON可以被多种编程语⾔解析和⽣成, 可以在不同的平台和语⾔之间进⾏数据交换和传输

  3. 轻量级: 相较于XML格式, JSON数据格式更加轻量级, 传输数据时占⽤带宽较⼩, 可以提⾼数据传输速度

  4. 易于扩展: JSON的数据结构灵活,⽀持嵌套对象和数组等复杂的数据结构,便于扩展和使⽤

  5. 安全性: JSON数据格式是⼀种纯⽂本格式,不包含可执⾏代码, 不会执⾏恶意代码,因此具有较⾼的安全性

public class JSONUtils {public static void main(String[] args) throws JsonProcessingException {ObjectMapper objectMapper = new ObjectMapper();UserInfo userInfo = new UserInfo();userInfo.setName("zhangsan");userInfo.setAge(18);userInfo.setId(12);// 对象转 JSONString s = objectMapper.writeValueAsString(userInfo);System.out.println(s);// JSON 转成 java 对象UserInfo userInfo1 = objectMapper.readValue(s,UserInfo.class);System.out.println(userInfo1);}
}
@RequestMapping("/r9")public String r9(@RequestBody UserInfo userInfo){return userInfo.toString();}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

获取URL中的参数

@PathVariable

@RequestMapping("/r10/{articleId}")public String r10(@PathVariable Integer articleId){return "articleId:"+articleId;}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上传文件

@RequestPart

@RequestMapping("/r11")public String r11(@RequestPart MultipartFile file){return "获取上传文件:" + file.getOriginalFilename();}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

@RequestMapping("/r11")public String r11(@RequestPart MultipartFile file) throws IOException {String fileName = file.getOriginalFilename();file.transferTo(new File("D:/temp/"+fileName));return "获取上传文件:" + file.getOriginalFilename();}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这段代码虽然也可以不加注解,但是建议还是将注解加上去

获取Cookie/Session
Cookie

HTTP 协议⾃⾝是属于 “⽆状态” 协议.

无状态协议:

默认情况下 HTTP 协议的客⼾端和服务器之间的这次通信, 和下次通信之间没有直接的联系.

但是实际开发中, 我们很多时候是需要知道请求之间的关联关系的.

例如登陆⽹站成功后, 第⼆次访问的时候服务器就能知道该请求是否是已经登陆过了.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Session

会话:对话的意思

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在计算机领域, 会话是⼀个客⼾与服务器之间的不中断的请求响应. 对客⼾的每个请求,服务器能够识

别出请求来⾃于同⼀个客⼾. 当⼀个未知的客⼾向Web应⽤程序发送第⼀个请求时就开始了⼀个会话.

当客⼾明确结束会话或服务器在⼀个时限内没有接受到客⼾的任何请求时,会话就结束了.

⽐如我们打客服电话

每次打客服电话, 是⼀个会话. 挂断电话, 会话就结束了

下次再打客服电话, ⼜是⼀个新的会话.

如果我们⻓时间不说话, 没有新的请求, 会话也会结束

服务器同⼀时刻收到的请求是很多的. 服务器需要清楚的区分每个请求是从属于哪个⽤⼾, 也就是属于哪个会话, 就需要在服务器这边记录每个会话以及与⽤⼾的信息的对应关系.

Session是服务器为了保存⽤⼾信息⽽创建的⼀个特殊的对象.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Session的本质就是⼀个 “哈希表”, 存储了⼀些键值对结构. Key 就是SessionID, Value 就是⽤⼾信息(⽤⼾信息可以根据需求灵活设计).

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SessionIsd是由服务器生成的一个"唯一性字符串",从Session机制的角度来看,这个唯一性字符串称为"Sessionld".但是站在整个登录流程中看待,也可以把这个唯一性字符串称为"token".
上述例子中的令牌ID,就可以看做是Sessionld,只不过令牌除了ID之外,还会带一些其他信息,比如时间,签名等.

  1. 当⽤⼾登陆的时候, 服务器在 Session 中新增⼀个新记录, 并把 sessionId返回给客⼾端. (通过HTTP 响应中的 Set-Cookie 字段返回).

  2. 客⼾端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId. (通过 HTTP 请求中的Cookie 字段带上).

  3. 服务器收到请求之后, 根据请求中的 sessionId在 Session 信息中获取到对应的⽤⼾信息, 再进⾏后续操作.找不到则重新创建Session, 并把SessionID返回

Session默认是保存在内存中的.如果重启服务器则Session数据就会丢失.

Cookie和Session的区别

在此之前我们可以举一个例子

学生入学

学校收集学生信息,姓名,班级,身份证号等等

那么学校该如何存储呢?

NumberNameAgeAttribute
1张三18CS
2李四17CS
3王五20CS

学校存储信息之后,给学生发学生证

学生出入学校,使用学生证(就好比是Cookie)来证明身份

但是,学生证可以造假,但是学校的教务系统的信息是造假不了的

于是乎:Cookie是可以造假的,但是Session不行

  1. 用户提供账号和密码,服务器进行验证。

  2. 服务器验证通过,会把信息存储在Session中,把SessionId返回给客户端(通过Set-Cookie的方式)

  3. 客户端收到响应,把sessionID存储在Cookie

  4. 后续的请求中,客户端带着SessionID去请求(带着Cookie信息去请求)

    request.getSession就是从Cookie中获取SessionID,并且根据SessionID获取Session信息

区别如下:

  • Cookie是客户端保存用户信息的一种机制.Session是服务器端保存用户信息的一种机制:
  • CookieSession之间主要是通过Sessionld关联起来的,SessionldCookieSession之间的桥梁
  • CookieSession经常会在一起配合使用.但是不是必须配合.
    • 完全可以用Cookie来保存一些数据在客户端.这些数据不一定是用户身份信息,也不一定是Sessionld
    • Session中的sessionld也不需要非得通过Cookie/Set-Cookie传递,比如通过URL传递

共同点:都是会话机制

Cookie是客户端机制

Session是服务器机制

Cookie存储的信息由程序员而定

Session也不一定必须存在Cookie

传统方式获取Cookie
@RestController
@RequestMapping("/request")
public class RequestController {@RequestMapping("/getCookie")public String getCookie(HttpServletRequest request) // 内置对象,有需要就加上,没需要就不加 需要几个就加几个{Cookie[] cookies = request.getCookies();//        Arrays.stream(cookies).forEach(x->{
//            System.out.println(x.getName()+":"+x.getValue());
//        });// 等价于if (cookies != null){for(Cookie c:cookies){System.out.println(c.getName()+":"+c.getValue());}return "获取Cookies成功";}elsereturn "获取Cookies不成功";}
}
SpringBoot获取Cookie
@RequestMapping("/getCookie2")public String getCookie2(@CookieValue ("riyewuxiushi")String riyewuxiushi){return "riyewuxiushi"+riyewuxiushi;}
传统方式获取Session
	@RequestMapping("/setSession")public String setSession(HttpServletRequest request){HttpSession session = request.getSession(); // 默认值为 truesession.setAttribute("userName","zhangsan");return "设置session成功";}@RequestMapping("/getSession")public String getSession(HttpServletRequest request){HttpSession session = request.getSession();String userName = (String) session.getAttribute("userName");return "登录用户:"+ userName;}
SpringBoot获取Session
@RequestMapping("/getSession2")public String getSession2(HttpSession session){ // 内置对象String userName = (String) session.getAttribute("userName");return "登录用户:"+ userName;}
	@RequestMapping("/getSession3")public String getSession3(@SessionAttribute(value = "userName",required = false) String userName){return "登录用户:"+ userName;}
获取Header
传统获取Header
	@RequestMapping("/getheader")public String getheader(HttpServletRequest request){String userAgent = request.getHeader("User-Agent");return "userAgent"+userAgent;}
springboot方式获取Header
	@RequestMapping("/getheader2")public String getheader2(@RequestHeader("User-Agent")String userAgent){return "userAgent"+userAgent;}

响应

返回静态页面
@RequestMapping("/return")
//@RestController
@Controller
// 多个注解的时候 注解不分先后顺序
public class ReturnController {@RequestMapping("/r1")public String r1(){return "/index.html";}
}
@RestContraller@Controller的区别

@RestController@Controller 是 Spring Framework 中用于标记类的注解,用于定义处理 HTTP 请求的控制器。它们之间有一些区别和联系。

区别:

  1. 返回值处理:@Controller 通常用于创建传统的基于视图的 Web 应用程序,它的方法可以返回模型数据和视图名称,最终由视图解析器解析为具体的视图。而 @RestController 则是用于创建 RESTful Web 服务的控制器,它的方法返回的是数据对象,会自动通过消息转换器将数据转为 JSON/XML 等格式,不会经过视图解析器。
  2. 默认行为:@RestController 组合了 @Controller@ResponseBody 注解的功能。@ResponseBody 注解表示方法的返回值将直接写入 HTTP 响应体中,而不是通过视图解析器解析为视图。因此,@RestController 类的每个方法都默认返回数据对象,而不是视图。
  3. 使用场景:@Controller 适用于传统的基于视图的 Web 应用程序,例如使用 Thymeleaf、JSP 或者其他模板引擎渲染视图。@RestController 适用于构建 RESTful Web 服务,响应 JSON 或 XML 格式的数据。

联系:

  1. 标记作用:@RestController@Controller 都是用于标记类的注解,将类声明为 Spring Framework 的组件,用于处理 HTTP 请求。
  2. 注解继承:@RestController@Controller 注解的特殊化,可以认为是 @Controller 的增强版本。@RestController 继承了 @Controller 的所有功能,同时还提供了自动将方法返回值转换为数据格式的能力。
// @RestController 源码
@Target({ElementType.TYPE}) // 表示注解的范围
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期
@Documented // 
// 上面三个是元注解:是可以注解到 其他注解 的注解
@Controller // ----> 这说明 RestContraller 是基于 Controller 实现的
@ResponseBody
public @interface RestController {@AliasFor(annotation = Controller.class)String value() default "";
}

@RestController = @Controller + @ResponseBody

@Controller : 告诉Spring帮我们管理那些程序

@ResponseBody:返回数据

路径问题
  • servlet路径有项目名,是因为一个tomcat下面可以部署多个项目,我们需要通过路径来进行区分
  • spring路径不需要有项目名,是因为springboot内置了tomcat,一个tomcat下面就部署当前这一个项目
  • 如果部署多个项目,就启动多个tomcat
一个项目部署多个服务

IDEA 2023.2新版如何将同一个项目开启多个

返回数据@ResponseBody
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}

即可以修饰类,又可以修饰方法

修饰类:表示该类所有的方法 返回的是数据

修饰方法:表示该方法返回的是数据

	@RequestMapping("/r1")public String r1(){return "/index.html";}@ResponseBody@RequestMapping("/r2")public String r2(){return "hello spring";}
返回HTML片段
	@ResponseBody@RequestMapping("/r3")public String r3(){return "<h1>我是返回的片段</h1>";}
  • get可以被缓存 幂等
  • post不可以被缓存
返回JSON
	@ResponseBody@RequestMapping("/r4")public UserInfo r4(){UserInfo userInfo = new UserInfo();userInfo.setId(1);userInfo.setName("zhangsan");userInfo.setAge(19);return userInfo;}@ResponseBody@RequestMapping("/r5")public Map<String ,String> r5(){HashMap map = new HashMap();map.put("k1","v1");map.put("k2","v2");return map;}//@ResponseBody@RequestMapping("/r6")public String r6(){return "/a.js";}@RequestMapping("/r7")public String r7(){return "/b.css";}
设置状态码
	@ResponseBody@RequestMapping("/r8")public String r8(HttpServletResponse response){response.setStatus(401);return "设置状态码成功";}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

状态码的设置不影响页面的显示

设置Header
设置Content-Type

我们通过设置 produces属性的值, 设置响应的报头Content-Type

// @RequestMapping源码@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {String name() default "";@AliasFor("path")String[] value() default {};@AliasFor("value")String[] path() default {};RequestMethod[] method() default {};String[] params() default {};String[] headers() default {};String[] consumes() default {};String[] produces() default {};
}
  1. value: 指定映射的URL

  2. method: 指定请求的method类型, 如GET, POST

  3. consumes: 指定处理请求(request)的提交内容类型(Content-Type),例如application/json,text/html;

  4. produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回

  5. Params: 指定request中必须包含某些参数值时,才让该⽅法处理

  6. headers: 指定request中必须包含某些指定的header值,才能让该⽅法处理请求

 	@ResponseBody@RequestMapping("/r9")public String r9(){return "123333";}@ResponseBody@RequestMapping(value = "/r9",produces = "application/json")public String r9(){return "123333";}
设置其他Header

设置其他Header的话, 需要使⽤Spring MVC的内置对象HttpServletResponse 提供的⽅法来进⾏设置

	@ResponseBody@RequestMapping(value = "/r10",produces = "application/json")public String r10(HttpServletResponse response){response.setHeader("myHeader","myHeaderValue");return "设置Header成功";}

void setHeader(String name, String value) 设置⼀个带有给定的名称和值的 header. 如果 name已经存在, 则覆盖旧的值

案例

学习建议:

最开始学习的时候:小步慢跑

每次写少量的代码,就进行测试

不要一次把代码全部写完,一次性进行测试

随着对代码的熟悉,可以逐渐加大步伐

加法计算器

前端代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><form action="calc/sum" method="post"><h1>计算器</h1>数字1:<input name="num1" type="text"><br>数字2:<input name="num2" type="text"><br><input type="submit" value=" 点击相加 "></form>
</body></html>

后端接到需求的时候

  1. 需求评审
  2. 开发
    1. 接口的定义
    2. 开发
    3. 测试(自行测试,与测试人员无关)
  3. 联调(后端和前端联调)
  4. 提测(测试人员的工作)
  5. 上线
  6. 维护
  7. 下线

后端代码

接口定义

两个原则

  1. 看我需要什么(请求参数)
  2. 看对方需要什么(响应结果)

请求参数:参与计算的两个数字

响应结果:计算结果

/calc/sum

参数:num1 , num2

返回结果:两者计算的数据


请求路径:calc/sum

请求⽅式:GET/POST

接⼝描述:计算两个整数相加

请求参数:

参数名类型是否必须备注
num1Integer参与计算的第一个数
num2Integer参与计算的第二个数

响应数据:

Content-Type : text/html

@RestController
@RequestMapping("/calc")
public class CalcController {@RequestMapping("/sum")public String sum(Integer num1, Integer num2){Integer sum  = num1 + num2;return "<h1>计算机计算结果: "+sum+"</h1>";}
}

问题可能出现的地方:

  1. 前端
  2. 后端
  3. 前后端交互
    1. 请求有没有发出去
    2. 后端有没有收到请求

用户登录

前端代码

// login.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>登录页面</title></head><body><h1>用户登录</h1>用户名:<input name="userName" type="text" id="userName"><br>密码:<input name="password" type="password" id="password"><br><input type="button" value="登录" onclick="login()"><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>function login() {$.ajax({type:"post",url:"/login/check",data:{userName:$("#userName").val(),password:$("#password").val()},success:function(result){if (result == true){// 用户名和密码正确location.href = "/index.html";// location.assign("index.html");// location.replace("index.html");}else{alert("用户名或密码错误");}}});}</script></body></html>
// index.html
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>用户登录首页</title></head><body>登录人: <span id="loginUser"></span><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>$.ajax({url:"/login/index",type:"get",success:function (result) {$("#loginUser").text(result);}});</script></body></html>

后端代码

用户验证:

/login/check

参数:

userName

password

响应:

用户和密码是否正确

true

false


获取登录的用户

/login/index

参数:

响应:

登录的用户

@RequestMapping("/login")
@RestController
public class LoginController {@RequestMapping("/check")public boolean check(String userName, String password, HttpSession session){// 校验账号和密码是否为空
//        if (userName == null || "".equals(userName) || password == null || "".equals(password)){
//            return false;
//        }if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){return false;}// 校验账号和密码是否正确// 模拟数据if("zhangsan".equals(userName) && "123456".equals(password)){ // 防止空指针,养成习惯 常量写在前面session.setAttribute("userName",userName);return true;}return false;}@RequestMapping("/index")public String index(HttpSession session){String userName = (String) session.getAttribute("userName");return userName;}
}

留言板

前端代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>留言板</title><style>.container {width: 350px;height: 300px;margin: 0 auto;/* border: 1px black solid; */text-align: center;}.grey {color: grey;}.container .row {width: 350px;height: 40px;display: flex;justify-content: space-between;align-items: center;}.container .row input {width: 260px;height: 30px;}#submit {width: 350px;height: 40px;background-color: orange;color: white;border: none;margin: 10px;border-radius: 5px;font-size: 20px;}</style></head><body><div class="container"><h1>留言板</h1><p class="grey">输入后点击提交, 会将信息显示下方空白处</p><div class="row"><span>谁:</span> <input type="text" name="" id="from"></div><div class="row"><span>对谁:</span> <input type="text" name="" id="to"></div><div class="row"><span>说什么:</span> <input type="text" name="" id="say"></div><input type="button" value="提交" id="submit" onclick="submit()"><!-- <div>A 对 B 说: hello</div> --></div><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>// 页面加载时,显示留言信息// 从后端获取留言信息,显示在页面上$.ajax({type:"get",url:"/message/getList",success:function (messages) {for (var message of messages){var html = "<div>"+message.from+"对"+message.to+"说:"+message.message+"</div>";$(".container").append(html);}}});function submit(){//1. 获取留言的内容var from = $('#from').val();var to = $('#to').val();var say = $('#say').val();if (from== '' || to == '' || say == '') {return;}$.ajax({type:"post",url:"/message/publish",data:{from:from,to:to,message:say},success:function (result) {if (result == true){// 添加成功//2. 构造节点var divE = "<div>"+from +"对" + to + "说:" + say+"</div>";//3. 把节点添加到页面上$(".container").append(divE);//4. 清空输入框的值$('#from').val("");$('#to').val("");$('#say').val("");}else{alert("发表失败");}}});}</script></body></html>

后端代码

  1. 提交留言:用户输入留言信息的时候,后端需要将留言信息保存起来

    URL:/message/publish

    参数

    • from:发表人
    • to:接收人
    • message:信息

    返回:提交成功/失败

    true、false

  2. 展示留言:页面展示的时候,需要从后端获取到所有的留言信息

    URL:/message/getList

    参数

    返回:全部的留言信息

    List<MessageInfo>

lombok工具包介绍
  1. 新项目

    创建项目的时候直接加入依赖

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 老项目

    在poe.xml中引入依赖,去maven中央仓库找

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version><scope>provided</scope></dependency>
    
@Data
public class MessageInfo {private String from;private String to;private String message;}

@Data 会自动生成 get、set方法

idea会自己进行反编译

单独使用可以@Getter @Setter

注解作用
@Getter自动添加getter方法
@Setter自动添加setter方法
@ToString自动添加toString方法
@EqualsAndHashCode⾃动添加 equals 和 hashCode ⽅法
@NoArgsConstructor自动添加无参构造方法
@AllArgsConstructor自动添加全属性构造方法,顺序按照属性的定义顺序
@NonNull属性不能为null
@RequiredArgsConstructor自动添加必需属性的构造方法,final+@NonNull的属性为必需

@Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor

+ @NoArgsConstructor

  1. 更快地引入依赖

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    下载这个就好啦~然后重启IDEA

    使用方法

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

package org.example.springmvc_demo;import lombok.Data;/*** @author 日夜无休时* @date 2024/1/29*/
@Data
public class MessageInfo {private String from;private String to;private String message;// 换一个新工具 lombook @Data
//    public String getFrom() {
//        return from;
//    }
//
//    public void setFrom(String from) {
//        this.from = from;
//    }
//
//    public String getTo() {
//        return to;
//    }
//
//    public void setTo(String to) {
//        this.to = to;
//    }
//
//    public String getMessage() {
//        return message;
//    }
//
//    public void setMessage(String message) {
//        this.message = message;
//    }}
package org.example.springmvc_demo;import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;/*** @author 日夜无休时* @date 2024/1/29*/
@RestController
@RequestMapping("/message")
public class MessageController {private  List<MessageInfo>messageInfos = new ArrayList<>();@RequestMapping("/publish")public boolean publishMessage(MessageInfo messageInfo){if (!StringUtils.hasLength(messageInfo.getFrom())|| !StringUtils.hasLength(messageInfo.getTo())|| !StringUtils.hasLength((messageInfo.getMessage()))){return false;}// 暂时存放在内存中messageInfos.add(messageInfo);return true;}@RequestMapping("/getList")public List<MessageInfo> getList(){for (MessageInfo messageInfo : messageInfos){}return messageInfos;}
}

图书管理系统

定义接口

接口定义:

  • 服务提供方:

    1. 提供什么服务

    2. 提供服务时,需要什么参数

    3. 处理之后,需要给对方什么响应

  • 客户端角度:

    1. 我需要什么服务
    2. 服务端的参数 我是否有
    3. 对方给我提供的信息,能否满足我的需求
  1. 登录

    URL: /user/login

    参数:用户名和密码

    userName

    password

    返回:

    true:用户名和密码正确

    false:用户名和密码错误

  2. 图书列表

    URL:/book/getList

    参数:无

    返回:图书列表

    List

MOCK

虚拟的、假的。开发的时候通常是几个团队并行开发,开发后需要进行测试(自测),如果测试时,依赖方还没完成开发,调用方就采用mock的方式,先进行测试。

应用分层

一种开发规范

三层架构(软件设计架构方式)

  1. 表现层:就是展示数据结果和接受用户指令的,是最靠近用户的一层
  2. 业务逻辑层:负责处理业务逻辑,里面有复杂业务的具体实现
  3. 数据层:负责存储和管理与应用程序相关的数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

命名规范
  1. 类名 大驼峰
  2. 变量名 小驼峰
MVC和三层架构之间的关系

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

共同点

  • 解耦(高内聚,低耦合)

    模块内 关系尽量紧密(高内聚)

    模块间 关系尽量关联低(低耦合)

    比如说:公司与公司之间,关联应该越小越好,公司内部,员工应该团结

SpringMVC小结

什么是springmvc

spring web mvc

@RequestMapping

既是类注解,也是方法注解

访问的URL路径 = 类路径 + 方法路径

默认支持 get/post,可以使用method属性来限制请求方式

请求

  1. 请求当个参数
  2. 请求多个参数
  3. 请求参数为对象
  4. 对参数重命名 @RequestParam 默认是必传参数,设置 required = false 就是非必传
  5. 设置参数为非必传
  6. 请求参数为JSON @RequestBody
  7. Cookie & Session
  8. 传递数组
  9. 传递集合@RequestParam
  10. 获取Header

响应

  1. 返回静态页面
  2. 返回数据@ResponseBody
  3. 返回HTML片段
  4. 返回JSON
  5. 设置响应头(状态码、编码、其他header

注解总结

  1. @RequestMapping: 路由映射
  2. @RequestParam: 后端参数重命名
  3. @RequestBody: 接收JSON类型的参数
  4. @PathVariable: 接收路径参数
  5. @RequestPart: 上传⽂件
  6. @ResponseBody: 返回数据
  7. @CookieValue: 从Cookie中获取值
  8. @SessionAttribute: 从Session中获取值
  9. @RequestHeader: 从Header中获取值
  10. @Controller: 定义⼀个控制器, Spring 框架启动时加载, 把这个对象交给Spring管理. 默认返回视图.
  11. @RestController: @ResponseBody + @Controller 返回数据

Cookie和Session

Cookie 和Session都是会话机制, Cookie是客⼾端机制, Session是服务端机制. ⼆者通过SessionId来关联. Spring MVC内置HttpServletRequest, HttpServletResponse两个对象. 需要使⽤时, 直接在⽅法中添加对应参数即可, Cookie和Session可以从HttpServletRequest中来获取, 也可以直接使⽤HttpServletResponse设置Http响应状态码.

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mpack简明教程

文章目录 摘要MessagePack简介MPACK的简单使用在定长的buffer存储不定长的数据读取截断的数据 摘要 本文先简单介绍MessagePack的基本概念。 然后&#xff0c;介绍一个MessagePack C API - MPack的通常使用。 接着尝试对MPack截断数据的读取。 注&#xff1a;本文完整代码见…

Android 13.0 SystemUI下拉状态栏定制二 锁屏页面横竖屏解锁图标置顶显示功能实现

1.前言 在13.0的系统rom定制化开发中,在关于systemui的锁屏页面功能定制中,由于在平板横屏锁屏功能中,时钟显示的很大,并且是在左旁边居中显示的, 由于需要和竖屏显示一样,所以就需要用到小时钟显示,然后同样需要居中,所以就来分析下相关的源码,来实现具体的功能 如图…

【MySQL】:DQL查询

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. DQL1.1 基本语法1.2 基础查询1.3 条件查询1.3 聚合函数 &#x1f324;️ 全篇…

如何解决缓存和数据库的数据不一致问题

数据不一致问题是操作数据库和操作缓存值的过程中&#xff0c;其中一个操作失败的情况。实际上&#xff0c;即使这两个操作第一次执行时都没有失败&#xff0c;当有大量并发请求时&#xff0c;应用还是有可能读到不一致的数据。 如何更新缓存 更新缓存的步骤就两步&#xff0…

c语言--一维数组传参的本质(详解)

目录 一、前言二、代码三、形式3.1形式13.2形式2 四、总结 一、前言 首先从⼀个问题开始&#xff0c;我们之前都是在函数外部计算数组的元素个数&#xff0c;那我们可以把函数传给⼀个函数后&#xff0c;函数内部求数组的元素个数吗&#xff1f; 二、代码 直接上代码&#x…

初识Qt | 从安装到编写Hello World程序

文章目录 1.前端开发简单分类2.Qt的简单介绍3.Qt的安装和环境配置4.创建简单的Qt项目 1.前端开发简单分类 前端开发&#xff0c;这里是一个广义的概念&#xff0c;不单指网页开发&#xff0c;它的常见分类 网页开发&#xff1a;前端开发的主要领域&#xff0c;使用HTML、CSS …

『运维备忘录』之 Sed 命令详解

运维人员不仅要熟悉操作系统、服务器、网络等只是&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

C++中的volatile:穿越编译器的屏障

C中的volatile&#xff1a;穿越编译器的屏障 在C编程中&#xff0c;我们经常会遇到需要与硬件交互或多线程环境下访问共享数据的情况。为了确保程序的正确性和可预测性&#xff0c;C提供了关键字volatile来修饰变量。本文将深入解析C中的volatile关键字&#xff0c;介绍其作用、…

【c++】list 模拟

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;能手撕list模拟 > 毒鸡汤&#xff1a;不为模糊…

蓝桥杯:C++模运算、快速幂

模运算 模运算是大数运算中的常用操作。如果一个数太大&#xff0c;无法直接输出&#xff0c;或者不需要直接输出&#xff0c;则可以对它取模&#xff0c;缩小数值再输出。取模可以防止溢出&#xff0c;这是常见的操作。 模是英文mod的音译&#xff0c;取模实际上是求余。 取…

交通管理|交通管理在线服务系统|基于Springboot的交通管理系统设计与实现(源码+数据库+文档)

交通管理在线服务系统目录 目录 基于Springboot的交通管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户信息管理 2、驾驶证业务管理 3、机动车业务管理 4、机动车业务类型管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计…

【超级干货】ArcGIS_空间连接_工具详解

帮助里对空间连接的解释&#xff1a; 根据空间关系将一个要素的属性连接到另一个要素。 目标要素和来自连接要素的被连接属性写入到输出要素类。 如上图所示&#xff0c;关键在于空间关系&#xff0c;只有当两个要素存在空间关系的时候&#xff0c;空间连接才有用武之地。 一…

方式0控制流水灯循环点亮

#include<reg51.h> //包含51单片机寄存器定义的头文件 #include<intrins.h> //包含函数_nop_&#xff08;&#xff09;定义的头文件 unsigned char code Tab[]{0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};//流水灯控制码&#xff0c;该数组被定义为全局变量 sbit…

《UE5_C++多人TPS完整教程》学习笔记15 ——《P16 会话接口委托(Session Interface Delegates)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P16 会话接口委托&#xff08;Session Interface Delegates&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&#xf…

2.12日学习打卡----初学RocketMQ(三)

2.12日学习打卡 目录&#xff1a; 2.12日学习打卡一. RocketMQ高级特性&#xff08;续&#xff09;消息重试延迟消息消息查询 二.RocketMQ应用实战生产端发送同步消息发送异步消息单向发送消息顺序发送消息消费顺序消息全局顺序消息延迟消息事务消息消息查询 一. RocketMQ高级特…

红蓝对抗:网络安全领域的模拟实战演练

引言&#xff1a; 随着信息技术的快速发展&#xff0c;网络安全问题日益突出。为了应对这一挑战&#xff0c;企业和组织需要不断提升自身的安全防护能力。红蓝对抗作为一种模拟实战演练方法&#xff0c;在网络安全领域得到了广泛应用。本文将介绍红蓝对抗的概念、目的、过程和…

【精品】关于枚举的高级用法

枚举父接口 public interface BaseEnum {Integer getCode();String getLabel();/*** 根据值获取枚举** param code* param clazz* return*/static <E extends Enum<E> & BaseEnum> E getEnumByCode(Integer code, Class<E> clazz) {Objects.requireNonN…

ASCII编码的诞生:解决字符标准化与跨平台通信的需求

title: ASCII编码的诞生&#xff1a;解决字符标准化与跨平台通信的需求 date: 2024/2/17 14:27:01 updated: 2024/2/17 14:27:01 tags: ASCII编码标准化跨平台字符集兼容性简洁性影响力 在计算机的发展过程中&#xff0c;字符的表示和传输一直是一个重要的问题。为了实现字符的…

python-自动化篇-终极工具-用GUI自动控制键盘和鼠标-pyautogui

文章目录 用GUI自动控制键盘和鼠标pyautogui 模块鼠标——记忆宫殿屏幕位置——移动地图——pyautogui.size鼠标位置——自身定位——pyautogui.position()移动鼠标——pyautogui.moveTo拖动鼠标——滚动鼠标——scroll 键盘按下键盘释放键盘 开始与结束通过注销关闭所有程序 用…

linux系统zabbix监控分布式监控的部署

分布式监控 服务器安装分布式监控安装工具安装mysql导入数据结构配置proxy端浏览器配置 zabbix server端监控到大量zabbix agent端&#xff0c;这样会使zabbix server端压力过大&#xff0c;使用zabbix proxy进行分布式监控 服务器安装分布式监控 安装工具 rpm -Uvh https://…