盛世宏图 发表于 2025-4-14 06:42:37

JavaWeb-04-Web后端底子(SpringBootWeb、HTTP协议、分层解耦、IOC和DI)

目录
一、SpringBootWeb入门
1.1 概述
1.2 入门程序
1.2.1 需求
1.2.2 开辟步调
1.3 入门解析 
二、HTTP协议
2.1 HTTP概述
2.1.1 介绍
2.1.2 特点
2.2 HTTP哀求协议
2.2.1 介绍
2.2.2 获取哀求数据 
2.3 HTTP响应协议 
2.3.1 格式介绍
2.3.2 响应状态码 
2.3.3 设置响应数据
三、SpringBootWeb案例
3.1 需求说明
3.2 代码实现
3.3 @ResponseBody
四、分层解耦
4.1 三层架构 
4.1.1 介绍
4.1.2 代码拆分 
4.2 分层解耦
4.2.1 标题分析
4.2.2 解耦思路
4.3 IOC&DI入门 
4.4 IOC详解
4.4.1 Bean的声明
4.4.2 组件扫描
4.5 DI详解
4.5.1 @Autowired用法
4.5.2 注意事项 

 
https://i-blog.csdnimg.cn/direct/fb8989e3bf4e4bc4a73ac457aa3d8dd7.png
      静态资源:   服务器上存储的不会改变的数据,通常不会根据用户的哀求而变革。比如:   HTML   、   CSS   、   JS   、图片、视频等   (   负责页面展示   )         动态资源:服务器端根据用户哀求和其他数据动态生成的,内容大概会在每次哀求时都发生变革。比如:   Servlet   、   JSP   等   (   负责逻辑处置惩罚)(注:现在基本利用Spring框架   )         B/S    架构:   Browser/Server   ,浏览器   /   服务器架构模式。客户端只需浏览器,应用程序的逻辑和数据都存在服务器端。   (   维护方便   体验一样平常   )         C/S    架构   : Client/Server   ,客户端   /   服务器架构模式。需要单独开辟维护客户端。(   体验不错   开辟维护麻烦   )     
一、SpringBootWeb入门

1.1 概述

https://i-blog.csdnimg.cn/direct/9da5bdb355b446bbb49d3b0aebaba0a3.png
Spring家属旗下这么多的技能,最底子、最核心的是 SpringFramework。其他的spring家属的技能,都是基于SpringFramework的,SpringFramework中提供很多实用功能,如:依靠注入、事务管理、web开辟支持、数据访问、消息服务等等。 
https://i-blog.csdnimg.cn/direct/bcd47bac5c1848b588b4e02e1da5a5f6.png 
直接基于SpringBoot进行项目构建和开辟,不但是Spring官方推荐的方式,也是现在企业开辟的主流。 
1.2 入门程序

1.2.1 需求

需求:基于SpringBoot的方式开辟一个web应用,浏览器发起哀求/hello后,给浏览器返回字符串 "Hello xxx ~"。

1.2.2 开辟步调

第1步:创建SpringBoot工程,并勾选Web开辟相关依靠
第2步:定义HelloController类,添加方法hello,并添加注解
1)创建SpringBoot工程(需要联网) (案例是在已经创建的空项目上进行的,空项目创建见上一章3.1)
https://i-blog.csdnimg.cn/direct/d09539f866664f5089fbb634de3ac144.png
https://i-blog.csdnimg.cn/direct/52751edfb023453f9ab4ef1aa27450fc.png
 
 https://i-blog.csdnimg.cn/direct/dcd4e032ecac49bb93037b04d3b0e701.png
https://i-blog.csdnimg.cn/direct/d4a6afa8f64d401cb73cdb82073076e1.png https://i-blog.csdnimg.cn/direct/2fc475b547424641bb86f16e51091d63.png
 
 注意!注意!注意! 假如下载失败大概是网络标题,发起更换成阿里云
https://i-blog.csdnimg.cn/direct/194db96852dd4a928940f44aa2f4dda4.png 
   https://start.aliyun.com/ 
 https://i-blog.csdnimg.cn/direct/b62b67f855cb4015818eca90cccbbe89.png
 下图中该类叫做启动类(可简朴理解为主类)
 https://i-blog.csdnimg.cn/direct/c1ec6d02efd84a7eba428790e4240726.png
2)定义HelloController类,添加方法hello,并添加注解
在下图路径新建一个类 
https://i-blog.csdnimg.cn/direct/1dd793ca8d824d3eba022144a7e262d9.png
上图类中的代码如下:
package com.orange.springbootweb_quickstart;


import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController //表示当前类是一个请求处理类
public class HelloController {

    @RequestMapping("/hello")
    public String sayHello(String name) {
      System.out.println("name: "+ name);
      return "Hello " + name+ "~~~~" ;
    }

}
 接着到启动类中选择debug的方式进行运行(如下图)
