Spring Boot 整合 Freemarker 模板引擎

在前后端分离架构大行其道的今天,模板引擎依然有着重要的地位和不可代替性。 Freemarker 是一款界开源的老牌模板引擎,使用 Java 开发,Spring 官方对 Freemarker 提供了支持。本文将会带你学习如何在 Spring Boot 中整合 Freemarker。 创建项目 在 pom.xml 添加 spring-boot-starter-web 和 spring-boot-starter-freemarker 依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> 编写模板 根据 Spring Boot 约定,模板文件应该放在 src/main/resources 目录下的 templates 目录中。 在 templates 目录中,创建 index 文件夹,用于存放渲染主页的 index.ftl 模板视图,如下: 其中 index.ftl 内容如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Freemarker</title> </head> <body> Hello ${title}! </body> </html> 如上,模板只是简单地输出了 Model 中的 title 属性。 配置属性 Spring Boot 提供了很多配置属性,可用于在 application.yaml | properties 中定制 Freemarker。 这些属性都以 spring.

解决 Spring WebFlux DataBufferLimitException

1、简介 本文将将会介绍在 Spring WebFlux 应用中出现 DataBufferLimitException 的原因,以及解决办法。 2、原因 在讲解决方案前,先解释一下这个异常的原因。 2.1、DataBufferLimitException 是啥? Spring WebFlux 限制 了编解码器(codec)中 Buffer 的内存大小,以避免应用出现内存问题。默认情况下,配置为 262,144 字节。如果超出限制,就会导致 DataBufferLimitException 异常。 2.2、编解码器是啥? spring-web 和 spring-core 模块通过非阻塞 I/O 和 Reactive Stream 背压提供了将字节内容序列化和反序列化为更高级对象的支持。编解码器 提供了一种替代 Java 序列化的方法。其中一个 优点 是,通常对象不需要实现 Serializable 接口。 3、服务端 先看服务端的 DataBufferLimitException 是如何发生的。 3.1、问题复现 尝试向 Spring WebFlux 服务器发送大小为 390 KB 的 JSON 请求体以触发异常。 使用 curl 命令向服务器发送 POST 请求: curl --location --request POST 'http://localhost:8080/1.0/process' \ --header 'Content-Type: application/json' \ --data-binary '@/tmp/390KB.json' 你可以看到,抛出了 DataBufferLimitException 异常:

在同一个 Bean 中调用另一个 @Cacheable 方法时缓存失效

1、简介 在 Spring 中可以使用 @Cacheable 注解通过 AOP 技术地轻松为 Bean 中的方法启用缓存功能。但是,当你尝试在 Bean 的其他方法中直接调用 @Cacheable 方法时,缓存功能会失效。 本文将会解释这种情况下缓存功能失效的原因以及解决办法。 2、复现问题 首先,初始化一个启用缓存的 Spring Boot 应用。然后创建一个 MathService,其中的 square 方法注解了 @Cacheable: @Service @CacheConfig(cacheNames = "square") public class MathService { private final AtomicInteger counter = new AtomicInteger(); @CacheEvict(allEntries = true) public AtomicInteger resetCounter() { counter.set(0); return counter; } @Cacheable(key = "#n") public double square(double n) { counter.incrementAndGet(); return n * n; } } 接着,再在 MathService 中创建一个 sumOfSquareOf2 方法,调用两次 square 方法:

Spring Boot 中 SSL Bundle 的用法

1、简介 以前在 Spring Boot 中配置 SSL 非常复杂,主要是证书有很多类型,如:JKS、PKCS #12 或 PEM。每种类型的配置方式又不一样。 幸运的是,Spring Boot 3.1 引入了 SSL Bundle,旨在简化 Spring Boot 中的 SSL 配置。在本教程中,我们将了解什么是 SSL Bundle,以及如何用它简化 Spring Boot 应用中的 SSL 配置。 2、Spring Boot SSL Bundle 通常,我们需要把 SSL 证书转换为可用的 Java 对象。 java.security.KeyStore 对象用于存储证书。 javax.net.ssl.KeyManager 对象用于管理密钥。 javax.net.ssl.SSLContext 对象用于创建安全的套接字连接(Socket Connection)。 每个类都需要更深入的理解和配置,使得整个过程变得繁琐且容易出错。各种 Spring Boot 组件可能还需要深入到不同的抽象层来应用这些设置,给任务增加了另一个难度层级。 SSL Bundle 将所有 SSL 的配置(如 Keystore、证书和私钥)封装成一个易于管理的单元。可以应用于一个或多个网络连接,无论它们是传入连接(嵌入式服务器)还是传出连接(HTTP 客户端)。 SSL Bundle 在 application.yaml 或 application.properties 中,配置属性前缀是 spring.ssl.bundle。 首先从 JKS Bundle 开始,使用 spring.ssl.bundle.jks 来配置 Java Keystore 证书:

