0%

在spring项目中我们可以通过@Value注解使用配置文件中的值,但是有时候我们想注入外部系统(如配置中心)中配置的值,这时候可以通过读取配置中心的值并作为PropertySource注入到Spring的 Environment中来实现

阅读全文 »

正则表达式是一种强大的文本匹配工具,通过特定的语法规则,可以实现对字符串的灵活匹配。在本文中,我们将介绍一系列常用的正则表达式,覆盖了匹配任意字符、多个字符包含匹配、排除字符包含匹配、重复匹配、单词边界匹配、字符串边界、子表达式(分组)、回溯引用以及前后查找等内容

阅读全文 »

虽然maven的目标是使项目构建是可移植的,尽量使在任何机器下都可以构建且结果相同,但是仍然会有一些情况需要我们根据环境等信息来构建不同的内容,这时候就需要用到 maven profiles

阅读全文 »

在使用Spring框架时,我们应该已经非常熟悉依赖注入这一概念,它可以帮我们节省构建类及设置相应依赖类的成本,只需要声明自己需要哪些类/接口即可使用,同时还可以更好的支持单元测试

这次我们看一下另一款依赖注入的框架guice,下面介绍的内容也都是来自官方文档

阅读全文 »

在当今信息爆炸的时代,对于大规模数据的搜索、分析和可视化变得至关重要。Elasticsearch(ES)作为一款开源搜索和分析引擎,为开发者和企业提供了一种强大而灵活的工具,能够轻松处理海量数据,并提供高效的搜索和分析能力

本文围绕 Elasticsearch 来讲解一下如何创建索引、进行数据写入和查询,快速入门ES使用

阅读全文 »

一般我们在 Java 项目中做并发编程,基本都是通过创建线程的方式来执行(JDK21支持了虚拟线程),但是线程有如下问题

  • 线程是不能无限创建的,而是受到操作系统的限制
  • 线程切换的时候有较高的上下文切换的成本

而协程可以理解为轻量级的线程,可以在一个线程中执行多个任务,而不需要线程切换的开销,同时也避免了线程数量的限制

这里看一下kotlin中的协程,首先需要引入单独的依赖包

1
2
3
4
// gradle.kts
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0-RC2")
}
阅读全文 »

我们都知道设计原则,追求开放封闭原则,封装变化

比如我们有一个统一的流程,运转良好且自认为设计的不错,这时来了个需求,说需要针对不同的来源要有一些特殊的处理,这时候我们如何支持呢?

一种简单的解决方案是加一个 if 判断,做一点特殊逻辑,但是这不是一个好的实践

这时候我们想到可以将这部分差异功能抽象为一个接口,然后有一种特殊的实现类还有一个默认的实现类。但是这时候另一个问题出现了,在执行到这部分逻辑的时候,如何确认使用哪个实现类呢(需要一个定位逻辑)

后面可能对于这个来源,还有一个地方需要处理,这时候又要有一套接口和定位逻辑

这时候我们可以考虑一下使用扩展点的方案,我们看下 COLA 提供的扩展点(示例来自源码)

阅读全文 »

《代码精进之路》中看到的拦截器实现,方式比较巧妙,在此记录一下

一般我们在代码中实现业务拦截器的功能,是通过Spring提供的AOP来实现的,底层也就是基于动态代理及反射,这里看一下使用另一种不同的实现方式

阅读全文 »

日常工作中,有一些功能如状态更新等需要遍历表中数据,如果数据量比较少的情况下,我们可以正常的使用数据库如mysql提供的limit和offset来实现分页的功能,但是如果数据量比较大,这时候就会有深分页的问题,产生慢SQL, 为了解决这个问题,一种实现方式就是通过主键ID+查询条件来过滤数据,使用类似如下语句

1
SELECT * FROM task WHERE id > $minId AND status = 1 ORDER BY id LIMIT 200

如果有其他条件导致需要扫描很大的行数才能扫描到的话,可能还需要限制id上限

1
SELECT * FROM task WHERE id > $minId AND id < $maxId AND status = 1 ORDER BY id LIMIT 200

之后每次使用查询的最大值更新变量minId,直到查询不出数据为止,具体对应到Java代码中大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class TaskStatusUpdater {
// 执行入口
public void execute() {
int limit = 200;
int minId = 0;

while (true) {
// 查询
List<Task> tasks = taskDao.listByMinId(minId, limit);
if (CollectionUtils.isEmpty(tasks)) {
break;
}
// 更新查询参数, 获取查询到的最大的ID
minId = tasks.stream()
.max(Comparator.comparing(Task::getId))
.map(Task::getId)
.get();
for (Task task : tasks) {
// 处理
operateTask(task);
}
}
}

private void operateTask(Task task) {
// 进行业务逻辑处理
}
}

这里可以看到,执行方法中大部分都是一些业务无关的控制代码,如果有不同的处理逻辑需要遍历,那么都要复制一下这一大坨的控制代码,是否有更好的写法呢?

阅读全文 »

之前介绍过 JSONPath 的使用,但是它只能支持一些简单场景下快速从json中获取指定的值,这次我们来看一款更强大的工具 JSONata,它是一种针对 JSON 数据的轻量级查询和转换语言,可以实现一些比较复杂的逻辑,现在我们就来看一下如何进行使用

阅读全文 »