https://i-blog.csdnimg.cn/direct/c91ed21a528044f8ab669332bbbc1258.png
 在控制台可以看到以下内容(如下图)(可以看到端口是8080)
https://i-blog.csdnimg.cn/direct/ad1cb9ca70a145629a03539308409876.png
 接下来在浏览器中输入下图内容并按下回车
https://i-blog.csdnimg.cn/direct/180fd6b5ba34423cb80aadb5336d7a23.png
1.3 入门解析 

1). 为什么一个main方法就可以将Web应用启动了?
https://i-blog.csdnimg.cn/direct/fe214a9f624641408b2214368bb8b54a.pnghttps://i-blog.csdnimg.cn/direct/6c79d2696ae8450590d56dd9c3ab7780.png
由于我们在创建springboot项目的时候,选择了web开辟的起步依靠 spring-boot-starter-web。而spring-boot-starter-web依靠,又依靠了spring-boot-starter-tomcat,由于maven的依靠传递特性,那么在我们创建的springboot项目中也就已经有了tomcat的依靠,这个其实就是springboot中内嵌的tomcat。(tomcat:web服务器) 
而我们运行引导类中的main方法,其实启动的就是springboot中内嵌的Tomcat服务器。 而我们所开辟的项目,也会自动的部署在该tomcat服务器中,并占用8080端标语 。(如下图) 
https://i-blog.csdnimg.cn/direct/7275c1fdf6d047b1b10c8c7baac95634.png
   起步依靠:


[*] 一种为开辟者提供简化设置和集成的机制,使得构建Spring应用程序更加轻松。起步依靠本质上是一组预定义的依靠项集合,它们一起提供了在特定场景下开辟Spring应用所需的所有库和设置。

[*] spring-boot-starter-web:包罗了web应用开辟所需要的常见依靠。
[*] spring-boot-starter-test:包罗了单元测试所需要的常见依靠。

[*] 官方提供的starter:https://docs.spring.io/spring-boot/docs/3.1.3/reference/htmlsingle/#using.build-systems.starters
 
 
二、HTTP协议

2.1 HTTP概述

2.1.1 介绍

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

   

[*] http是互联网上应用最为广泛的一种网络协议
[*] http协议要求:浏览器在向服务器发送哀求数据时,或是服务器在向浏览器发送响应数据时,都必须按照固定的格式进行数据传输
https://i-blog.csdnimg.cn/direct/88a370c2dd1b44eeb6304cac10c88120.png
2.1.2 特点

   

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

[*] 基于哀求-响应模型: 一次哀求对应一次响应(先哀求后响应)
   哀求和响应是一一对应关系,没有哀求,就没有响应
     
   

[*] HTTP协议是无状态协议: 对于数据没有影象本领。每次哀求-响应都是独立的
   无状态指的是客户端发送HTTP哀求给服务端之后,服务端根据哀求响应数据,响应完后,不会记载任何信息。
   

[*] 缺点: 多次哀求间不能共享数据
[*] 优点: 速度快
   
   

[*] 哀求之间无法共享数据会引发的标题:

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

   
   

[*] 详细利用的时候,我们发现京东是可以正常展示数据的,缘故原由是Java早已考虑到这个标题,并提出了利用会话技能(Cookie、Session)来办理这个标题。详细怎样来做,我们后面课程中会讲到。
    2.2 HTTP哀求协议

2.2.1 介绍

哀求协议:浏览器将数据以哀求格式发送到服务器。包罗:哀求行、哀求头 、哀求体
https://i-blog.csdnimg.cn/direct/4a493c93e40547db829ebd4270932f39.png
GET方式的哀求协议: 
https://i-blog.csdnimg.cn/direct/83471c491280477398128fb9efffc674.png
   

[*] 哀求行(以上图中红色部分) :HTTP哀求中的第一行数据。由:哀求方式、资源路径、协议/版本组成(之间利用空格分隔)

[*] 哀求方式:GET
[*] 资源路径:/brand/findAll?name=OPPO&status=1

[*] 哀求路径:/brand/findAll
[*] 哀求参数:name=OPPO&status=1

[*] 哀求参数是以key=value形式出现
[*] 多个哀求参数之间利用&连接

[*] 哀求路径和哀求参数之间利用?连接

[*] 协议/版本:HTTP/1.1

[*] 哀求头(以上图中黄色部分) :第二行开始,上图黄色部分内容就是哀求头。格式为key: value形式

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



[*] 常见的HTTP哀求头有:
https://i-blog.csdnimg.cn/direct/6d2b70116eab464d9d988dac1b3f2277.png   
   举例说明:服务端可以根据哀求头中的内容来获取客户端的相关信息,有了这些信息服务端就可以处置惩罚差别的业务需求。
