SpringBoot

1. 概述

1.1 SpringBoot的重要性

SpringBoot是Spring框架的扩展,通过约定优于配置的理念,简化了Spring应用的开发和部署,是Java企业级应用开发的主流框架。

本文内容

  • SpringBoot基础:核心概念和特性
  • 自动配置:自动配置原理和自定义
  • Web开发:RESTful API、WebMVC
  • 数据访问:JPA、MyBatis、Redis
  • 安全认证:Spring Security
  • 监控管理:Actuator监控
  • 部署运维:打包部署

1.2 本文内容结构

本文将从以下几个方面深入探讨SpringBoot:

  1. SpringBoot基础:核心概念和快速开始
  2. 自动配置:自动配置原理
  3. Web开发:RESTful API开发
  4. 数据访问:数据库集成
  5. 安全认证:Spring Security
  6. 监控管理:Actuator监控
  7. 部署运维:打包和部署

2. SpringBoot基础

2.1 SpringBoot特性

2.1.1 核心特性

SpringBoot核心特性

  1. 自动配置:自动配置Spring和第三方库
  2. 起步依赖:简化依赖管理
  3. 内嵌服务器:内嵌Tomcat、Jetty等
  4. 生产就绪:Actuator监控和管理
  5. 无需XML配置:基于Java配置和注解

SpringBoot优势

  • 快速开发:减少配置,提高开发效率
  • 约定优于配置:遵循约定,减少决策
  • 独立运行:可执行JAR,易于部署
  • 生产就绪:内置监控和管理功能

2.2 快速开始

2.2.1 创建SpringBoot项目

方式1:使用Spring Initializr

访问 https://start.spring.io/,选择依赖,生成项目。

方式2:使用Maven

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>

<groupId>com.example</groupId>
<artifactId>springboot-demo</artifactId>
<version>1.0.0</version>
<name>SpringBoot Demo</name>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<!-- Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Test Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

主应用类

1
2
3
4
5
6
7
8
9
10
11
12
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Controller示例

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

@GetMapping("/hello")
public String hello() {
return "Hello SpringBoot!";
}
}

2.3 核心注解

2.3.1 常用注解

SpringBoot核心注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// @SpringBootApplication:主应用类注解
@SpringBootApplication
public class Application {
// 等价于:
// @Configuration
// @EnableAutoConfiguration
// @ComponentScan
}

// @RestController:RESTful控制器
@RestController
@RequestMapping("/api")
public class ApiController {

@GetMapping("/users")
public List<User> getUsers() {
return userService.findAll();
}

@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userService.save(user);
}
}

// @Service:服务层
@Service
public class UserService {
// 业务逻辑
}

// @Repository:数据访问层
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 数据访问方法
}

// @Component:通用组件
@Component
public class CustomComponent {
// 组件逻辑
}

3. 自动配置

3.1 自动配置原理

3.1.1 自动配置机制

自动配置原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// SpringBoot自动配置通过@EnableAutoConfiguration实现
@SpringBootApplication
public class Application {
// @SpringBootApplication包含@EnableAutoConfiguration
}

// 自动配置类示例
@Configuration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}

条件注解

  • @ConditionalOnClass:类路径存在指定类时生效
  • @ConditionalOnMissingBean:容器中不存在指定Bean时生效
  • @ConditionalOnProperty:配置属性满足条件时生效

3.2 自定义自动配置

3.2.1 创建自动配置类

自定义自动配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 1. 创建配置属性类
@ConfigurationProperties(prefix = "custom")
public class CustomProperties {
private String name;
private int timeout = 1000;

// getter和setter
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getTimeout() {
return timeout;
}

public void setTimeout(int timeout) {
this.timeout = timeout;
}
}

// 2. 创建自动配置类
@Configuration
@EnableConfigurationProperties(CustomProperties.class)
@ConditionalOnClass(CustomService.class)
public class CustomAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public CustomService customService(CustomProperties properties) {
return new CustomService(properties);
}
}

// 3. 在META-INF/spring.factories中注册
// org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
// com.example.config.CustomAutoConfiguration

