环境:SpringBoot3.4.2
1. 简介
Spring Boot的简洁性常常掩盖了它处理复杂企业需求的强大能力。在本文中,我们将简要介绍7个高级Spring Boot主题,并通过简短的解释和示例来巩固基本理解。
接下来,我们将分别介绍上面的7个高级核心知识点。
2. 实战案例
2.1 使用Resilience4j实现弹性微服务
微服务需要优雅地处理瞬时故障。Resilience4j与Spring Boot集成,提供了断路器(Circuit Breaker)、重试(Retry)和速率限制器(Rate Limiter)等模式。
@Resource
@LoadBalanced
private RestTemplate restTemplate ;
@CircuitBreaker(name = "userService", fallbackMethod = "fallback")
publicString getUserDetails(String userId) {
return restTemplate.getForObject("http://user-service/users/" + userId, String.class);
}
public String fallback(String userId, Throwable ex) {
return String.format("失败获取用户【%s】, %s", userId, ex.getMessage()) ;
}
实现步骤:
- 引入依赖
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency>
- 配置阈值等信息
resilience4j:
circuitbreaker:
instances:
userService:
failure-rate-threshold: 60
sliding-window-type: count-based
sliding-window-size: 3
minimum-number-of-calls: 3
wait-duration-in-open-state: 10000
这里的userService就是我们在方法上@CircuitBreaker注解中的name属性值。
更多关于resilience4j教程,请查看下面文章
保障系统稳定,提升接口安全性
推荐学习!拆解Spring Cloud 4大核心组件,彻底搞懂工作原理(断路器Resilience4J)
2.2 使用Actuator自定义监控
Spring Boot Actuator暴露了用于指标、健康检查等功能的端点。通过自定义Actuator,你可以监控特定于应用程序的指标。
@Component
publicclassPackHealthIndicatorimplementsHealthIndicator{
@Override
public Health health(){
boolean isHealthy = checkCustomHealth() ;
return isHealthy ? Health.up().build() : Health.down().withDetail("Error", "Custom check failed").build() ;
}
privatebooleancheckCustomHealth(){
// 自定义健康检查逻辑
return true ;
}
}
如上定义后,输出如下:
实现步骤:
- 定义bean实现HealthIndicator
- 实现自定义健康状态的逻辑
- 访问/actuator/health(我上面改写了路径/ac)
2.3 分布式事务处理
跨多个服务管理事务可能颇具挑战性。可以使用诸如2PC/3PC,TCC,Saga等之类的模式来实现,或者使用Java事务API(JTA)来实现原子性。
使用Spring Boot和Kafka的Saga模式:
订单服务order-service
@KafkaListener(topics = "order-events")
public void processOrder(OrderEvent event) {
if ("ORDER_CREATED".equals(event.getStatus())) {
// 处理支付及库存
}
}
支付服务payment-service
根据处理结果发布成功或失败事件。
关键点:
- Saga通过事件驱动的工作流确保最终一致性。
- 像Axon这样的框架或编排工具可以简化实现过程。
有关分布式事务其它方案的实现,请查看下面文章:
实战案例SpringBoot整合Seata AT模式实现分布式事务【超详细】
2.4 使用Cache优化性能
缓存可以减少数据库的访问,从而减轻数据库的压力。
@Cacheable(value = "users", key = "#userId")
public User getUserById(String userId) {
return userRepository.findById(userId).orElseThrow(() -> new UserNotFoundException(userId));
}
实现步骤:
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 如果你要使用基于redis的缓存,那么引入下面的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 使用相关注解
通过在方法上使用@Cacheable,@CacheEvict或@CachePut注解。
2.5 简化异步编程
Spring Boot的@Async注解使得异步编程变得直接明了。
@Service
public class EmailService{
@Async
public void sendEmail(String email, String message) {
// 模拟耗时
Thread.sleep(3000) ;
System.out.printf("发送邮件到: %s%n", email);
}
}
// 开启异步任务支持
@EnableAsync
@Configuration
public class AsyncConfig{}
通过异步任务能明显加快接口API的响应速度。
注意:@EnableAsync是开启异步功能的关键,不要忘了。
有关异步任务的更多知识,请查看下面文章:
你用错了!详解SpringBoot异步任务&任务调度&异步请求线程池的使用及原理
SpringBoot异步任务@Async你真的会用吗?这6点细节你知道吗?
2.6 Gateway vs. Zuul网关技术
Spring Cloud Gateway是一个响应式API网关,它替代了Netflix Zuul,以提供更好的性能和灵活性。
Gateway关键特性:
- 路由和过滤
- 与Resilience4j集成的断路器
- 支持WebSocket和负载均衡
编程方式配置路由:
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
return builder.routes()
.route("user_route", r -> r.path("/users/**")
.uri("http://user-service"))
.build() ;
}
通过编程的方式可以动态的配置路由。
2.7 通过OAuth2或JWT保护应用程序
OAuth2和JWT通常用于保护API的安全性。
生成Token
public String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setExpiration(newDate(System.currentTimeMillis() + 1000 * 60 * 60))
.signWith(SignatureAlgorithm.HS512, "xxxooo")
.compact();
}
验证Token
public boolean validateToken(String token) {
Claims claims = Jwts.parser().setSigningKey("xxxooo")
.parseClaimsJws(token).getBody() ;
return claims.getExpiration().after(newDate()) ;
}
添加验证过滤器
public class AuthenticationFilter extends GenericFilterBean{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
try {
// TODO
// 验证Token,最终生成Authentication对象
Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request) ;
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception exp) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
PrintWriter writer = httpResponse.getWriter();
writer.print(exp.getMessage());
writer.flush();
writer.close();
return ;
}
filterChain.doFilter(request, response);
}
}
安全规则配置
@Bean
SecurityFilterChain filterChain(HttpSecurity http)throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(registry -> registry.requestMatchers("/**").authenticated())
// 可有可无,该配置注册BasicAuthenticationFilter过滤器读取header中的Authorization header数据,值以Basic 开头
.httpBasic(Customizer.withDefaults())
.sessionManagement(hssmc -> hssmc.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 这里添加的优先级是高于上面的BasicAuthenticationFilter
.addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) ;
return http.build();
}
以上就完成了简单的基于JWT的接口安全验证。