Java Web 后端开发知识总结
把 Spring Boot 后端开发涉及的核心技术串在一起,从请求进入 Filter 到数据返回浏览器,每一层做了什么。
Java Web 后端开发知识总结
做后端开发这段时间,接触的技术点挺多的。
Spring MVC、Spring Boot、MyBatis、JWT、ThreadLocal……一个个学的时候感觉都懂了,但真正写项目的时候又经常忘。
现在回过头把整个后端开发的知识体系梳理一遍,算是给自己做个总结,也方便以后查。
一个请求从哪来到哪去
先说最核心的东西:一个 HTTP 请求从浏览器发出,到我返回响应,中间经过了哪些层。
浏览器 → Filter → Interceptor → Controller → Service → Mapper → MySQL
浏览器 ← Filter ← Interceptor ← Controller ← Service ← Mapper ← MySQL
每一层都有明确的职责,不能乱。

Filter 是 Servlet 规范的,所有请求都会经过它,适合做全局的预处理,比如解析 Token、设置编码。
Interceptor 是 Spring MVC 的,粒度更细,可以针对特定路径拦截,适合做登录校验、权限检查。
Controller 负责接收请求、解析参数、调用 Service、返回响应。
Service 是业务逻辑的核心,事务管理也在这里。
Mapper 负责和数据库打交道,执行 SQL。
把它们分开的好处是,改一层的代码不会影响其他层,维护起来方便很多。
Filter 和 Interceptor 的区别
这两个东西长得有点像,经常有人搞混。
Filter 是 JavaWeb 规范里的东西,不依赖 Spring,所有请求都经过它。可以在 Servlet 之前修改请求,在 Servlet 之后修改响应。
Interceptor 是 Spring MVC 提供的,只在 Controller 前后执行,不能修改请求和响应本身,只能决定放不放过这个请求。
常见的用法:
Filter 负责解析 Token,把用户信息存入 ThreadLocal。
Interceptor 负责校验 ThreadLocal 里有没有用户信息,没有就返回 401。
这样分工明确,Filter 做通用处理,Interceptor 做业务相关的校验。
Controller 层:接收请求返回响应
Controller 层做的事情比较直接:拿到请求参数,调 Service,包装响应。
@RestController
@RequestMapping("/emps")
public class EmpController {
@Autowired
private EmpService empService;
@GetMapping
public Result page(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize) {
return Result.success(empService.page(page, pageSize));
}
@PostMapping
public Result save(@RequestBody Emp emp) {
empService.save(emp);
return Result.success();
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
empService.delete(id);
return Result.success();
}
}
常用的注解:
@RestController=@Controller+@ResponseBody,直接返回 JSON@RequestMapping映射请求路径@GetMapping、@PostMapping指定请求方式@RequestParam获取查询参数@RequestBody获取请求体(JSON 转对象)@PathVariable获取 URL 路径参数
Service 层:业务逻辑 + 事务
Service 层是业务逻辑的核心,通常会有对应的 Impl 实现类。
@Service
public class EmpServiceImpl implements EmpService {
@Autowired
private EmpMapper empMapper;
@Override
@Transactional
public void deleteEmpAndDept(Integer empId) {
empMapper.deleteById(empId);
// 删完员工再删部门,如果第二步失败,第一步回滚
deptMapper.deleteById(...);
}
}
@Transactional 标记事务,保证一组操作要么全成功要么全失败。
有几个坑要注意:
方法不是 public 的,事务不生效。同类内部调用绕过代理,事务也不生效。异常被 catch 了没重新抛出,事务也会提交而不是回滚。
IOC 和 DI:Spring 的核心思想
刚开始学 Spring 的时候,对 IOC 和 DI 这两个概念一直有点模糊。
后来想明白了,其实就一件事:把对象的创建权从程序员手里交出来,交给 Spring 容器。
以前要自己 new 对象:
UserService userService = new UserServiceImpl();
现在只需要声明依赖,Spring 会自动注入:
@Autowired
private UserService userService;
IOC 是思想,DI 是实现。
- IOC(控制反转):对象的创建和管理权交给容器
- DI(依赖注入):容器自动把依赖的对象注入进来
注入方式有三种,字段注入最常用,构造器注入最推荐(便于测试)。
AOP:横切关注点的分离
AOP 解决的问题是:有些功能不是核心业务,但每个地方都需要。
比如日志记录、权限校验、性能监控。
如果没有 AOP,这些逻辑要写在每个方法里,代码到处都是重复的。
有了 AOP,可以把这些公共逻辑抽成一个切面,自动织入到目标方法前后。
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.myapp.service..*(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long cost = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature().getName() + " 耗时 " + cost + "ms");
return result;
}
}
切入点表达式决定拦截哪些方法,通知类型决定在什么时候执行增强逻辑。
MyBatis:数据库访问
MyBatis 是持久层框架,负责把 Java 对象和数据库表映射起来。
可以用注解写 SQL,也可以用 XML 写,两种方式都行,看个人习惯。
注解方式简单直接:
@Mapper
public interface EmpMapper {
@Select("SELECT * FROM emp WHERE id = #{id}")
Emp findById(Integer id);
@Insert("INSERT INTO emp(name, phone) VALUES(#{name}, #{phone})")
void insert(Emp emp);
}
XML 方式更适合复杂的 SQL,尤其是动态 SQL:
<select id="selectByCondition" resultType="Emp">
SELECT * FROM emp
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="gender != null">
AND gender = #{gender}
</if>
</where>
</select>
<if>、<where>、<foreach> 这些标签让动态 SQL 写起来很方便。
认证与授权:JWT + ThreadLocal
登录认证是 Web 项目的标配。
传统方案是 Session,服务器存会话数据,客户端存 Session ID。
现在前后端分离的项目,一般用 JWT(JSON Web Token),无状态认证。
流程是这样的:
用户登录,后端验证账号密码,生成 JWT 返回给前端。前端把 Token 存在 localStorage 里,后续请求在请求头带上 Token。后端用 Filter 解析 Token,把用户信息存入 ThreadLocal,后续的 Service 层可以直接从 ThreadLocal 取当前用户。
// Token 解析
String token = request.getHeader("token");
Claims claims = JwtUtils.parseToken(token);
Integer userId = claims.get("userId", Integer.class);
// 存入 ThreadLocal
UserContext.set(userId);
// Service 层直接取
Integer userId = UserContext.get();
ThreadLocal 的本质是每个线程有自己的专属变量,请求开始时存入,请求结束时清理。
记得清理,不然线程池复用线程的时候,上一个请求的数据可能会影响下一个请求。
全局异常处理
项目里难免会抛异常,不可能每个方法都 try-catch。
用 @RestControllerAdvice 可以统一处理所有 Controller 抛出的异常。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Result handleBusinessException(BusinessException e) {
log.error("业务异常:", e);
return Result.error(e.getMessage());
}
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
log.error("未知异常:", e);
return Result.error("系统繁忙,请稍后再试");
}
}
这样不管哪个 Controller 出错,都能返回统一的错误格式。
Spring Boot:整合一切的底座
Spring Boot 本身不是一堆新技术,它是把 Spring framework、Spring MVC、MyBatis 这些框架整合到一起的工具。