比如:


[*] 差别浏览器解析HTML和CSS标签的效果会有不一致,以是就会导致相同的代码在差别的浏览器会出现差别的效果
[*] 服务端根据客户端哀求头中的数据获取到客户端的浏览器类型,就可以根据差别的浏览器设置差别的代码来到达一致的效果(这就是我们常说的浏览器兼容标题)
   

[*] 哀求体 :存储哀求参数

[*] GET哀求的哀求参数在哀求行中,故不需要设置哀求体

 POST方式的哀求协议:
https://i-blog.csdnimg.cn/direct/fcfa746ce1ce4cc5ab36d33436fe7ce5.png
   

[*] 哀求行(以上图中红色部分):包罗哀求方式、资源路径、协议/版本

[*] 哀求方式:POST
[*] 资源路径:/brand
[*] 协议/版本:HTTP/1.1

[*] 哀求头(以上图中黄色部分)
[*] 哀求体(以上图中绿色部分) :存储哀求参数

[*] 哀求体和哀求头之间是有一个空行隔开(作用:用于标记哀求头结束)

 GET哀求和POST哀求的区别:
https://i-blog.csdnimg.cn/direct/2b136f2aadf14b0a9c27c6e9db9a5b34.png
2.2.2 获取哀求数据 

Web服务器(Tomcat)对HTTP协议的哀求数据进行解析,并进行了封装(HttpServletRequest),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行利用,让Web开辟更加便捷。
https://i-blog.csdnimg.cn/direct/16e98986897d47eebff3439a738ef5d6.png
代码演示如下(项目创建看1.2入门程序):
https://i-blog.csdnimg.cn/direct/6e7fab2a66ad495f9cf517b06b0fb6bf.png
代码如下:
package com.orange.springbootweb_quickstart;

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

@RestController
public class RequestController {

    /**
   * 请求路径 http://localhost:8080/request?name=橙序研&age=22
   * @param request
   * @return
   */
    @RequestMapping("/request")
    public String request(HttpServletRequest request){

      //1.获取请求参数 name, age
      String name = request.getParameter("name");
      String age = request.getParameter("age");
      System.out.println("name = " + name + ", age = " + age);

      //2.获取请求路径
      String uri = request.getRequestURI();
      String url = request.getRequestURL().toString();
      System.out.println("uri = " + uri);
      System.out.println("url = " + url);

      //3.获取请求方式
      String method = request.getMethod();
      System.out.println("method = " + method);

      //4.获取请求头
      String header = request.getHeader("User-Agent");
      System.out.println("header = " + header);
      return "request success";
    }

}
 
 https://i-blog.csdnimg.cn/direct/562eefec43874187a61ec7204c25b9c4.png
https://i-blog.csdnimg.cn/direct/fb33265939ab4e338a37454cc3c6d44f.png 
2.3 HTTP响应协议 

2.3.1 格式介绍

https://i-blog.csdnimg.cn/direct/34b384ca5ded40d0aa6b97d0aaf19dec.png


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

[*] 协议/版本: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 响应状态码 

https://i-blog.csdnimg.cn/direct/c5e6b91c9feb419698681a7eb481e1e5.png
重定向解释:先向A服务器发出哀求,但是A没有却提供了Location,于是浏览器会再次向B服务器发出哀求(但其实用户并不知道)
https://i-blog.csdnimg.cn/direct/398c7b107dda453ca052cf94542f7d28.png 
 
https://i-blog.csdnimg.cn/direct/779f6901971a4878aed00cb218140be3.png 
 可以看到设置了状态码302,并且设置了Location,于是便进行重定向到Location(如下图)
https://i-blog.csdnimg.cn/direct/d456ffa6f7114914a8cbcc9382eae00a.png 
下图可以看出,重定向后的状态码为乐成了
https://i-blog.csdnimg.cn/direct/e65877946d15442d81c1d24faff5c031.png 
 常见的如下:
   

[*] 200 ok 客户端哀求乐成
[*] 404 Not Found 哀求资源不存在
[*] 500 Internal Server Error 服务端发生不可预期的错误
状态码大全: 
https://i-blog.csdnimg.cn/direct/a883a257dbff48f88d0b4d0d7911b43b.png
2.3.3 设置响应数据

Web服务器对HTTP协议的响应数据进行了封装(HttpServletResponse),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行利用,让Web开辟更加便捷。
https://i-blog.csdnimg.cn/direct/2ffd5cb74ac943de8c190d7906b5b72e.png 
方式一(项目文件管理参考2.2.2中的例子):
https://i-blog.csdnimg.cn/direct/a4aa6df608a947f1a2998112ee17504e.png
代码如下:
package com.orange.springbootweb_quickstart;

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

import java.io.IOException;

@RestController
public class ResponseController {

