目录
环境搭建与项目创建
系统要求
组件 | 版本要求 |
---|---|
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. 字段注入(不推荐)
@Autowired
private UserRepository userRepository;
// 2. 构造器注入(推荐)
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 3. Setter注入
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 4. 方法参数注入
@Autowired
public 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
@ConditionalOnLinux
public LinuxService linuxService() {
return new LinuxService();
}
}
// 自定义条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(LinuxCondition.class)
public @interface ConditionalOnLinux {
}
public class LinuxCondition implements Condition {
@Override
public 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
@ConditionalOnMissingBean
public 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: true
prefix: "自定义前缀"
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环境时
@ConditionalOnNotWebApplication
public 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 {
@Autowired
private 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
@ConditionalOnMissingBean
public 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 - 创建资源
@PostMapping
public 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;
@Valid
private List<ContactInfo> contacts;
@Valid
private 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 {
@Bean
public 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 {
@Override
public 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;
@Version
private 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;
@CreationTimestamp
private 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 {
@PersistenceContext
private EntityManager entityManager;
@Override
public 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();
}
@Override
public 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
@Bean
public DataSource primaryDataSource() {
return primaryDataSourceProperties()
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
// 从数据源
@Bean
@ConfigurationProperties("spring.datasource.secondary")
public DataSourceProperties secondaryDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public DataSource secondaryDataSource() {
return secondaryDataSourceProperties()
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
// 主数据源的JPA配置
@Primary
@Bean
public 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配置
@Bean
public 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
@Bean
public PlatformTransactionManager primaryTransactionManager(
@Qualifier("primaryEntityManagerFactory") EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
// 从数据源事务管理器
@Bean
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
}
// 配置文件
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/primary_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
secondary:
url: jdbc:mysql://localhost:3306/secondary_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
2. Redis 集成详解
// Redis配置
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
LettuceConnectionFactory factory = new LettuceConnectionFactory();
factory.setHostName("localhost");
factory.setPort(6379);
factory.setDatabase(0);
return factory;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 设置序列化器
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);