# SpringBoot简介
SSM=Spring+SpringMVC+Mybatis
传统Spring开发缺点:
1. 导入依赖繁琐
2. 项目配置繁琐
Spring Boot是全新框架(更像是一个工具,脚手架),是Spring提供的一个子项目,用于快速构建Spring应用程序。
随着Spring 3.0的发布,Spring 团队逐渐开始摆脱XML配置文件,并且在开发过程中大量使用“约定优先配置”(convention over configuration)的思想来摆脱Spring框架中各类繁复纷杂的配置。
SpringBoot的特性:
- 起步依赖 jar包的管理 starter
- 自动配置 AutoConfiguration
- 内置tomcat
springboot 4.0一下要选择 spring boot dev toos 和springboot web 才会有resources文件夹
三层架构

1. Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
2. Service:业务逻辑层。处理具体的业务逻辑。
3. Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。执行流程:
1. 前端发起的请求,由Controller层接收(Controller响应数据给前端)
2. Controller层调用Service层来进行逻辑处理(Service层处理完后,把处理结果返回给Controller层)
3. Serivce层调用Dao层(逻辑处理过程中需要用到的一些数据要从Dao层获取)
4. Dao层操作文件中的数据(Dao拿到的数据会返回给Service层)
IOC、DI
控制反转(Inversion on Control)IOC:对象的创建交给外部容器来完成(这里就是交给Spring容器),这就叫控制反转。
IOC:Spring就是一个大的内存容器(一块内存区域),三层架构里面上一层不需要再去new下一层对象,在上一层只需要写下一层的接口,new对象由Spring容器帮我们完成,各层直接向Spring容器要对象就可以。
class StudentController{ // 需要什么,就去创建什么(自己去new),这就叫“控制正转”(通俗一点就是自己控制new哪个对象) private IStudentService studentService = new StudentServiceImpl(); }
依赖注入:Dependency injection (DI)
现在new这个对象不是由自己new,是由Spring容器帮我们new对象,现在要得到这个Spring容器new出来的对象,就要“依赖”Spring容器,“注入”Spring容器new出来的对象。
IOC和DI区别:
IOC:解决对象创建的问题(对象的创建交给别人)Inversion of Control。
DI:在创建完对象后,对象关系的处理就是依赖注入(通过set方法实现依赖注入)Dependency injection。
先有IOC(对象创建),再有DI(处理对象关系)
在三层架构中最终实现的效果是:在Controller不会出现Service层具体的实现类代码,只会看到Service层接口,Service层也不会出现Dao层具体实现类的代码,也只会看到Dao层的接口。IOC:解决对象创建的问题(对象的创建交给别人)Inversion of Control。
DI:在创建完对象后,对象关系的处理就是依赖注入(通过set方法实现依赖注入)Dependency injection。
先有IOC(对象创建),再有DI(处理对象关系)
在三层架构中最终实现的效果是:在Controller不会出现Service层具体的实现类代码,只会看到Service层接口,Service层也不会出现Dao层具体实现类的代码,也只会看到Dao层的接口。
Spring 注解汇总
Spring 核心常用注解
核心重点:覆盖 Bean 的实例化注册、依赖注入、容器配置三类核心注解。
| 注解 | 说明 |
|---|---|
@Component |
通用组件注解,标注在类上,将该类实例化为 Bean 并交给 Spring 容器管理 |
@Controller |
标注在控制层类上,是 @Component 的衍生注解,用于标识表现层 Bean |
@Service |
标注在业务层类上,是 @Component 的衍生注解,用于标识业务层 Bean |
@Repository |
标注在数据访问层(DAO/Mapper)类上,是 @Component 的衍生注解,用于标识持久层 Bean |
@Autowired |
标注在字段/构造器/setter 方法上,默认按类型(byType)完成依赖注入 |
@Qualifier |
必须配合 @Autowired 使用,当容器存在多个同类型 Bean 时,按名称(byName)指定注入的 Bean |
@Scope |
标注 Bean 的作用范围,默认值为 singleton(单例),可选 prototype(多例)、request、session 等 |
@Configuration |
标注在类上,声明当前类为 Spring 配置类,替代 XML 配置文件,容器启动时会加载该类中的配置 |
@ComponentScan |
指定 Spring 容器初始化时扫描的包路径,自动扫描包下带组件注解的类并注册为 Bean |
@Bean |
标注在方法上,将方法的返回值对象存入 Spring IOC 容器,常用于将第三方类注册为 Bean |
@Import |
用于导入其他配置类或普通类,被导入的类会自动被 Spring 加载并注册为 Bean |
@Aspect / @Before / @After / @Around / @Pointcut |
用于 AOP 切面编程,分别标识切面类、前置通知、后置通知、环绕通知、切点表达式 |
SpringMVC 常用注解
核心重点:覆盖请求路径映射、参数接收、数据响应相关注解。
| 注解 | 说明 |
|---|---|
@RequestMapping |
请求路径映射注解,可标注在类和方法上;标注在类上时,类中所有方法共享该父路径 |
@GetMapping |
GET 请求专用映射注解,等价于 @RequestMapping(method = RequestMethod.GET) |
@PostMapping |
POST 请求专用映射注解,等价于 @RequestMapping(method = RequestMethod.POST) |
@RequestBody |
接收 HTTP 请求中的 JSON 数据,自动将 JSON 转换为 Java 实体对象 |
@RequestParam |
指定请求参数名称,适用于前后端参数名不一致的场景,也可设置参数默认值、是否必传 |
@PathVariable |
从 REST 风格 URL 的占位符中获取参数,例如 /user/{id} 路径中的 id |
@ResponseBody |
将 Controller 方法的返回对象转换为 JSON 格式,响应给客户端 |
@RequestHeader |
获取请求头中指定名称的数据,注入到控制器方法参数中 |
@RestController |
组合注解,等价于 @Controller + @ResponseBody,标注在类上时,类中所有方法都返回 JSON |
@CookieValue |
将指定名称的 Cookie 值,注入到控制器方法的参数中 |
SpringBoot 常用注解
核心重点:启动类、自动配置核心注解。
| 注解 | 说明 |
|---|---|
@SpringBootApplication |
SpringBoot 项目启动类核心注解,标注在启动类上,是一个组合注解 |
@SpringBootConfiguration |
等价于 @Configuration,用于标识 SpringBoot 的主配置类 |
@EnableAutoConfiguration |
开启 SpringBoot 自动配置功能,也可通过属性排除指定的自动配置类 |
@ComponentScan |
组件扫描注解,默认扫描启动类所在包及其子包下的所有组件 |
补充:@ComponentScan 是 Spring 注解还是 SpringBoot 注解?
spring MVC
@ComponentScan 本质是 Spring 框架原生注解,并非 SpringBoot 独有。
- 原生 Spring 中,它的作用是指定包扫描路径,自动识别
@Component、@Service、@Repository、@Controller等注解的类,并注册为 Spring Bean。 - 在 SpringBoot 中,它被整合进了启动注解
@SpringBootApplication中。@SpringBootApplication本质是@Configuration+@EnableAutoConfiguration+@ComponentScan三个注解的组合。 - 结论:它是 Spring 原生注解,在 SpringBoot 中被默认集成使用,无需手动声明也能生效,也可以手动配置自定义扫描路径。

MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。
1. Model(模型) – 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
2. View(视图) – 界面设计人员进行图形界面设计。
3. Controller(控制器) – 负责转发请求,对请求进行处理。
POJO(Plain Ordinary Java Object)简单的Java对象
JavaBean:一种规范,表达实体和信息的规范,便于封装重用
1. 所有属性为private
2. 提供默认无参构造方法
3. 提供getter和setter
4. 实现serializable接口
@RequestMapping("/selectAll")
public String selectAll(Model model) {
System.out.println("StudentController.selectAll");
List<Student> list = studentService.selectAll();
model.addAttribute("list", list);
return "student_list";
}
thymeleaf使用
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--<link rel="stylesheet" type="text/css" th:href="@{/static/bootstrap-3.4.1-dist/css/bootstrap.css}"/>-->
<link rel="stylesheet" type="text/css" href="/static/bootstrap-3.4.1-dist/css/bootstrap.css"/>
</head>
<body>
<table class="table table-bordered table-condensed table-hover table-striped">
<tr>
<td>编号</td>
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
</tr>
<tr th:each="student:${list}">
<td th:text="${student.id}"></td>
<td th:text="${student.name}"></td>
<td th:text="${student.age}"></td>
<td th:text="${student.gender}"></td>
</tr>
</table>
</body>
</html>
登录、转发、重定向、Get、Post
转发和重定向
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("/login")
public String login(String name, String password, Model model) {
User user = userService.login(name, password);
if (ObjectUtils.isEmpty(user)) {
model.addAttribute("errorMsg", "用户名或密码错误");
return "login";
}
return "redirect:/student/selectAll";
}
}
不需要加@ResponseBody 会使return变为json格式不是转发
Http Get和Post请求的区别?
GET 和 POST 是 HTTP 协议中两种常用的请求方法,它们在不同的场景和目的下有不同的特点和用法。
1. 语义上的区别:GET 通常用于查询资源,而 POST 通常用于创建。
2. 格式上的区别:GET 请求的参数通常放在 URL 中,形成查询字符串(querystring),用?形式拼接在地址栏后面,而 POST 请求的参数通常放在请求体(body)中,GET 请求的 URL 长度受到浏览器的限制,而 POST 请求的 body 大小则没有明确的限制。1. 安全性上的区别:GET 请求和 POST 请求都不是绝对安全的,因为 HTTP 协议本身是明文传输的,无论是 URL、请求体(body)中都可能被窃取或篡改。为了保证安全性,必须使用 HTTPS 协议来加密传输数据。不过,在一些场景下,GET 请求相比 POST 请求更容易泄露敏感数据,因为 GET 请求的参数会出现在 URL 中。
Cookie、Session
会话技术分为:Cookie和Session
- Cookie:数据存储在浏览器客户端本地,减少服务器端的存储的压力,安全性不好,客户端可以清除cookie
- Session:将数据存储到服务器端,安全性相对好,增加服务器的压力
Session与Cookie的区别
| 对比项 | Cookie | Session |
|---|---|---|
| 存储位置 | ✅ 客户端(浏览器) | ✅ 服务器端 |
| 保存数据 | 键值对,只能存字符串,小体积(4KB以内) | 键值对,值可以存储对象,容量大 |
| 数据安全性 | ❌ 容易被查看/篡改(保存在用户机器) | ✅ 更安全,保存在服务器 |
| 生命周期 | 可设置过期时间,默认浏览器关闭失效 | 服务器关闭或超时失效(如30分钟) |
| 是否占用服务器资源 | ❌ 不占用 | ✅ 占用(每个用户一个 Session) |
| 典型使用场景 | 记住用户名、自动登录、广告跟踪 | 用户登录状态、权限控制 |
Cookie:数据存在客户端,不安全但节省服务器资源;
Session:数据存在服务器,更安全但占内存,需要通过 Cookie 维持连接。
@RequestMapping("/setCookie")
public void setCookie(HttpServletResponse response) {
Cookie cookie = new Cookie("goods", "IPhone");
// Cookie有效路径,表示这个 Cookie 对当前域名下所有 URL 路径生效,是登录认证 Cookie 最常用的设置。
cookie.setPath("/");
// 有效时间(秒)
cookie.setMaxAge(60 * 60);
response.addCookie(cookie);
}
@RequestMapping("/getCookie1")
@ResponseBody
public String getCookie1(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
System.out.println(cookie.getName() + ":" + cookie.getValue());
}
return "ok";
}
//更简洁的写法
@RequestMapping("/getCookie2")
@ResponseBody
public String getCookie2(@CookieValue("goods") String goods) {
System.out.println(goods);
return "ok";
}
存储到seesion中
@RequestMapping("/login")
public String login(String name, String password, Model model, HttpSession session) {
System.out.println("UserController.login");
User user = userService.login(name, password);
if (ObjectUtils.isEmpty(user)) {
model.addAttribute("errorMsg", "用户名或密码错误");
return "login";
}
session.setAttribute("user", user);
return "redirect:/student/selectAll";
}
登录拦截器
//拦截器的作用:浏览器访问服务器的请求,都要首先经过拦截器
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.判断用户有没有登录
//2.如果登录了,就放行,可以访问后台的资源
//3.如果没有登录,跳转到登录界面
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
if (user == null) {
//没有登录,跳转到登录界面
response.sendRedirect("/user/toLogin");
return false;
}
//已经登录,放行
return true;
}
}
拦截器配置 WebMvcConfigurer提供了配置SpringMVC底层的所有组件入口
// @Component <bean>
// @Controller @Service @Repository
// 这四个注解的作用是一样的,下面三个不一样主要是为了区分不同层
// @Configuration用于定义配置类,可以替换xml配置文件,
// 加了这个注解的类的内部包含一个或多个被@Bean注解的方法
@Configuration
public class WebConfig implements WebMvcConfigurer {
/* <bean name="student" class="com.situ.springboot.pojo.Student"/>*/
@Bean
public Student createStudent() {
return new Student();
}
/*<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path=""/>
<bean class="com.situ.mvc.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
</mvc:interceptors>*/
// 这个方法用来注册拦截器,我们写的拦截器需要在这里配置才能生效
@Override
public void addInterceptors(InterceptorRegistry registry) {
//把登录的拦截器配置上才能起作用
// addPathPatterns("/**") 拦截器拦截所有的请求,静态资源也拦截了,需要放行
// excludePathPatterns 代表哪些请求不需要拦截
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/user/toLogin", "/user/login", "/user/logout", "/static/**");
}
}
axios
最大特点:异步请求,不刷新整个页面,只刷新局部也叫局部刷新。

案例使用
<form id="loginForm">
<label for="name">用户名</label>
<input type="text" id="name" name="name"
placeholder="请输入用户名" required autofocus/>
<br>
<label for="password">密码</label>
<input type="password" id="password" name="password"
placeholder="请输入密码" required/>
<br>
<input type="submit" value="登录"/>
</form>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
document.getElementById('loginForm').addEventListener('submit', function (event) {
//是 JavaScript 中用于阻止浏览器默认行为的核心方法。阻止表单自动提交与页面刷新
event.preventDefault();
let name = document.getElementById('name').value;
let password = document.getElementById('password').value;
//方式一:
//let params = new URLSearchParams();
//params.append("name", name);
//params.append("password", password);
//方式二:
let params = {
name: name,
password: password
}
axios.post('/user/login', params).then(function (resp) {
let result = resp.data;
console.log(result);
if (result.code == 1) {
alert(result.msg);
location.href = '/student/selectAll';
} else {
alert(result.msg);
}
}).catch(function (error) {
alert('请求失败,请稍后重试');
});
});
</script>
请求方式
1. axios.get(url[, config]) 查找
2. axios.delete(url[, config]) 删除
3. axios.post(url[, data[, config]]) 添加
4. axios.put(url[, data[, config]]) 更新
get和delect中没有data 传值时 要axios.delete('/student/delectById',{params:{id:id}}).then(function(resp){--}用config包裹这params来传递数据
四种都可以拼接
axios.delete("/teacher/deleteById?id=5")
location.href = '/student/selectAll1';返回值为get类型
data 是请求体(body),但 body 是什么格式,取决于你传的数据类型:
| x-www-form-urlencoded | json | |
|---|---|---|
| 来源 | HTML 表单默认就是这格式 | 前后端分离 / RESTful API 主流 |
| 前端产生 | new URLSearchParams() 后 append | 直接传js普通对象,axios 自动 JSON.stringify |
| 格式 | 键值对,& 分隔,= 连接 name=zhangsan&age=23 | JSON 结构{“name”:”zhangsan”,”age”:23,”gender”:”男”} |
| Spring 接收 | 参数绑定 / 对象绑定 | 必须 @RequestBody |
Axios 的 config 理解成请求配置对象,里面放的是请求控制信息:
{ params: {}, // URL参数
headers: {}, // 请求头
timeout: 5000, // 超时
}
async function login() {
const url = '/api/user/login'
const data = { username: 'admin', password: '123456' }
// 第三段 config 配置
const config = {
timeout: 10000, // 超时10秒
headers: {
'token': localStorage.getItem('token'), // 携带登录令牌
'Content-Type': 'application/json'
}
}
const res = await axios.post(url, data, config)
console.log(res.data)
}
后端 → 前端(后端把数据传给页面)
1. Model 传值(单次请求有效,转发可用、重定向失效)
底层数据存放于 request 请求域,一次请求生命周期内生效。
@GetMapping("/list")
public String list(Model model){
model.addAttribute("userName","管理员");
model.addAttribute("userList", userList);
return "user/list";
}
页面取值:
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<table class="table table-striped table-bordered table-hover table-condensed">
<tr>
<td>ID</td>
<td>名字</td>
<td>年龄</td>
<td>性别</td>
<td>操作</td>
<td>添加</td>
</tr>
<tr th:each="student:${students}">
<td th:text="${student.id}">1</td>
<td th:text="${student.name}">zhangsan</td>
<td th:text="${student.age}">23</td>
<td th:text="${student.gender}">23</td>
<td><button class="btn btn-danger btn-sm" th:onclick="|delectById(${student.id})|">删除</button></td>
<td><button class="btn btn-success btn-sm" th:onclick="|update(${student.id})|">修改</button></td>
</tr>
</table>
特点:
- 仅转发页面可以拿到数据;
return redirect:/xxx重定向会丢失 Model 数据; - 适合列表渲染、表单回填。
2. Session 传值(会话级别,浏览器不关闭就一直存在)
数据存入 session域,整个会话所有页面均可读取。
// 方式1:注入HttpSession
@GetMapping("/login")
public String login(HttpSession session){
session.setAttribute("loginUser","张三");
return "index";
}
// 方式2:Model自动存入session
model.addAttribute("loginUser","张三").setSessionAttributes("loginUser");
页面取值:
<span th:text="${session.loginUser}"></span>
适用场景:存放登录用户、全局登录状态。
二者核心区别
| 载体 | 生命周期 | 重定向能否保留数据 |
|---|---|---|
| Model(request) | 一次请求结束销毁 | ❌ 重定向丢失 |
| Session | 浏览器会话期间有效 | ✅ 任意页面均可读取 |
前端 → 后端(页面把数据传给后端)
方式1:原生 Form 表单提交(同步提交,页面刷新)
① 普通表单(能正常传参)
表单 name 属性和后端实体字段一致即可接收参数:
<form th:action="@{/user/add}" method="post">
<input name="username">
<input name="age">
<button type="submit">提交</button>
</form>
后端接收:
// 等价默认 @ModelAttribute,接收表单参数
@PostMapping("/user/add")
public String add(User user){}
② 阻止默认提交
正确阻止默认提交后依然可以传参:
<form id="form">
<input name="username" id="username">
<button onclick="submitForm()">提交</button>
</form>
<script>
function submitForm(event){
// 阻止表单默认刷新提交
event.preventDefault();
// 方式A:序列化表单参数传给后端
let formData = new FormData(document.getElementById('form'));
axios.post('/user/add', formData).then(res=>{})
}
</script>
拦截表单只是阻止浏览器原生同步跳转,依旧可以拿到表单数据异步提交。
方式2:Axios 异步提交(主流,页面不刷新)
分为两种传参格式:
- JSON格式(后端搭配 @RequestBody)
- 表单键值对(后端搭配 @ModelAttribute)
axios.post('/user/add',`username=张三&age=18`,{
headers:{'Content-Type':'application/x-www-form-urlencoded'}
})
两种URL传参方式
URL拼接参数(查询参数)
地址样式:/delete?id=1&name=dxk 前端参数拼接在 ? 后面,使用 & 分隔多个参数,后端使用 @RequestParam 接收。
前端写法
<!-- 超链接 -->
<a th:href="@{/delete?id=1}">删除</a>
<!-- axios 发送 -->
axios.get('/delete?id=1')
// 等价写法
axios.get('/delete', { params: { id: 1 } })
后端接收
// 写法1:参数名和前端参数名一致
@GetMapping("/delete")
public String delete(@RequestParam Integer id) {
System.out.println(id);
return "redirect:/list";
}
// 写法2:参数名不一致,指定name映射
@GetMapping("/delete")
public String delete(@RequestParam(name = "id") Integer uid) {
return "redirect:/list";
}
// 写法3:非必传参数,设置required=false
@GetMapping("/delete")
public String delete(@RequestParam(value = "id", required = false) Integer id) {
return "redirect:/list";
}
特点
- 参数暴露在URL中,适合传递简单非敏感参数;
- 参数顺序无要求;
- 参数可以省略(配置
required=false)。
REST风格路径参数
地址样式:/user/1、/user/1/edit 参数拼接在URL路径中,后端使用 @PathVariable 绑定路径占位符。
前端写法
<a th:href="@{/user/1}">查看用户</a>
axios.get('/user/1')
后端接收
// 写法1:占位符名称与形参名称一致
@GetMapping("/user/{id}")
public String getUser(@PathVariable Integer id) {
System.out.println(id);
return "detail";
}
// 写法2:名称不一致,手动绑定
@GetMapping("/user/{uid}")
public String getUser(@PathVariable("uid") Integer id) {
return "detail";
}
// 多路径参数示例
@GetMapping("/user/{id}/{name}")
public String getUser(@PathVariable Integer id, @PathVariable String name) {
return "detail";
}
特点
- URL风格简洁,符合REST规范;
- 路径占位符必须传值,不能省略;
- 常用于详情、删除接口:
/delete/5。
对比表格
| 方式 | 地址格式 | 注解 | 是否可缺省参数 | 适用场景 |
|---|---|---|---|---|
| 查询参数 | /delete?id=1 |
@RequestParam |
可以(required=false) | 筛选、分页、模糊查询 |
| 路径参数 | /delete/1 |
@PathVariable |
不可省略 | 详情、删除、REST接口 |
混合使用示例
同时存在路径参数 + 查询参数 地址:/user/1?type=edit
@GetMapping("/user/{id}")
public void test(@PathVariable Integer id, @RequestParam String type){
// id = 1,type = edit
}
RequestParam和RequestBody
| 对比项 | @RequestParam | @RequestBody |
|---|---|---|
| 参数位置 | URL 查询串 ?id=1 |
请求体 Body |
| 支持请求方式 | GET/POST/PUT/DELETE 全都支持 | 仅 POST、PUT、DELETE(GET 无 body,不能用) |
| 数据格式 | key=value&key=value |
JSON 对象 |
| Content-Type | application/x-www-form-urlencoded、普通 URL 参数 |
application/json |
| 能否接收对象 | 可以,但需要实体属性和参数名一一对应 | 直接完整映射实体类,最适合传完整对象 |
| 典型场景 | 分页、删除、根据 ID 查询、筛选 | 新增、修改,传递完整表单对象 |