SpringBoot Web 项目要支持响应数据的自动压缩只需要在application.properties中配置server.compression.enabled=true即可,默认为 false. 这样对于默认server.compression.min-response-size=2KB达到 2KB 大小的响应,并且请求头中有Accept-Encoding: gzip或deflate就会压缩响应数据。
相关的配置请参考:SpringBoot Server Properties- server.compression.enabled: 默认为 false
- server.compression.excluded-user-agents: 针对哪些 user-agents(逗号分隔) 不压缩,默认为空
- server.compression.mime-types: 会对哪些响应的 content-type 进行压缩,默认为 text/html, text/xml, text/plain, text/css, text/javascript, application/javascript, application/json, application/xml
- server.compression.min-response-size: 默认为 2KB
文本数据压缩比可达到百分之七八十,对于节约网络消耗来说是非常可关的,不过要些许 CPU 资源。说完了响应的自动压缩,如果请求数据较大也应考虑对请求进行压缩。比如客户端发送请求时带上Content-Encoding:gzip, 并且请求内容是 gzip 压缩的。
提示:如果 SpringBoot Web 是放在 AWS API Gateway 后端,那么 AWS API Gateway 会在看到请求头 Content-Encoding 的值为 gzip, compress, deflate, 或 br, 会自动解压缩请求数据,然后转发解压缩后的数据到后端,这时候 SpringBoot Web 无需进行请求数据的解压处理。不过对响应数据的压缩是 SpringBoot Web 要做的,AWS API Gateway 并无该功能。
SpringBoot 默认不支持自动解压缩请求内容,如果手动在 Controller 方法中,可以接收字节数组,然后自行解压缩。比如定义如下的 Controller 方法 Read More
ThreadLocal 是 Java 编程人员要掌握的一个基本类,似乎没什么太多要说。但因为本文要牵出 TransmittableThreadlLocal, 再顺带说下几乎隐形的 InheritableThreadLocal。
ThreadLocal 用于保存与线程绑定的数据,它在框架内部使用的很频繁,但凡见到 XxxContextHolder.currentContext() 之类的十之八九用到了 ThreadLocal, 如 Spring 框架中的
RequestContextHolder1public abstract class RequestContextHolder { 2 private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes"); 3 private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context");
Read More
出现此错误的大致环境如下- SpringBoot 2.7.17, SpringWeb 项目,所引用入的 spring-webmvc-5.3.30, spring 6 已解决
- JDK 1.8 或 JDK 17
- 依赖了 jackson-dataformat-xml:2.12.6 和 jackson-dataformat-cbor:2.12.6, 它会在 RestTemplate 加上 application/xml, application/cbor 等 Accept 类型
- 代码中用 RestTemplate 调用此应用的 Endpoint, 未设置任何头
后面会详细列出能重现此问题的 pom.xml 配置及 Java 代码
在执行restTemplate.getForEntity("http://localhost:8080/test2", String.class)
时出现如下错误 Read More
写作此篇是作为对 Spring 使用 Cache 解析及使用不同类型的 Cache 一文的补充,该文中提到了自定 CacheManager 及配置spring.cache.type来选择自己的 Cache 实例,但对 Spring 是如何确定具体 Cache 实现未作展开。本文将介绍选择 Cache 实现的几种方式- 默认选择 Cache
- 声明 Spring Bean
Cache - 声明 Spring Bean
CacheManager - 通过
spring.cache.type属性选择 - 引入相应的 Cache 实现依赖
Read More- 通常我们都会配置 Web 服务端对响应数据进行压缩,如用 Apache 的 mod_deflate 模块,或配置 Tomcat connector 启用压缩,又或者是在 Java Web 项目中加 Web Filter 来压缩特定的响应数据。这样客户端发送 HTTP 请求时在头中声明如 Accept-Encoding: gzip,服务端就可能会对响应数据进行压缩,同时带上 Content-Encoding: gzip 响应头。
有时候 HTTP Post 的数据太大同样会要求客户端在传输数据之前对请求数据进行压缩,本文主要关注服务端如何自动解压客户端发出的压缩数据。
先以 Apache2 为例,以 Ubuntu 20.04 为例,用命令apt-get install apache2安装 Apache 2.4.41, 它自动启用了 mod_deflate 模块。mod_deflate 模块的配置文件 /etc/apache2/modes-enabled/deflate.conf 内容如下1<IfModule mod_deflate.c> 2 <IfModule mod_filter.c> 3 AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css 4 AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript 5 AddOutputFilterByType DEFLATE application/rss+xml 6 AddOutputFilterByType DEFLATE application/xml 7 </IfModule> 8</IfModule>
它表示只对以上特定的响应数据类型进行压缩,下面来测试下对 html 内容的压缩 Read More 
Java 1.5(Tiger) 个人认为最为激动人心的两个特性是泛型与注解(Java Versions, Features and History)。泛型自然是不必说了,注解对 Java 世界的改变比泛型伟大的多(现在框架的注解配置),在 Java 1.5 之前我们只能在 Javadoc 注释中做文章,于是只能用 XDoclet 那样不伦不类的东西。Java 的注解发展到现在几乎可以使用在书写代码时的任何地方,见
java.lang.annotation.ElementType中的类型,囊括了 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER(since 1.8), TYPE_USE(since 1.8)。Java 1.5 基本确定了注解的基本框架,包括元注解(meta-annotation); 直到 Java 8 又扩展了注解的使用范围,列举如下:
创建类实例
new@Interned MyObject();类型映射
myString = (@NonNull String) str;implements 语句中
class UnmodifiableList<T> implements@Readonly List<@Readonly T> { ... }throw exception声明
void monitorTemperature() throws@Critical TemperatureException { ... }解析前面 ElementType Java 8 增加的 TYPE_PARAMETER和 TYPE_USE 注解使用新场合。ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中。ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中(如: 声明语句、泛型和强制转换语句中的类型) Read More

