一、SpringBoot整合全局异常处理器
1. 非前后端分离(了解)
适用于传统MVC应用,返回视图页面:
@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
ModelAndView mv = new ModelAndView();
// 设置错误信息
mv.addObject("message", ex.getMessage());
mv.addObject("exception", ex);
// 跳转到错误页面
mv.setViewName("error");
return mv;
}
}
2. 前后端分离(重点)
适用于RESTful API,返回JSON格式数据:
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理所有异常
*/
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
// 记录日志
log.error("系统异常: ", e);
// 返回统一格式
return Result.error("系统异常,请联系管理员");
}
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result handleBusinessException(BusinessException e) {
return Result.error(e.getMessage());
}
/**
* 处理参数验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleValidException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return Result.error(message);
}
}
// 统一返回结果类
@Data
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("操作成功");
result.setData(data);
return result;
}
public static <T> Result<T> error(String message) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMessage(message);
return result;
}
}
二、SpringBoot整合JUnit
1. 添加依赖
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2. 编写测试类
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 测试演进历程:
* 1. 传统方式:main方法启动Spring
* new ClassPathXmlApplicationContext("applicationContext.xml");
*
* 2. Spring整合JUnit4:
* @RunWith(SpringJUnit4ClassRunner.class)
* @ContextConfiguration("applicationContext.xml")
*
* 3. SpringBoot整合JUnit5(推荐):
* @SpringBootTest
*/
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void testGetUserById() {
// 模拟测试
User user = userService.getUserById(1L);
assertNotNull(user);
assertEquals("张三", user.getName());
}
@Test
@Transactional
@Rollback
void testSaveUser() {
User user = new User();
user.setName("李四");
user.setAge(25);
User savedUser = userService.save(user);
assertNotNull(savedUser.getId());
}
// Mock测试示例
@Test
void testWithMock() {
// 使用Mockito模拟依赖
UserRepository mockRepo = mock(UserRepository.class);
when(mockRepo.findById(1L)).thenReturn(Optional.of(new User(1L, "王五")));
UserService service = new UserService(mockRepo);
User user = service.getUserById(1L);
assertEquals("王五", user.getName());
}
}
三、SpringBoot多环境配置
1. 多环境配置语法
-
主配置文件:
application.yml -
环境配置文件:
application-{profile}.yml
2. 创建多套环境配置
application-dev.yml(开发环境):
server:
port: 8081
servlet:
context-path: /dev-api
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dev_db?useSSL=false&serverTimezone=Asia/Shanghai
username: dev_user
password: dev123
logging:
level:
com.example: debug
application-test.yml(测试环境):
server:
port: 8082
servlet:
context-path: /test-api
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://test-server:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai
username: test_user
password: test123
logging:
level:
com.example: info
application-prod.yml(生产环境):
server:
port: 8083
servlet:
context-path: /api
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://prod-master:3306/prod_db?useSSL=false&serverTimezone=Asia/Shanghai
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
logging:
level:
com.example: warn
file:
name: logs/application.log
3. 激活环境
方式一:在application.yml中指定
# application.yml
spring:
profiles:
active: dev
方式二:启动参数指定
# 命令行启动
java -jar myapp.jar --spring.profiles.active=prod
# 或使用系统属性
java -Dspring.profiles.active=test -jar myapp.jar
方式三:通过环境变量
# 设置环境变量
export SPRING_PROFILES_ACTIVE=prod
方式四:使用分组(Spring Boot 2.4+)
# application.yml
spring:
profiles:
active: dev,audit
group:
dev: dev,debug
prod: prod,audit,metrics
四、SpringBoot整合Logback日志
1. 添加依赖(已包含在starter中)
SpringBoot默认使用Logback,无需额外配置依赖。
2. Logback配置文件
创建logback-spring.xml在resources目录下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义日志输出格式 -->
<property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天归档,保存30天 -->
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<!-- 错误日志单独输出 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/error.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- 多环境配置 -->
<springProfile name="dev">
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
<logger name="com.example" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
</springProfile>
<springProfile name="test">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</springProfile>
<springProfile name="prod">
<root level="WARN">
<appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
<logger name="com.example" level="INFO" additivity="false">
<appender-ref ref="FILE"/>
</logger>
</springProfile>
</configuration>
3. 在代码中使用日志
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
public User getUser(Long id) {
// 不同级别的日志
logger.trace("trace级别日志");
logger.debug("debug级别日志,用户ID: {}", id);
logger.info("info级别日志");
logger.warn("warn级别日志");
logger.error("error级别日志");
// 使用占位符,避免字符串拼接
logger.info("用户查询: id={}, time={}", id, System.currentTimeMillis());
return userService.getUserById(id);
}
}
五、完整项目结构示例
src/main/java/
├── com.example.demo
│ ├── config/
│ │ └── GlobalExceptionHandler.java
│ ├── controller/
│ │ └── UserController.java
│ ├── service/
│ │ └── UserService.java
│ └── DemoApplication.java
├── resources/
│ ├── application.yml
│ ├── application-dev.yml
│ ├── application-test.yml
│ ├── application-prod.yml
│ └── logback-spring.xml
└── test/java/
└── com.example.demo
└── UserServiceTest.java
总结要点
-
异常处理 :前后端分离项目使用
@RestControllerAdvice+@ExceptionHandler -
单元测试 :使用
@SpringBootTest注解,支持依赖注入 -
多环境配置 :通过
application-{profile}.yml和spring.profiles.active管理 -
日志配置 :使用
logback-spring.xml,支持多环境不同日志级别 -
最佳实践:生产环境关闭调试日志,使用文件日志并配置滚动策略