SpringBoot

SpringBoot

1.快速搭建

用IDEA创建一个springboot项目

参考博客:

如何在idea中创建一个SpringBoot项目(超详细教学)_idea创建springboot项目_叫我老伯的博客-CSDN博客

直接按照上面的方法写内容就行,依赖会自动下载。注意博客的world都拼错来

HelloWorld.java:

路径:demo\src\main\java\com\example\demo\controller

内容:

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//请求处理类
@RestController //返回json格式
public class HelloWorld {
    @RequestMapping("/hello")
    public String hello()
    {
        return "hello Spring Boot!!!";
    }
}

2.再写一个引导类:HelloApplication.java

路径:demo\src\main\java\com\example\demo

内容:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
*引导类 springboot项目的入口 运行main方法就可以启动项目
 */
@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args){
        SpringApplication.run(HelloApplication.class,args);
    }
}

就可以运行了

通配符请求:

@GetMapping("/test/*")
public String test(){
    return "通配符请求";
}

2.HTTP

2.1概述

HTTP:Hyper Text Transfer Protocol(超文本传输协议),规定了浏览器与服务器之间数据传输的规则。

我们刚才初步认识了HTTP协议,那么我们在看看HTTP协议有哪些特点:

  • **基于TCP协议: ** 面向连接,安全

    TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全

  • 基于请求-响应模型: 一次请求对应一次响应(先请求后响应)

    请求和响应是一一对应关系,没有请求,就没有响应

  • HTTP协议是无状态协议: 对于数据没有记忆能力。每次请求-响应都是独立的

    无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。

    • 缺点: 多次请求间不能共享数据
    • 优点: 速度快

    请求之间无法共享数据会引发的问题:

    • 如:京东购物。加入购物车和去购物车结算是两次请求
    • 由于HTTP协议的无状态特性,加入购物车请求响应结束后,并未记录加入购物车是何商品
    • 发起去购物车结算的请求后,因为无法获取哪些商品加入了购物车,会导致此次请求无法正确展示数据

2.2HTTP-请求协议

浏览器和服务器是按照HTTP协议进行数据通信的。

HTTP协议又分为:请求协议和响应协议

  • 请求协议:浏览器将数据以请求格式发送到服务器
    • 包括:请求行、请求头 、请求体
  • 响应协议:服务器将数据以响应格式返回给浏览器
    • 包括:响应行 、响应头 、响应体

在HTTP1.1版本中,浏览器访问服务器的几种方式:

请求方式 请求说明
GET 获取资源。
向特定的资源发出请求。例:http://www.baidu.com/s?wd=itheima
POST 传输实体主体。
向指定资源提交数据进行处理请求(例:上传文件),数据被包含在请求体中。
OPTIONS 返回服务器针对特定资源所支持的HTTP请求方式。
因为并不是所有的服务器都支持规定的方法,为了安全有些服务器可能会禁止掉一些方法,例如:DELETE、PUT等。那么OPTIONS就是用来询问服务器支持的方法。
HEAD 获得报文首部。
HEAD方法类似GET方法,但是不同的是HEAD方法不要求返回数据。通常用于确认URI的有效性及资源更新时间等。
PUT 传输文件。
PUT方法用来传输文件。类似FTP协议,文件内容包含在请求报文的实体中,然后请求保存到URL指定的服务器位置。
DELETE 删除文件。
请求服务器删除Request-URI所标识的资源
TRACE 追踪路径。
回显服务器收到的请求,主要用于测试或诊断
CONNECT 要求用隧道协议连接代理。
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器

在我们实际应用中常用的也就是 :GET、POST

GET方式的请求协议:

  • 请求行 :HTTP请求中的第一行数据。由:请求方式、资源路径、协议/版本组成(之间使用空格分隔)

    • 请求方式:GET
    • 资源路径:/brand/findAll?name=OPPO&status=1
      • 请求路径:/brand/findAll
      • 请求参数:name=OPPO&status=1
        • 请求参数是以key=value形式出现
        • 多个请求参数之间使用&连接
      • 请求路径和请求参数之间使用?连接
    • 协议/版本:HTTP/1.1
  • 请求头 :第二行开始,上图黄色部分内容就是请求头。格式为key: value形式

    • http是个无状态的协议,所以在请求头设置浏览器的一些自身信息和想要响应的形式。这样服务器在收到信息后,就可以知道是谁,想干什么了

    常见的HTTP请求头有:

    Host: 表示请求的主机名
    
    User-Agent: 浏览器版本。 例如:Chrome浏览器的标识类似Mozilla/5.0 ...Chrome/79 ,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...)like Gecko
    
    Accept:表示浏览器能接收的资源类型,如text/*,image/*或者*/*表示所有;
    
    Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;
    
    Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate等。
    
    Content-Type:请求主体的数据类型
    
    Content-Length:数据主体的大小(单位:字节)
    

