StopWatch计时器
- 概念:一个非线程安全的工具类,用来记录一个或一组任务的执行时间。
public class StopWatch {
// 相当于StopWatch的Primary key
private final String id;
// 是否开启多任务保存
private boolean keepTaskList = true;
// 任务列表
private final List<TaskInfo> taskList = new LinkedList<>();
// 开启时间
private long startTimeNanos;
// 任务名称
@Nullable
private String currentTaskName;
// 最后一个任务对象
@Nullable
private TaskInfo lastTaskInfo;
// 任务数量
private int taskCount;
// 总用时
private long totalTimeNanos;
// 构造函数
public StopWatch() {
this("");
}
// 指定id的构造函数
public StopWatch(String id) {
this.id = id;
}
// 获得id
public String getId() {
return this.id;
}
// 设置是否开启保存多任务
public void setKeepTaskList(boolean keepTaskList) {
this.keepTaskList = keepTaskList;
}
// 对一个未命名的任务开启计时
public void start() throws IllegalStateException {
start("");
}
// 对一个命名任务开启计时
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
this.currentTaskName = taskName;
this.startTimeNanos = System.nanoTime();
}
// 停止当前任务
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
long lastTime = System.nanoTime() - this.startTimeNanos;
this.totalTimeNanos += lastTime;
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
// 判断当前计时器是否正在运行
public boolean isRunning() {
return (this.currentTaskName != null);
}
// 获得计时器当前任务名
@Nullable
public String currentTaskName() {
return this.currentTaskName;
}
// 获取最后任务的完成时间
public long getLastTaskTimeNanos() throws IllegalStateException {
if (this.lastTaskInfo == null) {
throw new IllegalStateException("No tasks run: can't get last task interval");
}
return this.lastTaskInfo.getTimeNanos();
}
// 获取最后任务的完成时间
public long getLastTaskTimeMillis() throws IllegalStateException {
if (this.lastTaskInfo == null) {
throw new IllegalStateException("No tasks run: can't get last task interval");
}
return this.lastTaskInfo.getTimeMillis();
}
// 获取最后任务的名称
public String getLastTaskName() throws IllegalStateException {
if (this.lastTaskInfo == null) {
throw new IllegalStateException("No tasks run: can't get last task name");
}
return this.lastTaskInfo.getTaskName();
}
// 获取最后一个任务对象
public TaskInfo getLastTaskInfo() throws IllegalStateException {
if (this.lastTaskInfo == null) {
throw new IllegalStateException("No tasks run: can't get last task info");
}
return this.lastTaskInfo;
}
// 获取总用时
public long getTotalTimeNanos() {
return this.totalTimeNanos;
}
// 获取总用时
public long getTotalTimeMillis() {
return nanosToMillis(this.totalTimeNanos);
}
// 获取总用时
public double getTotalTimeSeconds() {
return nanosToSeconds(this.totalTimeNanos);
}
// 获取总任务数
public int getTaskCount() {
return this.taskCount;
}
// 获取所有任务
public TaskInfo[] getTaskInfo() {
if (!this.keepTaskList) {
throw new UnsupportedOperationException("Task info is not being kept!");
}
return this.taskList.toArray(new TaskInfo[0]);
}
/**
* Get a short description of the total running time.
*/
public String shortSummary() {
return "StopWatch '" + getId() + "': running time = " + getTotalTimeNanos() + " ns";
}
/**
* Generate a string with a table describing all tasks performed.
* <p>For custom reporting, call {@link #getTaskInfo()} and use the task info
* directly.
*/
public String prettyPrint() {
StringBuilder sb = new StringBuilder(shortSummary());
sb.append('\n');
if (!this.keepTaskList) {
sb.append("No task info kept");
}
else {
sb.append("---------------------------------------------\n");
sb.append("ns % Task name\n");
sb.append("---------------------------------------------\n");
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMinimumIntegerDigits(9);
nf.setGroupingUsed(false);
NumberFormat pf = NumberFormat.getPercentInstance();
pf.setMinimumIntegerDigits(3);
pf.setGroupingUsed(false);
for (TaskInfo task : getTaskInfo()) {
sb.append(nf.format(task.getTimeNanos())).append(" ");
sb.append(pf.format((double) task.getTimeNanos() / getTotalTimeNanos())).append(" ");
sb.append(task.getTaskName()).append("\n");
}
}
return sb.toString();
}
/**
* Generate an informative string describing all tasks performed
* <p>For custom reporting, call {@link #getTaskInfo()} and use the task info
* directly.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder(shortSummary());
if (this.keepTaskList) {
for (TaskInfo task : getTaskInfo()) {
sb.append("; [").append(task.getTaskName()).append("] took ").append(task.getTimeNanos()).append(" ns");
long percent = Math.round(100.0 * task.getTimeNanos() / getTotalTimeNanos());
sb.append(" = ").append(percent).append("%");
}
}
else {
sb.append("; no task info kept");
}
return sb.toString();
}
private static long nanosToMillis(long duration) {
return TimeUnit.NANOSECONDS.toMillis(duration);
}
private static double nanosToSeconds(long duration) {
return duration / 1_000_000_000.0;
}
/**
* Nested class to hold data about one task executed within the {@code StopWatch}.
*/
public static final class TaskInfo {
private final String taskName;
private final long timeNanos;
TaskInfo(String taskName, long timeNanos) {
this.taskName = taskName;
this.timeNanos = timeNanos;
}
/**
* Get the name of this task.
*/
public String getTaskName() {
return this.taskName;
}
/**
* Get the time in nanoseconds this task took.
* @since 5.2
* @see #getTimeMillis()
* @see #getTimeSeconds()
*/
public long getTimeNanos() {
return this.timeNanos;
}
/**
* Get the time in milliseconds this task took.
* @see #getTimeNanos()
* @see #getTimeSeconds()
*/
public long getTimeMillis() {
return nanosToMillis(this.timeNanos);
}
/**
* Get the time in seconds this task took.
* @see #getTimeMillis()
* @see #getTimeNanos()
*/
public double getTimeSeconds() {
return nanosToSeconds(this.timeNanos);
}
}
}
- 创建StopWatch实例
- 调用start()方法
- 执行业务校验
- 保存任务名称
- 记录当前系统时间
- 调用stop()方法
- 执行业务校验
- 计算任务耗时
- 将当前任务添加到任务列表中(keepTaskList = true时)
- 任务执行数加一
- 清空当前任务
ApplicationRunner启动加载器
- 概念:开发中有可能需要实现在项目启动后执行的功能,如读取配置文件,数据库连接等。SpringBoot提供的一种简单的实现方案就是CommandLineRunner和ApplicationRunner接口,实现功能的代码放在实现的run方法中,它们执行的时机为容器启动完成的时候。
- 实现方式:
- 实现CommandLineRunner接口,重写run方法,通过order排序。
- 实现ApplicationRunner接口,重写run方法,通过order排序。(order值相同ApplicationRunner优先)
源码分析
- 以run()方法的callRunners()方法为入口。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
// 1.获取所有实现ApplicationRunner接口的类
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 2.获取所有实现CommandLineRunner的类
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 3.根据其@Order进行排序
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
// 4.调用ApplicationRunner其run方法
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
// 5.调用CommandLineRunner其run方法
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}