多个配置文件的时候,Spring 不会 “优先” 选择某个配置类,而是根据配置类之间的依赖关系和扫描规则加载:
- 若某个配置类被 显式指定为主配置类(如作为
AnnotationConfigApplicationContext
的参数),则它会成为入口,其他配置类通过@Import
或扫描被引入。 - 若通过
@ComponentScan
扫描到多个配置类(均标注@Configuration
),则它们会被合并加载,不存在 “优先级”,但需注意 Bean 定义的冲突(如同名 Bean)。
将启动类作为主配置类(标注 @SpringBootApplication
),并通过扫描或 @Import
引入其他配置类。
有spring boot 2.4升级到spring boot 3.5 test类要做如下修改 ,
package com.neuedu.demo;
import org.junit.jupiter.api.Test; // JUnit 5 的 @Test 注解
import org.springframework.boot.test.context.SpringBootTest;
// 移除 @RunWith(SpringRunner.class)(JUnit 5 不需要此注解)
@SpringBootTest
public class DemoJavaconfigApplicationTests {
@Test // 使用 JUnit 5 的 @Test
public void contextLoads() {
// 测试逻辑(保持不变)
}
}
关键改动:
-
移除 JUnit 4 注解
- 移除
@RunWith(SpringRunner.class)
,因为 JUnit 5 不再需要此注解。 - Spring Boot 3.5 默认使用 JUnit 5,
@SpringBootTest
会自动适配。
- 移除
-
更新
@Test
注解的包- 从
import org.junit.Test;
(JUnit 4)改为import org.junit.jupiter.api.Test;
(JUnit 5)。
- 从
-
依赖检查
确保pom.xml
中没有显式依赖 JUnit 4(Spring Boot 3.5 默认使用 JUnit 5):xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 移除 JUnit 4 依赖(如果有) --> <!-- <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> --> </dependencies>
常用 JUnit 5 注解替换
JUnit 4 注解 | JUnit 5 替换注解 | 说明 |
---|---|---|
@Before | @BeforeEach | 每个测试方法前执行 |
@After | @AfterEach | 每个测试方法后执行 |
@BeforeClass | @BeforeAll | 所有测试方法前执行(静态方法) |
@AfterClass | @AfterAll | 所有测试方法后执行(静态方法) |
@Ignore | @Disabled | 禁用测试方法 |
@RunWith(SpringRunner.class) | 移除(改用 SpringExtension) | JUnit 5 不再需要此注解 |
spring boot starter 说明:
Dependency management is a critical aspects of any complex project. And doing this manually is less than ideal; the more time you spent on it the less time you have on the other important aspects of the project.
Spring Boot starters were built to address exactly this problem. Starter POMs are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need, without having to hunt through sample code and copy-paste loads of dependency descriptors.
We have more than 30 Boot starters available – let’s see some of them in the following sections.
参考:https://www.baeldung.com/spring-boot-starters
用 spring boot 3.5 内置的logback
spring:
application:
name : "his5"
mvc:
view:
prefix: "/WEB-INF/views/"
suffix: ".jsp"
logging:
level:
root: DEBUG # 全局日志级别为 DEBUG
org.springframework: INFO # Spring框架相关包为 INFO 级别
# org.hibernate: INFO # Hibernate相关包为 INFO 级别
com.neuedu.demo: DEBUG # 自定义包为 DEBUG 级别
# 日志文件配置
file:
name: logs/application.log # 日志文件路径和名称
# max-size: 10MB # 单个文件最大 10MB
# max-history: 30 # 保留历史文件最多 30 天
# total-size-cap: 1GB # 日志总大小上限 1GB
# 日志格式配置
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
# # 日志组配置(可选)
# group:
# web: org.springframework.web # 定义 "web" 组包含 Spring Web 包
# sql: org.hibernate.SQL # 定义 "sql" 组包含 Hibernate SQL 包
# 特定组的日志级别
# level:
# web: DEBUG # Web 相关包为 DEBUG 级别
# sql: DEBUG # SQL 相关包为 DEBUG 级别
应用:通过 SLF4J 门面 调用日志接口,这样可以保持与日志实现(Logback/Log4j2)的解耦
package com.mg.his5.mvc;
import com.mg.his5.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//restful @RestController class
@Controller
@RequestMapping("/his")
public class MvcController {
private static final Logger logger = LoggerFactory.getLogger(MvcController.class);
@GetMapping("/hello")
@ResponseBody
public String hello() {
logger.debug("This is a debug log"); // 应同时输出到控制台和文件
return "Hello, MVC!";
}
@GetMapping("/test")
public ModelAndView index () {
return new ModelAndView("index/index");
}
//return user information function
@GetMapping("/user")
@ResponseBody
public User getUser(){
User u= new User();
u.setUsername("testUser");
u.setPassword("testPassword");
return u;
}
}
也使用 @Slf4j
注解简化代码(需引入 Lombok)
// 添加 Lombok 依赖后,直接使用 @Slf4j 注解
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderController {
public void processOrder() {
log.info("处理订单:{}", orderId); // 直接使用 log 对象,无需手动创建
}
}
Web
ViewResolver
在 SpringBoot 里,ViewResolver 的作用类似于 “翻译官”。当你在控制器里返回一个逻辑视图名称时,就好比你告诉这个 “翻译官” 你想要什么。然后,ViewResolver 会把这个逻辑名称 “翻译” 成实际的物理视图文件,比如 JSP、Thymeleaf 模板或者 FreeMarker 模板等,就像 “翻译官” 把一种语言翻译成另一种语言一样。这样,SpringBoot 就能准确地找到并渲染相应的视图页面,呈现给用户。
举个例子,当控制器返回 “home” 时,ViewResolver 可能会把它解析为 “/WEB-INF/views/home.jsp” 或者 “classpath:/templates/home.html”。
简单来说,ViewResolver 的主要作用就是建立起逻辑视图名称和实际视图文件之间的映射关系,让 SpringBoot 能够正确地找到并显示用户请求的页面。
,当你在 Spring Boot 项目中引入了 spring-boot-starter-web
依赖后,Spring Boot 会根据你的类路径自动配置合适的 默认视图解析器(ViewResolver)。具体配置取决于你添加的其他依赖:
1. 默认视图解析器的自动配置
Spring Boot 会根据以下条件自动配置视图解析器:
Thymeleaf(推荐)其实最推荐的还是restful 这里是讨论遗留项目的一些问题
如果你添加了 Thymeleaf 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- 默认配置:
- 视图文件位置:
src/main/resources/templates/
- 视图后缀:
.html
- 逻辑视图名
home
会解析为templates/home.html
。
- 视图文件位置:
FreeMarker
如果你添加了 FreeMarker 依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
- 默认配置:
- 视图文件位置:
src/main/resources/templates/
- 视图后缀:
.ftl
- 视图文件位置:
JSP(需额外配置)
如果使用 JSP,需要手动添加 JSP 引擎依赖(如 tomcat-embed-jasper
),并通过配置类或 application.properties
指定视图解析器:
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
2. 无模板引擎依赖时的行为
如果只引入 spring-boot-starter-web
,但没有添加任何模板引擎依赖(如 Thymeleaf、FreeMarker):
- 默认行为:Spring Boot 不会自动配置视图解析器。
- 常见用途:此时项目通常作为 RESTful API 服务,返回 JSON/XML 数据而非视图页面。
3. 自定义视图解析器
如果你需要覆盖默认配置(例如使用 JSP 或多个视图解析器),可以通过以下方式手动配置:
示例:配置 JSP 视图解析器
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
public class ViewResolverConfig {
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
总结
- 有模板引擎依赖(如 Thymeleaf):
spring-boot-starter-web
会自动配置对应的视图解析器。 - 无模板引擎依赖:不会自动配置视图解析器,项目通常作为 API 服务。(主流)
- 手动配置:通过
@Configuration
类和@Bean
方法自定义视图解析器。
应该优先使用 Thymeleaf 或 FreeMarker,它们与 Spring Boot 集成更简单,无需额外配置。全新项目 还是restful吧。
ModelAndView
和 ViewResolver
是 Spring MVC 中两个核心概念,它们共同协作处理视图渲染,但职责不同:
1. ModelAndView 的作用
ModelAndView
是一个用于封装视图名称和数据模型的类,它解决的问题是:
- 将业务数据传递给视图:控制器处理请求后,将需要展示的数据(如用户信息、订单列表)存入
ModelAndView
。 - 指定渲染哪个视图:通过设置逻辑视图名称(如
"home"
),告诉 Spring MVC 需要渲染哪个页面。
示例代码
@Controller
public class UserController {
@GetMapping("/user")
public ModelAndView getUser() {
// 创建 ModelAndView 对象
ModelAndView mav = new ModelAndView();
// 添加数据到模型
mav.addObject("username", "John Doe");
mav.addObject("age", 30);
// 设置逻辑视图名称
mav.setViewName("user-profile"); // 对应 user-profile.html 或 .jsp
return mav;
}
}
2. ViewResolver 的作用
ViewResolver
负责将 逻辑视图名称(如 "user-profile"
)解析为 实际的物理视图文件(如 user-profile.html
或 JSP)。它解决的问题是:
- 视图定位:根据逻辑名称找到对应的模板文件。
- 视图技术适配:支持不同视图技术(Thymeleaf、JSP、Freemarker 等)。
示例配置
@Bean
public ViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine()); // 模板引擎
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
3. 两者的协作关系
- 控制器返回
ModelAndView
:- 控制器处理请求后,创建
ModelAndView
对象,存入数据并设置逻辑视图名。
- 控制器处理请求后,创建
- DispatcherServlet 接收
ModelAndView
:- Spring MVC 的前端控制器
DispatcherServlet
拿到ModelAndView
后,将其传递给ViewResolver
。
- Spring MVC 的前端控制器
- ViewResolver 解析视图:
ViewResolver
根据逻辑视图名(如"user-profile"
),结合配置(如前缀/templates/
、后缀.html
),定位到实际文件(如templates/user-profile.html
)。
- 视图渲染:
ViewResolver
返回具体的View
对象(如ThymeleafView
),由其负责将模型数据渲染到视图中。
4. 对比和总结
ModelAndView | ViewResolver |
---|---|
控制器的返回值,封装数据和视图名称 | 全局组件,负责视图名称到物理视图的映射 |
面向业务逻辑层 | 面向框架配置层 |
每个请求对应一个 ModelAndView 实例 | 全局共享配置,通常单例 |
5. 其他视图返回方式
除了 ModelAndView
,Spring MVC 还支持更简洁的返回方式:
- 返回字符串(逻辑视图名):
@GetMapping("/user") public String getUser(Model model) { model.addAttribute("username", "John Doe"); return "user-profile"; // 直接返回逻辑视图名 }
- 返回
ResponseEntity
(用于 REST API,不涉及视图解析):@GetMapping("/api/user") public ResponseEntity<User> getUser() { return ResponseEntity.ok(new User("John Doe")); }
- ModelAndView:是控制器传递数据和视图名称的载体,解决 “渲染什么数据” 和 “用哪个视图渲染” 的问题。
- ViewResolver:是框架组件,解决 “如何找到视图文件” 的问题。
- 协作流程:控制器返回
ModelAndView
→ DispatcherServlet 调用 ViewResolver → ViewResolver 定位视图文件 → 视图渲染数据。
Formatter和Converter Converter优先级更高,Formatter 是双向的 常用于表单
局部应用上 @DateTimeFormat 更方便
维度 | HttpMessageConverter | Formatter | Converter |
---|---|---|---|
接口 | HttpMessageConverter<T> | Formatter<T> | Converter<S, T> |
方向 | 双向(HTTP ↔ Java) | 双向(文本 ↔ Java) | 单向(S → T) |
场景 | HTTP 请求 / 响应处理 | 表单、URL 参数、模板显示 | 通用类型转换 |
配置方式 | configureMessageConverters() | addFormatters() | addFormatters() |
HttpMessageConverter作用
-
请求数据转换
将 HTTP 请求的 body(如 JSON、XML)转换为控制器方法的参数(如@RequestBody User user
)。 -
响应数据转换
将控制器方法返回的对象(如User
)转换为 HTTP 响应的 body(如 JSON、XML)。 -
支持多种格式
内置支持 JSON、XML、表单数据(form-data)、文本等格式,并可扩展自定义格式。
工作流程(双向):
HTTP 请求 (JSON) → HttpMessageConverter → Java 对象 (User) → 控制器方法
控制器返回对象 (User) → HttpMessageConverter → HTTP 响应 (JSON)
Spring MVC 内置了多种 HttpMessageConverter
实现:
内置HttpMessageConverter
转换器
转换器类名 | 支持的格式 |
---|---|
MappingJackson2HttpMessageConverter | JSON (默认使用 Jackson) |
Jaxb2RootElementHttpMessageConverter | XML (JAXB) |
FormHttpMessageConverter | 表单数据 (application/x-www-form-urlencoded) |
StringHttpMessageConverter | 文本 (text/plain) |
ByteArrayHttpMessageConverter | 二进制数据 (application/octet-stream) |
自定义转换器?无非是自己照着套路 搞个转换器,配置文件中重写configureMessageConverters方法,加上自定义转换器即可。
自定义页面跳转:
告诉 Spring MVC:“当用户访问某些路径(如 /
、/index
)时,直接返回指定的视图(如 login
、main
),不需要写 Controller 方法。”
适合纯页面跳转的场景(比如登录页、主页),避免为了跳转页面写一堆空的 @GetMapping
方法。
配置全局错误处理
- 简单场景:直接用
@ControllerAdvice + @ExceptionHandler
,快速统一捕获和处理异常,返回 JSON 或视图。 - 需接管默认错误流程:实现
ErrorController
,完全自定义错误响应。 - 模板引擎项目:配置
error
页面,结合@ControllerAdvice
做复杂异常处理。 - 处理 Spring 内置异常:继承
ResponseEntityExceptionHandler
,简化参数校验等异常的处理逻辑。
package com.mg.his5.tools;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
// @RestControllerAdvice 会自动将返回值转为 JSON,无需额外加 @ResponseBody
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
// 捕获所有未处理的异常(可根据需要细化,如指定特定异常类型)
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAllExceptions(Exception e, HttpServletRequest request) {
logger.error("请求 {} 发生异常: {}", request.getRequestURL(), e.getMessage(), e);
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"服务器内部错误,请稍后重试",
e.getMessage() // 生产环境建议隐藏具体异常信息,避免暴露敏感内容
);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
// 示例:处理特定异常(如空指针),可针对性返回更友好信息
@ExceptionHandler(NullPointerException.class)
public ResponseEntity<ErrorResponse> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
logger.error("请求 {} 发生空指针异常: {}", request.getRequestURL(), e.getMessage(), e);
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"参数错误或数据未正确初始化",
e.getMessage()
);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
// 自定义错误响应体(根据需求设计结构)
static class ErrorResponse {
private int code;
private String message;
private String detail;
public ErrorResponse(int code, String message, String detail) {
this.code = code;
this.message = message;
this.detail = detail;
}
// Getter、Setter 略
}
}
优先用 @ControllerAdvice + @ExceptionHandler
处理绝大多数异常,简单高效;只有在需要深度定制 Spring Boot 默认错误流程(如 Filter 异常、完全自定义 404 响应 )时,再补充 CustomErrorController
,并严格区分二者的处理范围,避免重复或冲突。
Thymeleaf 简介
传统项目用的比较多一点,主流还是建议各位用前端框架。
Thymeleaf 是一款 Java 开源的模板引擎,用于在服务端动态渲染 HTML/XML 页面。它的设计目标是让模板在 浏览器中直接预览(不依赖后端逻辑),同时支持与 Spring MVC 等框架深度集成,实现动态数据填充。
完整示例(Spring Boot + Thymeleaf)
以下是一个简单的 Spring Boot 项目,演示 Thymeleaf 的基本用法:
1. 添加依赖(Maven)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
2. 创建实体类
// User.java
package com.example.demo.entity;
public class User {
private String name;
private int age;
// 构造方法、Getter、Setter 略
}
3. 编写控制器
// HomeController.java
package com.example.demo.controller;
import com.example.demo.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Arrays;
import java.util.List;
@Controller
public class HomeController {
@GetMapping("/")
public String home(Model model) {
// 填充单个对象
User user = new User("Alice", 25);
model.addAttribute("user", user);
// 填充列表数据
List<String> hobbies = Arrays.asList("阅读", "编程", "旅行");
model.addAttribute("hobbies", hobbies);
// 填充布尔值(用于条件判断)
model.addAttribute("isStudent", true);
return "index"; // 对应 templates/index.html
}
}
4. 编写 Thymeleaf 模板(index.html
)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf 示例</title>
</head>
<body>
<h1>欢迎使用 Thymeleaf!</h1>
<!-- 输出单个对象属性 -->
<p>用户姓名:<span th:text="${user.name}">默认姓名</span></p>
<p>用户年龄:<span th:text="${user.age}">默认年龄</span></p>
<!-- 条件判断(th:if/th:unless) -->
<div th:if="${isStudent}">
<p>该用户是学生</p>
</div>
<div th:unless="${isStudent}">
<p>该用户不是学生</p>
</div>
<!-- 循环遍历(th:each) -->
<h3>兴趣爱好:</h3>
<ul>
<li th:each="hobby : ${hobbies}" th:text="${hobby}">爱好1</li>
</ul>
<!-- 链接跳转(th:href) -->
<a th:href="@{/about}">关于我们</a>
</body>
</html>
5. 配置 Thymeleaf(可选,使用默认配置时可省略)
# application.properties
spring.thymeleaf.prefix=classpath:/templates/ # 模板文件前缀(默认)
spring.thymeleaf.suffix=.html # 模板文件后缀(默认)
spring.thymeleaf.mode=HTML5 # 模板模式(默认 HTML)
spring.thymeleaf.cache=false # 开发环境关闭缓存(生产环境建议开启)
6. 启动项目并访问
运行 Spring Boot 启动类后,访问 http://localhost:8080
,页面将显示动态填充的数据:
欢迎使用 Thymeleaf!
用户姓名:Alice
用户年龄:25
该用户是学生
兴趣爱好:
- 阅读
- 编程
- 旅行
关于我们
关键语法说明
语法 | 作用 |
---|---|
th:text="${expression}" | 输出表达式结果,自动转义防止 XSS。 |
th:if="${condition}" | 条件判断,满足条件时渲染标签。 |
th:each="item : ${list}" | 循环遍历列表,item 为当前元素。 |
th:href="@{/path}" | 生成 URL,支持相对路径、绝对路径和请求参数(如 @{/user?id=1} )。 |
th:value="${obj.prop}" | 填充表单字段值(如 <input th:value="${user.name}"> )。 |