在 Spring 中应用 ServiceLocator 方式来获取 Spring Bean 的介绍参考了那么多,其实还是数官方的 ServiceLocatorFactoryBean JavaDoc 文档最言简意该了。Spring 的 ServiceLocator 仿佛用处不大,说到底就是类似于下方找寻某个 Spring Bean 一样:
ApplicationContext context = ...; Service service = context.getBean(ServiceImpl.class); Service service = context.getBean("myService");
只是有了 ServiceLocatorFactoryBean(它本质上就是一个 FactoryBean) 后我们不需要直接与 ApplicationContext 打交道,且多个的 Spring Bean 可以从相关的一个 FactoryBean 获得。下面用两个例子来演示(代码中刨去了 package 和 import 部分的代码)
一:实现类只有一个 Spring Bean 时
接口类 Parser(我们要定位就是它的实现类) Read More

Spring 相关代码分析
本文通过对 Spring 的源代码来理解它是如何扫描 Bean 与资源的,因为自己有一个类似的需求,想把一堆的配置文件丢到 resources 下某个目录中,在程序启动的时候能加载它们。因为文件名是不一定的,所以不能直接指定文件名来加载,通过对 Spring 扫描资源的理解后,可以在自己的代码中手工扫描那些配置文件,以后有任何新的配置文件只需要扔到相应的配置目录即可。
下面以一个最简单的 Spring Boot 项目为例,调试并观察源代码1@SpringBootApplication 2@ComponentScan(basePackages = "cc.unmi") 3public class DemoApplication { 4 public static void main(String[] args) { 5 SpringApplication.run(DemoApplication.class, args); 6 } 7}
还是直奔主题吧,不一步一步的去探寻到底是哪个实现类去扫描资源的,用 Google 找到的是ClassPathScanningCandidateComponentProvider, 因此直接在这个类的敏感位置上打上断点,比如它的构造函数 Read More
用过 Spring 的 @EnableScheduling 的都知道,我们用三种形式来部署计划任务,即 @Scheduled 注解的 fixedRate(fixedRateString), fixedDelay(fixedDelayString), 以及 cron. cron 不在这里讨论的范畴。我们着重在如何理解 fixedRate 和 fixedDelay 的区别。
在 Spring 的 Scheduled 注解的 JavaDoc 对此的解释很简单
public abstract long fixedRate
Execute the annotated method with a fixed period in milliseconds between invocations.
public abstract long fixedDelay
Execute the annotated method with a fixed period in milliseconds between the end of the last invocation and the start of the next.只是说是 fixedRate 任务两次执行时间间隔是任务的开始点,而 fixedDelay 的间隔是前次任务的结束与下次任务的开始。
大致用示意字符串来表示如下(每个 T1, 或 T2 代表任务执行秒数(每次任务执行时间不定),假定 fixedRate 或 fixedDelay 的值是 5 秒,用 W 表示等待的数)
fixedRate: T1.T1WWWT2.T2.T2WW.T3.T3.T3.T3.T3.T4.T4.T4.T4.T4.T4.T4T5T5WWWT6.T6........
fixedDelay: T1.T1.WWWWW.T2.T2.T2WWWWW.T3.T3.T3.T3.T3.WWWWW.T4.T4.T4.T4.T4.T4.T4.WWWWWT6.T6......
一般来说能理解到上面两个场景已经差不多了,相比而言 fixedDelay 简单些,盯着上一次任务的屁股就行。 Read More
Spring 项目中把大量的 SQL 分散在 Java 代码中,无 Here Doc 的情况下用加号来连接写着实在是不爽,于是之前思考这个 Spring 项目中把 SQL 语句写在 .sql 文件中 -- 把它们写在 *.sql 文件中,但是这个 *.sql 需要特定的格式来标识属性 Key--!select.user
而且还需要一个额外的类 SqlPropertySourceFactory 来解析上面的 *.sql 文件, 识别出 select.user 是 Key, 紧接着后面的块是相应的属性值,用注解引用它时还有点额外的 factory 属性来配置,如
select id, firstname, lastname, address --!update.user
update ........@PropertySource(value = "classpath:sql/queries.sql", factory = SqlPropertySourceFactory.class)
所以一直在思考是否能够再简单些,是否能用一个自定义的注解,如@SqlPropertySource("classpath:sql/queries.sql")
捉摸了很久,似乎有点难度,不过再不断发掘的过程中找到了这个类org.springframework.core.io.support.PropertiesLoaderUtils, 有下面的代码片断 Read More