3.3 配置文件

3.3.1 application.properties

application.properties配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 服务器配置
server.port=8080
server.servlet.context-path=/api

# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=password
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.format_sql=true

# 日志配置
logging.level.root=INFO
logging.level.com.example=DEBUG
logging.file.name=application.log

# 自定义配置
custom.name=MyApp
custom.timeout=5000

application.yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
server:
port: 8080
servlet:
context-path: /api

spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver

jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true

logging:
level:
root: INFO
com.example: DEBUG
file:
name: application.log

custom:
name: MyApp
timeout: 5000

多环境配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# application.yml
spring:
profiles:
active: dev

---
# application-dev.yml
server:
port: 8080

---
# application-prod.yml
server:
port: 8080
logging:
level:
root: WARN

4. Web开发

4.1 RESTful API

4.1.1 RESTful控制器

RESTful API示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@RestController
@RequestMapping("/api/users")
public class UserController {

@Autowired
private UserService userService;

// GET /api/users
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.findAll();
return ResponseEntity.ok(users);
}

// GET /api/users/{id}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}

// POST /api/users
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User created = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

// PUT /api/users/{id}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
User updated = userService.update(user);
return ResponseEntity.ok(updated);
}

// DELETE /api/users/{id}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteById(id);
return ResponseEntity.noContent().build();
}
}

4.2 参数绑定

4.2.1 请求参数处理

参数绑定示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@RestController
@RequestMapping("/api")
public class ParameterController {

// 路径变量
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}

// 请求参数
@GetMapping("/users")
public List<User> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String name) {
return userService.findUsers(page, size, name);
}

// 请求体
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userService.save(user);
}

// 请求头
@GetMapping("/info")
public String getInfo(@RequestHeader("User-Agent") String userAgent) {
return "User-Agent: " + userAgent;
}

// Cookie
@GetMapping("/cookie")
public String getCookie(@CookieValue("sessionId") String sessionId) {
return "SessionId: " + sessionId;
}
}

4.3 异常处理

4.3.1 全局异常处理

全局异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@RestControllerAdvice
public class GlobalExceptionHandler {

// 处理业务异常
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
ErrorResponse error = new ErrorResponse(e.getCode(), e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}

// 处理参数验证异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(
MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", message);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}

// 处理其他异常
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
ErrorResponse error = new ErrorResponse("INTERNAL_ERROR", e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}

// 错误响应类
public class ErrorResponse {
private String code;
private String message;
private long timestamp;

public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
this.timestamp = System.currentTimeMillis();
}

// getter和setter
}

4.4 拦截器

4.4.1 自定义拦截器

拦截器示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 1. 创建拦截器
@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: {} {}", request.getMethod(), request.getRequestURI());
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) {
logger.info("Response status: {}", response.getStatus());
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
if (ex != null) {
logger.error("Exception occurred", ex);
}
}
}

