环境配置

常用三注解辨析
| 注解 | 参数来源 | 用途 |
|---|---|---|
| @PathVariable | URL 路径的一部分 | 获取资源 ID 或路径里的动态变量 |
| @RequestParam | URL 中 ? 后面的参数(Query String),或表单 | 获取简单的 key-value 参数(name=xxx) |
| @RequestBody | HTTP 请求体(JSON) | 获取整个 JSON 内容,并反序列化成对象 |
参数来源
| 注解 | 参数来自哪里 |
|---|---|
| @PathVariable | URL 路径 |
| @RequestParam | URL 参数 / 表单 |
| @RequestBody | JSON 请求体 |
使用场景:
| 场景 | 推荐使用 |
|---|---|
| 删除、更新某个资源 | @PathVariable |
| 分页/搜索/筛选 | @RequestParam |
| 提交表单(JSON) |
@RequestBody |
是否接收JSON
| 注解 | 能否接收 JSON |
|---|---|
| @PathVariable | ❌ 不支持 |
| @RequestParam | ❌ 不支持 |
| @RequestBody | ✅ 用于 JSON |
能否接收大量数据
| 注解 | 接大量字段? |
|---|---|
| @PathVariable | ❌(只适合单个值) |
| @RequestParam | ⚠️ 不适合(字段多会很乱) |
| @RequestBody | ✅ 最合适(JSON) |
部门管理接口实现
controller层
package com.itheima.controller;
import com.itheima.pojo.Dept;
import com.itheima.pojo.Result;
import com.itheima.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@RequestMapping("/depts") //抽取公共路径
/*
--- 在类中抽取公共路径,实际方法路径为Request内的value值 + 具体方法的value值 ---
*/
@RestController
public class DeptController {
@Autowired
DeptService deptService;
// @RequestMapping(value = "/depts",method = RequestMethod.GET)
// 限定请求方式为get
@GetMapping// ("/depts"),公共路径被抽取
public Result list(){
log.info("查询员工信息");
List<Dept> allList = deptService.list();
return Result.success(allList);
}
//限定请求方式为delete
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id){
log.info("删除员工{}",id);
deptService.delete(id);
return Result.success();
}
@PostMapping
public Result add(@RequestBody Dept dept)// 注解表示接收参数为JSON格式的dept类
{
log.info("新增部门:{}",dept);
deptService.add(dept);
return Result.success();
}
@GetMapping("/{id}")
public Result selectById(@PathVariable Integer id){
log.info("根据{}查询数据",id);
Dept dept = deptService.selectById(id);
return Result.success(dept);
}
@PutMapping
public Result update(@RequestBody Dept dept){
log.info("修改部门:{}",dept);
deptService.update(dept);
return Result.success();
}
}
service层实现类
package com.itheima.service.impl;
import com.itheima.mapper.DeptMapper;
import com.itheima.pojo.Dept;
import com.itheima.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class DeptServicelmpl implements DeptService {
@Autowired
DeptMapper deptMapper;
@Override
public List<Dept> list() {
List<Dept> allList = deptMapper.list();
return allList;
}
@Override
public void delete(Integer id) {
deptMapper.deleteByID(id);
}
@Override
public void add(Dept dept) {
dept.setCreateTime(LocalDateTime.now());
dept.setUpdateTime(LocalDateTime.now());
deptMapper.add(dept);
} //进行属性补充(部门除了传递的参数名称外还有创建时间,更新时间等属性)
@Override
public void update(Dept dept) {
dept.setUpdateTime(LocalDateTime.now());
deptMapper.update(dept);
}
@Override
public Dept selectById(Integer id) {
return deptMapper.selectById(id);
}
}
service层接口
package com.itheima.service;
import com.itheima.pojo.Dept;
import java.util.List;
public interface DeptService {
List<Dept> list();
void delete(Integer id);
void add(Dept dept);
void update(Dept dept);
Dept selectById(Integer id);
}
接口
package com.itheima.mapper;
import com.itheima.pojo.Dept;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface DeptMapper {
@Select("select * from dept")
List<Dept> list();
@Delete("delete from dept where id = #{id}")
void deleteByID(Integer id);
@Insert("insert into dept(name, create_time, update_time) VALUES (#{name}, #{createTime}, #{updateTime})")
void add(Dept dept);
@Update("update dept set name = #{name}, update_time = #{updateTime} where id = #{id}")
void update(Dept dept);
@Select("select * from dept where id = #{id}")
Dept selectById(Integer id);
}
员工管理接口实现
MySQL中分页查询语法

