外观
SpringBoot 笔记
约 3612 字大约 12 分钟
2025-08-16
一、Spring Boot 简介
1.1 什么是 Spring Boot?
Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是简化 Spring 应用的初始搭建以及开发过程。它采用"约定优于配置"(Convention over Configuration)的理念,无需复杂的 XML 配置,通过少量的配置就能快速创建独立运行、产品级别的 Spring 应用。
1.2 Spring Boot 核心特点
- 自动配置:根据项目依赖自动配置 Spring 应用
- 起步依赖:简化 Maven/Gradle 依赖管理
- 内嵌服务器:无需部署 WAR 文件,内置 Tomcat、Jetty 等
- 生产就绪特性:提供健康检查、指标监控等生产级功能
- 无代码生成:不使用代码生成,而是利用 Spring 4 的条件化配置
- 简化部署:可打包为独立的 JAR 文件,直接运行
1.3 为什么选择 Spring Boot?
- 快速开发:省去大量样板代码和配置
- 微服务友好:天然支持微服务架构
- 社区活跃:拥有庞大的开发者社区和丰富的资源
- 企业级支持:被众多大企业采用和验证
- 与 Spring 生态无缝集成:轻松集成 Spring Security、Spring Data 等
二、环境搭建
2.1 项目创建方式
推荐方式:使用 Spring Initializr
在线创建:
- 访问 https://start.spring.io
- 选择项目类型(Maven/Gradle)
- 选择 Spring Boot 版本(推荐 2.7.x 或 3.x)
- 填写 Group 和 Artifact
- 选择依赖(Web, Data JPA, MySQL 等)
- 生成并下载项目
IDE 内置工具:
- IntelliJ IDEA:File > New > Project > Spring Initializr
- Eclipse:Spring Tool Suite 插件
2.2 项目结构
src/main/java
└── com
└── example
└── demo
├── DemoApplication.java # 主启动类
├── controller # 控制器
├── service # 服务层
├── repository # 数据访问层
├── entity # 实体类
├── config # 配置类
└── dto # 数据传输对象2.3 核心依赖
Maven 配置示例:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!-- Web 开发 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据访问 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>Gradle 配置示例:
plugins {
id 'org.springframework.boot' version '3.0.5'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'mysql:mysql-connector-java'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}三、核心配置
3.1 配置文件
Spring Boot 支持多种格式的配置文件:
application.propertiesapplication.yml(推荐,更简洁)
application.properties 示例:
# 服务器配置
server.port=8080
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialectapplication.yml 示例(推荐):
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect3.2 多环境配置
配置文件命名规则:
application-{profile}.properties/yml
常用环境:
application-dev.properties:开发环境application-test.properties:测试环境application-prod.properties:生产环境
激活指定环境:
# application.properties
spring.profiles.active=dev或通过命令行参数:
java -jar myapp.jar --spring.profiles.active=prod3.3 自定义配置
使用 @ConfigurationProperties:
@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppConfig {
private String name;
private String version;
private String description;
private Jwt jwt;
@Data
public static class Jwt {
private String secret;
private long expiration;
}
}配置文件:
app:
name: My Application
version: 1.0.0
description: A Spring Boot application
jwt:
secret: my-secret
expiration: 86400000四、Web 开发
4.1 RESTful API 开发
控制器示例:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
// GET /api/users - 获取所有用户
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
// GET /api/users/{id} - 获取指定用户
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// POST /api/users - 创建新用户
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
User savedUser = userService.save(user);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
return ResponseEntity.created(location).body(savedUser);
}
// PUT /api/users/{id} - 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
return userService.findById(id)
.map(user -> {
user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());
User updatedUser = userService.save(user);
return ResponseEntity.ok().body(updatedUser);
})
.orElse(ResponseEntity.notFound().build());
}
// DELETE /api/users/{id} - 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
if (!userService.existsById(id)) {
return ResponseEntity.notFound().build();
}
userService.deleteById(id);
return ResponseEntity.noContent().build();
}
}4.2 请求参数处理
@RestController
@RequestMapping("/api")
public class ParameterController {
// 路径参数
@GetMapping("/users/{id}")
public String getUserById(@PathVariable Long id) {
return "User ID: " + id;
}
// 查询参数
@GetMapping("/search")
public String searchUsers(@RequestParam String keyword,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
return "Searching for '" + keyword + "', page " + page + ", size " + size;
}
// 请求体
@PostMapping("/login")
public String login(@RequestBody LoginRequest loginRequest) {
return "Logging in as " + loginRequest.getUsername();
}
// 请求头
@GetMapping("/headers")
public String headers(@RequestHeader("User-Agent") String userAgent) {
return "Your browser: " + userAgent;
}
// Cookie
@GetMapping("/cookies")
public String cookies(@CookieValue("JSESSIONID") String sessionId) {
return "Session ID: " + sessionId;
}
}
// 请求体对象
public class LoginRequest {
private String username;
private String password;
// getter/setter
}4.3 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理资源未找到异常
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
"Resource Not Found",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
// 处理参数验证异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}
// 处理所有其他异常
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"Internal Server Error",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
// 错误响应类
@Data
@AllArgsConstructor
public static class ErrorResponse {
private int status;
private String title;
private String message;
}
}4.4 拦截器
@Component
public class LoggingInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.info("请求开始: {} {}", request.getMethod(), request.getRequestURI());
return true; // 继续处理
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
logger.info("请求处理完成: {} {}", request.getMethod(), request.getRequestURI());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
logger.info("请求完成: {} {}", request.getMethod(), request.getRequestURI());
if (ex != null) {
logger.error("请求处理异常", ex);
}
}
}
// 权限拦截器
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HttpSession session = request.getSession();
if (session.getAttribute("user") == null) {
response.sendRedirect("/login");
return false;
}
return true;
}
}拦截器配置:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoggingInterceptor loggingInterceptor;
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor)
.addPathPatterns("/**"); // 拦截所有请求
registry.addInterceptor(authInterceptor)
.addPathPatterns("/admin/**", "/user/**") // 只拦截特定路径
.excludePathPatterns("/login", "/public/**"); // 排除特定路径
}
}五、数据访问
5.1 Spring Data JPA
实体类:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@Column(unique = true)
private String email;
// 省略getter/setter
}Repository 接口:
public interface UserRepository extends JpaRepository<User, Long> {
// 根据用户名查询
User findByUsername(String username);
// 根据邮箱查询
User findByEmail(String email);
// 自定义查询
@Query("SELECT u FROM User u WHERE u.username LIKE %:keyword%")
List<User> searchByUsername(@Param("keyword") String keyword);
}服务层:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(User user) {
return userRepository.save(user);
}
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
public List<User> searchUsers(String keyword) {
return userRepository.searchByUsername(keyword);
}
}5.2 MyBatis 集成
依赖配置:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>配置:
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: trueMapper 接口:
@Mapper
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(Long id);
@Insert("INSERT INTO users(username, password, email) VALUES(#{username}, #{password}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
@Update("UPDATE users SET username=#{username}, email=#{email} WHERE id=#{id}")
int update(User user);
@Delete("DELETE FROM users WHERE id=#{id}")
int deleteById(Long id);
}服务层:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(Long id) {
return userMapper.findById(id);
}
public void createUser(User user) {
userMapper.insert(user);
}
}5.3 事务管理
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Transactional
public User createUser(User user, String roleName) {
User savedUser = userRepository.save(user);
Role role = roleRepository.findByName(roleName);
if (role == null) {
throw new RuntimeException("Role not found: " + roleName);
}
UserRole userRole = new UserRole();
userRole.setUser(savedUser);
userRole.setRole(role);
userRoleRepository.save(userRole);
return savedUser;
}
}事务传播行为:
REQUIRED(默认):支持当前事务,如果没有则新建REQUIRES_NEW:新建事务,如果当前存在事务则挂起SUPPORTS:支持当前事务,如果没有则以非事务方式执行NOT_SUPPORTED:以非事务方式执行,如果当前存在事务则挂起MANDATORY:支持当前事务,如果没有则抛出异常NEVER:以非事务方式执行,如果当前存在事务则抛出异常NESTED:如果当前存在事务,则在嵌套事务内执行
六、安全控制
6.1 Spring Security 基础
依赖配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>基础配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/home").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout
.permitAll()
);
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}6.2 JWT 集成
JWT 工具类:
@Component
public class JwtTokenProvider {
@Value("${app.jwtSecret}")
private String jwtSecret;
@Value("${app.jwtExpirationInMs}")
private int jwtExpirationInMs;
public String generateToken(UserPrincipal userPrincipal) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(Long.toString(userPrincipal.getId()))
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public Long getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
// invalid JWT signature
} catch (MalformedJwtException ex) {
// invalid JWT
} catch (ExpiredJwtException ex) {
// token is expired
} catch (UnsupportedJwtException ex) {
// JWT is unsupported
} catch (IllegalArgumentException ex) {
// JWT claims string is empty
}
return false;
}
}JWT 认证过滤器:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Long userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}安全配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
}七、常用功能
7.1 日志管理
application.yml 配置:
logging:
level:
root: info
com.example: debug
file:
name: logs/application.log
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"使用示例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public User getUserById(Long id) {
logger.info("正在查询用户,ID: {}", id);
try {
User user = userRepository.findById(id).orElse(null);
if (user == null) {
logger.warn("用户不存在,ID: {}", id);
}
return user;
} catch (Exception e) {
logger.error("查询用户时发生错误,ID: {}", id, e);
throw e;
}
}
}7.2 缓存
启用缓存:
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}使用缓存:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
return userRepository.save(user);
}
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}配置缓存:
spring:
cache:
type: redis
redis:
time-to-live: 3600000 # 1小时
cache-names: users,roles7.3 定时任务
启用定时任务:
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}定时任务示例:
@Component
public class ScheduledTasks {
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
// 每30秒执行一次
@Scheduled(fixedRate = 30000)
public void reportCurrentTime() {
log.info("现在时间: {}", dateFormat.format(new Date()));
}
// 每天8:00执行
@Scheduled(cron = "0 0 8 * * ?")
public void dailyTask() {
log.info("执行每日任务");
}
// 应用启动后延迟5秒执行,之后每10秒执行一次
@Scheduled(initialDelay = 5000, fixedRate = 10000)
public void delayedTask() {
log.info("执行延迟任务");
}
}7.4 异步处理
启用异步:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}异步方法:
@Service
public class AsyncService {
@Async
public CompletableFuture<String> asyncTask() {
try {
Thread.sleep(1000);
return CompletableFuture.completedFuture("任务完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
}
}
@Async
public void sendEmail(String email) {
// 发送邮件逻辑
}
}调用异步方法:
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/async-task")
public CompletableFuture<String> executeAsyncTask() {
return asyncService.asyncTask();
}
@GetMapping("/send-email")
public String sendEmail(@RequestParam String email) {
asyncService.sendEmail(email);
return "邮件发送请求已提交";
}
}八、监控与运维
8.1 Actuator
依赖配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>配置:
management:
endpoints:
web:
exposure:
include: "*" # 暴露所有端点,生产环境应谨慎
base-path: /actuator
endpoint:
health:
show-details: always
shutdown:
enabled: true常用端点:
/actuator/health:应用健康状态/actuator/info:应用信息/actuator/metrics:应用指标/actuator/env:环境变量/actuator/beans:所有Spring Beans/actuator/mappings:所有@RequestMapping路径/actuator/httptrace:HTTP请求追踪/actuator/shutdown:关闭应用(需启用)
8.2 自定义健康检查
@Component
public class CustomHealthIndicator implements HealthIndicator {
private final AtomicBoolean healthy = new AtomicBoolean(true);
@Override
public Health health() {
if (healthy.get()) {
return Health.up()
.withDetail("custom", "一切正常")
.build();
}
return Health.down()
.withDetail("custom", "服务异常")
.build();
}
public void setHealth(boolean isHealthy) {
healthy.set(isHealthy);
}
}8.3 自定义指标
@Service
public class UserService {
private final Counter userCounter;
private final Timer userTimer;
public UserService(MeterRegistry meterRegistry) {
userCounter = Counter.builder("users.created")
.description("用户创建计数")
.register(meterRegistry);
userTimer = Timer.builder("users.process.time")
.description("用户处理时间")
.register(meterRegistry);
}
public User createUser(User user) {
userCounter.increment();
return userTimer.record(() -> {
// 创建用户逻辑
return user;
});
}
}九、最佳实践
9.1 项目结构规范
src/main/java
└── com
└── example
├── Application.java # 主启动类
├── config # 配置类
├── controller # 控制器
├── service # 服务层
│ ├── impl # 服务实现
├── repository # 数据访问层
├── entity # 实体类
├── dto # 数据传输对象
├── exception # 自定义异常
├── security # 安全相关
├── util # 工具类
└── aspect # AOP切面9.2 配置管理
- 使用
@ConfigurationProperties统一管理配置 - 按环境分离配置文件:
application-dev.yml,application-prod.yml - 敏感信息使用环境变量或配置中心管理
- 避免在配置文件中硬编码敏感信息
环境变量示例:
spring:
datasource:
url: ${DB_URL}
username: ${DB_USER}
password: ${DB_PASSWORD}9.3 错误处理最佳实践
- 统一异常处理:使用
@ControllerAdvice和@ExceptionHandler - 定义清晰的错误响应结构
- 为不同异常类型提供有意义的错误消息
- 记录详细的错误日志,但不要暴露给客户端
- 使用合适的HTTP状态码
9.4 性能优化
启动优化:
- 使用
spring.main.lazy-initialization=true延迟初始化Bean - 排除不必要的自动配置:
@SpringBootApplication(excludeAutoConfiguration = {...}) - 使用
spring.devtools.restart.enabled=false禁用开发工具重启
- 使用
JVM 优化:
- 适当调整堆内存大小:
-Xms512m -Xmx512m - 选择合适的垃圾收集器
- 监控GC情况,调整相关参数
- 适当调整堆内存大小:
数据库优化:
- 合理使用索引
- 避免N+1查询问题
- 适当使用缓存
- 批量操作代替单条操作
十、部署与运行
10.1 打包与运行
Maven 打包:
mvn clean package运行 JAR 文件:
java -jar target/myapp-0.0.1-SNAPSHOT.jar后台运行:
nohup java -jar target/myapp-0.0.1-SNAPSHOT.jar > app.log 2>&1 &10.2 容器化部署
Dockerfile 示例:
# 使用官方的Java 17运行时作为父镜像
FROM openjdk:17-jdk-slim
# 设置工作目录
WORKDIR /app
# 将JAR文件复制到容器中
COPY target/myapp-*.jar app.jar
# 暴露端口
EXPOSE 8080
# 定义环境变量
ENV SPRING_PROFILES_ACTIVE=prod
# 容器启动时运行Java应用
ENTRYPOINT ["java","-jar","app.jar"]构建和运行:
# 构建镜像
docker build -t myapp:latest .
# 运行容器
docker run -d -p 8080:8080 --name myapp myapp:latest10.3 生产环境建议
监控:
- 使用 Prometheus + Grafana 监控应用指标
- 配置适当的告警规则
- 监控 JVM 指标、GC 情况、线程池状态
日志:
- 使用 ELK(Elasticsearch, Logstash, Kibana)或 Splunk 集中管理日志
- 确保日志格式统一,便于分析
- 设置合理的日志保留策略
安全:
- 定期更新依赖,修复安全漏洞
- 使用HTTPS保护传输安全
- 避免暴露敏感端点
- 实施适当的访问控制
