一、使用开发工具
IDEA
二、使用插件
1、Alibaba-Java-Coding-Gidelines 阿里巴巴Java开发规约
2、SonarLint 代码质量检测插件
3、MetricsReload检测圈复杂度
三、注意事项
1、避免IDEA自动导入"*"的问题
编译器操作:
左上角 File->settings->Editor->Code Style->java->imports
设置 Class count to use import with '*' 值为500
设置 Names count to use static import with '*' 值为300
2、删除未使用包的导入。
3、类及方法写注释,标明编写人。
4、类名、方法名、变量做到见名知意。
5、注意代码格式。
6、方法参数个数不宜太多。
7、抽取重复代码。
8、对于IO资源,能够继承处理Closeable的,尽量使用try-with-resource,这样可以去除finally。
9、时间格式转换使用线程安全的:DateFormatUtils.format、DateUtils.parseDate。
10、线程尽量使用线程池管理。
11、注意事务中最好不要使用锁,防止死锁的出现。
12、统一系统日志格式规范,及减少不必要的日志内容,为了容易排查问题,个人觉得最好按业务模块分日志。
13、避免魔法值的出现。
14、避免重复制轮子。
15、利用MetricsReload检测圈复杂度,减少逻辑的判断。
16、注意数据库中触发器的使用是否会影响到插入数据的返回结果。
四、个例问题处理
1、定时任务动态读取数据库配置
@Scheduled等的配置可以读取配置文件值,及初始化的常理,但不能动态读取配置,因此需要做优化
解决方案:重写 SchedulingConfigurer 的 configureTasks 方法,添加自定义的触发方法
具体代码如下:
@Slf4j
@Configuration
public class SchedulingExecutorConfig implements SchedulingConfigurer, AsyncConfigurer {
private ScheduledTaskRegistrar taskRegistrar;
private Map<String, TriggerTask> triggerTaskMap = new ConcurrentHashMap<>();
private Map<String, TriggerTask> notDealTriggerTaskMap = new ConcurrentHashMap<>();
/**
* 定时任务使用的线程池
* @return
*/
@Bean(destroyMethod = "shutdown", name = "taskScheduler")
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("scheduler-");
scheduler.setAwaitTerminationSeconds(120);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
this.taskRegistrar = scheduledTaskRegistrar;
scheduledTaskRegistrar.setTaskScheduler(taskScheduler());
if(!notDealTriggerTaskMap.isEmpty()){
for (Map.Entry<String, TriggerTask> entry : notDealTriggerTaskMap.entrySet()){
addTriggerTask(entry.getKey(), entry.getValue());
}
}
}
/**
* 添加任务
* @param taskId 任务id
* @param triggerTask
*/
public void addTriggerTask(String taskId, TriggerTask triggerTask) {
if(StringUtils.isEmpty(taskRegistrar)){
notDealTriggerTaskMap.put(taskId, triggerTask);
return;
}
if(triggerTaskMap.keySet().contains(taskId)){
return;
}
taskRegistrar.addTriggerTask(triggerTask.getRunnable(), triggerTask.getTrigger());
triggerTaskMap.put(taskId, triggerTask);
}
public ScheduledTaskRegistrar getTaskRegistrar() {
return this.taskRegistrar;
}
}
定时任务的类可继承InitializingBean
@Override
public void afterPropertiesSet() {
try {
schedulingExecutorConfig.addTriggerTask("paramRecvSchedulerCheckVersion",new TriggerTask(this :: checkVersion
, triggerContext -> {
paramConfig.initCheckVersionFixedDelay();
// 任务触发,可修改任务的执行周期
PeriodicTrigger trigger = new PeriodicTrigger(Long.parseLong(paramConfig.getCheckVersionFixedDelay()));
return trigger.nextExecutionTime(triggerContext);
}));
} catch (Exception e) {
log.error("初始化定时任务异常:", e);
}
}
2、关闭tomcat出现长时间未关闭问题
经使用jconsole发现,关闭tomcat时,线程池并没有关闭
解决方案:关闭tomcat时,关闭队列中等待执行的任务线程,正在执行的,等执行完再关闭,如果超两分钟还没有关闭的,强制关闭线程,些处虽说有可能涉及到事务,但影响不大,最多只是回滚事务
/**
* 容器关闭监听器
* @author djz
*/
@Slf4j
@Component
public class ShutdownListener implements ApplicationListener<ContextClosedEvent> {
@Resource
@Qualifier("paramTaskExecutor")
private ThreadPoolTaskExecutor paramTaskExecutor;
@Resource
@Qualifier("taskExecutor")
private ThreadPoolTaskExecutor taskExecutor;
@Resource
@Qualifier("taskScheduler")
private ThreadPoolTaskScheduler taskScheduler;
@Resource
private SchedulingExecutorConfig schedulingExecutorConfig;
@Override
public void onApplicationEvent(ContextClosedEvent event) {
log.info("onApplicationEvent start");
//关闭线程池
paramTaskExecutor.shutdown();
log.info("paramTaskExecutor线程池已关闭");
taskExecutor.shutdown();
log.info("taskExecutor线程池已关闭");
ScheduledTaskRegistrar taskRegistrar = schedulingExecutorConfig.getTaskRegistrar();
shutDownScheduledTask(taskRegistrar.getScheduledTasks());
try {
taskScheduler.shutdown();
} catch (Exception e) {
log.info("执行taskScheduler线程池关闭异常", e);
}
log.info("taskScheduler线程池已关闭");
}
/**
* 关闭队列的定时任务
* @param scheduledTasks
*/
private void shutDownScheduledTask(Set<ScheduledTask> scheduledTasks) {
log.info("关闭队列的定时任务:{}", JSON.toJSON(scheduledTasks));
Iterator<ScheduledTask> iterator = scheduledTasks.iterator();
while (iterator.hasNext()) {
ScheduledTask scheduledTask = iterator.next();
Class<?> scheduledTaskClass = scheduledTask.getClass();
try {
Field field = scheduledTaskClass.getDeclaredField("future");
field.setAccessible(true);
ScheduledFuture<?> future = (ScheduledFuture<?>) field.get(scheduledTask);
if (future != null && !future.isDone()) {
future.cancel(false);
}
} catch (Exception e) {
log.info("异常", e);
}
}
}
}
也注意线程池的配置
//表明等待所有线程执行完,默认为false
setWaitForTasksToCompleteOnShutdown(true);
//设置等待时间后,强制关闭线程
setAwaitTerminationSeconds(120);