    @RequestMapping("response")
    public void response(HttpServletResponse response) throws IOException {

      //1. 设置响应状态码
      response.setStatus(401);

      //2. 设置响应头
      response.setHeader("name","orange");

      //3. 设置响应体
      response.getWriter().write("<h1>hello response</h1>");

    }
}

https://i-blog.csdnimg.cn/direct/167b13159f0e4565b9a397136d96b5f0.png
https://i-blog.csdnimg.cn/direct/4e1335b70f0d4bb9b4acd9472eb073e3.png
https://i-blog.csdnimg.cn/direct/4a3f940574d14a539691da86d61012d1.png
https://i-blog.csdnimg.cn/direct/97238185cafd41a9be6cd9412849b61c.png 
方式二:
代码如下:
package com.orange.springbootweb_quickstart;

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

import java.io.IOException;

@RestController
public class ResponseController {

    @RequestMapping("response")
    public void response(HttpServletResponse response) throws IOException {

      //1. 设置响应状态码
      response.setStatus(401);

      //2. 设置响应头
      response.setHeader("name","orange");

      //3. 设置响应体
      response.getWriter().write("<h1>hello response</h1>");

    }

    // 方式二
    @RequestMapping("/response2")
    public ResponseEntity<String> response2(){

      return ResponseEntity
                .status(401)
                .header("name","orange2")
                .body("<h1>hello response2</h1>");

    }


}
https://i-blog.csdnimg.cn/direct/1dc604d3a9924277ad281d14cfb24742.png
   响应状态码 和 响应头假如没有特别要求的话,通常不手动设定。服务器会根据哀求处置惩罚的逻辑,自动设置响应状态码和响应头。 
 
三、SpringBootWeb案例

3.1 需求说明

需求:基于SpringBoot开辟web程序,完成用户列表的渲染展示
https://i-blog.csdnimg.cn/direct/7b3476d56cd94d0bbbfbfae7b746c5a0.png
当在浏览器地址栏,访问前端静态页面(http://localhost:8080/user.html)后,在前端页面上,会发送ajax哀求,哀求服务端(http://localhost:8080/list),服务端程序加载 user.txt 文件中的数据,读取出来后最终给前端页面响应json格式的数据,前端页面再将数据渲染展示在表格中。
 
3.2 代码实现

1). 准备工作:再创建一个SpringBoot工程(见1.2.2),并勾选web依靠、lombok依靠。
https://i-blog.csdnimg.cn/direct/7ec6808bd58248d69693b62c9b05fb4c.png
在pom文件中在引入以下依靠(记得革新)
      <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
      </dependency> 2)资料准备如下图: 
https://i-blog.csdnimg.cn/direct/c73d3377c99342469966781f1f0df448.png
四个准备文件内容如下(链接: https://pan.baidu.com/s/1Q8TpWxIL-Q44Q05fJIOHgA?pwd=0706 提取码: 0706 )
准备一个txt文件,内容如下
1,daqiao,1234567890,大乔,22,2024-07-15 15:05:45
2,xiaoqiao,1234567890,小乔,18,2024-07-15 15:12:09
3,diaochan,1234567890,貂蝉,21,2024-07-15 15:07:16
4,lvbu,1234567890,吕布,28,2024-07-16 10:05:15
5,zhaoyun,1234567890,赵云,27,2024-07-16 11:03:28
6,zhangfei,1234567890,张飞,31,2024-07-16 11:03:28
7,guanyu,1234567890,关羽,34,2024-07-16 12:05:12
8,liubei,1234567890,刘备,37,2024-07-16 15:03:28 3)创建代码如下 
创建一个Java文件(如下图): 
https://i-blog.csdnimg.cn/direct/8c3948920fab4c3bac66e51acbeaa2ff.png
User代码如下:
package com.orange.springbootweb_001.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;


@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private Integer age;
    private LocalDateTime updateTime;

}
假如报以下错误:
   java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid' 
办理方法一:更新lombok 版本
办理方法二:利用以下代码:
package com.orange.springbootweb_001.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;


public class User {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private Integer age;
    private LocalDateTime updateTime;

    public User(Integer id, String username, String password, String name, Integer age, LocalDateTime updateTime) {
      this.id = id;
      this.username = username;
      this.password = password;
      this.name = name;
      this.age = age;
      this.updateTime = updateTime;

    }

    public LocalDateTime getUpdateTime() {
      return updateTime;
    }

    public void setUpdateTime(LocalDateTime updateTime) {
      this.updateTime = updateTime;
    }

    public Integer getId() {
      return id;
    }

    public void setId(Integer id) {
      this.id = id;
    }

    public String getUsername() {
      return username;
    }

