Java 设计模式 --- Template 模式 Java Template 模式 Java 模板设计模式
一、概述
模板设计模式: 父类定义通用抽象的功能方法,子类负责具体的实现。 本文将以一个通用的定时任务,处理模式,来讲解java 模板设计模式。
假设定时任务,要实现以下功能:
- 开关 --- 随时可以关闭
- 锁资源 --- 避免多实例重复执行
- 任务执行,时间记录
- 任务处理
- 资源释放
大致的代码如下:
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.scheduling.annotation.Scheduled;/*** Description: 通用任务处理* @author wu* @version 1.0* @date 2023/3/26 21:16*/
@Slf4j
public class GeneralTaskTest {/*** 分布式锁 , 默认: true*/private boolean lock = true;/*** 启用 定时任务标记 , 默认: true*/private boolean enable = true;@Scheduled(cron = "0/10 * * * * ?")public void task(){// 1、任务开关// apollo 配置 / 数据库配置等if(!enable){log.info("任务没有打开");return;}// 获取锁 --- redis , zk 等// 2、分布式锁 --- 避免多实例执行if(!lock){log.info("该实例,没有获取到锁");return;}// 3、执行逻辑log.info("开始执行~ ");final StopWatch stopWatch = StopWatch.createStarted();try {System.out.println(" 执行定时任务处理逻辑~");// 测试异常// int i = 1/0;}catch (Exception e){// 4、异常处理log.error("task execute failure , {}", ExceptionUtils.getStackTrace(e));}finally {// 5、资源关闭// 释放锁资源stopWatch.stop();log.info("任务执行结束,耗时:{} ms",stopWatch.getTime());}}}
二、思路设计
1、 创建 BaseTaskTemplate 类
有2个属性, enbale 和lock ,分别标记 开关和锁 , 提供 getter / setter 方法,用于子类处理锁标记。
/*** 分布式锁 , 默认: true*/
private boolean lock = true;/*** 启用 定时任务标记 , 默认: true*/
private boolean enable = true;
2、核心方法: taskExecute 任务执行类 如下
- beforeExec : 前置处理,用于获取锁 , 开关字段处理
- exec(): 业务逻辑处理
- afterExec(): 资源释放 等
public void taskExecute(){beforeExec();if(!lock || !enable){log.info(this.getClass().getName() + " , 没有获取到锁,或者定时任务没有开启 ~ lock={} , enable={}", lock, enable);return;}log.info(this.getClass().getName() +" {} ,任务开始执行 -- "+ new Date().getTime());final StopWatch stopWatch = StopWatch.createStarted();try {exec();} catch (Exception e) {log.error(this.getClass().getName() + ",定时任务执行出错, cause by : {}", ExceptionUtils.getStackTrace(e));
// throw e; // throw e} finally {afterExec();stopWatch.stop();log.info("finally 程序执行结束,耗时:{} ms ", stopWatch.getTime());}log.info("任务执行结束~ over ...");}
三、代码实现
1、BaseTaskTemplate ,通用任务模板,代码如下:
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.StopWatch;import java.util.Date;/*** Description: BaseTaskTemplate , 通用定时任务执行模板* @author w* @version 1.0* @date 2023/3/9 15:20* @remarks: 该任务迁移到 scheduler 分支* @see*/
@Slf4j
public class BaseTaskTemplate {/*** 分布式锁 , 默认: true*/private boolean lock = true;/*** 启用 定时任务标记 , 默认: true*/private boolean enable = true;public void taskExecute(){beforeExec();if(!lock || !enable){log.info(this.getClass().getName() + " , 没有获取到锁,或者定时任务没有开启 ~ lock={} , enable={}", lock, enable);return;}log.info(this.getClass().getName() +" {} ,任务开始执行 -- "+ new Date().getTime());final StopWatch stopWatch = StopWatch.createStarted();try {exec();} catch (Exception e) {log.error(this.getClass().getName() + ",定时任务执行出错, cause by : {}", ExceptionUtils.getStackTrace(e));
// throw e; // throw e} finally {afterExec();stopWatch.stop();log.info("finally 程序执行结束,耗时:{} ms ", stopWatch.getTime());}log.info("任务执行结束~ over ...");}protected void afterExec() {}protected void beforeExec() {}protected void exec() throws Exception {}public boolean isLock() {return lock;}public void setLock(boolean lock) {this.lock = lock;}public boolean isEnable() {return enable;}public void setEnable(boolean enable) {this.enable = enable;}
}
2、创建 UserTaskTemplate 类
继承 BaseTaskTemplate 重写 exec 方法,写具体业务处理逻辑, 创建 task 方法, 配置对应的 定时策略,在方法体中使用 super.taskExecute(); 调用任务处理,完整代码如下
import com.runcode.springboottourist.template.BaseTaskTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;/*** Description: UserTaskTemplate* @author w* @version 1.0* @date 2023/3/10 14:41*/
@Slf4j
@Component
public class UserTaskTemplate extends BaseTaskTemplate {@Scheduled(cron = "0/10 * * * * ?")public void task (){log.info("UserTaskTemplate === ");super.taskExecute();}@Overrideprotected void exec() throws Exception {TimeUnit.SECONDS.sleep(1);log.info(" 执行定时任务逻辑处理 ~ ");}@Value("${UserTaskTemplate.enable:true}")@Overridepublic void setEnable(boolean enable) {super.setEnable(enable);}@Overrideprotected void beforeExec() {log.warn("beforeExec ===>>>");// 获取锁 --- redis.getLock}@Overrideprotected void afterExec() {log.warn("beforeExec ===>>>");// 释放锁 --- redis.releaseLock}
}
3、测试结果如下:
2023-03-26 21:38:30.002 INFO 119920 --- [uler pool===>>4] c.r.s.task.UserTaskTemplate : UserTaskTemplate ===
2023-03-26 21:38:30.002 WARN 119920 --- [uler pool===>>4] c.r.s.task.UserTaskTemplate : beforeExec ===>>>
2023-03-26 21:38:30.002 INFO 119920 --- [uler pool===>>4] c.r.s.template.BaseTaskTemplate : com.runcode.springboottourist.task.UserTaskTemplate {} ,任务开始执行 -- 1679837910002
2023-03-26 21:38:31.014 INFO 119920 --- [uler pool===>>4] c.r.s.task.UserTaskTemplate : 执行定时任务逻辑处理 ~
2023-03-26 21:38:31.014 WARN 119920 --- [uler pool===>>4] c.r.s.task.UserTaskTemplate : afterExec ===>>>
2023-03-26 21:38:31.014 INFO 119920 --- [uler pool===>>4] c.r.s.template.BaseTaskTemplate : finally 程序执行结束,耗时:1011 ms
2023-03-26 21:38:31.014 INFO 119920 --- [uler pool===>>4] c.r.s.template.BaseTaskTemplate : 任务执行结束~ over ...
四、总结
1、 若又有一个任务需要执行,直接继承 BaseTaskTemplate 类即可,重写 exec 方法;
- 需要配置锁资源 , 重写 beforeExec 和 afterExec 方法 ;
- 需要配置 开关 , 重写 setEnable 方法,加上 @Value 注解即可
2、beforeExec 、afterExec 方法,是参考 线程池 ThreadPoolExecutor 的 实现, 如下:


参考资料:
Spring @Value 赋值
Java 设计模式 --- Builder模式 Java Builder 模式


