两大核心特性:
起步依赖:一个 starter 引入一组相关依赖,版本都是官方测过的,不用自己管版本冲突。
自动配置:根据引入的依赖自动配置 Bean,引入 MyBatis 的 starter 就自动配好 SqlSessionFactory,不用手动写配置类。
启动类上的 @SpringBootApplication 包含三个注解:
@SpringBootConfiguration:声明这是一个配置类@ComponentScan:扫描启动类所在包及其子包@EnableAutoConfiguration:开启自动配置
知识体系全景
把以上所有内容串在一起,后端开发的知识体系大概是这样:
┌─────────────────────────────────────────────┐
│ Spring Boot │
│ (起步依赖 + 自动配置,整合一切) │
├──────────┬──────────────┬───────────────────┤
│ SpringMVC │ Spring Core │ MyBatis │
│ │ │ │
│ 接收请求 │ IOC/DI │ Mapper 接口 │
│ 响应数据 │ AOP │ 动态 SQL │
│ 拦截器 │ 事务管理 │ 结果映射 │
│ 全局异常 │ │ │
├────────────┴──────────────┴───────────────────┤
│ JavaWeb 基础 │
│ Filter、Cookie、Session │
├────────────────────────────────────────────────┤
│ 解决方案 │
│ JWT(认证)、ThreadLocal(上下文) │
└────────────────────────────────────────────────┘
小结
后端开发的核心就几件事:
- 接收请求(Controller)
- 处理业务(Service)
- 操作数据库(Mapper)
- 统一管理(Spring Boot)
理解了分层架构和各层职责,再结合 IOC/AOP/事务这些概念,整个后端开发的知识体系就清晰了。
学习过程就是不断踩坑、不断补基础的过程。前面觉得简单的东西,写到后面会发现坑还挺多。但每次解决一个问题,理解就会深一层。