Spring Boot实战¶
🎯 学习目标¶
完成本章学习后,你将能够:
1. Spring Boot核心理念¶
1.1 什么是Spring Boot¶
Spring Boot是基于Spring Framework的快速开发框架,核心理念是约定优于配置(Convention over Configuration)。它通过自动配置、起步依赖和嵌入式服务器,极大简化了Spring应用的创建和部署流程。
核心优势: - 内嵌Tomcat/Jetty,无需外部部署War包 - 自动配置(Auto-Configuration),减少XML配置 - 起步依赖(Starter),一站式引入相关Jar包 - Actuator生产级监控 - 与Spring Cloud无缝集成,便于微服务化
1.2 环境搭建¶
方式一:Spring Initializr(推荐)
访问 https://start.spring.io,选择: - Project: Maven - Language: Java - Spring Boot: 3.2.x - Packaging: Jar - Java: 21
勾选依赖:Spring Web、Spring Data JPA、MySQL Driver、Lombok
方式二:IDEA创建
IntelliJ IDEA → New Project → Spring Initializr → 选择依赖 → 生成
项目基本结构:
my-app/
├── src/main/java/com/example/myapp/
│ ├── MyAppApplication.java // 启动类
│ ├── controller/ // 控制层
│ ├── service/ // 业务层
│ ├── repository/ // 持久层
│ ├── entity/ // 实体类
│ ├── config/ // 配置类
│ └── dto/ // 数据传输对象
├── src/main/resources/
│ ├── application.yml // 主配置
│ ├── application-dev.yml // 开发环境配置
│ ├── application-prod.yml // 生产环境配置
│ └── static/ // 静态资源
├── src/test/java/ // 测试代码
└── pom.xml // Maven配置
1.3 第一个Spring Boot应用¶
// pom.xml 核心依赖
// <parent>
// <groupId>org.springframework.boot</groupId>
// <artifactId>spring-boot-starter-parent</artifactId>
// <version>3.2.5</version>
// </parent>
// <dependencies>
// <dependency>
// <groupId>org.springframework.boot</groupId>
// <artifactId>spring-boot-starter-web</artifactId>
// </dependency>
// </dependencies>
package com.example.myapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // = @Configuration + @EnableAutoConfiguration + @ComponentScan
public class MyAppApplication {
public static void main(String[] args) {
SpringApplication.run(MyAppApplication.class, args);
}
}
启动后访问 http://localhost:8080,Spring Boot应用即运行。
2. 核心注解详解¶
2.1 启动与配置注解¶
// @SpringBootApplication 是组合注解,包含:
// @Configuration — 标识当前类为配置类,可定义@Bean
// @EnableAutoConfiguration — 开启自动配置
// @ComponentScan — 自动扫描当前包及子包下的组件
@SpringBootApplication
public class MyAppApplication {
public static void main(String[] args) {
SpringApplication.run(MyAppApplication.class, args);
}
}
// @Configuration + @Bean:手动注册Bean
@Configuration
public class AppConfig {
@Bean // 方法返回值注册为Spring容器中的Bean,默认单例
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
@ConditionalOnProperty(name = "feature.cache.enabled", havingValue = "true")
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users");
}
}
2.2 组件注解(分层架构)¶
// @Controller / @RestController — 控制层
// @RestController = @Controller + @ResponseBody(直接返回JSON)
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
// @Autowired — 自动注入(推荐构造器注入)
private final UserService userService;
// Spring 4.3+,单构造器可省略@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping // @GetMapping 映射HTTP GET请求到此方法
public List<UserDTO> getAllUsers() {
return userService.findAll();
}
}
// @Service — 业务层
@Service // @Service 标注业务层组件,Spring自动扫描注册为Bean
public class UserService {
private final UserRepository userRepo;
public UserService(UserRepository userRepo) {
this.userRepo = userRepo;
}
public List<UserDTO> findAll() {
return userRepo.findAll().stream()
.map(this::toDTO)
.toList();
}
private UserDTO toDTO(User user) {
return new UserDTO(user.getId(), user.getName(), user.getEmail());
}
}
// @Repository — 持久层(自动转换数据库异常为Spring DataAccessException)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
@Query("SELECT u FROM User u WHERE u.status = :status")
List<User> findByStatus(@Param("status") String status);
}
2.3 其他常用注解¶
// @Value — 注入配置值
@Value("${app.name:DefaultApp}")
private String appName;
// @Autowired + @Qualifier — 指定注入哪个Bean(存在多个同类型Bean时)
@Autowired // @Autowired 自动注入Spring容器中的Bean
@Qualifier("mysqlDataSource")
private DataSource dataSource;
// @PostConstruct / @PreDestroy — 生命周期回调
@Component
public class StartupRunner {
@PostConstruct
public void init() {
System.out.println("应用启动后执行初始化逻辑");
}
}
// @Scope — 控制Bean作用域
@Scope("prototype") // 每次获取创建新实例(默认singleton)
@Component
public class PrototypeBean { }
// @Lazy — 延迟初始化
@Lazy
@Service
public class HeavyService { }
3. RESTful API开发¶
3.1 标准CRUD接口¶
@RestController
@RequestMapping("/api/v1/users")
@Validated
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// GET /api/v1/users — 查询全部
@GetMapping
public ResponseEntity<List<UserDTO>> list(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Page<UserDTO> result = userService.findAll(PageRequest.of(page, size));
return ResponseEntity.ok(result.getContent());
}
// GET /api/v1/users/{id} — 按ID查询
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getById(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// POST /api/v1/users — 新增
@PostMapping
public ResponseEntity<UserDTO> create(@RequestBody @Valid CreateUserRequest req) {
UserDTO created = userService.create(req);
URI location = URI.create("/api/v1/users/" + created.getId());
return ResponseEntity.created(location).body(created);
}
// PUT /api/v1/users/{id} — 更新
@PutMapping("/{id}")
public ResponseEntity<UserDTO> update(
@PathVariable Long id,
@RequestBody @Valid UpdateUserRequest req) {
return ResponseEntity.ok(userService.update(id, req));
}
// DELETE /api/v1/users/{id} — 删除
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}
3.2 参数校验(JSR 380 / Hibernate Validator)¶
// DTO + 校验注解
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 50, message = "用户名长度2-50")
private String name;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@NotNull(message = "年龄不能为空")
@Min(value = 1, message = "年龄最小为1")
@Max(value = 150, message = "年龄最大为150")
private Integer age;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
// getter/setter 省略
}
// 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(404, ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
ErrorResponse error = new ErrorResponse(500, "服务器内部错误");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
3.3 统一响应格式¶
// 统一返回结构
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> { // <T> 泛型参数,使类支持任意数据类型
private int code;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data);
}
public static <T> ApiResponse<T> error(int code, String message) {
return new ApiResponse<>(code, message, null);
}
}
// 使用示例
@GetMapping("/{id}")
public ApiResponse<UserDTO> getById(@PathVariable Long id) {
return ApiResponse.success(userService.findById(id));
}
4. 数据持久层¶
4.1 Spring Data JPA¶
// 实体类
@Entity
@Table(name = "t_user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String name;
@Column(nullable = false, unique = true, length = 100)
private String email;
@Enumerated(EnumType.STRING)
private UserStatus status;
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
}
// Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
List<User> findByStatusOrderByCreatedAtDesc(UserStatus status);
@Query("SELECT u FROM User u WHERE u.name LIKE %:keyword% OR u.email LIKE %:keyword%")
Page<User> search(@Param("keyword") String keyword, Pageable pageable);
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateStatus(@Param("id") Long id, @Param("status") UserStatus status);
}
4.2 MyBatis整合¶
// application.yml
// mybatis:
// mapper-locations: classpath:mapper/*.xml
// type-aliases-package: com.example.entity
// configuration:
// map-underscore-to-camel-case: true
// Mapper接口
@Mapper
public interface UserMapper {
@Select("SELECT * FROM t_user WHERE id = #{id}")
User findById(Long id);
@Insert("INSERT INTO t_user(name, email, status) VALUES(#{name}, #{email}, #{status})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
// 复杂SQL用XML
List<User> searchByCondition(UserQueryParam param);
}
// mapper/UserMapper.xml
// <mapper namespace="com.example.mapper.UserMapper">
// <select id="searchByCondition" resultType="User">
// SELECT * FROM t_user
// <where>
// <if test="name != null and name != ''">
// AND name LIKE CONCAT('%', #{name}, '%')
// </if>
// <if test="status != null">
// AND status = #{status}
// </if>
// </where>
// ORDER BY created_at DESC
// </select>
// </mapper>
4.3 事务管理¶
@Service
public class OrderService {
private final OrderRepository orderRepo;
private final InventoryService inventoryService;
public OrderService(OrderRepository orderRepo, InventoryService inventoryService) {
this.orderRepo = orderRepo;
this.inventoryService = inventoryService;
}
// @Transactional 保证方法内所有数据库操作在同一事务中
@Transactional(rollbackFor = Exception.class) // 所有异常都回滚
public Order createOrder(CreateOrderRequest req) {
// 1. 扣减库存
inventoryService.deductStock(req.getProductId(), req.getQuantity());
// 2. 创建订单
Order order = new Order();
order.setUserId(req.getUserId());
order.setProductId(req.getProductId());
order.setAmount(req.getAmount());
order.setStatus(OrderStatus.CREATED);
return orderRepo.save(order);
// 如果中间任何一步抛异常,整体回滚
}
// 事务传播行为
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOperation(String operation) {
// 即使外层事务回滚,日志记录也会独立提交
}
}
@Transactional 常见坑: - 默认只回滚RuntimeException,建议加rollbackFor = Exception.class - 同类内部方法调用不走代理,事务注解无效(需注入自身或用AopContext) - private方法上的注解无效
5. AOP面向切面编程¶
// 日志切面示例
@Aspect
@Component
@Slf4j
public class LoggingAspect {
// 切点:匹配controller包下所有方法
@Pointcut("execution(* com.example.controller..*.*(..))")
public void controllerMethods() {}
// 环绕通知
@Around("controllerMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
Object[] args = joinPoint.getArgs();
log.info(">>> 调用方法: {} | 参数: {}", methodName, Arrays.toString(args));
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long elapsed = System.currentTimeMillis() - startTime;
log.info("<<< 方法返回: {} | 耗时: {}ms", methodName, elapsed);
return result;
} catch (Exception ex) {
log.error("!!! 方法异常: {} | 异常: {}", methodName, ex.getMessage());
throw ex;
}
}
}
// 自定义注解 + AOP实现权限校验
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireRole {
String value(); // 所需角色
}
@Aspect
@Component
public class AuthAspect {
@Before("@annotation(requireRole)")
public void checkRole(JoinPoint joinPoint, RequireRole requireRole) {
String requiredRole = requireRole.value();
// 从SecurityContext获取当前用户角色
String currentRole = SecurityContextHolder.getContext()
.getAuthentication().getAuthorities().toString();
if (!currentRole.contains(requiredRole)) {
throw new AccessDeniedException("需要角色: " + requiredRole);
}
}
}
// 使用
@RequireRole("ADMIN")
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
6. Spring Security + JWT认证¶
6.1 JWT工具类¶
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration:86400000}") // 默认24小时
private long expiration;
// 生成Token
public String generateToken(String username, List<String> roles) {
return Jwts.builder()
.subject(username)
.claim("roles", roles)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + expiration))
.signWith(Keys.hmacShaKeyFor(secret.getBytes()))
.compact();
}
// 解析Token
public Claims parseToken(String token) {
return Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(secret.getBytes()))
.build()
.parseSignedClaims(token)
.getPayload();
}
// 验证Token
public boolean validateToken(String token) {
try {
parseToken(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
public String getUsernameFromToken(String token) {
return parseToken(token).getSubject();
}
}
6.2 Security配置¶
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtAuthenticationFilter jwtFilter;
public SecurityConfig(JwtAuthenticationFilter jwtFilter) {
this.jwtFilter = jwtFilter;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll()
)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
// JWT过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
public JwtAuthenticationFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
if (jwtUtil.validateToken(token)) {
String username = jwtUtil.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
}
7. 配置管理¶
7.1 多环境配置¶
# application.yml — 公共配置
spring:
profiles:
active: dev # 默认开发环境
application:
name: my-app
server:
port: 8080
# application-dev.yml — 开发环境
spring:
datasource:
url: jdbc:mysql://localhost:3306/myapp_dev
username: root
password: root123
jpa:
show-sql: true
hibernate:
ddl-auto: update
logging:
level:
com.example: DEBUG
# application-prod.yml — 生产环境
spring:
datasource:
url: jdbc:mysql://prod-db:3306/myapp
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
show-sql: false
hibernate:
ddl-auto: none
logging:
level:
com.example: WARN
7.2 类型安全配置¶
@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
private String name;
private String version;
private Security security = new Security();
private List<String> allowedOrigins = new ArrayList<>();
@Data
public static class Security {
private String jwtSecret;
private long jwtExpiration = 86400000;
private int maxLoginAttempts = 5;
}
}
// application.yml
// app:
// name: MyApplication
// version: 1.0.0
// security:
// jwt-secret: my-secret-key-at-least-32-characters
// jwt-expiration: 86400000
// allowed-origins:
// - http://localhost:3000
// - https://example.com
// 使用
@RestController
public class InfoController {
private final AppProperties appProperties;
public InfoController(AppProperties appProperties) {
this.appProperties = appProperties;
}
@GetMapping("/info")
public Map<String, String> info() {
return Map.of(
"name", appProperties.getName(),
"version", appProperties.getVersion()
);
}
}
8. 缓存集成(Redis)¶
// 引入依赖: spring-boot-starter-data-redis
// application.yml:
// spring:
// data:
// redis:
// host: localhost
// port: 6379
// password:
// lettuce:
// pool:
// max-active: 8
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
@Service
public class UserService {
private final UserRepository userRepo;
public UserService(UserRepository userRepo) {
this.userRepo = userRepo;
}
// 查询时缓存,key为 "user::1"
@Cacheable(value = "user", key = "#id")
public UserDTO findById(Long id) {
return userRepo.findById(id)
.map(this::toDTO)
.orElseThrow(() -> new ResourceNotFoundException("用户不存在: " + id));
}
// 更新时清除缓存
@CacheEvict(value = "user", key = "#id")
@Transactional
public UserDTO update(Long id, UpdateUserRequest req) {
User user = userRepo.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("用户不存在: " + id));
user.setName(req.getName());
user.setEmail(req.getEmail());
return toDTO(userRepo.save(user));
}
// 更新时同时更新缓存
@CachePut(value = "user", key = "#result.id")
@Transactional
public UserDTO create(CreateUserRequest req) {
User user = new User();
user.setName(req.getName());
user.setEmail(req.getEmail());
return toDTO(userRepo.save(user));
}
private UserDTO toDTO(User user) {
return new UserDTO(user.getId(), user.getName(), user.getEmail());
}
}
9. 消息队列集成¶
9.1 RabbitMQ¶
// 引入: spring-boot-starter-amqp
// application.yml:
// spring:
// rabbitmq:
// host: localhost
// port: 5672
// username: guest
// password: guest
@Configuration
public class RabbitMQConfig {
public static final String ORDER_QUEUE = "order.queue";
public static final String ORDER_EXCHANGE = "order.exchange";
public static final String ORDER_ROUTING_KEY = "order.create";
@Bean
public Queue orderQueue() {
return QueueBuilder.durable(ORDER_QUEUE).build();
}
@Bean
public DirectExchange orderExchange() {
return new DirectExchange(ORDER_EXCHANGE);
}
@Bean
public Binding binding(Queue orderQueue, DirectExchange orderExchange) {
return BindingBuilder.bind(orderQueue).to(orderExchange).with(ORDER_ROUTING_KEY);
}
}
// 生产者
@Service
public class OrderProducer {
private final RabbitTemplate rabbitTemplate;
public OrderProducer(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void sendOrderMessage(OrderMessage message) {
rabbitTemplate.convertAndSend(
RabbitMQConfig.ORDER_EXCHANGE,
RabbitMQConfig.ORDER_ROUTING_KEY,
message
);
}
}
// 消费者
@Component
@Slf4j
public class OrderConsumer {
@RabbitListener(queues = RabbitMQConfig.ORDER_QUEUE)
public void handleOrder(OrderMessage message) {
log.info("收到订单消息: {}", message);
// 处理订单逻辑...
}
}
9.2 Kafka¶
// 引入: spring-kafka
// application.yml:
// spring:
// kafka:
// bootstrap-servers: localhost:9092
// consumer:
// group-id: my-group
// auto-offset-reset: earliest
// 生产者
@Service
public class KafkaProducerService {
private final KafkaTemplate<String, String> kafkaTemplate;
public KafkaProducerService(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void send(String topic, String key, String message) {
kafkaTemplate.send(topic, key, message)
.whenComplete((result, ex) -> {
if (ex == null) {
System.out.println("发送成功: offset=" + result.getRecordMetadata().offset());
} else {
System.err.println("发送失败: " + ex.getMessage());
}
});
}
}
// 消费者
@Component
@Slf4j
public class KafkaConsumerService {
@KafkaListener(topics = "order-topic", groupId = "order-group")
public void listen(ConsumerRecord<String, String> record) {
log.info("Kafka消费: topic={}, key={}, value={}, offset={}",
record.topic(), record.key(), record.value(), record.offset());
}
}
10. 微服务入门(Spring Cloud)¶
10.1 核心组件概览¶
| 组件 | 功能 | 主流方案 |
|---|---|---|
| 服务注册发现 | 服务地址管理 | Nacos / Consul / Eureka |
| 负载均衡 | 请求分发 | Spring Cloud LoadBalancer |
| API网关 | 统一入口 | Spring Cloud Gateway |
| 配置中心 | 集中配置管理 | Nacos Config / Spring Cloud Config |
| 服务熔断 | 容错保护 | Sentinel / Resilience4j |
| 分布式链路追踪 | 调用链跟踪 | Micrometer Tracing / Zipkin |
10.2 Nacos服务注册发现¶
// 引入: spring-cloud-starter-alibaba-nacos-discovery
// application.yml
// spring:
// cloud:
// nacos:
// discovery:
// server-addr: localhost:8848
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
// 服务调用(使用OpenFeign)
@FeignClient(name = "order-service")
public interface OrderClient {
@GetMapping("/api/orders/user/{userId}")
List<OrderDTO> getOrdersByUserId(@PathVariable("userId") Long userId);
}
10.3 Spring Cloud Gateway¶
# gateway-service application.yml
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service # lb表示负载均衡
predicates:
- Path=/api/users/**
filters:
- StripPrefix=0
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
11. Docker容器化部署¶
11.1 Dockerfile¶
# 多阶段构建
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
# JVM参数优化
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
11.2 Docker Compose¶
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_USERNAME=root
- DB_PASSWORD=secret
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: myapp
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
mysql-data:
构建运行:
# 构建并启动所有服务
docker-compose up -d --build
# 查看日志
docker-compose logs -f app
# 停止
docker-compose down
✏️ 练习¶
- 初级:创建一个Spring Boot项目,实现对
Book实体的完整CRUD RESTful API,使用Spring Data JPA + H2内存数据库 - 中级:在上述项目中加入参数校验、全局异常处理、统一响应格式;集成Spring Security + JWT认证
- 高级:将应用拆分为user-service和book-service两个微服务,使用Nacos注册发现 + OpenFeign调用 + Spring Cloud Gateway网关;编写Dockerfile并使用Docker Compose编排部署
📋 面试高频考点¶
Spring Boot基础¶
- Spring Boot自动配置原理? —
@EnableAutoConfiguration→spring.factories/AutoConfiguration.imports→ 条件注解(@ConditionalOnClass等)决定是否生效 - Spring Boot启动流程? —
SpringApplication.run()→ 创建ApplicationContext → 刷新容器(解析配置类 → 注册Bean → 实例化) → 执行Runner - @SpringBootApplication包含什么? —
@Configuration+@EnableAutoConfiguration+@ComponentScan
Spring核心¶
- Spring IOC和DI的区别? — IOC是控制反转思想(容器管理对象生命周期),DI是依赖注入实现方式
- Bean的作用域? — singleton(默认)、prototype、request、session、application
- Bean的生命周期? — 实例化 → 属性填充 → Aware接口 → BeanPostProcessor前 → InitializingBean/init-method → BeanPostProcessor后 → 使用 → DisposableBean/destroy-method
- @Transactional失效场景? — 同类方法内部调用、非public方法、异常被吞、使用错误的传播行为
安全与架构¶
- JWT vs Session认证? — JWT无状态、适合分布式;Session有状态、服务端存储
- Spring Cloud核心组件? — 注册中心、配置中心、网关、负载均衡、熔断器、链路追踪
- 微服务拆分原则? — 单一职责、高内聚低耦合、按业务域拆分(DDD)
📌 下一步学习:13-JVM深入理解 — 掌握Java虚拟机底层原理