    public void setUsername(String username) {
      this.username = username;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public String getPassword() {
      return password;
    }

    public void setPassword(String password) {
      this.password = password;
    }

    public Integer getAge() {
      return age;
    }

    public void setAge(Integer age) {
      this.age = age;
    }




}
 再创建如下包和类(如下图)
https://i-blog.csdnimg.cn/direct/0b0be83b107744a3b01a321df056eb63.png
代码如下:


package com.orange.springbootweb_001.controller;

import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;


// 用户信息Controller
@RestController
public class UserController {

    @RequestMapping("/list")
    public List<User> list() throws FileNotFoundException {

      //1. 加载并读取user.txt文件,获取用户数据
      //InputStream in = new FileInputStream("src/main/resources/user.txt");
      InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
      ArrayList<String> lines =IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());

      //2. 解析用户信息,封装为User对象 ->list集合
      List<User> userList = lines.stream().map(line ->{

            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts);
            String username = parts;
            String password = parts;
            String name = parts;
            Integer age = Integer.parseInt(parts);
            LocalDateTime updateTime = LocalDateTime.parse(parts, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            returnnew User(id,username,password,name,age,updateTime);

      }).toList();

      //3. 返回数据(json)
      return userList;


    }
}

完整项目分享:
通过网盘分享的文件:springbootweb_001
链接: https://pan.baidu.com/s/1Xth0setM-nuRjKjqqw4HEA?pwd=0706 提取码: 0706 
--来自百度网盘超级会员v4的分享 
3.3 @ResponseBody

前面我们学习过HTTL协议的交互方式:哀求响应模式(有哀求就有响应)。那么Controller程序呢,除了接收哀求外,还可以进行响应。
在我们前面所编写的controller方法中,都已经设置了响应数据。
controller方法中的return的效果,怎么就可以响应给浏览器呢?
答案:利用@ResponseBody注解

@ResponseBody注解:


[*] 类型:方法注解、类注解
[*] 位置:誊写在Controller方法上或类上
[*] 作用:将方法返回值直接响应给浏览器,假如返回值类型是实体对象/集合,将会转换为JSON格式后在响应给浏览器
但是在我们所誊写的Controller中,只在类上添加了@RestController注解、方法添加了@RequestMapping注解,并没有利用@ResponseBody注解,怎么给浏览器响应呢?
这是由于,我们在类上加了@RestController注解,而这个注解是由两个注解组合起来的,分别是:@Controller 、@ResponseBody。 那也就意味着,我们在类上已经添加了@ResponseBody注解了,而一旦在类上加了@ResponseBody注解,就相当于该类所有的方法中都已经添加了@ResponseBody注解。
   提示:前后端分离的项目中,一样平常直接在哀求处置惩罚类上加@RestController注解,就无需在方法上加@ResponseBody注解了
四、分层解耦

4.1 三层架构 

4.1.1 介绍

在我们进行程序设计以及程序开辟时,尽大概让每一个接口、类、方法的职责更单一些(单一职责原则)。
   单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。
这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利于后期的维护。
我们之前开辟的程序呢,并不满足单一职责原则:
https://i-blog.csdnimg.cn/direct/d7e5bcf838984407a0495fda7b3fb049.png 那其实我们上述案例的处置惩罚逻辑呢,从组成上看可以分为三个部分:
   

[*] 数据访问:负责业务数据的维护利用,包罗增、删、改、查等利用。
[*] 逻辑处置惩罚:负责业务逻辑处置惩罚的代码。
[*] 哀求处置惩罚、响应数据:负责,接收页面的哀求,给页面响应数据。
按照上述的三个组成部分,在我们项目开辟中呢,可以将代码分为三层,如下图所示:
https://i-blog.csdnimg.cn/direct/7f1c48f4a5624482abc3bbedfad9e715.png 
   

[*] Controller:控制层。接收前端发送的哀求,对哀求进行处置惩罚,并响应数据。
[*] Service:业务逻辑层。处置惩罚详细的业务逻辑。
[*] Dao:数据访问层(Data Access Object),也称为长期层。负责数据访问利用,包罗数据的增、删、改、查。
4.1.2 代码拆分 

 完整项目:通过网盘分享的文件:springbootweb_001_拆分项目
链接: https://pan.baidu.com/s/1_ZrT8gsT24huK2aqKRQvrg?pwd=0706 提取码: 0706 
--来自百度网盘超级会员v4的分享
我们利用三层架构头脑,来改造下之前的程序:
   

[*] 控制层包名:com.orange.controller
[*] 业务逻辑层包名:com.orange.service
[*] 数据访问层包名:com.orange.dao
项目架构如下图所示:
https://i-blog.csdnimg.cn/direct/27ff1b54047f432da30a95110f0541cb.png 
(下面顺序倒着来,好理解一点) 
 3). 数据访问层:负责数据的访问利用,包罗数据的增、删、改、查