举例说明:服务端可以根据请求头中的内容来获取客户端的相关信息,有了这些信息服务端就可以处理不同的业务需求。

比如:

  • 不同浏览器解析HTML和CSS标签的结果会有不一致,所以就会导致相同的代码在不同的浏览器会出现不同的效果
  • 服务端根据客户端请求头中的数据获取到客户端的浏览器类型,就可以根据不同的浏览器设置不同的代码来达到一致的效果(这就是我们常说的浏览器兼容问题)
  • 请求体 :存储请求参数
    • GET请求的请求参数在请求行中,故不需要设置请求体

POST方式的请求协议:

  • 请求行(以上图中红色部分):包含请求方式、资源路径、协议/版本
    • 请求方式:POST
    • 资源路径:/brand
    • 协议/版本:HTTP/1.1
  • 请求头(以上图中黄色部分)
  • 请求体(以上图中绿色部分) :存储请求参数
    • 请求体和请求头之间是有一个空行隔开(作用:用于标记请求头结束)

GET请求和POST请求的区别:

区别方式 GET请求 POST请求
请求参数 请求参数在请求行中。
例:/brand/findAll?name=OPPO&status=1
请求参数在请求体中
请求参数长度 请求参数长度有限制(浏览器不同限制也不同) 请求参数长度没有限制
安全性 安全性低。原因:请求参数暴露在浏览器地址栏中。 安全性相对高

2.3 HTTP-响应协议

2.3.1格式

与HTTP的请求一样,HTTP响应的数据也分为3部分:响应行、响应头 、响应体

  • 响应行(以上图中红色部分):响应数据的第一行。响应行由协议及版本、响应状态码、状态码描述组成

    • 协议/版本:HTTP/1.1
    • 响应状态码:200
    • 状态码描述:OK
  • 响应头(以上图中黄色部分):响应数据的第二行开始。格式为key:value形式

    • http是个无状态的协议,所以可以在请求头和响应头中设置一些信息和想要执行的动作,这样,对方在收到信息后,就可以知道你是谁,你想干什么

    常见的HTTP响应头有:

    Content-Type:表示该响应内容的类型,例如text/html,image/jpeg ;
    
    Content-Length:表示该响应内容的长度(字节数);
    
    Content-Encoding:表示该响应压缩算法,例如gzip ;
    
    Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒 ;
    
    Set-Cookie: 告诉浏览器为当前页面所在的域设置cookie ;
    
  • 响应体(以上图中绿色部分): 响应数据的最后一部分。存储响应的数据
    • 响应体和响应头之间有一个空行隔开(作用:用于标记响应头结束)

2.3.2响应状态码

状态码分类 说明
1xx 响应中 --- 临时状态码。表示请求已经接受,告诉客户端应该继续请求或者如果已经完成则忽略
2xx 成功 --- 表示请求已经被成功接收,处理已完成
3xx 重定向 --- 重定向到其它地方,让客户端再发起一个请求以完成整个处理
4xx 客户端错误 --- 处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等
5xx 服务器端错误 --- 处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP版本不支持等

参考: 资料/SpringbootWeb/响应状态码.md

关于响应状态码,我们先主要认识三个状态码,其余的等后期用到了再去掌握:

  • 200 ok 客户端请求成功
  • 404 Not Found 请求资源不存在
  • 500 Internal Server Error 服务端发生不可预期的错误

2.4请求响应

2.4.1基础操作

1.下载postman模拟请求

在springboot项目里加入以下代码:

package com.example.demo.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
//
public class HelloController {
    @RequestMapping("/simpleParam")
    public String simpleParam(HttpServletRequest request)
    {
        String name = request.getParameter("name");
        String ageStr = request.getParameter("age");
        int age = Integer.parseInt(ageStr);
        System.out.println(name + ":" + age);
        return "OK";
    }
}

或者简化写法:

@RequestMapping("/simpleParam")
public String simpleParam(String name,Integer age)
{
    System.out.println(name + ":" + age);
    return "OK";
}

2.然后去postman里创建一个请求

这个是get的格式:

http://localhost:8080/simpleParam?name=Tom&age=10

这个是POST的格式:

http://localhost:8080/simpleParam

然后post需要更改请求体:

postman测试( POST请求 ):

image-20220826181117898

结论:不论是GET请求还是POST请求,对于简单参数来讲,只要保证请求参数名和Controller方法中的形参名保持一致,就可以获取到请求参数中的数据值。

2.4.2 参数名不一致

可以使用Spring提供的@RequestParam注解完成映射

在方法形参前面加上 @RequestParam 然后通过value属性执行请求参数名,从而完成映射。代码如下:

