前言 现在的 Web 应用大都是读多写少。除了缓存以外还可以通过数据库 “主从复制” 架构,把读请求路由到从数据库节点上,实现读写分离,从而大大提高应用的吞吐量。
通常,我们在 Spring Boot 中只会用到一个数据源,即通过 spring.datasource 进行配置。前文 《在 Spring Boot 中配置和使用多个数据源》 介绍了一种在 Spring Boot 中定义、使用多个数据源的方式。但是这种方式对于实现 “读写分离” 的场景不太适合。首先,多个数据源都是通过 @Bean 定义的,当需要新增额外的从数据库时需要改动代码,非常不够灵活。其次,在业务层中,如果需要根据读、写场景切换不同数据源的话只能手动进行。
对于 Spring Boot “读写分离” 架构下的的多数据源,我们需要实现如下需求:
可以通过配置文件新增数据库(从库),而不不需要修改代码。 自动根据场景切换读、写数据源,对业务层是透明的。 幸运的是,Spring Jdbc 模块类提供了一个 AbstractRoutingDataSource 抽象类可以实现我们的需求。
它本身也实现了 DataSource 接口,表示一个 “可路由” 的数据源。
核心的代码如下:
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean { // 维护的所有数据源 @Nullable private Map<Object, DataSource> resolvedDataSources; // 默认的数据源 @Nullable private DataSource resolvedDefaultDataSource; // 获取 Jdbc 连接 @Override public Connection getConnection() throws SQLException { return determineTargetDataSource().
1、概览 跨站脚本攻击(Cross-Site Scripting,XSS)一直稳居最常见的 十大网络攻击 之列。XSS 攻击发生在 Web 服务器处理用户恶意输入时,未经验证或编码即在页面上渲染。与 XSS 攻击类似,代码注入和点击劫持通过窃取用户数据和冒充用户身份来对 Web 应用造成严重影响。
本文将会带你了解如何使用 Spring Security 通过内容安全策略(Content-Security-Policy)保护 Web 应用免受点击劫持、代码注入和 XSS 攻击。
2、Content Security Policy 内容安全策略(Content Security Policy,简称 CSP)是一种 HTTP 响应头,可大大减少 现代浏览器 中的代码注入攻击,如 XSS、点击劫持 等。
Web 服务器通过 Content-Security-Policy Header 部指定了浏览器可以渲染的资源的列表。这些资源可以是浏览器渲染的任何内容,例如 CSS、JavaScript、图像等。
该 Header 的语法如下:
Content-Security-Policy: <directive>; <directive>; <directive> ; ... 此外,还可以将此策略设置为 HTML 页面中 <meta> 标签的一部分:
<meta http-equiv="Content-Security-Policy" content="<directive>;<directive>;<directive>; ..."> 每个 指令 都包含一个具有多个值的 key。指令可以不止一个,每个指令之间用分号(;) 分隔:
Content-Security-Policy: script-src 'self' https://baeldung.com; style-src 'self'; 如上例所示,有两个指令(script-src 和 style-src),而指令 script-src 有两个值(self 和 https://baeldung.
1、简介 随着微服务架构越来越流行,在不同服务器上运行多个服务变得越来越普遍。本文将带你了解如何使用 Spring Cloud Load Balancer(负载均衡器) 创建容错性更强的应用。
2、负载均衡是什么? 负载均衡是在同一应用的不同实例之间分配流量的过程。
为了容错,每个应用通常都要运行多个实例。因此,当一个服务需要与另一个服务通信时,它需要选择一个特定的实例来发送请求。
负载均衡,有很多算法:
随机选择:随机选择一个实例 循环:每次按相同顺序选择实例 最少连接:选择当前连接最少的实例 权重指标:使用权重指标选择最佳实例(例如 CPU 或内存使用率) IP 哈希(Hash):使用客户端 IP 的哈希值映射到实例 以上只是负载均衡算法的几个例子,每种算法都有其优缺点。
随机选择和轮循很容易实现,但可能无法优化服务的使用。相反,最少连接和权重指标比较复杂,但通常能创造更优化的服务利用率。IP 哈希可以保证客户端每次都命中同一台实例,意味着实例可以保存一些客户端的状态信息,但它的容错性不强。
3、Spring Cloud Load Balancer 简介 Spring Cloud Load Balancer 用来创建以负载均衡方式与其他应用通信的应用。可以使用任意算法,在进行远程服务调用时轻松实现负载均衡。
接下来,我们通过实例来进行说明。首先,创建一个简单的服务器应用。服务器只有一个 HTTP 端点,可以作为多个实例运行。
然后,创建一个客户端应用,使用 Spring Cloud Load Balancer 在服务器的不同实例之间轮流发送请求。
3.1、示例服务器 创建一个简单的 Spring Boot 应用:
@SpringBootApplication @RestController public class ServerApplication { public static void main(String[] args) { SpringApplication.run(ServerApplication.class, args); } @Value("${server.instance.id}") String instanceId; @GetMapping("/hello") public String hello() { return String.
1、概览 在 Spring Boot 应用中,可以同时包含基于注解和基于 XML 的配置来混合定义 Bean。在这种环境中,如果你在测试类中使用基于 XML 的配置可能会遇到 “Failed to load ApplicationContext” 异常。因为 Application Context 没有加载到 Test Context 中。
本文将会带你了解如何把 XML Application Context 集成到 Spring Boot 应用的测试中。
2、“Failed to load ApplicationContext” 异常 在 Spring Boot 应用中集成基于 XML 的 Application Context 来重现该异常。
首先,假设有一个包含 Service Bean 定义的 application-context.xml 文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="employeeServiceImpl" class="com.baeldung.xmlapplicationcontext.service.EmployeeServiceImpl" /> </beans> 在 webapp/WEB-INF/ 位置添加 application-context.xml 文件:
再创建一个 Service 接口和实现类:
1、概览 Spring Cloud Gateway 是一个响应式的轻量级网关,是 Spring Cloud 体系中一个比较重要的组件。本文将带你了解如何在其基础上快速实现 OAuth 2.0 认证、授权。
2、OAuth 2.0 快速回顾 OAuth 2.0 标准是一个成熟的标准,在互联网上广泛使用,是用户和应用安全访问资源的一种安全机制。
其中涉及的关键术语如下:
Resource(资源):只有经过授权的客户端才能检索的任何类型的信息。 Client(客户端):消费资源的应用,通常通过 REST API 消费资源。 Resource Server(资源服务器):负责向授权客户端提供资源的服务。 Resource Owner(资源所有者):实体(人或应用),拥有资源,并最终负责向客户端授予对该资源的访问权限。 Token(令牌):客户端获取的一段信息,并作为请求的一部分发送给资源服务器以进行身份验证。 Identity Provider(身份提供商,即 IDP):验证用户凭证并向客户端发放 Access Token。 Authentication Flow(认证模式/流程):客户端获得有效 Token 必须经过的一系列步骤。 你可以通过 Auth0 的相关文档了解更多详细内容。
3、OAuth 2.0 模式 Spring Cloud Gateway 主要用于以下用途之一:
OAuth Client(客户端) OAuth Resource Server(资源服务器) 我们来逐个了解。
3.1、Spring Cloud Gateway 作为 OAuth 2.0 客户端 在这种情况下,任何未经身份认证的传入请求都将触发授权码流程。一旦网关获取到 Token,它将在向后端服务发起请求时使用该 Token。
在实际应用中,一个很好的例子是聚合了 “社交应用” 的应用:对于每个支持的社交应用,网关将充当 OAuth 2.0 客户端。
因此,前端(通常是使用 Angular、React 或类似 UI 框架构建的 SPA 应用)可以代表终端户无缝访问这些应用上的数据。更重要的是:用户无需暴露自己的凭证。
1、概览 本文将带你了解如何 Swagger 文档界面中隐藏 BasicErrorController。
3、问题 如果应用中包含了一个 BasicErrorController,Swagger 默认会将其所有端点也包含在生成的文档中。
我们需要提供自定义配置来移除不需要的 Controller。
例如,项目中的 Rest Controller 如下:
@RestController @RequestMapping("good-path") public class RegularRestController { @ApiOperation(value = "This method is used to get the author name.") @GetMapping("/getAuthor") public String getAuthor() { return "Name Surname"; } } 另外,还包含一个继承 BasicErrorController 的 Error Controller:
@Component @RequestMapping("my-error-controller") public class MyErrorController extends BasicErrorController { // basic constructor } 启动应用,访问文档!你可以看到,my-error-controller 包含在生成的文档中:
4、解决办法 有四种方式可以从 Swagger 文档中排除资源。
4.1、通过 basePackage() 方法排除 通过指定 Controller 所在的包,可以排除其他不需要的资源。
1、概览 本文将带你了解 Spring Cloud Sleuth,以及如何在 Spring Boot 中使用它进行链路追踪。
它可以在日志中添加额外有用的信息,并通过唯一链路 ID 帮助 Debug。这些操作在 Sleuth 术语中称为追踪(Trace)。它们可以由多个步骤组成,称为 Span。
例如,链路追踪可以是一个从应用中查询数据的 GET 请求。当应用处理该请求时,可以将其分割成更小的步骤:用户授权、执行数据库查询、转换响应。每个步骤都是属于同一链路追踪的唯一 Span。
在某些情况下,我们可能需要获取当前 Trace 或 Span 的 ID。例如,当发生事故时,我们可以将这些信息发送给开发团队。然后,他们就可以用它来调试和解决问题。
2、应用设置 创建一个 Spring Boot 项目,并添加 spring-cloud-starter-sleuth dependency 依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> <version>3.1.0</version> </dependency> 此 Starter 可与 Spring Boot 完美集成,并提供 Spring Cloud Sleuth 开箱即用的配置。
接下来,在 application.properties 文件中设置应用名称,这样就能在日志中看到该名称以及 Trace 和 Span ID:
spring.application.name=Baeldung Sleuth Tutorial 现在,创建一个 REST Controller,提供一个 GET 端点:
@RestController public class SleuthTraceIdController { @GetMapping("/traceid") public String getSleuthTraceId() { return "Hello from Sleuth"; } } 启动应用后访问这个 API 端点 http://localhost:8080/traceid,你可以看到响应:Hello from Sleuth。
1、概览 Zuul 是 Netflix 推出的基于 JVM 的网关和服务器端负载均衡器。Zuul 的规则引擎提供了灵活性,可以编写规则和过滤器(Filter)来增强在 Spring Cloud 微服务架构中的路由功能。。
本文将会带你了解如何通过自定义 Error Filter 来自定义 Zuul 中的异常和 Error 响应。当代码执行过程中发生错误时,这些自定义 Error Filter 将被执行。
2、Zuul 异常 Zuul 中处理的所有异常都是 ZuulException。
ZuulException 不能通过 @ControllerAdvice 捕获,也不能通过 @ExceptionHandling 对方法进行注解。这是因为 ZuulException 是由 Error Filter 抛出的。因此,它会跳过后续的过滤链,永远不会到达 Error Controller。下图描述了 Zuul 中错误处理的层次结构:
出现 ZuulException 时,Zuul 会显示以下错误响应:
{ "timestamp": "2022-01-23T22:43:43.126+00:00", "status": 500, "error": "Internal Server Error" } 在某些情况下,可能需要自定义 ZuulException 响应中的错误信息或状态码。这时,Zuul Filter 就能派上用场了。
3、自定义 Zuul 异常 spring-cloud-starter-netflix-zuul Starter 包括三种 Filter:Pre(前置)、Post(后置)和 Error(错误)。
本文重点深入了解 Error Filter。
1、概览 本文将带你了解 “HttpMessageNotWritableException: no converter for [class …] with preset Content-Type” 异常的原因以及解决办法。
2、原因 异常的堆栈信息说明了一切:Spring 找不到合适的 HttpMessageConverter,无法将 Java 对象转换为 HTTP 响应。
Spring 依靠客户端的 Accept Header 来检测它需要响应的媒体类型(Media Type)。
因此,使用未预注册消息转换器(Message Converter)的 Media Type 将导致 Spring 抛出异常。
3、重现异常 通过一个实际例子来重现异常。
创建一个 Handler Method,指定一种媒体 Media Type(用于响应),这种类型没有注册 HttpMessageConverter。
例如,使用 MediaType.APPLICATION_XML_VALUE 或字符串 application/xml:
@GetMapping(value = "/student/v3/{id}", produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity<Student> getV3(@PathVariable("id") int id) { return ResponseEntity.ok(new Student(id, "Robert", "Miller", "BB")); } 接着,使用 cURL 对 http://localhost:8080/api/student/v3/1 发起请求:
curl http://localhost:8080/api/student/v3/1 端点响应如下:
1、概览 一般的 Spring Boot 应用通常只需要配置一个数据库,但是有时也可能需要访问多个数据源。本文将带你了解如何在 Spring Boot 中配置和使用多个数据源。
2、默认行为 通常,我们会在 application.yml 中声明 Spring Boot 数据源,如下:
spring: datasource: url: ... username: ... password: ... driverClassname: ... Spring 会将这些配置映射到 org.springframework.boot.autoconfigure.jdbc.DataSourceProperties 的实例。
DataSourceProperties 的定义如下:
@ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean { // ... /** * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default. */ private String driverClassName; /** * JDBC URL of the database. */ private String url; /** * Login username of the database.