在 com.orange.dao中创建UserDao接口,代码如下: 
package com.orange.springbootweb_001.dao;

import java.util.List;

public interface UserDao {
    public List<String> findAll();
}
在 com.orange.dao.impl 中创建UserDaoImpl接口,代码如下 
package com.orange.springbootweb_001.dao.impl;

import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.dao.UserDao;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class UserDaoImpl implements UserDao {
    public List<String> findAll() {

      //1. 加载并读取user.txt文件,获取用户数据
      //InputStream in = new FileInputStream("src/main/resources/user.txt");
      InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
      ArrayList<String> lines =IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());
      return lines;
    }
}
2). 业务逻辑层:处置惩罚详细的业务逻辑
在 com.orange.service中创建UserSerivce接口,代码如下:
package com.orange.springbootweb_001.service;

import com.orange.springbootweb_001.pojo.User;

import java.util.List;

public interface UserService {
    public List<User> findAll();
}
在 com.orange.service.impl 中创建UserSerivceImpl接口,代码如下: 
package com.orange.springbootweb_001.service.impl;

import com.orange.springbootweb_001.dao.UserDao;
import com.orange.springbootweb_001.dao.impl.UserDaoImpl;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

public class UserServiceImpl implements UserService {

    //调用Dao,获取数据
    private UserDao userDao = new UserDaoImpl();

    @Override
    public List<User> findAll() {

      List<String> lines = userDao.findAll();
      //2. 解析用户信息,封装为User对象 ->list集合
      List<User> userList = lines.stream().map(line ->{

            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts);
            String username = parts;
            String password = parts;
            String name = parts;
            Integer age = Integer.parseInt(parts);
            LocalDateTime updateTime = LocalDateTime.parse(parts, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            returnnew User(id,username,password,name,age,updateTime);

      }).toList();

      return userList;
    }
}
1). 控制层:接收前端发送的哀求,对哀求进行处置惩罚,并响应数据
在 com.orange.controller 中创建UserController类,代码如下: 
package com.orange.springbootweb_001.controller;import cn.hutool.core.io.IoUtil;import com.orange.springbootweb_001.pojo.User;import com.orange.springbootweb_001.service.UserService;import com.orange.springbootweb_001.service.impl.UserServiceImpl;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.InputStream;import java.nio.charset.StandardCharsets;import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;import java.util.ArrayList;import java.util.List;// 用户信息Controller@RestControllerpublic class UserController {    // 调用service,获取数据    private UserService userService = new UserServiceImpl();    @RequestMapping("/list")    public List<User> list() throws FileNotFoundException {      //3. 返回数据(json)      return userService.findAll();    }}/*

package com.orange.springbootweb_001.controller;

import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;


// 用户信息Controller
@RestController
public class UserController {

    @RequestMapping("/list")
    public List<User> list() throws FileNotFoundException {

      //1. 加载并读取user.txt文件,获取用户数据
      //InputStream in = new FileInputStream("src/main/resources/user.txt");
      InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
      ArrayList<String> lines =IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());

      //2. 解析用户信息,封装为User对象 ->list集合
      List<User> userList = lines.stream().map(line ->{

            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts);
            String username = parts;
            String password = parts;
            String name = parts;
            Integer age = Integer.parseInt(parts);
            LocalDateTime updateTime = LocalDateTime.parse(parts, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            returnnew User(id,username,password,name,age,updateTime);

      }).toList();

      //3. 返回数据(json)
      return userList;


    }
}

*/ https://i-blog.csdnimg.cn/direct/5cbc741e2a98411993990f8358f150f8.png

4.2 分层解耦

4.2.1 标题分析

由于我们现在在程序中,需要什么对象,直接new一个对象 new UserServiceImpl(),如下图
https://i-blog.csdnimg.cn/direct/b38c9fc6542f4f36b3deac80b91f239e.png
假如说我们需要更换实现类,比如由于业务的变更,UserServiceImpl 不能满足现有的业务需求,我们需要切换为 UserServiceImpl2 这套实现,就需要修改Contorller的代码,需要创建 UserServiceImpl2 的实现new UserServiceImpl2(),如下图
https://i-blog.csdnimg.cn/direct/dd792356ba9844f0b8ce26eeb7fd6ea1.png
Service中调用Dao,也是雷同的标题。这种呢,我们就称之为层与层之间 耦合 了。 那什么是耦合呢 ?
首先需要了解软件开辟涉及到的两个概念:内聚和耦合。


[*] 内聚:软件中各个功能模块内部的功能接洽。
[*] 耦合:衡量软件中各个层/模块之间的依靠、关联的程度。
软件设计原则:高内聚低耦合。
   高内聚:指的是一个模块中各个元素之间的接洽的紧密程度,假如各个元素(语句、程序段)之间的接洽程度越高,则内聚性越高,即 "高内聚"。
