Spring Boot 完整教程 - 从入门到精通(全面版)
目录
环境搭建与项目创建
核心注解详解
依赖管理深入理解
自动配置原理详解
Web 开发详解
数据访问层详解
配置系统深入
AOP 面向切面编程
事务管理
数据校验
异常处理机制
日志系统
缓存机制
定时任务
事件机制
国际化支持
文件上传下载
Spring Security 安全
环境搭建与项目创建
系统要求
组件 | 版本要求 |
---|---|
Java | JDK 8+ |
Spring Boot | 2.7.x / 3.x |
Maven | 3.6.3+ |
Gradle | 6.8+ |
开发环境检查
# 检查 Java 版本
java -version
javac -version# 检查 Maven 版本
mvn -version# 检查环境变量
echo $JAVA_HOME
echo $MAVEN_HOME
创建项目的四种方式
方式一:Spring Initializr 网站
- 访问 https://start.spring.io/
- 选择项目配置
- 下载项目压缩包
方式二:IDE 集成
IntelliJ IDEA:
File -> New -> Project -> Spring Initializr
Eclipse (STS):
File -> New -> Other -> Spring Boot -> Spring Starter Project
方式三:命令行工具
# 使用 Spring Boot CLI
spring init --dependencies=web,jpa,mysql my-project# 使用 Maven archetype
mvn archetype:generate -DgroupId=com.example \-DartifactId=demo \-DarchetypeArtifactId=maven-archetype-quickstart \-DinteractiveMode=false
方式四:手动创建
完整的项目结构:
demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── demo/
│ │ │ ├── DemoApplication.java
│ │ │ ├── controller/
│ │ │ ├── service/
│ │ │ ├── repository/
│ │ │ ├── entity/
│ │ │ ├── dto/
│ │ │ ├── config/
│ │ │ └── util/
│ │ └── resources/
│ │ ├── static/
│ │ ├── templates/
│ │ ├── application.yml
│ │ └── application-dev.yml
│ └── test/
│ └── java/
│ └── com/
│ └── example/
│ └── demo/
├── target/
├── pom.xml
└── README.md
详细的 pom.xml 配置:
<?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><!-- Spring Boot 父 POM --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/></parent><!-- 项目信息 --><groupId>com.example</groupId><artifactId>demo</artifactId><version>1.0.0-SNAPSHOT</version><name>demo</name><description>Spring Boot Demo Project</description><packaging>jar</packaging><!-- 属性配置 --><properties><java.version>8</java.version><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><!-- 依赖管理 --><dependencies><!-- Spring Boot Web Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot Data JPA Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- MySQL 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- Spring Boot Validation --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!-- Spring Boot Test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 开发工具 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!-- 配置处理器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency></dependencies><!-- 构建配置 --><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>
核心注解详解
@SpringBootApplication 深入理解
@SpringBootApplication 源码分析:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 标识为配置类
@EnableAutoConfiguration // 启用自动配置
@ComponentScan // 组件扫描
public @interface SpringBootApplication {// 排除特定的自动配置类Class<?>[] exclude() default {};// 排除特定的自动配置类名String[] excludeName() default {};// 指定扫描的包String[] scanBasePackages() default {};// 指定扫描的类Class<?>[] scanBasePackageClasses() default {};
}
等价写法:
// 使用 @SpringBootApplication
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}// 等价于以下写法
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
Spring 核心注解详解
1. 依赖注入注解
@Autowired 详解:
@Service
public class UserService {// 1. 字段注入(不推荐)@Autowiredprivate UserRepository userRepository;// 2. 构造器注入(推荐)private final UserRepository userRepository;@Autowiredpublic UserService(UserRepository userRepository) {this.userRepository = userRepository;}// 3. Setter注入private UserRepository userRepository;@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}// 4. 方法参数注入@Autowiredpublic void init(UserRepository userRepository) {this.userRepository = userRepository;}
}
@Resource vs @Autowired:
@Service
public class OrderService {// @Autowired 按类型注入,如果有多个同类型Bean,再按名称@Autowired@Qualifier("orderRepositoryImpl") // 指定具体实现private OrderRepository orderRepository;// @Resource 按名称注入,JSR-250标准@Resource(name = "orderRepositoryImpl")private OrderRepository orderRepository2;
}
2. 组件注解
@Component 家族:
// 通用组件
@Component
public class CommonUtil {public String generateId() {return UUID.randomUUID().toString();}
}// 服务层
@Service
public class UserService {// 业务逻辑
}// 持久层
@Repository
public class UserRepository {// 数据访问逻辑
}// 控制层
@Controller
public class UserController {// 请求处理逻辑
}// REST控制层
@RestController // = @Controller + @ResponseBody
public class UserRestController {// REST API逻辑
}
3. 配置注解
@Configuration 详解:
@Configuration
public class DatabaseConfig {@Bean@Primary // 主要的Bean,当有多个同类型Bean时优先选择public DataSource primaryDataSource() {return DataSourceBuilder.create().url("jdbc:mysql://localhost:3306/primary").username("root").password("123456").build();}@Bean("secondaryDataSource")public DataSource secondaryDataSource() {return DataSourceBuilder.create().url("jdbc:mysql://localhost:3306/secondary").username("root").password("123456").build();}@Bean@ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")public CacheManager cacheManager() {return new SimpleCacheManager();}
}
4. 条件注解
@Conditional 家族:
@Configuration
public class ConditionalConfig {// 当类存在时@Bean@ConditionalOnClass(RedisTemplate.class)public RedisService redisService() {return new RedisService();}// 当Bean不存在时@Bean@ConditionalOnMissingBean(DataSource.class)public DataSource defaultDataSource() {return new EmbeddedDatabaseBuilder().build();}// 当属性匹配时@Bean@ConditionalOnProperty(prefix = "app.email",name = "enabled",havingValue = "true",matchIfMissing = false)public EmailService emailService() {return new EmailService();}// 自定义条件@Bean@ConditionalOnLinuxpublic LinuxService linuxService() {return new LinuxService();}
}// 自定义条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(LinuxCondition.class)
public @interface ConditionalOnLinux {
}public class LinuxCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return System.getProperty("os.name").toLowerCase().contains("linux");}
}
依赖管理深入理解
Spring Boot Starter 原理
Starter 的结构:
spring-boot-starter-web/
├── META-INF/
│ └── spring.factories
└── pom.xml (依赖声明)
spring.factories 文件:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
常用 Starter 详解
1. spring-boot-starter-web
<!-- 包含的主要依赖 -->
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId></dependency>
</dependencies>
2. spring-boot-starter-data-jpa
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-jpa</artifactId></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId></dependency>
</dependencies>
版本管理最佳实践
1. 使用 Spring Boot BOM:
<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.7.12</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
2. 版本覆盖:
<properties><!-- 覆盖 Spring Boot 管理的版本 --><mysql.version>8.0.33</mysql.version><jackson.version>2.15.2</jackson.version>
</properties>
自定义 Starter
1. 创建 autoconfigure 模块:
// 自动配置类
@Configuration
@ConditionalOnClass(MyService.class)
@ConditionalOnProperty(prefix = "myservice", name = "enabled", havingValue = "true")
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic MyService myService(MyServiceProperties properties) {return new MyService(properties);}
}// 配置属性类
@ConfigurationProperties(prefix = "myservice")
public class MyServiceProperties {private boolean enabled = true;private String prefix = "默认前缀";private int timeout = 30;// getter/setter...
}// 服务类
public class MyService {private final MyServiceProperties properties;public MyService(MyServiceProperties properties) {this.properties = properties;}public String processMessage(String message) {return properties.getPrefix() + ": " + message;}
}
2. spring.factories 配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.myservice.autoconfigure.MyServiceAutoConfiguration
3. 使用自定义 Starter:
<dependency><groupId>com.example</groupId><artifactId>my-service-spring-boot-starter</artifactId><version>1.0.0</version>
</dependency>
# application.yml
myservice:enabled: trueprefix: "自定义前缀"timeout: 60
自动配置原理详解
自动配置的工作流程
1. 启动时的处理流程:
@SpringBootApplication↓
@EnableAutoConfiguration↓
AutoConfigurationImportSelector↓
读取 META-INF/spring.factories↓
加载所有 AutoConfiguration 类↓
根据 @Conditional 注解过滤↓
创建符合条件的 Bean
深入理解 @EnableAutoConfiguration
@EnableAutoConfiguration 源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}
条件注解深入分析
所有条件注解及其作用:
public class ConditionalExamples {// 当指定的类在classpath中存在时@ConditionalOnClass(RedisTemplate.class)public RedisConfig redisConfig() { return new RedisConfig(); }// 当指定的类在classpath中不存在时@ConditionalOnMissingClass("com.example.SomeClass")public DefaultConfig defaultConfig() { return new DefaultConfig(); }// 当指定的Bean存在时@ConditionalOnBean(DataSource.class)public JdbcTemplate jdbcTemplate(DataSource dataSource) {return new JdbcTemplate(dataSource);}// 当指定的Bean不存在时@ConditionalOnMissingBean(DataSource.class)public DataSource defaultDataSource() {return new EmbeddedDatabaseBuilder().build();}// 当指定的属性存在且匹配时@ConditionalOnProperty(prefix = "spring.datasource",name = "url",matchIfMissing = false)public DataSource configuredDataSource() { return null; }// 当运行在Web环境中@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)public WebConfig webConfig() { return new WebConfig(); }// 当不是Web环境时@ConditionalOnNotWebApplicationpublic ConsoleConfig consoleConfig() { return new ConsoleConfig(); }// 当指定的资源存在时@ConditionalOnResource(resources = "classpath:config/app.properties")public ExternalConfig externalConfig() { return new ExternalConfig(); }// 当表达式为true时@ConditionalOnExpression("${app.feature.enabled:false}")public FeatureConfig featureConfig() { return new FeatureConfig(); }// 当Java版本匹配时@ConditionalOnJava(JavaVersion.EIGHT)public Java8Config java8Config() { return new Java8Config(); }// 当指定的JNDI存在时@ConditionalOnJndi("java:comp/env/jdbc/MyDataSource")public JndiConfig jndiConfig() { return new JndiConfig(); }
}
查看自动配置报告
1. 启用调试模式:
# application.properties
debug=true
2. 通过代码获取:
@RestController
public class AutoConfigReportController {@Autowiredprivate ConditionEvaluationReport conditionEvaluationReport;@GetMapping("/autoconfig")public Map<String, Object> getAutoConfigReport() {Map<String, Object> report = new HashMap<>();// 匹配的配置Map<String, ConditionEvaluationReport.ConditionAndOutcomes> conditions = conditionEvaluationReport.getConditionAndOutcomesBySource();report.put("matched", conditions.entrySet().stream().filter(entry -> entry.getValue().isFullMatch()).collect(Collectors.toMap(Map.Entry::getKey,entry -> entry.getValue().getOutcomes())));// 未匹配的配置report.put("unmatched", conditions.entrySet().stream().filter(entry -> !entry.getValue().isFullMatch()).collect(Collectors.toMap(Map.Entry::getKey,entry -> entry.getValue().getOutcomes())));return report;}
}
自定义自动配置
完整的自动配置示例:
// 1. 配置属性类
@ConfigurationProperties(prefix = "app.email")
@Data
public class EmailProperties {private boolean enabled = false;private String host = "localhost";private int port = 25;private String username;private String password;private boolean auth = false;private boolean starttls = false;
}// 2. 服务类
public class EmailService {private final EmailProperties properties;public EmailService(EmailProperties properties) {this.properties = properties;}public void sendEmail(String to, String subject, String content) {// 发送邮件的逻辑System.out.println("发送邮件到: " + to);System.out.println("主题: " + subject);System.out.println("内容: " + content);}
}// 3. 自动配置类
@Configuration
@ConditionalOnClass(EmailService.class)
@ConditionalOnProperty(prefix = "app.email", name = "enabled", havingValue = "true")
@EnableConfigurationProperties(EmailProperties.class)
public class EmailAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic EmailService emailService(EmailProperties properties) {return new EmailService(properties);}@Bean@ConditionalOnBean(EmailService.class)public EmailController emailController(EmailService emailService) {return new EmailController(emailService);}
}// 4. 控制器
@RestController
@RequestMapping("/api/email")
public class EmailController {private final EmailService emailService;public EmailController(EmailService emailService) {this.emailService = emailService;}@PostMapping("/send")public String sendEmail(@RequestParam String to,@RequestParam String subject,@RequestParam String content) {emailService.sendEmail(to, subject, content);return "邮件发送成功";}
}
Web 开发详解
Spring MVC 架构深入
Spring MVC 执行流程:
请求 → DispatcherServlet → HandlerMapping → Handler → HandlerAdapter → Controller → ModelAndView → ViewResolver → View → 响应
RESTful API 设计详解
1. HTTP 方法语义
@RestController
@RequestMapping("/api/users")
public class UserController {// GET - 获取资源@GetMapping // 获取所有用户public List<User> getUsers(@RequestParam(defaultValue = "0") int page,@RequestParam(defaultValue = "10") int size) {return userService.findAll(page, size);}@GetMapping("/{id}") // 获取单个用户public ResponseEntity<User> getUser(@PathVariable Long id) {User user = userService.findById(id);return ResponseEntity.ok(user);}// POST - 创建资源@PostMappingpublic ResponseEntity<User> createUser(@Valid @RequestBody User user) {User savedUser = userService.save(user);URI location = URI.create("/api/users/" + savedUser.getId());return ResponseEntity.created(location).body(savedUser);}// PUT - 更新整个资源@PutMapping("/{id}")public ResponseEntity<User> updateUser(@PathVariable Long id, @Valid @RequestBody User user) {user.setId(id);User updatedUser = userService.save(user);return ResponseEntity.ok(updatedUser);}// PATCH - 部分更新资源@PatchMapping("/{id}")public ResponseEntity<User> patchUser(@PathVariable Long id,@RequestBody Map<String, Object> updates) {User updatedUser = userService.partialUpdate(id, updates);return ResponseEntity.ok(updatedUser);}// DELETE - 删除资源@DeleteMapping("/{id}")public ResponseEntity<Void> deleteUser(@PathVariable Long id) {userService.deleteById(id);return ResponseEntity.noContent().build();}
}
2. 请求参数处理详解
@RestController
@RequestMapping("/api/advanced")
public class AdvancedController {// 路径变量@GetMapping("/users/{id}/orders/{orderId}")public Order getOrder(@PathVariable("id") Long userId,@PathVariable("orderId") Long orderId) {return orderService.findByUserAndId(userId, orderId);}// 请求参数@GetMapping("/search")public List<User> searchUsers(@RequestParam String keyword, // 必需参数@RequestParam(defaultValue = "name") String sortBy, // 默认值@RequestParam(required = false) String category, // 可选参数@RequestParam List<String> tags) { // 列表参数return userService.search(keyword, sortBy, category, tags);}// 请求头@GetMapping("/profile")public User getProfile(@RequestHeader("Authorization") String token,@RequestHeader(value = "Accept-Language", defaultValue = "zh-CN") String lang) {return userService.findByToken(token);}// Cookie@GetMapping("/preferences")public UserPreferences getPreferences(@CookieValue("sessionId") String sessionId) {return userService.findPreferences(sessionId);}// 矩阵变量@GetMapping("/matrix/{id}")public String matrixVar(@PathVariable String id,@MatrixVariable String name,@MatrixVariable int age) {return "ID: " + id + ", Name: " + name + ", Age: " + age;}// 访问: /matrix/123;name=john;age=25// 请求体绑定@PostMapping("/complex")public ResponseEntity<String> handleComplex(@Valid @RequestBody ComplexRequest request,BindingResult bindingResult) {if (bindingResult.hasErrors()) {return ResponseEntity.badRequest().body("参数错误");}return ResponseEntity.ok("处理成功");}
}// 复杂请求对象
@Data
@Valid
public class ComplexRequest {@NotBlank(message = "名称不能为空")private String name;@NotNull(message = "年龄不能为空")@Min(value = 0, message = "年龄不能小于0")@Max(value = 150, message = "年龄不能大于150")private Integer age;@Email(message = "邮箱格式不正确")private String email;@Validprivate List<ContactInfo> contacts;@Validprivate Address address;
}
3. 响应处理详解
@RestController
public class ResponseController {// 基本响应@GetMapping("/basic")public String basicResponse() {return "Hello World";}// JSON响应@GetMapping("/json")public User jsonResponse() {return new User(1L, "张三", "zhang@example.com");}// 响应实体 - 完全控制HTTP响应@GetMapping("/entity")public ResponseEntity<User> entityResponse() {User user = userService.findById(1L);return ResponseEntity.ok().header("Custom-Header", "custom-value").lastModified(Instant.now()).eTag("\"user-1\"").body(user);}// 不同状态码响应@PostMapping("/status")public ResponseEntity<ApiResponse> statusResponse(@RequestBody User user) {try {User savedUser = userService.save(user);ApiResponse response = new ApiResponse("success", "用户创建成功", savedUser);return ResponseEntity.status(HttpStatus.CREATED).body(response);} catch (Exception e) {ApiResponse response = new ApiResponse("error", e.getMessage(), null);return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);}}// 文件下载响应@GetMapping("/download/{filename}")public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {Resource resource = fileService.loadFileAsResource(filename);return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"").body(resource);}// 流式响应@GetMapping(value = "/stream", produces = MediaType.TEXT_PLAIN_VALUE)public ResponseEntity<StreamingResponseBody> streamResponse() {StreamingResponseBody stream = outputStream -> {for (int i = 0; i < 1000; i++) {outputStream.write(("数据行 " + i + "\n").getBytes());outputStream.flush();Thread.sleep(10); // 模拟延迟}};return ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN).body(stream);}
}// 统一API响应格式
@Data
@AllArgsConstructor
public class ApiResponse<T> {private String status;private String message;private T data;
}
内容协商
@RestController
public class ContentNegotiationController {// 根据Accept头返回不同格式@GetMapping(value = "/data", produces = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE})public User getData() {return new User(1L, "张三", "zhang@example.com");}// 根据请求参数返回不同格式@GetMapping("/data")public ResponseEntity<User> getDataWithParam(@RequestParam(defaultValue = "json") String format) {User user = new User(1L, "张三", "zhang@example.com");if ("xml".equals(format)) {return ResponseEntity.ok().contentType(MediaType.APPLICATION_XML).body(user);} else {return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(user);}}
}
跨域处理(CORS)
// 方式1: 注解方式
@RestController
@CrossOrigin(origins = "http://localhost:3000") // 允许特定源
public class CorsController {@CrossOrigin(origins = "*", maxAge = 3600) // 方法级别配置@GetMapping("/api/data")public String getData() {return "跨域数据";}
}// 方式2: 全局配置
@Configuration
public class CorsConfig {@Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();// 允许的源configuration.setAllowedOriginPatterns(Arrays.asList("*"));// 允许的HTTP方法configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));// 允许的头部configuration.setAllowedHeaders(Arrays.asList("*"));// 允许携带凭证configuration.setAllowCredentials(true);// 预检请求缓存时间configuration.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}
}// 方式3: WebMvcConfigurer
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/api/**").allowedOrigins("http://localhost:3000", "https://example.com").allowedMethods("GET", "POST", "PUT", "DELETE").allowedHeaders("*").allowCredentials(true).maxAge(3600);}
}
数据访问层详解
JPA 深入理解
1. 实体关系映射
// 用户实体
@Entity
@Table(name = "users", indexes = {@Index(name = "idx_email", columnList = "email"),@Index(name = "idx_username", columnList = "username")
})
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false, unique = true, length = 50)private String username;@Column(nullable = false, unique = true, length = 100)private String email;@Column(nullable = false)private String password;@Enumerated(EnumType.STRING)@Column(nullable = false)private UserStatus status = UserStatus.ACTIVE;@CreationTimestamp@Column(name = "created_at", nullable = false, updatable = false)private LocalDateTime createdAt;@UpdateTimestamp@Column(name = "updated_at", nullable = false)private LocalDateTime updatedAt;// 一对多关系 - 用户的订单@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)private List<Order> orders = new ArrayList<>();// 多对多关系 - 用户的角色@ManyToMany(fetch = FetchType.LAZY)@JoinTable(name = "user_roles",joinColumns = @JoinColumn(name = "user_id"),inverseJoinColumns = @JoinColumn(name = "role_id"))private Set<Role> roles = new HashSet<>();// 一对一关系 - 用户档案@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)private UserProfile profile;@Versionprivate Long version; // 乐观锁
}// 订单实体
@Entity
@Table(name = "orders")
@Data
@NoArgsConstructor
public class Order {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false, unique = true)private String orderNumber;@Column(nullable = false, precision = 10, scale = 2)private BigDecimal totalAmount;@Enumerated(EnumType.STRING)private OrderStatus status = OrderStatus.PENDING;@CreationTimestampprivate LocalDateTime createdAt;// 多对一关系 - 订单属于用户@ManyToOne(fetch = FetchType.LAZY)@JoinColumn(name = "user_id", nullable = false)private User user;// 一对多关系 - 订单项@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)private List<OrderItem> orderItems = new ArrayList<>();
}// 订单项实体
@Entity
@Table(name = "order_items")
@Data
@NoArgsConstructor
public class OrderItem {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false)private String productName;@Column(nullable = false)private Integer quantity;@Column(nullable = false, precision = 10, scale = 2)private BigDecimal price;// 多对一关系@ManyToOne(fetch = FetchType.LAZY)@JoinColumn(name = "order_id", nullable = false)private Order order;
}// 枚举类型
public enum UserStatus {ACTIVE, INACTIVE, SUSPENDED
}public enum OrderStatus {PENDING, PAID, SHIPPED, DELIVERED, CANCELLED
}
2. Repository 详解
// 基础 Repository
public interface UserRepository extends JpaRepository<User, Long> {// 查询方法命名规则Optional<User> findByUsername(String username);Optional<User> findByEmail(String email);List<User> findByStatus(UserStatus status);List<User> findByCreatedAtBetween(LocalDateTime start, LocalDateTime end);List<User> findByUsernameContainingIgnoreCase(String username);List<User> findByRolesName(String roleName);// 排序和分页List<User> findByStatusOrderByCreatedAtDesc(UserStatus status);Page<User> findByStatus(UserStatus status, Pageable pageable);// @Query 注解 - JPQL@Query("SELECT u FROM User u WHERE u.email = ?1")Optional<User> findByEmailJPQL(String email);@Query("SELECT u FROM User u WHERE u.username LIKE %:username% AND u.status = :status")List<User> findByUsernameContainingAndStatus(@Param("username") String username, @Param("status") UserStatus status);// 原生SQL查询@Query(value = "SELECT * FROM users WHERE email = ?1", nativeQuery = true)Optional<User> findByEmailNative(String email);@Query(value = "SELECT u.*, COUNT(o.id) as order_count " +"FROM users u LEFT JOIN orders o ON u.id = o.user_id " +"GROUP BY u.id ORDER BY order_count DESC",nativeQuery = true)List<Object[]> findUsersWithOrderCount();// 更新查询@Modifying@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")int updateUserStatus(@Param("id") Long id, @Param("status") UserStatus status);@Modifying@Query("DELETE FROM User u WHERE u.status = :status AND u.createdAt < :date")int deleteInactiveUsers(@Param("status") UserStatus status, @Param("date") LocalDateTime date);// 投影查询@Query("SELECT u.id as id, u.username as username, u.email as email FROM User u")List<UserProjection> findAllProjected();// 动态查询支持@Query("SELECT u FROM User u WHERE " +"(:username IS NULL OR u.username LIKE %:username%) AND " +"(:email IS NULL OR u.email = :email) AND " +"(:status IS NULL OR u.status = :status)")Page<User> findUsersDynamic(@Param("username") String username,@Param("email") String email,@Param("status") UserStatus status,Pageable pageable);
}// 投影接口
public interface UserProjection {Long getId();String getUsername();String getEmail();
}// DTO投影类
@Data
@AllArgsConstructor
public class UserSummary {private Long id;private String username;private String email;private Long orderCount;
}
3. 自定义Repository实现
// 自定义Repository接口
public interface UserRepositoryCustom {List<User> findUsersByCriteria(UserSearchCriteria criteria);Page<User> findUsersWithDynamicQuery(UserSearchCriteria criteria, Pageable pageable);
}// 自定义Repository实现
@Repository
public class UserRepositoryCustomImpl implements UserRepositoryCustom {@PersistenceContextprivate EntityManager entityManager;@Overridepublic List<User> findUsersByCriteria(UserSearchCriteria criteria) {CriteriaBuilder cb = entityManager.getCriteriaBuilder();CriteriaQuery<User> query = cb.createQuery(User.class);Root<User> root = query.from(User.class);List<Predicate> predicates = new ArrayList<>();// 动态添加查询条件if (criteria.getUsername() != null) {predicates.add(cb.like(cb.lower(root.get("username")), "%" + criteria.getUsername().toLowerCase() + "%"));}if (criteria.getEmail() != null) {predicates.add(cb.equal(root.get("email"), criteria.getEmail()));}if (criteria.getStatus() != null) {predicates.add(cb.equal(root.get("status"), criteria.getStatus()));}if (criteria.getCreatedAfter() != null) {predicates.add(cb.greaterThanOrEqualTo(root.get("createdAt"), criteria.getCreatedAfter()));}if (criteria.getCreatedBefore() != null) {predicates.add(cb.lessThanOrEqualTo(root.get("createdAt"), criteria.getCreatedBefore()));}query.where(cb.and(predicates.toArray(new Predicate[0])));query.orderBy(cb.desc(root.get("createdAt")));return entityManager.createQuery(query).getResultList();}@Overridepublic Page<User> findUsersWithDynamicQuery(UserSearchCriteria criteria, Pageable pageable) {CriteriaBuilder cb = entityManager.getCriteriaBuilder();// 查询数据CriteriaQuery<User> query = cb.createQuery(User.class);Root<User> root = query.from(User.class);List<Predicate> predicates = buildPredicates(cb, root, criteria);query.where(cb.and(predicates.toArray(new Predicate[0])));// 添加排序if (pageable.getSort().isSorted()) {List<javax.persistence.criteria.Order> orders = new ArrayList<>();pageable.getSort().forEach(order -> {if (order.isAscending()) {orders.add(cb.asc(root.get(order.getProperty())));} else {orders.add(cb.desc(root.get(order.getProperty())));}});query.orderBy(orders);}TypedQuery<User> typedQuery = entityManager.createQuery(query);typedQuery.setFirstResult((int) pageable.getOffset());typedQuery.setMaxResults(pageable.getPageSize());List<User> users = typedQuery.getResultList();// 查询总数CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);Root<User> countRoot = countQuery.from(User.class);countQuery.select(cb.count(countRoot));countQuery.where(cb.and(buildPredicates(cb, countRoot, criteria).toArray(new Predicate[0])));Long total = entityManager.createQuery(countQuery).getSingleResult();return new PageImpl<>(users, pageable, total);}private List<Predicate> buildPredicates(CriteriaBuilder cb, Root<User> root, UserSearchCriteria criteria) {List<Predicate> predicates = new ArrayList<>();if (criteria.getUsername() != null) {predicates.add(cb.like(cb.lower(root.get("username")), "%" + criteria.getUsername().toLowerCase() + "%"));}if (criteria.getEmail() != null) {predicates.add(cb.equal(root.get("email"), criteria.getEmail()));}if (criteria.getStatus() != null) {predicates.add(cb.equal(root.get("status"), criteria.getStatus()));}return predicates;}
}// 搜索条件类
@Data
public class UserSearchCriteria {private String username;private String email;private UserStatus status;private LocalDateTime createdAfter;private LocalDateTime createdBefore;
}// 完整的Repository接口
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {// JPA方法查询...
}
数据库连接配置
1. 多数据源配置
@Configuration
public class DataSourceConfig {// 主数据源@Primary@Bean@ConfigurationProperties("spring.datasource.primary")public DataSourceProperties primaryDataSourceProperties() {return new DataSourceProperties();}@Primary@Beanpublic DataSource primaryDataSource() {return primaryDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();}// 从数据源@Bean@ConfigurationProperties("spring.datasource.secondary")public DataSourceProperties secondaryDataSourceProperties() {return new DataSourceProperties();}@Beanpublic DataSource secondaryDataSource() {return secondaryDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();}// 主数据源的JPA配置@Primary@Beanpublic LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(@Qualifier("primaryDataSource") DataSource dataSource) {LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();factory.setDataSource(dataSource);factory.setPackagesToScan("com.example.demo.entity.primary");factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());Properties jpaProperties = new Properties();jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");jpaProperties.setProperty("hibernate.hbm2ddl.auto", "update");jpaProperties.setProperty("hibernate.show_sql", "true");factory.setJpaProperties(jpaProperties);return factory;}// 从数据源的JPA配置@Beanpublic LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(@Qualifier("secondaryDataSource") DataSource dataSource) {LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();factory.setDataSource(dataSource);factory.setPackagesToScan("com.example.demo.entity.secondary");factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());Properties jpaProperties = new Properties();jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");jpaProperties.setProperty("hibernate.hbm2ddl.auto", "update");jpaProperties.setProperty("hibernate.show_sql", "false");factory.setJpaProperties(jpaProperties);return factory;}// 主数据源事务管理器@Primary@Beanpublic PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryEntityManagerFactory") EntityManagerFactory factory) {return new JpaTransactionManager(factory);}// 从数据源事务管理器@Beanpublic PlatformTransactionManager secondaryTransactionManager(@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory factory) {return new JpaTransactionManager(factory);}
}// 配置文件
spring:datasource:primary:url: jdbc:mysql://localhost:3306/primary_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driversecondary:url: jdbc:mysql://localhost:3306/secondary_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
2. Redis 集成详解
// Redis配置
@Configuration
@EnableCaching
public class RedisConfig {@Beanpublic RedisConnectionFactory redisConnectionFactory() {LettuceConnectionFactory factory = new LettuceConnectionFactory();factory.setHostName("localhost");factory.setPort(6379);factory.setDatabase(0);return factory;}@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);// 设置序列化器Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);