// 2. 注册拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Autowired
private LoggingInterceptor loggingInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/public/**");
}
}

5. 数据访问

5.1 JPA

5.1.1 Spring Data JPA

JPA配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 实体类
@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String name;

@Column(unique = true)
private String email;

// getter和setter
}

// Repository接口
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

// 方法名查询
List<User> findByName(String name);

List<User> findByEmailContaining(String email);

// 自定义查询
@Query("SELECT u FROM User u WHERE u.name LIKE %:name%")
List<User> searchByName(@Param("name") String name);

// 原生SQL
@Query(value = "SELECT * FROM users WHERE email = ?1", nativeQuery = true)
User findByEmailNative(String email);
}

// Service层
@Service
@Transactional
public class UserService {

@Autowired
private UserRepository userRepository;

public List<User> findAll() {
return userRepository.findAll();
}

public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new BusinessException("User not found"));
}

public User save(User user) {
return userRepository.save(user);
}

public void deleteById(Long id) {
userRepository.deleteById(id);
}
}

5.2 MyBatis

5.2.1 MyBatis集成

MyBatis配置

1
2
3
4
5
6
<!-- pom.xml -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
1
2
3
4
5
6
# application.yml
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Mapper接口
@Mapper
public interface UserMapper {

@Select("SELECT * FROM users WHERE id = #{id}")
User findById(Long id);

@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(User user);

@Update("UPDATE users SET name = #{name} WHERE id = #{id}")
void update(User user);

@Delete("DELETE FROM users WHERE id = #{id}")
void deleteById(Long id);
}

5.3 Redis

5.3.1 Redis集成

Redis配置

1
2
3
4
5
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
# application.yml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 3000
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Redis配置类
@Configuration
public class RedisConfig {

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);

// 使用Jackson2JsonRedisSerializer序列化
Jackson2JsonRedisSerializer<Object> serializer =
new Jackson2JsonRedisSerializer<>(Object.class);
template.setValueSerializer(serializer);
template.setKeySerializer(new StringRedisSerializer());

template.afterPropertiesSet();
return template;
}
}

// 使用Redis
@Service
public class CacheService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value, 1, TimeUnit.HOURS);
}

public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}

public void delete(String key) {
redisTemplate.delete(key);
}
}

6. 安全认证

6.1 Spring Security

6.1.1 Spring Security配置

Spring Security配置

1
2
3
4
5
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private UserDetailsService userDetailsService;

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.and()
.csrf().disable();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

6.2 JWT认证

6.2.1 JWT集成

JWT配置

1
2
3
4
5
6
<!-- pom.xml -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// JWT工具类
@Component
public class JwtUtil {

private String secret = "mySecretKey";
private long expiration = 86400000; // 24小时

public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}

private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}

public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}

public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}

private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}

public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}

private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}

private Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
}

7. 监控管理

7.1 Actuator

7.1.1 Actuator配置

Actuator配置

1
2
3
4
5
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true

Actuator端点

  • /actuator/health:健康检查
  • /actuator/info:应用信息
  • /actuator/metrics:指标信息
  • /actuator/prometheus:Prometheus指标

7.2 自定义端点

7.2.1 创建自定义端点

自定义端点

1
2
3
4
5
6
7
8
9
10
11
12
@Component
@Endpoint(id = "custom")
public class CustomEndpoint {

@ReadOperation
public Map<String, Object> custom() {
Map<String, Object> info = new HashMap<>();
info.put("status", "OK");
info.put("timestamp", System.currentTimeMillis());
return info;
}
}

8. 部署运维

8.1 打包部署

8.1.1 打包可执行JAR

Maven打包

1
2
3
4
5
6
7
8
# 打包
mvn clean package

# 运行
java -jar target/springboot-demo-1.0.0.jar

# 指定配置文件
java -jar target/springboot-demo-1.0.0.jar --spring.profiles.active=prod

Docker部署

1
2
3
4
FROM openjdk:8-jre-alpine
VOLUME /tmp
COPY target/springboot-demo-1.0.0.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
1
2
3
4
5
# 构建镜像
docker build -t springboot-demo:1.0.0 .

# 运行容器
docker run -d -p 8080:8080 springboot-demo:1.0.0

8.2 生产环境配置

8.2.1 生产环境优化

生产环境配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# application-prod.yml
server:
port: 8080
compression:
enabled: true

spring:
jpa:
show-sql: false
hibernate:
ddl-auto: validate

logging:
level:
root: WARN
com.example: INFO
file:
name: /var/log/application.log
max-size: 10MB
max-history: 30

9. 总结

9.1 核心要点

  1. 自动配置:SpringBoot通过自动配置简化开发
  2. 起步依赖:简化依赖管理
  3. 内嵌服务器:可独立运行
  4. 生产就绪:Actuator提供监控和管理

9.2 关键理解

  1. 约定优于配置:遵循约定,减少配置
  2. 开箱即用:提供默认配置,快速开发
  3. 易于扩展:支持自定义配置和扩展

9.3 最佳实践

  1. 合理使用自动配置:理解自动配置原理
  2. 多环境配置:使用profile管理不同环境
  3. 完善监控:使用Actuator监控应用
  4. 安全配置:合理配置安全策略

相关文章