什么是SpringBoot
Spring Boot是一个构建在Spring框架顶部的项目。它提供了一种简便,快捷的方式来设置,配置和运行基于Web的简单应用程序。
它是一个Spring模块,提供了 RAD(快速应用程序开发)功能。它用于创建独立的基于Spring的应用程序,因为它需要最少的Spring配置,因此可以运行。

简而言之,Spring Boot是 Spring Framework 和 嵌入式服务器的组合。
在Spring Boot不需要XML配置(部署描述符)。它使用约定优于配置软件设计范例,这意味着可以减少开发人员的工作量。
我们可以使用Spring STS IDE 或 Spring Initializr 进行开发Spring Boot Java应用程序。
入门程序
需求:基于SpringBoot开发一个Web应用,浏览器发送一个请求/hello之后,给浏览器返回一个字符串"Hello Xxx"。

步骤:
1:创建springboot工程,并勾选web开发相关依赖
2:定义HelloController类,添加方法hello,并添加注解。
package com.itheima;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//启动类/引导类
@SpringBootApplication
public class SpringbootWebQuickstartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebQuickstartApplication.class, args);
}
}
首先是创建这个springboot模块后自带的启动类。
package com.itheima;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // 当前类是一个请求处理类
public class HelloController {
@RequestMapping("/hello") // 请求路径
public String hello(String name){
System.out.println("hello " + name);
return "Hello " + name + " ~";
}
}
然后我们要自己写一个请求处理类。

运行成功的结果,从上往下第三行可以看到8080(http)就是我们要在浏览器中输入的URL地址

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

特点:
1:基于TCP协议:面向连接,安全
2:基于请求--响应模型的:一次请求对应一次响应
3:HTTP协议是无状态的协议:对于事物处理没有记忆能力。每次请求--响应都是独立的。
缺点:多次请求间不能共享数据。
优点:速度快
请求协议:
请求数据格式

请求方式-GET:请求参数在运行中,没有请求体,如:/brand/findAll?name=OPPO&status=1.GET请求大小在浏览器中是有限制的。
请求方式-POST:请求参数在请求体中,POST请求大小是没有限制的。
Web服务器(Tomcat)对HTTP协议的请求数据进行解析,并进行了封装(HttpServletRequest),在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷
package com.itheima;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RequestController {
@RequestMapping("/request")
public String request(HttpServletRequest request){
//1:获取请求方式
String method = request.getMethod(); // GET
System.out.println("请求方式:" + method);
//2:获取请求url地址
String url = request.getRequestURL().toString(); // http://localhost:8080/request
System.out.println("请求url:" + url);
//3:获取请求协议
String protocol = request.getProtocol(); // HTTP/1.1
System.out.println("请求协议:" + protocol);
//4:获取请求参数 -name, age
String name = request.getParameter("name");
String age = request.getParameter("age");
System.out.println("请求参数:" + name + " " + age);
//5:获取请求头 -Accept
String accept = request.getHeader("Accept"); // text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*
System.out.println("请求头:" + accept);
return "OK";
}
}
响应协议
响应数据格式


重点要知道的3个状态码
200:客户端请求成功
404:请求资源不存在,URL资源输入有误,或者网站资源被删除了
500:服务器发生不可预期的错误
Web服务器对HTTP协议的响应数据进行了封装(HttpServletResponse),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷
package com.itheima;
import jakarta.servlet.http.HttpServletRequest;
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 {
/**
方式一:HttpServletResponse设置响应数据
*/
@RequestMapping("/response")
public void response(HttpServletResponse response) throws IOException {
//1:设置响应状态码
response.setStatus(200);
//2:设置响应头
response.setHeader("name", "itheima");
//3:设置响应体
response.getWriter().write("<h1>hello response</h1>");
}
/**
方式二:ResponseEntity设置响应数据 -Spring中提供的方式
*/
@RequestMapping("/response2")
public ResponseEntity<String> response2() {
return ResponseEntity.status(200)
.header("name", "itheima")
.body("<h1>hello response</h1>");
}
}
注意:响应状态码 和 响应头如果没有特护要求的话,通常不手动设定。服务器会根据请求处理的逻辑,自动设置状态码和响应头。
案例
1:准备工作:
创建一个SpringBoot工程,并勾选web依赖,lombook
引入资料中准备好的用户数据文件(user.txt),及前端静态页面
定义一个实体类,用来封装用户信息
2:开发服务端程序,接收请求,读取文本数据并响应
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data // 使用lombok依赖,自动生成getter和setter方法
@AllArgsConstructor // 使用lombok依赖,自动生成全参构造方法
@NoArgsConstructor // 使用lombok依赖,自动生成无参构造方法
public class User {
private Integer id;
private String username;
private String password;
private String name;
private Integer age;
private LocalDateTime updateTime; // 保证和前端页面中的属性保持一致,不能乱写
}
package com.itheima.controller;
import cn.hutool.core.io.IoUtil;
import com.itheima.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
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;
import java.util.stream.Collectors;
@RestController
public class UserController {
@RequestMapping("/list")
public List<User> list() {
//1:加载并读取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> usersList = lines.stream().map(line -> {
String[] split = line.split(",");
Integer id = Integer.parseInt(split[0]);
String username = split[1];
String password = split[2];
String name = split[3];
Integer age = Integer.parseInt(split[4]);
LocalDateTime updateTime = LocalDateTime.parse(split[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new User(id, username, password, name, age, updateTime);
}).toList();
//3:返回数据(json)
return usersList;
}
}

