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);
		}
	}
}
  • 使用步骤:
  1. 创建StopWatch实例
  2. 调用start()方法
    1. 执行业务校验
    2. 保存任务名称
    3. 记录当前系统时间
  3. 调用stop()方法
    1. 执行业务校验
    2. 计算任务耗时
    3. 将当前任务添加到任务列表中(keepTaskList = true时)
    4. 任务执行数加一
    5. 清空当前任务

ApplicationRunner启动加载器

  • 概念:开发中有可能需要实现在项目启动后执行的功能,如读取配置文件,数据库连接等。SpringBoot提供的一种简单的实现方案就是CommandLineRunner和ApplicationRunner接口,实现功能的代码放在实现的run方法中,它们执行的时机为容器启动完成的时候。
  • 实现方式:
    1. 实现CommandLineRunner接口,重写run方法,通过order排序。
    2. 实现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);
        }
    }
}