PageHelper插件


基于pagehelper的mapper接口
@Mapper
public interface EmpMapper {
/* 不使用插件mapper部分代码实现
@Select("select count(*) from emp")
public Long count();
@Select("select * from emp limit #{start}, #{pageCount}")
public List<Emp> page(int start, int pageCount);
*/
//员工信息查询
@Select("select * from emp")
public List<Emp> list();
}
基于pagehelper的service接口
@Service
public class EmpServicelmpl implements EmpService {
@Autowired
EmpMapper empMapper;
/* 不使用pagehelper插件的代码实现
@Override
public PageBean page(int page, int pageSize) {
//获取记录总数
Long count = empMapper.count();
//返回查询数据列表
int start = (page - 1) * pageSize;
List<Emp> list= empMapper.page(start,pageSize);
return new PageBean(count,list);
}
*/
@Override
public PageBean page(int page, int pageSize) {
//设置分页参数
PageHelper.startPage(page,pageSize);
//执行查询
List<Emp> emplist = empMapper.list();
Page<Emp> p = (Page<Emp>)emplist;
//封装pageBean对象
PageBean pageBean = new PageBean(p.getTotal(),p.getResult());
return pageBean;
}
}
员工分页查询
controller层
package com.itheima.controller;
import com.itheima.pojo.Emp;
import com.itheima.pojo.PageBean;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
import java.util.List;
@Slf4j
@RestController
public class EmpController {
@Autowired
EmpService empService;
@GetMapping("/emps")
public Result page(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int pageSize,
String name, Short gender,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
log.info("分页查询:{},{}", page, pageSize);
PageBean pagebean = empService.page(page,pageSize,name,gender,begin,end);
return Result.success(pagebean);
}
}
service接口及其实现类层
package com.itheima.service;
import com.itheima.pojo.PageBean;
import java.time.LocalDate;
public interface EmpService {
PageBean page(int page, int pageSize, String name, Short gender, LocalDate begin, LocalDate end);
}
package com.itheima.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import com.itheima.pojo.PageBean;
import com.itheima.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.List;
@Service
public class EmpServicelmpl implements EmpService {
@Autowired
EmpMapper empMapper;
/* 不使用pagehelper插件的代码实现
@Override
public PageBean page(int page, int pageSize) {
//获取记录总数
Long count = empMapper.count();
//返回查询数据列表
int start = (page - 1) * pageSize;
List<Emp> list= empMapper.page(start,pageSize);
return new PageBean(count,list);
}
*/
@Override
public PageBean page(int page, int pageSize, String name, Short gender, LocalDate begin, LocalDate end) {
//设置分页参数
PageHelper.startPage(page,pageSize);
//执行查询
List<Emp> emplist = empMapper.list(name, gender, begin, end);
Page<Emp> p = (Page<Emp>)emplist;
//封装pageBean对象
PageBean pageBean = new PageBean(p.getTotal(),p.getResult());
return pageBean;
}
}
mapper接口及其xml映射文件
package com.itheima.mapper;
import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDate;
import java.util.List;
@Mapper
public interface EmpMapper {
/* 不使用插件mapper部分代码实现
@Select("select count(*) from emp")
public Long count();
@Select("select * from emp limit #{start}, #{pageCount}")
public List<Emp> page(int start, int pageCount);
*/
//员工信息查询
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
<where>
<if test="name != null and name !='' ">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null and gender !='' ">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
</mapper>
员工批量删除
controller层
@DeleteMapping("/{ids}")
public Result delete(@PathVariable List<Integer> ids) {
log.info("删除员工");
empService.delete(ids);
return Result.success();
}
service接口及其实现类
//service接口
void delete(List<Integer> ids);
//service实现类
@Override
public void delete(List<Integer> ids) {
empMapper.delete(ids);
}
mapper接口及其映射文件
void delete(List<Integer> ids);
<!-- 批量删除员工 -->
<delete id="delete">
delete from emp where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
文件上传
前端页面html文件存放处

服务端


本地存储
服务器接收到上传的文件后将文件存储到本地磁盘中
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) throws Exception {
log.info("文件上传:{} {} {}", username, age, image);
//获取原始文件名
String originalFilename = image.getOriginalFilename();
//构造唯一文件名(不能重复)--uuid(唯一通用识别码)
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));//获取文件拓展名
String newFileName = UUID.randomUUID().toString() + suffix;
log.info("新获得的文件名{}", newFileName);
//将文件储存在本地磁盘里
image.transferTo(new File("D:\\java\\" + newFileName));
return Result.success();
}
}
修改可上传存储文件的最大大小:配置大小文件
#单个文件上传的最大大小
spring.servlet.multipart.max-file-size=10MB
#单词请求上传的最大大小(可上传多个文件)
spring.servlet.multipart.max-request-size=100MB
云存储(后续补充)
新增员工(需要上传员工图片)
修改员工
根据id查询数据实现数据回显
//controller层
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
log.info("查询员工信息:{}",id);
Emp emp = empService.getById(id);
return Result.success(emp);
}
//service层
//根据id查询员工
Emp getById(Integer id);
@Override
public void update(Emp emp) {
emp.setUpdateTime(LocalDateTime.now());
empMapper.update(emp);
}
//mapper层
@Select("select * from emp where id = #{id}")
Emp getById(Integer id);
根据id修改员工
//controller层
@PutMapping
public Result update(@RequestBody Emp emp) {
log.info("更新员工:{}",emp.toString());
empService.update(emp);
return Result.success();
}
//service层
void update(Emp emp);
@Override
public void update(Emp emp) {
emp.setUpdateTime(LocalDateTime.now());
empMapper.update(emp);
}
//mapper层及其xml映射文件
void update(Emp emp);
<!-- 修改员工 -->
<update id="update">
update emp
<set>
<if test="image != null">
image = #{image},
</if>
<if test="username != null">
username = #{username},
</if>
<if test="name != null">
name = #{name},
</if>
<if test="gender != null">
gender = #{gender},
</if>
<if test="job != null">
job = #{job},
</if>
<if test="entryDate != null">
entrydate = #{entryDate},
</if>
<if test="deptId != null">
dept_Id = #{deptId},
</if>
<if test="updateTime != null">
update_time = #{updateTime}
</if>
</set>
where id = #{id}
</update>
配置文件

将参数配置在配置文件中,以后修改参数只需要在配置文件中修改,通过@Value注解注入给成员变量,无需再手动修改成员变量经过编译。
配置格式

不同配置文件的区别

yml配置文件(最常使用)

yml数据格式
server:
port: 9000
#定义对象/map集合
user:
name: Tom
age: 20
address: beijing
#定义数组/集合
#数组名
hobby:
-java
-c
-game
-sport
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/talis
username: root
password: 123456
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
ConfigurationProperties配置文件:
通过ConfigurationProperties配置配置项前缀,一次注入属性值,可以避免通过Value注解一个一个注入实体类的属性值(配置文件名与成员变量名要一致)

ConfigurationProperties与Value注解区别:
单个注入与批量注入

登录校验
登录

校验
基本流程

会话技术
会话:浏览器访问web服务器资源,会话建立,在会话中可以有多次请求响应,直到断开连接,会话结束
会话跟踪:服务器识别多次请求是否同一浏览器发出,以便在同一会话中实现请求响应的数据共享

cookie

跨域操作

session


| 项目 | Cookie | Session |
|---|---|---|
| 存储位置 | 浏览器 | 服务器内存 |
| 安全性 | 低 | 高 |
| 是否携带敏感数据 | ❌ 不建议 | ✔ 可存登录信息 |
| 大小限制 | 有(4KB) | 服务器内存决定 |
| 生命周期 | 由浏览器控制 | 由服务器控制(30min) |
| 是否依赖 Cookie | —— | ✔ 依赖 JSESSIONID |
| 项目 | Session | Token |
|---|---|---|
| 状态存储 | 服务器内存 | 客户端保存,服务器无状态 |
| 跨端兼容(APP/浏览器) | 不方便 | 非常方便 |
| 分布式扩展 | 需要共享 Session | 天然支持 |
| 安全性 | 中等 | 可更高(签名/加密) |
令牌(Token)

JWT令牌⭐️⭐️

JWT令牌生成校验:
引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
public void testJWT() {
Map<String, Object> claims = new HashMap<>();
claims.put("username", "zhangsan");
claims.put("password", "123456");
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256,"fQk0YH6vJWLQv2Nv62zyqm7EaY5kU0+FLwA+IqK7i7Y=")//签名算法
.setClaims(claims)//自定义内容(载荷)
.setExpiration(new Date(System.currentTimeMillis() + 3600*1000))//设置令牌有效期
.compact();
System.out.println(jwt);
}
@Test
public void testParseJWT() {
Claims claim = Jwts.parser()
.setSigningKey("fQk0YH6vJWLQv2Nv62zyqm7EaY5kU0+FLwA+IqK7i7Y=")
.build()
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsInVzZXJuYW1lIjoiemhhbmdzYW4iLCJleHAiOjE3NjM4ODgwNzl9.7HrYrsOtlFdmtB6knvrKhsreL0EV4aOL4aOwATS_g-k")
.getBody();
System.out.println(claim);
}
令牌非法与报错:

登录后下发令牌
filter(过滤器)

步骤

filter类

package com.itheima.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter{
@Override//初始化方法,只调用一次
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("DemoFilter init");
}
@Override//拦截到请求后调用,调用多次
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter");
//放行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override//销毁方法,只调用一次
public void destroy() {
System.out.println("DemoFilter destroy");
}
}
//启动类注解
@ServletComponentScan//使用三大组件必须加入的注解(filter/listener/servlet),开启对servlet组件的支持
@SpringBootApplication
filter执行流程详解

拦截路径

过滤器链
配置多个过滤器,实现多个过滤器

登录校验功能实现
过滤器流程


@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//获取url
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String url = request.getRequestURI().toString();
log.info("登录请求{}", url);
//判断是否为登录操作
if (url.contains("login")) {
log.info("登录操作,放行");
filterChain.doFilter(servletRequest, servletResponse);
return;
}
//不是登录操作,获取请求头令牌
String jwt = request.getHeader("token");
//是否有请求头令牌
if (!StringUtils.hasLength(jwt)) {
//没有请求头令牌,未登录
log.info("无令牌,返回未登录错误信息");
Result error = Result.error("NOT_LOGIN");
//手动将对象转为JSON格式数据 -->阿里巴巴fastJSON依赖
String json = JSONObject.toJSONString(error);
response.getWriter().write(json);
return;
}
//解析token
try {
JwtUtils.parseJWT(jwt);
} catch (Exception e) {
log.info("解析令牌失败,返回未登录错误信息");
Result error = Result.error(jwt);
//手动将对象转为JSON格式数据 -->阿里巴巴fastJSON依赖
String NOT_LOGIN = JSONObject.toJSONString(error);
response.getWriter().write(NOT_LOGIN);
return;
}
//放行
log.info("令牌合法,放行");
filterChain.doFilter(servletRequest, servletResponse);
}
}
Interceptor(拦截器) ⭐️⭐️
拦截器简介
作用类似于过滤器

使用步骤:
1.创建拦截器,继承HandleInterceptor接口并重写方法
2.注册配置拦截器:创建配置类,实现接口WebMvcConfiguration,加上Configuration注解注明是一个配置类

Interceptor流程详解
拦截路径

拦截流程

异常处理
全局异常处理器

//全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandle {
@ExceptionHandler(Exception.class)//捕获异常的类型
public Result ex(Exception ex){
ex.printStackTrace();
return Result.error("操作失败");
}
}
4206

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