在 Spring Boot 中快速处理 CORS 跨域

跨域问题每个人都会遇到,特别是在前后端分离的系统中。 如果你正在使用 Spring Boot 开发后端应用,并且浏览器中遇到了跨域问题。类似于如下: Access to fetch at 'http://127.0.0.1/demo' from origin 'https://springdoc.cn' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. 但是你又不想去了解跨域、CORS 这些到底是个啥,你只想快速地解决这个问题。 那你可以把如下配置类,添加到你的 Spring Boot 应用中,它可以解决 99% 以上的跨域问题: // TODO package 声明 import java.time.Duration; import java.util.Arrays; import java.util.stream.Stream; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.

限制 Webclient 的并发请求数量

1、简介 本文介绍了一些限制 WebClient 并发请求数量的方式。 2、服务端 限制 WebClient 并发请求数量是为了避免服务器因大量并发请求而宕机。有些服务自身也提供了一些限制策略。 2.1、一个简单的 Controller 为了演示,先定义一个简单的 @RestController,它返回固定范围的随机数字: @RestController @RequestMapping("/random") public class RandomController { @GetMapping Integer getRandom() { return new Random().nextInt(50); } } 接下来,我们将模拟一些耗时的操作,并限制并发请求的数量。 2.2、服务器限制并发请求数 修改服务,模拟一个更真实的场景。 首先,限制服务器可接受的并发请求数,并在达到限制时抛出异常。 其次,增加处理响应的延迟,模拟耗时的操作。 创建 Concurrency 用于限制并发数量: public class Concurrency { public static final int MAX_CONCURRENT = 5; static final AtomicInteger CONCURRENT_REQUESTS = new AtomicInteger(); public static int protect(IntSupplier supplier) { try { if (CONCURRENT_REQUESTS.incrementAndGet() > MAX_CONCURRENT) { throw new UnsupportedOperationException("max concurrent requests reached"); } TimeUnit.

在 Spring 中使用 Groovy

1、概览 Groovy 是一种功能强大的动态 JVM 语言,具有众多特性。在 Spring 中使用 Groovy 可以大大提高应用程序的灵活性和可读性。从 Spring 4 开始 支持基于 Groovy 的配置。 在本教程中,我们将了解 Groovy 与 Spring 结合使用的各种方法。首先介绍了如何使用 Spring 提供的多个选项来创建 Groovy Bean 定义。接下来,了解如何使用 Groovy 脚本加载 Application Context。最后,学习如何使用 XML 和 GroovyScriptEngine 类将 Groovy 作为脚本执行(无需编译)。 2、Maven 依赖 首先,在 pom.xml 中定义 Groovy 依赖: <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy</artifactId> <version>3.0.12</version> </dependency> 此外,还需要添加 GMavenPlus 插件来编译 Groovy 文件: <build> <plugins> <plugin> <groupId>org.codehaus.gmavenplus</groupId> <artifactId>gmavenplus-plugin</artifactId> <version>1.9.0</version> <executions> <execution> <goals> <goal>addSources</goal> <goal>addTestSources</goal> <goal>generateStubs</goal> <goal>compile</goal> <goal>generateTestStubs</goal> <goal>compileTests</goal> <goal>removeStubs</goal> <goal>removeTestStubs</goal> </goals> </execution> </executions> </plugin> </plugins> </build> 3、Bean 定义 传统上,开发人员通过 XML 配置来声明 Bean。这种方式后来被通过 Java 注解以编程方式定义 Bean 所取代。另一种声明 Bean 的方式是通过 Groovy 脚本。

获取 Spring Boot 应用的 PID

PID 是进程标识符(Process Identifier)的缩写。在操作系统中,每个运行的进程都被分配一个唯一的 PID,用于标识进程。PID 通常是一个整数值,可以用来跟踪和管理进程的状态、资源分配以及进程间的通信。 本文将会介绍几种获取 Spring Boot 应用 PID 的方法。 ApplicationPidFileWriter ApplicationPidFileWriter 是 Spring Boot 提供的一个 Listener,它可以在应用启动后把 PID 写入到指定的文件。 它需要在启动前,通过编程式配置到应用中,并且需要在配置文件中指定要写入 PID 的文件。 application.yaml 配置如下: spring: pid: # 写入 PID 的文件,可以是相对路径或绝对路径 file: app.pid # 如果 PID 无法写入,是否抛出异常 fail-on-write-error: true 在启动前,配置 ApplicationPidFileWriter 监听器: import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.ApplicationPidFileWriter; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication springApplication = new SpringApplicationBuilder() .listeners(new ApplicationPidFileWriter()) // PID 监听 .

在 Spring Boot 中使用 SSL Bundle 配置 SSL

安全套接字层(SSL)和传输层安全(TLS)是在分层或面向服务的架构中保护系统之间通信的关键组件。在这样的架构中,Spring Boot 应用程序通常接受传入的网络连接或创建传出连接,开发人员的任务是配置应用程序以在这样的安全环境中工作。 如果你曾经使用过 Java 的 Security 和 SSL API,你可能意识到这不是一项特别简单的任务。你可能会在网上复制各种代码片段。有几个因素使得与 SSL 工作变得痛苦。 首先,你可能会收到用于生产环境的 SSL 资源文件,如证书和私钥。你可能需要为预生产测试生成不同的证书(通常使用自签名证书颁发机构)。这些证书通常以 Java Keystore 文件的形式存在,可以是 JKS 或 PKCS #12 格式,或者它们可能是 PEM 编码的文本文件。每种文件类型都需要不同的处理方式。 一旦你有了证书,你需要将其转换为可以传递给 Java Connection API 的形式。麻烦点就在这里,因为 Connection API 可以以多种方式进行配置: 有的要你提供 java.security.KeyStore(Keystore 和 Truststore) 实例。 有的要你提供 javax.net.ssl.KeyManager 和 javax.net.ssl.TrustManager 实例。 有的要你提供 javax.net.ssl.SSLContext 实例。 SSL 也是相当底层的,因此通常需要逐层解除抽象,以使用 java.security 或 java.net.ssl 包中的对象进行配置。例如,如果你想在 Spring RestTemplate 上配置SSL,你需要深入到支持它的 ClientHttpRequestFactory。对于典型的 Spring Boot 应用程序,可能是一个 HttpComponentsClientHttpRequestFactory、OkHttp3ClientHttpRequestFactory 或 SimpleClientHttpRequestFactory。每个 Factory 都提供了不同的配置API。 Spring Boot 配置 SSL 或 TLS 并不是什么新鲜事,但团队决定全面审视当前对 SSL 的支持,寻找改进和扩展支持的机会。我们希望 Spring Boot 3.

@EnableMethodSecurity 注解

1、概览 使用 Spring Security,可以为应用配置身份认证和授权,以控制方法(如端点)的访问权限。 在 5.6 之前,使用 @EnableGlobalMethodSecurity 注解是标准的做法,在 5.6 之后,@EnableMethodSecurity 引入了一种更灵活的方法来配置方法安全授权(Method Security)。 在本教程中,我们将通过示例代码了解 @EnableMethodSecurity 如何代替 @EnableGlobalMethodSecurity,以及他们之间的区别。 2、@EnableMethodSecurity 和 @EnableGlobalMethodSecurity 让我们来看看方法授权如何与 @EnableMethodSecurity 和 @EnableGlobalMethodSecurity 配合使用。 2.1、@EnableMethodSecurity 通过 @EnableMethodSecurity,可以看到 Spring Security 为授权类型(Authorization Type)采用基于 Bean 的配置。 我们现在为每种类型都设置了一个配置,而不是全局配置。例如,Jsr250MethodSecurityConfiguration: @Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) class Jsr250MethodSecurityConfiguration { // ... @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) Advisor jsr250AuthorizationMethodInterceptor() { return AuthorizationManagerBeforeMethodInterceptor.jsr250(this.jsr250AuthorizationManager); } @Autowired(required = false) void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) { this.jsr250AuthorizationManager.setRolePrefix(grantedAuthorityDefaults.getRolePrefix()); } } MethodInterceptor 主要包含一个 AuthorizationManager,它现在将 “检查和返回最终决策的 AuthorizationDecision 对象” 的责任委托给适当的实现,这里是 AuthenticatedAuthorizationManager。