低耦合:指的是软件中各个层、模块之间的依靠关联程序越低越好。
 
4.2.2 解耦思路

1)将要用到的对象交给一个容器管理。
https://i-blog.csdnimg.cn/direct/4241d7e44bb64c55b566742c33c78a10.png
2). 应用程序中用到这个对象,就直接从容器中获取
https://i-blog.csdnimg.cn/direct/44c729fb380d4dfc85834e8d7c9c4823.png
 想要实现上述解耦利用,就涉及到Spring中的两个核心概念:
   

[*] 控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种头脑称为控制反转。


[*] 对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器。

[*] 依靠注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依靠的资源,称之为依靠注入。

[*] 程序运行时需要某个资源,此时容器就为其提供这个资源。
[*] 例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象。

[*] bean对象:IOC容器中创建、管理的对象,称之为:bean对象。
https://i-blog.csdnimg.cn/direct/f6f8ab0bf5f64d508cb3515ebedf8171.png

4.3 IOC&DI入门 

1). 将Service及Dao层的实现类,交给IOC容器管理 
在实现类加上 @Component 注解,就代表把当前类产生的对象交给IOC容器管理。
A. UserDaoImpl 
package com.orange.springbootweb_001.dao.impl;

import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.dao.UserDao;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

@Component
public class UserDaoImpl implements UserDao {
    public List<String> findAll() {

      //1. 加载并读取user.txt文件,获取用户数据
      //InputStream in = new FileInputStream("src/main/resources/user.txt");
      InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
      ArrayList<String> lines =IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());
      return lines;
    }
}
B. UserServiceImpl
package com.orange.springbootweb_001.service.impl;

import com.orange.springbootweb_001.dao.UserDao;
import com.orange.springbootweb_001.dao.impl.UserDaoImpl;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Component
public class UserServiceImpl implements UserService {

    //调用Dao,获取数据
    private UserDao userDao = new UserDaoImpl();

    @Override
    public List<User> findAll() {

      List<String> lines = userDao.findAll();
      //2. 解析用户信息,封装为User对象 ->list集合
      List<User> userList = lines.stream().map(line ->{

            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts);
            String username = parts;
            String password = parts;
            String name = parts;
            Integer age = Integer.parseInt(parts);
            LocalDateTime updateTime = LocalDateTime.parse(parts, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            returnnew User(id,username,password,name,age,updateTime);

      }).toList();

      return userList;
    }
}
2). 为Controller 及 Service注入运行时所依靠的对象 
 参加注解:@Autowired,表现程序在运行的时候会自动从容器中在到这个类型的bean对象,并赋值给成员变量
 A. UserServiceImpl
package com.orange.springbootweb_001.service.impl;

import com.orange.springbootweb_001.dao.UserDao;
import com.orange.springbootweb_001.dao.impl.UserDaoImpl;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Component
public class UserServiceImpl implements UserService {

    //调用Dao,获取数据
    @Autowired
    private UserDao userDao;

    @Override
    public List<User> findAll() {

      List<String> lines = userDao.findAll();
      //2. 解析用户信息,封装为User对象 ->list集合
      List<User> userList = lines.stream().map(line ->{

            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts);
            String username = parts;
            String password = parts;
            String name = parts;
            Integer age = Integer.parseInt(parts);
            LocalDateTime updateTime = LocalDateTime.parse(parts, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            returnnew User(id,username,password,name,age,updateTime);

      }).toList();

      return userList;
    }
}
B. UserController
package com.orange.springbootweb_001.controller;import cn.hutool.core.io.IoUtil;import com.orange.springbootweb_001.pojo.User;import com.orange.springbootweb_001.service.UserService;import com.orange.springbootweb_001.service.impl.UserServiceImpl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.InputStream;import java.nio.charset.StandardCharsets;import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;import java.util.ArrayList;import java.util.List;// 用户信息Controller@RestControllerpublic class UserController {    // 调用service,获取数据    @Autowired    private UserService userService ;    @RequestMapping("/list")    public List<User> list() throws FileNotFoundException {      //3. 返回数据(json)      return userService.findAll();    }}/*

package com.orange.springbootweb_001.controller;

import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;


// 用户信息Controller
@RestController
public class UserController {

    @RequestMapping("/list")
    public List<User> list() throws FileNotFoundException {

      //1. 加载并读取user.txt文件,获取用户数据
      //InputStream in = new FileInputStream("src/main/resources/user.txt");
      InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
      ArrayList<String> lines =IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());

      //2. 解析用户信息,封装为User对象 ->list集合
      List<User> userList = lines.stream().map(line ->{

            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts);
            String username = parts;
            String password = parts;
            String name = parts;
            Integer age = Integer.parseInt(parts);
            LocalDateTime updateTime = LocalDateTime.parse(parts, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            returnnew User(id,username,password,name,age,updateTime);

      }).toList();