package com.example.demo.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    // http://localhost:8080/simpleParam?name=Tom&age=20
    // 请求参数名:name
    //springboot方式
    @RequestMapping("/simpleParam")
    public String simpleParam(@RequestParam("name") String username , Integer age ){
        System.out.println(username+"  :  "+age);
        return "OK";
    }
}

注意事项:

@RequestParam中的required属性默认为true(默认值也是true),代表该请求参数必须传递,如果不传递将报错

然后用上面的GET方式测试一下

2.5 参数

2.5.1实体参数

我们可以考虑将请求参数封装到一个实体类对象中。 要想完成数据封装,需要遵守如下规则:请求参数名与实体类的属性名相同

step1:创建一个跟controller同级的目录pojo,然后创建一个类叫User:

点alt+insert可以快速生成方法 点击getter and setter

package com.example.demo.controller;

import com.example.demo.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    // http://localhost:8080/simpleParam?name=Tom&age=20
    // 请求参数名:name
    //springboot方式
    @RequestMapping("/simpleParam")
    public String simpleParam(@RequestParam("name") String username , Integer age ){
        System.out.println(username+"  :  "+age);
        return "OK";
    }
    @RequestMapping("/simplePojo")
    public String simplePojo (User user){
       System.out.println(user);
       return "OK";
    }
}

然后在controller添加以下代码:

@RequestMapping("/simplePojo")
public String simplePojo (User user){
   System.out.println(user);
   return "OK";
}

然后用postman测试:

http://localhost:8080/simplePojo?name=ITCAST&age=16

2.5.2数组集合参数

后端程序接收多个值的方式有两种:

  1. 数组
  2. 集合

1.数组

数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数

Controller方法:

@RestController
public class HelloController {
    //数组集合参数
    @RequestMapping("/arrayParam")
    public String arrayParam(String[] hobby){
        System.out.println(Arrays.toString(hobby));
        return "OK";
    }
}

Postman测试:

http://localhost:8080/arrayParam?hobby=game&hobby=java

 http://localhost:8080/arrayParam?hobby=game ,java

2.集合

集合参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam 绑定参数关系

@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby){
    System.out.println(hobby);
    return "OK";
}

postman测试类似数组

2.5.3日期参数

Controller方法:

@RequestMapping("/dateParam")
public String dateParam (@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime ){
    //按照格式转化为updateTime
    System.out.println(updateTime);
    return "OK";
}

postman测试:

http://localhost:8080/dateParam?updateTime=2022-12-12 10:00:05

2.5.4JSON格式参数

选择post,选择raw的方式,然后在右边选择数据格式为json

{
    "name":"ITCAST",
    "age":16,
    "address":{
        "province":"北京",
        "city":"北京"
    }
}

@RequestBody把json封装成对象

Controller方法:

@RequestMapping("/jsonParam")
public String jsonParam (@RequestBody User user){
    System.out.println(user);
    return "OK";
}

如果返回的是一个对象,会转化为json

2.6统一响应结果

2.6.1统一响应

如果我们开发一个大型项目,项目中controller方法将成千上万,使用上述方式将造成整个项目难以维护。在真实的项目开发中,无论是哪种方法,我们都会定义一个统一的返回结果。

格式:

public class Result{
    //响应码:1是成功,2是失败
    private Integer code;
    //提示信息
    private String msg;
    //返回的数据
    private Object data;
}

改造:

@RequestMapping("/hello")
public Result hello(){
    System.out.println("Hello World ~");
    //return new Result(1,"success","Hello World ~");
    return Result.success("Hello World ~");
}

//响应统一格式的结果
@RequestMapping("/getAddr")
public Result getAddr(){
    Address addr = new Address();
    addr.setProvince("广东");
    addr.setCity("深圳");
    return Result.success(addr);
}

//响应统一格式的结果
@RequestMapping("/listAddr")
public Result listAddr(){
    List<Address> list = new ArrayList<>();

    Address addr = new Address();
    addr.setProvince("广东");
    addr.setCity("深圳");

    Address addr2 = new Address();
    addr2.setProvince("陕西");
    addr2.setCity("西安");

    list.add(addr);
    list.add(addr2);
    return Result.success(list);
}

Springboot项目的静态资源(html,css,js等前端资源)默认存放目录为:classpath:/static 、 classpath:/public、 classpath:/resources

classpath就是resources,在里面创建static,public,resources名字的文件夹就可

2.6.2案例

step1:把需要的文件目录建立出来

step2:

EmpController内容

package com.example.demo.controller;

import com.example.demo.pojo.Emp;
import com.example.demo.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.itheima.pojo.Result;

import java.util.List;

