摘要

我有很多定时任务,都是想要一个就写一个,虽然大部分代码都是拿来就用,但还是有点麻烦,而且不支持动态更新。

正文

最近开始跑步了,工作以后涨了40斤,吃得比以前在家可好的多了,又不锻炼,胖很正常。

3.jpg

争取超越以前的巅峰时刻!

4.jpg

我有很多定时任务,都是想要一个就写一个,虽然大部分代码都是拿来就用,但还是有点麻烦,而且不支持动态更新。

github上有一个签到项目挺好的,但是对于我来说,又不太适用。

就想着按我自己的需求自己实现一个。

  1. 基本定时功能。开销不能太大,选用**定时任务线程池**
  2. 动态更新签到数据。考虑到不能占用过多内存,犹豫适用sqlite还是缓存,最终决定要快速响应,所以牺牲空间,使用缓存
  3. 权限控制。因为我不太擅长写页面,接口文档直接暴露出去了,但是使用AOP加了权限控制。
  4. 代码符合高内聚(模块尽量细分)低耦合(多模块之间关联尽量少)规则。相信看过我之前代码的,会发现就是一坨屎,所有的东西,都砸到一块了,不利于后期扩展。这次就想解决该问题,现在我如果想要新增功能,只需要新创建一个Runnable即可。
  5. 通知功能。目前支持邮件,想着接入QQ,还未实现。

老规矩,参考的文章放出来

2.jpg

记录需要注意的问题。

查询模块结构

idea2019版本,项目名称右键-选择Diagrams-选择show diagrams-选择project module-右键后选择add module and library。

如果想要更直观的看,可以右键-选择layout-选择hierarchic group layout

整个模块的结构划分,我还是比较喜欢的。

1.jpg

打包

因为是多模块的,在子模块(被调用的模块,相当于是提供入口的模块)中,pom配置需要加入exec

xml
 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包名称,可以这样配置

xml
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直接打包前端页面,可以直接使用默认配置即可。可以查看源码。

5.jpg

如果不想使用默认配置,可以进行如下配置

yml
1
2
3
4
5
spring:
  # 配置页面的静态路径
  web:
    resources:
      static-locations: file:/root/project/scheduledtask

AOP权限校验

aop注解依赖

xml
1
2
3
4
5
<!--解析切入点表达式-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

创建注解

java
1
2
3
4
5
6
@Target({ElementType.METHOD})//作用在类上
@Retention(RetentionPolicy.RUNTIME)//当前被描述的注解,会保留到class字节码文件中,并被jvm读取到。一般也只会用到这个
@Documented//注解被抽取到api文档中
@Inherited//注解被子类继承
public @interface PermissionVerify {
}

配置aop切入点

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
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);
        }

    }

}

在指定的接口方法上,直接添加注解即可。

java
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));
}

最后,启动项目

java
1
nohup java -jar scheduled-task.jar >task.log 2>&1 &

线程分析工具

linux中输入

sh
1
2
# 输出该pid的线程信息,并保存至thread.log
jstack pid | tee thread.log

然后使用在线的java检测工具世界级的堆Dump分析——Java、Android内存转储分析器,进行分析即可。

个人比较喜欢使用的工具,FastThread

6.jpg