近几年为了解决一些不同项目的业务问题陆续开发了很多工具类。但是每次新建项目都要把这些工具类拽来拽去特别麻烦。 所以搞了一个工具包,提交到了中央仓库。每次新建项目的时候只需要引入依赖即可。
Common Boot Starter
小于 1 分钟
近几年为了解决一些不同项目的业务问题陆续开发了很多工具类。但是每次新建项目都要把这些工具类拽来拽去特别麻烦。 所以搞了一个工具包,提交到了中央仓库。每次新建项目的时候只需要引入依赖即可。
//SpEL 解析器
SpelExpressionParser parser = new SpelExpressionParser();
//上下文数据
User user = new User();
user.setId(1);
user.setName("张三");
user.setCreateTime(LocalDate.now());
user.setAge(18);
user.setBalance(BigDecimal.TEN);
StandardEvaluationContext context = new StandardEvaluationContext(user);
//数值计算
System.out.println(parser.parseExpression("1+1").getValue()); //2
System.out.println(parser.parseExpression("age*(age+2)").getValue(context)); //360
System.out.println(parser.parseExpression("balance*2").getValue(context)); //20
System.out.println(parser.parseExpression("balance/2").getValue(context)); //5
System.out.println(parser.parseExpression("balance-3").getValue(context)); //7
System.out.println(parser.parseExpression("balance^2").getValue(context)); //100
//字符串拼接
System.out.println(parser.parseExpression("'name: '+name").getValue(context)); //name: 张三
//Map
System.out.println(parser.parseExpression("{name: name}").getValue(context)); //{name=张三}
//方法调用
System.out.println(parser.parseExpression("pow(balance, 4)").getValue(context)); //10000
//逻辑运算
System.out.println(parser.parseExpression("balance > 20").getValue(context)); //false
System.out.println(parser.parseExpression("balance == 20 or balance == 10").getValue(context)); //true
System.out.println(parser.parseExpression("balance == 10 and balance < age").getValue(context)); //true
最近在写一个开发工具包,实现启动自动创建代理 bean 的方法,看了一下 OpenFeign 的源码。之前写的方法也可以实现之前的文章。 这里介绍 OpenFeign 的实现方法:
package cn.linkot.boot.meow.annocation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Cat {
String value() default "";
}
在 Spring Web MVC 项目中通常我们可以直接使用 ThreadLocal 对值进行保存,同一次请求内多次获取都能拿到值。 但是在 Web Flux 中一个请求可能经过不同线程,所以无法使用这种方法。可以使用 Mono 对象的 context 功能来替代。
Spring Boot 版本
2.6.3
使用 Mono 的 contextWrite 方法保存值。
public final Mono<T> contextWrite(Function<Context, Context> contextModifier) {
return onAssembly(new MonoContextWrite(this, contextModifier));
}
使用 GlobalFilter 过滤所有请求,用 ModifyRequestBodyGatewayFilterFactory 生成的 filter 通过 RewriteFunction 修改请求体。
@Component
@Order(10)
public class RequestBodyModifyFilter implements GlobalFilter{
@Resource
private ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return reqBodyRewriter().filter(exchange, chain);
}
/**
* 修改请求体的filter
*/
@Bean
private GatewayFilter reqBodyRewriter(){
return modifyRequestBodyGatewayFilterFactory.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(
String.class, String.class, rewriteFunction()
));
}
/**
* 此处是对原请求体的修改方法
*/
private RewriteFunction<String, String> rewriteFunction(){
return (serverWebExchange, s) -> {
//对请求体进行操作、s为原请求体
return Mono.just("修改后的结果");
};
}
}
2024/02/05 - 建议使用这种方法来实现同样的效果。
基本的 spring boot 和 mybatis 配置, web 用来测试
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
开发中经常会遇到需要定时任务的需求,这里分享一下我写的工具类。
先上代码
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
/**
* 定时任务调度服务
* @author sa@linkot.cn
*/
@Service
@Slf4j
public class SchedulerService {
private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
private final Map<String, ScheduledFuture<?>> map = new HashMap<>();
public SchedulerService(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
this.threadPoolTaskScheduler = threadPoolTaskScheduler;
}
/**
* 当前是否存在该key的任务
* @param key 任务key
*/
public boolean exist(String key){
return map.get(key)!=null;
}
/**
* 不进行管理的一次性任务,任务必定会执行且无法取消
*/
public void schedule(Runnable runnable, Date startAt) {
threadPoolTaskScheduler.schedule(runnable, startAt);
}
/**
* 进行管理的一次性任务,任务一段时间后执行,执行前可取消
*/
public void schedule(String key, Runnable runnable, Date startAt) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.schedule(new AutoRemoveTask(key, runnable, this), startAt));
}
/**
* 进行管理的一次性任务,任务一段时间后执行,执行前可取消,执行后不自动移除
*/
public void scheduleDontRemove(String key, Runnable runnable, Date startAt) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.schedule(runnable, startAt));
}
/**
* 进行管理的任务,使用触发器判断是否执行,可取消
*/
public void schedule(String key, Runnable runnable, Trigger trigger) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.schedule(runnable, trigger));
}
/**
* 进行管理的任务,以固定间隔重复执行,可取消
*/
public void scheduleAtFixedRate(String key, Runnable runnable, Long period) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.scheduleAtFixedRate(runnable, period));
}
/**
* 进行管理的任务,指定日期后开始按固定间隔执行,可取消
*/
public void scheduleAtFixedRate(String key, Runnable runnable, Date startAt, Long period) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.scheduleAtFixedRate(runnable, startAt, period));
}
/**
* 取消某个key的定时任务
*/
public void cancel(String key){
cancel(key, true, "");
}
/**
* 取消某个key的定时任务
*/
public void cancel(String key, String reason){
cancel(key, true, reason);
}
/**
* 取消某个key的定时任务
* @param interrupt 是否强制取消
*/
public void cancel(String key, boolean interrupt, String reason){
if (ObjectUtils.isEmpty(key)) {
return;
}
log.info("取消定时任务:{} -> {}", key, reason);
ScheduledFuture<?> s = map.get(key);
if (s!=null){
s.cancel(interrupt);
map.remove(key);
}else {
log.info("任务不在列表中:{},不存在或已经执行完毕", key);
}
}
/**
* 执行完一次后删除自身的任务
* @author sa@linkot.cn
*/
private static class AutoRemoveTask implements Runnable{
private final String key;
private final Runnable runnable;
private final SchedulerService service;
/**
* 执行一次后自动删除的任务
* @param key 任务 key
* @param runnable 执行
* @param service 管理定时任务的service
*/
public AutoRemoveTask(String key, Runnable runnable,
SchedulerService service) {
this.key = key;
this.runnable = runnable;
this.service = service;
}
@Override
public void run() {
runnable.run();
service.cancel(key);
}
}
}