摘要
我有很多定时任务,都是想要一个就写一个,虽然大部分代码都是拿来就用,但还是有点麻烦,而且不支持动态更新。
正文
最近开始跑步了,工作以后涨了40斤,吃得比以前在家可好的多了,又不锻炼,胖很正常。
争取超越以前的巅峰时刻!
我有很多定时任务,都是想要一个就写一个,虽然大部分代码都是拿来就用,但还是有点麻烦,而且不支持动态更新。
github上有一个签到项目挺好的,但是对于我来说,又不太适用。
就想着按我自己的需求自己实现一个。
- 基本定时功能。开销不能太大,选用**定时任务线程池**
- 动态更新签到数据。考虑到不能占用过多内存,犹豫适用sqlite还是缓存,最终决定要快速响应,所以牺牲空间,使用缓存。
- 权限控制。因为我不太擅长写页面,接口文档直接暴露出去了,但是使用AOP加了权限控制。
- 代码符合高内聚(模块尽量细分)低耦合(多模块之间关联尽量少)规则。相信看过我之前代码的,会发现就是一坨屎,所有的东西,都砸到一块了,不利于后期扩展。这次就想解决该问题,现在我如果想要新增功能,只需要新创建一个Runnable即可。
- 通知功能。目前支持邮件,想着接入QQ,还未实现。
老规矩,参考的文章放出来
记录需要注意的问题。
查询模块结构
idea2019版本,项目名称右键-选择Diagrams-选择show diagrams-选择project module-右键后选择add module and library。
如果想要更直观的看,可以右键-选择layout-选择hierarchic group layout
整个模块的结构划分,我还是比较喜欢的。
打包
因为是多模块的,在子模块(被调用的模块,相当于是提供入口的模块)中,pom配置需要加入exec
1
2
3
4
5
6
7
8
9
10
11
| <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
|
在需要作为启动模块的,就保持默认即可,如果想要修改输出的jar包名称,可以这样配置
1
2
3
4
5
6
7
8
9
| <build>
<finalName>scheduled-task</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
|
更详细的配置说明,参考上面参考的springboot2.5.2官方文档。
配置前端页面
如果使用Springboot直接打包前端页面,可以直接使用默认配置即可。可以查看源码。
如果不想使用默认配置,可以进行如下配置
1
2
3
4
5
| spring:
# 配置页面的静态路径
web:
resources:
static-locations: file:/root/project/scheduledtask
|
AOP权限校验
aop注解依赖
1
2
3
4
5
| <!--解析切入点表达式-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
|
创建注解
1
2
3
4
5
6
| @Target({ElementType.METHOD})//作用在类上
@Retention(RetentionPolicy.RUNTIME)//当前被描述的注解,会保留到class字节码文件中,并被jvm读取到。一般也只会用到这个
@Documented//注解被抽取到api文档中
@Inherited//注解被子类继承
public @interface PermissionVerify {
}
|
配置aop切入点
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
29
30
31
32
33
| @Component
@Aspect
public class PermissionVerifyAspect {
private static final Logger log = LoggerFactory.getLogger(PermissionVerifyAspect.class);
/**
* 切入点
*/
@Pointcut("@annotation(top.meethigher.scheduledtask.openweb.annotation.PermissionVerify)")
public void webLog() {
}
/**
* 前置通知
*
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws WebCommonException {
// 拿到当前线程绑定的request,接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
...
if(没有权限)
throw new WebCommonException(ResponseEnum.PERMISSION_VERIFY_EXPIRED);
}
}
}
|
在指定的接口方法上,直接添加注解即可。
1
2
3
4
5
6
| @ApiOperation(value = "查询一条", notes = "查询一条")
@PostMapping("/queryOne")
@PermissionVerify
public BaseResponse<CacheManagerResponse> queryOne(@RequestParam("key") String key) {
return ResponseUtils.getSuccessResponse(cacheManagerService.queryOne(key));
}
|
最后,启动项目
1
| nohup java -jar scheduled-task.jar >task.log 2>&1 &
|
线程分析工具
linux中输入
1
2
| # 输出该pid的线程信息,并保存至thread.log
jstack pid | tee thread.log
|
然后使用在线的java检测工具世界级的堆Dump分析——Java、Android内存转储分析器,进行分析即可。
个人比较喜欢使用的工具,FastThread