Spring Boot 教程

快速构建企业级应用

目录

1. Spring Boot 概述

Spring Boot 是一个基于 Spring 框架的快速开发工具,它简化了 Spring 应用的配置和部署过程。Spring Boot 遵循"约定优于配置"的原则,提供了大量自动配置功能,使开发者能够快速构建独立运行、生产级别的 Spring 应用。

1.1 Spring Boot 的主要特性

1.2 Spring Boot 与 Spring 的关系

Spring Boot 是 Spring 框架的扩展,它简化了 Spring 应用的开发过程。Spring Boot 不是替代 Spring,而是建立在 Spring 之上的工具,它使用 Spring 的所有功能,但简化了配置和部署。

小提示

Spring Boot 的设计目标是简化 Spring 应用的开发,它通过自动配置和约定优于配置的原则,减少了开发者的工作量。

2. 快速入门

2.1 环境准备

在开始 Spring Boot 开发之前,需要准备以下环境:

2.2 创建 Spring Boot 项目

有几种方式可以创建 Spring Boot 项目:

使用 Spring Initializr

访问 https://start.spring.io/,填写项目信息,选择依赖,然后下载项目。

使用 IDE

在 IntelliJ IDEA 中,选择 File > New > Project,然后选择 Spring Initializr。

使用 Maven

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

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

2.3 第一个 Spring Boot 应用

创建一个简单的 Hello World 应用:

主应用类

package com.example.demo;

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

@SpringBootApplication
public class DemoApplication {

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

控制器类

package com.example.demo;

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, Spring Boot!";
    }
}

运行应用

运行主应用类,然后在浏览器中访问 http://localhost:8080/hello

注意

Spring Boot 应用默认使用 8080 端口,如果该端口被占用,可以在 application.properties 中修改 server.port 属性。

3. 项目结构

3.1 标准目录结构

Spring Boot 项目通常遵循以下目录结构:

src/
├── main/
│   ├── java/
│   │   └── com/
│   │       └── example/
│   │           └── demo/
│   │               ├── DemoApplication.java
│   │               ├── controller/
│   │               ├── service/
│   │               ├── repository/
│   │               ├── model/
│   │               └── config/
│   └── resources/
│       ├── application.properties
│       ├── application.yml
│       ├── static/
│       └── templates/
└── test/
    └── java/
        └── com/
            └── example/
                └── demo/
                    └── DemoApplicationTests.java

3.2 主要组件

3.3 包扫描

Spring Boot 会自动扫描主应用类所在包及其子包中的组件。如果需要扫描其他包,可以使用 @ComponentScan 注解:

@SpringBootApplication
@ComponentScan(basePackages = {"com.example.demo", "com.example.other"})
public class DemoApplication {
    // ...
}
小提示

遵循标准的项目结构可以使代码更加清晰,便于维护和扩展。同时,也有助于其他开发者快速理解项目。

4. 配置管理

4.1 配置文件

Spring Boot 支持多种配置文件格式:

application.properties 示例

# 服务器配置
server.port=8080
server.servlet.context-path=/api

# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/demo
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

application.yml 示例

server:
  port: 8080
  servlet:
    context-path: /api

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

4.2 配置属性绑定

可以使用 @ConfigurationProperties 注解将配置属性绑定到 Java 类:

@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
    private String name;
    private String description;
    
    // getters and setters
}

4.3 环境配置

可以使用 spring.profiles.active 属性激活特定的环境配置:

# application.properties
spring.profiles.active=dev

或者在命令行中指定:

java -jar app.jar --spring.profiles.active=prod

4.4 外部化配置

Spring Boot 支持多种外部化配置方式,按优先级从高到低:

  1. 命令行参数
  2. JNDI 属性
  3. Java 系统属性
  4. 操作系统环境变量
  5. 配置文件
注意

敏感信息(如数据库密码)不应该直接写在配置文件中,而应该使用环境变量或外部配置服务。

5. Web 开发

5.1 RESTful API

使用 @RestController 和 @RequestMapping 注解创建 RESTful API:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping
    public List<User> getAllUsers() {
        // 获取所有用户
        return userService.findAll();
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        // 获取指定 ID 的用户
        return userService.findById(id);
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        // 创建新用户
        return userService.save(user);
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        // 更新用户
        user.setId(id);
        return userService.save(user);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        // 删除用户
        userService.deleteById(id);
    }
}

5.2 请求参数处理

Spring Boot 提供了多种注解来处理请求参数:

5.3 静态资源

Spring Boot 默认从以下位置提供静态资源:

5.4 模板引擎

Spring Boot 支持多种模板引擎,如 Thymeleaf、FreeMarker、Groovy 等:

// 添加 Thymeleaf 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
// 控制器
@Controller
public class HomeController {

    @GetMapping("/")
    public String home(Model model) {
        model.addAttribute("message", "Hello, Thymeleaf!");
        return "home";
    }
}
<!-- home.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Home</title>
</head>
<body>
    <h1 th:text="${message}">Default Message</h1>
</body>
</html>
小提示

在开发 RESTful API 时,建议使用 @RestController 注解,它会自动将返回值转换为 JSON 格式。

6. 数据访问

6.1 JPA 和 Hibernate

使用 Spring Data JPA 简化数据访问:

// 添加 JPA 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
// 实体类
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String username;
    private String email;
    
    // getters and setters
}
// 仓库接口
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByUsername(String username);
    User findByEmail(String email);
}