@RestController
public class EmpController {
    @RequestMapping("/listEmp")
    public Result list(){
        //加载并解析xml文件
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println(file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
        //对数据进行转换处理
        empList.stream().forEach(emp -> {
            String gender = emp.getGender();
            if("1".equals(gender)){
                emp.setGender("男");
            }
            else if("2".equals(gender)){
                emp.setGender("女");
            }
            String job = emp.getJob();
            if("1".equals(job)){
                emp.setJob("讲师");
            }
            else if("2".equals(job)){
                emp.setJob("班主任");
            }
            else if("3".equals(job)){
                emp.setJob("就业指导");
            }
        });
        //响应数据
        return Result.success(empList);
    }
}

然后postman发送:

http://localhost:8080/listEmp

可以看到解析出来的数据

2.7分层解耦

2.7.1三层架构

上述案例的处理逻辑呢,从组成上看可以分为三个部分:

  • 数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。
  • 逻辑处理:负责业务逻辑处理的代码。
  • 请求处理、响应数据:负责,接收页面的请求,给页面响应数据。

按照上述的三个组成部分,在我们项目开发中呢,可以将代码分为三层:

image-20221204193837678

  • Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
  • Service:业务逻辑层。处理具体的业务逻辑。
  • Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。

2.7.2 IOC&DI

任务:完成Controller层、Service层、Dao层的代码解耦

  • 思路:
    1. 删除Controller层、Service层中new对象的代码
    2. Service层及Dao层的实现类,交给IOC容器管理@Component
    3. 为Controller及Service注入运行时依赖的对象@Autowired
      • Controller程序中注入依赖的Service层对象
      • Service程序中注入依赖的Dao层对象

修改完了之后:

EmpDaoA.java

@Component //当前类交给ioc容器管理 成为IOC容器的bean
public class EmpDaoA implements Empdao {
    @Override
    public List<Emp> listEmp() {
        //加载并解析emp.xml
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println(file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
        return empList;
    }
}

EmpServiceA.java

@Component//当前类交给ioc容器管理 成为IOC容器的bean
public class EmpServiceA implements EmpService {
    @Autowired//自动装配,按照类型(EmpDaoA)找到对应的,然后注入
    private EmpDaoA empDao;
    @Override
    public List<Emp> listEmp() {
        List<Emp>empList = empDao.listEmp();
        empList.stream().forEach (emp -> {
            String gender = emp.getGender();
            if("1".equals(gender)){
                emp.setGender("男");
            }
            else if("2".equals(gender)){
                emp.setGender("女");
            }
            String job = emp.getJob();
            if("1".equals(job)){
                emp.setJob("讲师");
            }
            else if("2".equals(job)){
                emp.setJob("班主任");
            }
            else if("3".equals(job)){
                emp.setJob("就业指导");
            }
        });
        return empList;
    }
}

EmpController.java

@RestController
public class EmpController {
   @Autowired //IOC容器回提供该类型的bean对象,并赋值给该变量 -依赖注入
    private EmpService empService;
    @RequestMapping("/listEmp")
    public Result list(){
        List<Emp> empList = empService.listEmp();
        //响应数据
        return Result.success(empList);
    }
}

详解:

而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:

  • @Controller (标注在控制层类上)

  • @Service (标注在业务层类上)

  • @Repository (标注在数据访问层类上)

    就是一般不用@Component 改成细分的 如果没有细分

给bean起名字:

@Repository("daoA") //当前bean叫daoA
public class EmpDaoA implements Empdao {
    @Override
    public List<Emp> listEmp() {
        //加载并解析emp.xml
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println(file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
        return empList;
    }
}

注意事项:

  • 声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
  • 使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。

2.7.3组件扫描

使用四大注解声明的bean,要想生效,还需要被组件扫描注解@ComponentScan扫描

@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了引导类声明注解 @SpringBootApplication 中,默认扫描的范围是SpringBoot启动类所在包及其子包。

2.7.4DI详解

@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)

入门程序举例:在EmpController运行的时候,就要到IOC容器当中去查找EmpService这个类型的对象,而我们的IOC容器中刚好有一个EmpService这个类型的对象,所以就找到了这个类型的对象完成注入操作。

但是有一种情况就是如果有多个一样类型的,就会报错,无法完成自动装配

如何解决上述问题呢?Spring提供了以下几种解决方案:

  • @Primary

  • @Qualifier

  • @Resource

使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。

image-20221204232501679

使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。

  • @Qualifier注解不能单独使用,必须配合@Autowired使用

image-20221204233333606

使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。

image-20221204233637735

面试题 : @Autowird 与 @Resource的区别

  • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
  • @Autowired 默认是按照类型注入,而@Resource是按照名称注入
posted @ 2024-06-18 21:44  wlqtc  阅读(28)  评论(0)    收藏  举报