      //3. 返回数据(json)
      return userList;


    }
}

*/ 启动服务,运行测试。 打开浏览器,地址栏直接访问:http://localhost:8080/user.html 。 依然正常访问,就说明入门程序完成了。 已经完成了层与层之间的解耦。
https://i-blog.csdnimg.cn/direct/ddfab42f45f941f5b13a8a8b63f68076.png
4.4 IOC详解

4.4.1 Bean的声明

要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一: 
https://i-blog.csdnimg.cn/direct/ddd5597820604f878368e3142b867a76.png
   注意1:声明bean的时候,可以通过注解的value属性指定bean的名字,假如没有指定,默认为类名首字母小写。
注意2:利用以上四个注解都可以声明bean,但是在springboot集成web开辟中,声明控制器bean只能用@Controller。
那么此时,我们就可以利用 @Service 注解声明Service层的bean。 利用 @Repository 注解声明Dao层的bean。 代码实现如下: 
Service层:
package com.orange.springbootweb_001.service.impl;

import com.orange.springbootweb_001.dao.UserDao;
import com.orange.springbootweb_001.dao.impl.UserDaoImpl;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    //调用Dao,获取数据
    @Autowired
    private UserDao userDao;

    @Override
    public List<User> findAll() {

      List<String> lines = userDao.findAll();
      //2. 解析用户信息,封装为User对象 ->list集合
      List<User> userList = lines.stream().map(line ->{

            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts);
            String username = parts;
            String password = parts;
            String name = parts;
            Integer age = Integer.parseInt(parts);
            LocalDateTime updateTime = LocalDateTime.parse(parts, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            returnnew User(id,username,password,name,age,updateTime);

      }).toList();

      return userList;
    }
}
Dao层:
package com.orange.springbootweb_001.dao.impl;

import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

@Repository
public class UserDaoImpl implements UserDao {
    public List<String> findAll() {

      //1. 加载并读取user.txt文件,获取用户数据
      //InputStream in = new FileInputStream("src/main/resources/user.txt");
      InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
      ArrayList<String> lines =IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());
      return lines;
    }
}
4.4.2 组件扫描

   标题:利用前面学习的四个注解声明的bean,一定会生效吗?
答案:不一定。(缘故原由:bean想要生效,还需要被组件扫描)


[*] 前面声明bean的四大注解,要想生效,还需要被组件扫描注解 @ComponentScan 扫描。
[*] 该注解虽然没有显式设置,但是现实上已经包罗在了启动类声明注解 @SpringBootApplication 中,默认扫描的范围是启动类地点包及其
https://i-blog.csdnimg.cn/direct/42a83a2d87b144e38419bc25ce0bb04c.png
以是,我们在项目开辟中,只需要按照如上项目结构,将项目中的所有的业务类,都放在启动类地点包的子包中,就无需考虑组件扫描标题。 
4.5 DI详解

4.5.1 @Autowired用法

依靠注入,是指IOC容器要为应用程序去提供运行时所依靠的资源,而资源指的就是对象。
在入门程序案例中,我们利用了@Autowired这个注解,完成了依靠注入的利用,而这个Autowired翻译过来叫:自动装配。
@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入利用)
https://i-blog.csdnimg.cn/direct/7e6707db0ea141c2a9b6ef9a65a04a22.png
   在项目开辟中,基于@Autowired进行依靠注入时,基本都是第一种和第二种方式。(官方推荐第二种方式,由于会更加规范)但是在企业项目开辟中,很多的项目中,也会选择第一种方式由于更加简洁、高效(在规范性方面进行了妥协)。
4.5.2 注意事项 

那假如在IOC容器中,存在多个相同类型的bean对象
在下面的例子中,我们准备了两个UserService的实现类,并且都交给了IOC容器管理。 代码如下:https://i-blog.csdnimg.cn/direct/c7e521c2c98449c0b6fa0d6015339c35.png
 此时,我们启动项目会发现,控制台报错了:
https://i-blog.csdnimg.cn/direct/e053375b3b34499ea580d9d1f03e8176.png
出现错误的缘故原由呢,是由于在Spring的容器中,UserService这个类型的bean存在两个,框架不知道详细要注入哪个bean利用,以是就报错了。
怎样办理上述标题呢?Spring提供了以下几种办理方案:


[*] @Primary
[*] @Qualifier
[*] @Resource
https://i-blog.csdnimg.cn/direct/154e31afd39d430fb3ccd64770c38cb4.png
   @Autowird 与 @Resource的区别


[*] @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
[*] @Autowired 默认是按照类型注入,而@Resource是按照名称注入
 
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: JavaWeb-04-Web后端底子(SpringBootWeb、HTTP协议、分层解耦、IOC和DI)