6.2 MyBatis

使用 MyBatis 进行数据访问:

// 添加 MyBatis 依赖
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>
// Mapper 接口
@Mapper
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User findById(Long id);
    
    @Insert("INSERT INTO users(username, email) VALUES(#{username}, #{email})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);
}

6.3 JDBC

使用 JdbcTemplate 进行数据访问:

// 添加 JDBC 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
// 服务类
@Service
public class UserService {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(Long id) {
        return jdbcTemplate.queryForObject(
            "SELECT * FROM users WHERE id = ?",
            new Object[]{id},
            (rs, rowNum) -> new User(
                rs.getLong("id"),
                rs.getString("username"),
                rs.getString("email")
            )
        );
    }
}

6.4 事务管理

使用 @Transactional 注解管理事务:

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public User createUser(User user) {
        // 创建用户
        User savedUser = userRepository.save(user);
        
        // 创建用户角色
        roleService.assignDefaultRole(savedUser.getId());
        
        return savedUser;
    }
}
注意

选择合适的数据访问技术取决于项目需求。JPA 适合简单的 CRUD 操作,MyBatis 适合复杂的 SQL 查询,JDBC 适合需要完全控制 SQL 的场景。

7. 安全控制

7.1 Spring Security

使用 Spring Security 实现安全控制:

// 添加 Spring Security 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
// 安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/", "/home", "/public/**").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.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        
        UserDetails admin = User.withDefaultPasswordEncoder()
            .username("admin")
            .password("admin")
            .roles("ADMIN")
            .build();
        
        return new InMemoryUserDetailsManager(user, admin);
    }
}

7.2 JWT 认证

使用 JWT 进行无状态认证:

// 添加 JWT 依赖
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
// JWT 服务
@Service
public class JwtService {
    
    @Value("${jwt.secret}")
    private String secret;
    
    @Value("${jwt.expiration}")
    private Long expiration;
    
    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(System.currentTimeMillis()))
            .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
            .signWith(getSigningKey(), SignatureAlgorithm.HS256)
            .compact();
    }
    
    // 其他方法...
}

7.3 OAuth2 和 OpenID Connect

使用 Spring Security OAuth2 实现 OAuth2 和 OpenID Connect:

// 添加 OAuth2 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
// 安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                .loginPage("/login")
            );
        
        return http.build();
    }
}
小提示

在生产环境中,应该使用更安全的密码编码器,如 BCryptPasswordEncoder,而不是默认的密码编码器。

8. 测试

8.1 单元测试

使用 JUnit 和 Spring Test 进行单元测试:

// 添加测试依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
// 服务测试
@SpringBootTest
class UserServiceTest {

    @Autowired
    private UserService userService;
    
    @MockBean
    private UserRepository userRepository;
    
    @Test
    void testCreateUser() {
        // 准备测试数据
        User user = new User();
        user.setUsername("testuser");
        user.setEmail("test@example.com");
        
        when(userRepository.save(any(User.class))).thenReturn(user);
        
        // 执行测试
        User savedUser = userService.createUser(user);
        
        // 验证结果
        assertNotNull(savedUser);
        assertEquals("testuser", savedUser.getUsername());
        verify(userRepository).save(any(User.class));
    }
}

8.2 集成测试

使用 @SpringBootTest 和 MockMvc 进行集成测试:

@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private UserService userService;
    
    @Test
    void testGetUser() throws Exception {
        // 准备测试数据
        User user = new User();
        user.setId(1L);
        user.setUsername("testuser");
        
        when(userService.findById(1L)).thenReturn(user);
        
        // 执行测试
        mockMvc.perform(get("/api/users/1"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.id").value(1))
            .andExpect(jsonPath("$.username").value("testuser"));
    }
}

8.3 测试配置

使用 @TestConfiguration 和 @TestPropertySource 配置测试环境:

@SpringBootTest
@TestPropertySource(properties = {
    "spring.datasource.url=jdbc:h2:mem:testdb",
    "spring.datasource.driver-class-name=org.h2.Driver",
    "spring.datasource.username=sa",
    "spring.datasource.password=",
    "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect"
})
class DataAccessTest {
    // 测试代码...
}
注意

在测试中,应该使用 H2 等内存数据库,而不是实际的数据库,以避免测试数据污染生产数据。

9. 部署

9.1 打包应用

使用 Maven 或 Gradle 打包应用:

# Maven
mvn clean package

# Gradle
gradle bootJar

9.2 运行应用

使用 java -jar 命令运行应用:

java -jar target/demo-0.0.1-SNAPSHOT.jar

9.3 外部化配置

在运行时指定配置:

java -jar demo-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod --server.port=8081

9.4 容器化部署

使用 Docker 容器化部署:

# Dockerfile
FROM openjdk:17-jdk-slim
COPY target/demo-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
# 构建镜像
docker build -t demo-app .

# 运行容器
docker run -p 8080:8080 demo-app

9.5 云平台部署

Spring Boot 应用可以部署到各种云平台,如 AWS、Azure、Google Cloud 等。

小提示

在生产环境中,应该使用适当的内存设置和垃圾回收参数来优化 JVM 性能。

10. 最佳实践

10.1 项目结构

10.2 配置管理

10.3 异常处理

10.4 性能优化

10.5 安全最佳实践

笔记

遵循最佳实践可以帮助构建高质量、可维护的 Spring Boot 应用。根据项目需求和团队情况,选择合适的最佳实践。

返回首页