可以发现响应体返回的是json格式的数据,那么这是什么原因呢?
原因就是@RestController这个注解的底层封装了一个注解:@ReponseBody
这个注解的作用的就是将Controller的返回值直接作为响应体的数据直接响应给前端,如果说返回值是对象或者集合,会先转json再返回
分层解耦
因为所有的功能都在一个方法里,所以上述项目的代码有两个问题:复用性差和难以维护。
我们可以通过分层解耦来解决这两个问题。
三层架构

controller:控制层,接收前端所发出的请求,对请求进行处理并响应数据。
service:业务逻辑层,处理具体的业务逻辑。
dao:数据访问层(Data Access Object) (持久层),负责数据的访问操作,包括数据的增删改查。

这里命名的时候要注意规范,接口的实现类我们一般命名成 XxxxImpl。
我们首先完成Dao层面的接口和接口实现类
package com.itheima.dao;
import java.util.List;
public interface UserDao {
/**
* 加载用户数据
*/
public List<String> findAll();
}
package com.itheima.dao.impl;
import cn.hutool.core.io.IoUtil;
import com.itheima.dao.UserDao;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class UserDaoImpl implements UserDao {
@Override
public List<String> findAll() {
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
return lines;
}
}
我们再完成Service层面的接口和接口实现类
package com.itheima.service;
import com.itheima.pojo.User;
import java.util.List;
public interface UserService {
/*
* 查询所有用户信息
* */
public List<User> findAll();
}
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import com.itheima.pojo.User;
import com.itheima.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();
List<User> usersList = lines.stream().map(line -> {
String[] split = line.split(",");
Integer id = Integer.parseInt(split[0]);
String username = split[1];
String password = split[2];
String name = split[3];
Integer age = Integer.parseInt(split[4]);
LocalDateTime updateTime = LocalDateTime.parse(split[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new User(id, username, password, name, age, updateTime);
}).toList();
return usersList;
}
}
最后是controller层返回
@RestController
public class UserController {
private UserService userService = new UserServiceImpl();
@RequestMapping("/list")
public List<User> list() {
//首先调用UserService方法,获取数据
List<User> usersList = userService.findAll();
//最后返回数据
return usersList;
}
}
最后可以看一下整个流程还是比较清晰的。

IOC & DI(入门)
为了遵守软件设计的原则:高内聚低耦合。
控制反转:Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
依赖注入:Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
Bean对象:IOC容器中创建,管理的对象,称之为Bean。

1:将Dao 及 Service层的实现类,交给IOC容器管理。
2:为Controller 及 Service注入运行时所依赖的对象
首先是Dao层
package com.itheima.dao.impl;
import cn.hutool.core.io.IoUtil;
import com.itheima.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 // 将当前组件添加到Spring容器中
public class UserDaoImpl implements UserDao {
@Override
public List<String> findAll() {
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
return lines;
}
}
然后是Service层
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import com.itheima.pojo.User;
import com.itheima.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 {
@Autowired //应用程序时,会自动查询该类型的bean对象,并赋值给该成员变量;
//首先要从Dao层调取数据
private UserDao userDao;
@Override
public List<User> findAll() {
List<String> lines = userDao.findAll();
List<User> usersList = lines.stream().map(line -> {
String[] split = line.split(",");
Integer id = Integer.parseInt(split[0]);
String username = split[1];
String password = split[2];
String name = split[3];
Integer age = Integer.parseInt(split[4]);
LocalDateTime updateTime = LocalDateTime.parse(split[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new User(id, username, password, name, age, updateTime);
}).toList();
return usersList;
}
}
最后是controller层
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/list")
public List<User> list() {
//首先调用UserService方法,获取数据
List<User> usersList = userService.findAll();
//最后返回数据
return usersList;
}
}
IOC详解
要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一:

如果我们不指定Bean对象的名字,它就默认是当前类名且第一个字母小写
当然我们也可以指定类名
@Repository("userDao")
前面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描。
该注解虽然没有显示配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication中,默认扫描的范围是启动类所在包及其子包。
DI详解
三种注入方式及其优缺点
方式1:属性注入
@Autowired
private UserService userService;
优点:代码简洁,方便快速开发
缺点:隐藏了类之间的依赖关系,可能会破坏类的封装性。
方式2:构造器注入
private final UserService userService;
@Autowired(如果当前类中只有这么一个构造函数,@Autowired可以省略)
public UserController(UserService userService) {
this.userService = userService;
}
优点:能清晰地看到类的依赖关系,提高代码的安全性
缺点:代码繁琐,如果构造参数过多,可能导致构造函数臃肿。
// 方式3:setter方法注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
优点:保持了类的封装性。依赖关系更清晰。
缺点:需要额外编写setter方法,增加了代码量
最后一个问题:如果一个接口有多个实现类,即存在多个相同类型的bean,那么我怎么拿到我需要的那个bean呢?

可以用这三个方案来解决问题,@Primary是写在Service层的,代表默认先用那个bean,其他两个都是写在Controller层的。
@Resource 与 @Autowired区别:
* @Autowired是Spring框架提供的注解,而@Resource是JavaEE规范提供的。
* @Autowired是默认按照类型注入,而@Resource默认是按照名称注入。
365

被折叠的 条评论
为什么被折叠?



