1. 项目概述
这个SpringBoot自学笔记系列的第一篇,聚焦于Web开发中最基础的四个核心概念:路由映射、视图渲染、请求处理和会话管理。作为Java开发者转型现代Web开发的必经之路,这些基础组件的掌握程度直接决定了后续框架学习的效率。
我在带新人团队时发现,很多开发者虽然能快速搭建出SpringBoot项目,但对这些基础机制的理解往往停留在"能用但不知道为什么"的阶段。比如:
- 为什么Controller方法返回值能自动变成JSON响应?
- 模板引擎如何与Thymeleaf协同工作?
- Session和Cookie的关系到底怎么处理?
这篇笔记将用真实项目中的代码示例,拆解这些"日用而不知"的技术细节。适合以下读者:
- 刚接触SpringBoot的传统Java开发者
- 从其他语言转Java的Web开发者
- 需要系统梳理Web基础概念的初中级工程师
2. 核心组件解析
2.1 路由映射机制
SpringBoot的路由配置相比传统Spring MVC更加简洁,但背后的原理值得深究。通过这个简单的UserController示例:
@RestController @RequestMapping("/api/users") public class UserController { @GetMapping("/{id}") public User getUser(@PathVariable Long id) { return userService.findById(id); } }几个关键设计点:
@RestController是组合注解,包含@Controller和@ResponseBody- 路径匹配支持Ant风格通配符(如
/admin/**) - 方法参数绑定支持
@PathVariable、@RequestParam等6种方式
实际踩坑:当同时存在
/users/new和/users/{id}时,必须严格定义路径顺序,否则会优先匹配到{id}路径
路由查找的底层流程:
- DispatcherServlet接收到HTTP请求
- 遍历HandlerMapping实现类(RequestMappingHandlerMapping优先)
- 根据URL、HTTP方法、consumes/produces条件匹配
- 返回HandlerExecutionChain(包含拦截器链)
2.2 视图渲染实践
虽然现在前后端分离是主流,但服务端渲染仍有其适用场景。SpringBoot默认支持的模板引擎包括:
| 引擎类型 | 文件扩展名 | 特点 |
|---|---|---|
| Thymeleaf | .html | 原生HTML语法,SEO友好 |
| FreeMarker | .ftl | 模板继承功能强大 |
| Groovy | .tpl | 动态语言特性 |
以Thymeleaf为例的典型配置:
spring: thymeleaf: prefix: classpath:/templates/ suffix: .html cache: false # 开发时关闭缓存 encoding: UTF-8模板文件user/profile.html:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title th:text="${user.name} + '的个人主页'"></title> </head> <body> <div th:if="${user.vip}">VIP标识</div> </body> </html>性能提示:生产环境一定要开启模板缓存,否则每次请求都会重新解析
2.3 请求处理全流程
从HTTP请求到业务方法执行的完整链路:
过滤器阶段(FilterChain)
- 字符编码处理
- 跨域配置检查
- 权限预校验
参数绑定阶段
- 基本类型:
@RequestParam - 复杂对象:自动绑定表单字段(需要setter方法)
- JSON体:
@RequestBody+ Jackson反序列化
- 基本类型:
返回值处理
- 视图名称:由ViewResolver解析
- ResponseEntity:可自定义状态码/头信息
- 普通对象:走MessageConverter转换
常见问题排查表:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 400 Bad Request | 缺少必要参数 | 检查@RequestParam(required=true) |
| 415 Unsupported Media Type | 请求头Content-Type不匹配 | 添加consumes="application/json" |
| 406 Not Acceptable | 客户端Accept与produces不匹配 | 检查produces声明 |
2.4 会话管理方案
分布式系统下的会话管理有几种典型方案:
Server Session
@PostMapping("/login") public String login(HttpSession session) { session.setAttribute("user", authUser); // 默认使用JSESSIONID Cookie }需要配合Redis实现集群共享:
spring: session: store-type: redis timeout: 1800Token方案
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) { http.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); return http.build(); }Cookie+Session混合
@GetMapping("/profile") public String profile(@CookieValue("token") String token) { // 验证token有效性 }
安全警告:使用Session时务必配置
httpOnly和secure属性,防止XSS攻击
3. 深度配置技巧
3.1 自定义参数解析器
实现HandlerMethodArgumentResolver可以扩展参数绑定逻辑。比如处理JWT令牌:
public class JwtArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(JwtClaim.class); } @Override public Object resolveArgument(...) { String token = request.getHeader("Authorization"); return JwtUtils.parseClaims(token); } }注册解析器:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new JwtArgumentResolver()); } }3.2 异常统一处理
@ControllerAdvice的几种典型用法:
- 业务异常处理
@ExceptionHandler(BizException.class) public ResponseEntity<ErrorResult> handleBizEx(BizException ex) { return ResponseEntity.status(500) .body(new ErrorResult(ex.getCode(), ex.getMessage())); }- 参数校验错误
@ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleValidEx(MethodArgumentNotValidException ex) { List<String> errors = ex.getBindingResult() .getFieldErrors() .stream() .map(FieldError::getDefaultMessage) .collect(Collectors.toList()); return ResponseEntity.badRequest().body(errors); }3.3 异步请求处理
Servlet异步特性在SpringBoot中的使用:
@GetMapping("/async") public Callable<String> asyncTask() { return () -> { Thread.sleep(3000); // 模拟耗时操作 return "Async Result"; }; }配置参数(application.yml):
server: tomcat: threads: max: 200 # 最大工作线程数 accept-count: 100 # 等待队列长度4. 性能优化实践
4.1 路由匹配优化
当Controller方法超过50个时,路由匹配可能成为性能瓶颈。可以通过以下方式优化:
使用
@RequestMapping的params条件缩小匹配范围@GetMapping(path = "/search", params = "type=quick") public Result quickSearch() {...}将通用前缀提取到类级别
@RequestMapping@RequestMapping("/admin") @Controller public class AdminController { @GetMapping("/users") // 实际路径是/admin/users public String userList() {...} }
4.2 视图渲染加速
Thymeleaf模板的缓存配置策略:
# 生产环境配置 spring.thymeleaf.cache=true # 开发时设置模板解析缓存大小 spring.thymeleaf.template-resolver-order=1 spring.thymeleaf.cache-manager=LRU spring.thymeleaf.cache-size=1004.3 会话存储优化
Redis会话存储的序列化配置:
@Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer() { return new GenericJackson2JsonRedisSerializer(); // 替代默认JDK序列化 }关键参数说明:
spring.session.redis.flush-mode: immediate/on_savespring.session.redis.namespace: 避免多应用key冲突spring.session.timeout: 单位秒,默认30分钟
5. 安全防护要点
5.1 CSRF防护
在表单提交场景下的防护方案:
- 启用默认防护
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); return http.build(); } }- 表单中嵌入token
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>5.2 会话固定保护
Spring Security默认提供的防护措施:
http.sessionManagement() .sessionFixation().migrateSession();可选策略:
none: 不做防护(危险)newSession: 创建全新会话migrateSession: 迁移属性(默认)changeSessionId: 仅更改ID(Servlet 3.1+)
5.3 请求头安全
推荐的安全头配置:
http.headers() .xssProtection() .contentSecurityPolicy("script-src 'self'") .frameOptions().sameOrigin();会添加以下响应头:
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- Strict-Transport-Security: max-age=31536000
- Content-Security-Policy: default-src 'self'
6. 测试验证方案
6.1 路由测试
使用MockMvc测试Controller的典型写法:
@SpringBootTest @AutoConfigureMockMvc class UserControllerTest { @Autowired private MockMvc mockMvc; @Test void getUserById() throws Exception { mockMvc.perform(get("/api/users/123") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("张三")); } }6.2 会话测试
模拟带Session的请求:
@Test void testWithSession() throws Exception { MockHttpSession session = new MockHttpSession(); session.setAttribute("token", "test123"); mockMvc.perform(get("/profile") .session(session)) .andExpect(status().isOk()); }6.3 性能测试
使用JMeter测试模板渲染性能时,重点关注:
- 90%响应时间
- 错误率
- 吞吐量(requests/sec)
建议测试场景:
- 纯静态页面基准测试
- 包含10个动态变量的复杂模板
- 高并发下的会话创建性能
7. 扩展思考方向
7.1 函数式路由
Spring5引入的RouterFunction方式:
@Bean public RouterFunction<ServerResponse> userRoutes() { return route() .GET("/api/users/{id}", req -> ok().body(userService.findById(req.pathVariable("id")))) .POST("/api/users", req -> created(URI.create("/api/users/" + savedUser.getId())) .body(savedUser)) .build(); }与传统注解方式的对比优势:
- 更好的组合性
- 更清晰的请求处理流程
- 便于测试
7.2 响应式会话管理
WebFlux中的会话处理示例:
@GetMapping("/session") public Mono<String> getSession(WebSession session) { session.getAttributes().put("visitTime", Instant.now()); return Mono.just("Session ID: " + session.getId()); }关键区别:
- WebSession是非阻塞接口
- 需要配合Redis Reactive实现集群
- 会话超时通过
spring.webflux.session.timeout配置
7.3 服务端推技术
基于SseEmitter的实时更新:
@GetMapping("/stream") public SseEmitter streamData() { SseEmitter emitter = new SseEmitter(30_000L); executor.execute(() -> { try { for (int i = 0; i < 10; i++) { emitter.send("Event " + i); Thread.sleep(1000); } emitter.complete(); } catch (Exception ex) { emitter.completeWithError(ex); } }); return emitter; }适用场景:
- 实时日志推送
- 股票价格更新
- 聊天消息广播
这些基础组件的深入理解,是构建健壮Web应用的基石。在实际项目中,我建议从简单实现开始,然后逐步引入更高级的特性。比如先使用简单的Session管理,等业务量上来后再考虑分布式会话方案。