1、概览 本文将带你了解 Spring 中一种特殊类型的 Filter(过滤器)OncePerRequestFilter。
通过实例了解它的功能和用法。
2、OncePerRequestFilter 是什么? 回顾一下 Filter 的工作原理。Filter 可以在 Servlet 执行之前或之后调用。当请求被调度给一个 Servlet 时,RequestDispatcher 可能会将其转发给另一个 Servlet。另一个 Servlet 也有可能使用相同的 Filter。在这种情况下,同一个 Filter 会被调用多次。
但是,有时需要确保每个请求只调用一次特定的 Filter。一个常见的用例是在使用 Spring Security 时。当请求通过过滤器链(Filter Chain)时,对请求的身份证认证应该只执行一次。
在这种情况下,可以继承 OncePerRequestFilter。Spring 保证 OncePerRequestFilter 只对指定请求执行一次。
3、使用 OncePerRequestFilter 处理同步请求 定义一个继承了 OncePerRequestFilter 的 AuthenticationFilter Filter 类,并覆写 doFilterInternal() 方法:
public class AuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String usrName = request.getHeader(“userName”); logger.info("Successfully authenticated user " + userName); filterChain.
1、概览 本文将带你了解如何解决在 Spring Boot API 返回状态码 “415 Unsupported MediaType” 的问题,以及出现该问题的原因。
2、背景 我们一个老客户要求我们给他的系统再开发一个桌面应用,类似于用户管理。新的桌面应用,直接调用原系统的 API 服务。
3、API 请求 通过 API 来获取所有用户:
curl -X GET https://baeldung.service.com/user 成功获取响应。接着,获取一个单独的用户:
curl -X GET https://baeldung.service.com/user/{user-id} 响应如下:
{ "id": 1, "name": "Jason", "age": 23, "address": "14th Street" } 一切也OK,根据响应,可以确定用户拥有以下参数:id、name、age 和 address。
现在,尝试添加新用户:
curl -X POST -d '{"name":"Abdullah", "age":28, "address":"Apartment 2201"}' https://baeldung.service.com/user/ 结果,这次服务器响应了 HTTP 状态码为 415 的错误响应:
{ "timestamp": "yyyy-MM-ddThh:mm:ss.SSS+00:00", "status": 415, "error": "Unsupported Media Type", "path": "/user/" } 在弄清 “为什么会出现这个错误?” 之前,需要先弄清楚 “这个错误是什么?”。
在前后端分离架构、SPA 应用大行其道的今天,模板引擎已经逐渐被淘汰了。更别提 JSP 这种上古模板引擎了。
Spring Boot 推荐使用 FreeMarker、Groovy、Thymeleaf 或者 Mustache 作为模板引擎。不推荐 JSP,主要是 JSP 的编译方式比价特殊,它需要先把 JSP 代码编译为 Servlet,最后通过执行 Servlet 来输出模板内容。
当然,在 Spring Boot 中使用 JSP 也是可以的,只需要些许配置即可。
创建 Spring Boot 项目 创建 Spring Boot(3.0.3)项目,在 pom.xml 中添加如下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Servlet --> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> </dependency> <!-- Tomcat 嵌入式 JSP 解析器 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!-- JSP jstl --> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> 注意,jstl 需要自己手动添加版本号,它没有被 Spring Boot 管理。
在 Spring Boot 支持的嵌入式容器中只有 Tomcat 支持使用 JSP,Undertow 和 Jetty 均不支持!
1、简介 流式数据在现实生活中的一些例子包括传感器数据、股票市场事件流和系统日志。在本文中,我们通过构建一个简单的字数统计流式应用来介绍如何在 Spring Boot 中使用 Kafka Streams。
2、概览 Kafka Streams 在 Kafka Topic 和关系型数据库表之间提供了一种对偶性。它使我们能够对一个或多个流式事件进行连接、分组、聚合和过滤等操作。
Kafka 流的一个重要概念是处理器拓扑(Processor Topology)。处理器拓扑是 Kafka Stream 对一个或多个事件流进行操作的蓝图。从本质上讲,处理器拓扑可视为有向无环图。在这个图中,节点分为源节点、处理器节点和汇节点,而边则代表流事件的流向。
位于拓扑结构顶端的源接收来自 Kafka 的流数据,将其向下传递到执行自定义操作的处理器节点,并通过汇节点流出到新的 Kafka Topicc。在进行核心处理的同时,还利用检查点(Checkpoint)定期保存数据流的状态,以实现容错和弹性。
3、依赖 首先在 POM 中添加 spring-kafka 和 kafka-streams 依赖:
<dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> <version>2.7.8</version> </dependency> <dependency> <groupId>org.apache.kafka</groupId <artifactId>kafka-streams</artifactId> <version>2.7.1</version> </dependency> 4、示例 示例应用从输入的 Kafka Topic 中读取流式事件。读取记录后,它会对记录进行处理,分割文本并计算单个字数。随后,它将更新的字数发送到 Kafka 输出。除了输出 Topic 外,还要创建一个简单的 REST 服务,通过 HTTP 端点公开该计数。
总之,输出 Topic 将不断更新从输入事件中提取的单词及其更新计数。
4.1、配置 在 Java 配置类中定义 Kafka Stream 配置:
@Configuration @EnableKafka @EnableKafkaStreams public class KafkaConfig { @Value(value = "${spring.
1、简介 本文将带你了解如何通过调整 Spring 应用的配置、JVM 参数和使用 GraalVM 原生镜像来缩短 Spring Boot 的启动时间。
2、调整 Spring 应用 首先,创建一个 Spring Boot(2.5.4)应用,添加 Spring Web、Spring Actuator 和 Spring Security 依赖。
还要添加 spring-boot-maven-plugin 插件,并配置将应用打包到 jar 文件中:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <configuration> <finalName>springStartupApp</finalName> <mainClass>com.baeldung.springStart.SpringStartApplication</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> 使用标准的 java -jar 命令运行 jar 文件,并查看应用的启动时间。
c.b.springStart.SpringStartApplication : Started SpringStartApplication in 3.403 seconds (JVM running for 3.961) 如上,应用启动时间约为 3.4 秒。我们把这个时间作为下文调整的参考。
2.1、延迟初始化 Spring 支持延迟初始化。延迟初始化意味着 Spring 不会在启动时创建所有 Bean。此外,Spring 在需要 Bean 之前不会注入任何依赖。从 Spring Boot 2.
前言 现在的 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、概览 在 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 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.
Spring Boot 对于发送邮件这种常用功能也提供了开箱即用的 Starter:spring-boot-starter-mail。
通过这个 starter,只需要简单的几行配置就可以在 Spring Boot 中实现邮件发送,可用于发送验证码、账户激活等等业务场景。
本文将通过实际的案例带你了解如何在 Spring Boot 中使用 QQ 邮箱发送邮件。
关于 Spring 对邮件支持的更多细节,你可以参阅 中文文档。
创建 Spring Boot 应用 在 pom.xml 中添加 spring-boot-starter-mail 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> 属性配置 在 application.yaml 中配置属性。
spring: mail: # 指定邮件服务器地址 host: smtp.qq.com # 登录账户 username: 747692844@qq.com # 登录密码 password: "<你的密码/授权码>" # 端口 port: 465 # 默认编码 default-encoding: UTF-8 # 使用的协议 protocol: smtps # 其他的属性 properties: "mail.smtp.connectiontimeout": 5000 "mail.smtp.timeout": 3000 "mail.smtp.writetimeout": 5000 "mail.
MyBatis Plus 是 MyBatis 框架的一个增强。除了基本的 MyBatis 功能外,它还提供了快速的 CURD 方法,以及投影查询、分页查询、动态条件等等功能,极大的提高了开发效率。
本文将会通过案例教你如何在 Spring Boot 中整合 MyBatis Plus。
文中使用的软件版本如下:
Spring Boot:3.0.3 MySQL:8.0.0 MyBatis Plus:3.5.4 初始化演示数据 首先在本地数据库执行以下 SQL 脚本,创建一张名为 t_user 的数据表:
CREATE TABLE `t_user` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `enabled` tinyint unsigned NOT NULL COMMENT '是否启用。0:禁用,1:启用', `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '名字', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户'; 然后再执行如下脚本,创建初始记录: