计时器源码
org.springframework.boot.SpringApplication#run(java.lang.String…)
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
//1、启动计时器
stopWatch.start();
try {
/*......*/
//2、停止计时器
stopWatch.stop();
/*......*/
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
return context;
}
//1
//id
private final String id;
//是否需要保存每次执行过的任务信息
private boolean keepTaskList = true;
//任务集合
private final List<TaskInfo> taskList = new LinkedList<TaskInfo>();
//当前任务的开始时间
private long startTimeMillis;
//计时器是否在运行
private boolean running;
//当前正在执行的任务名
private String currentTaskName;
//最近一次的任务信息
private TaskInfo lastTaskInfo;
//当前计时器共执行了多少个任务
private int taskCount;
//执行任务的总耗时
private long totalTimeMillis;
//1.1
public void start() throws IllegalStateException {
//1.1.1
start("");
}
//1.1.1
public void start(String taskName) throws IllegalStateException {
//判断当前计时器是否正在工作
if (this.running) {
//工作的直接抛出异常
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
//否则 将计时器状态设置为工作状态
this.running = true;
//设置任务名称
this.currentTaskName = taskName;
//设置开始时间为当前系统时间
this.startTimeMillis = System.currentTimeMillis();
}
//2
public void stop() throws IllegalStateException {
//判断当前计时器是否正在工作
if (!this.running) {
//不是工作状态的话,抛出异常
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
//获取任务的执行时间
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
//更新计时器执行的总时间
this.totalTimeMillis += lastTime;
//更新最后一次执行任务的信息
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
//是否需要记录任务
if (this.keepTaskList) {
//需要的话 添加到任务集合中
this.taskList.add(lastTaskInfo);
}
//更新计时器执行任务的个数
++this.taskCount;
//更新任务状态为停止状态
this.running = false;
//任务名清空
this.currentTaskName = null;
}
演示使用
public static void main(String[] args) throws InterruptedException {
//SpringApplication.run(Application.class, args);
StopWatch myWatch = new StopWatch("myWatch");
myWatch.start("task1");
Thread.sleep(2000L);
myWatch.stop();
myWatch.start("task2");
Thread.sleep(3000L);
myWatch.stop();
myWatch.start("task3");
Thread.sleep(1000L);
myWatch.stop();
System.out.println(myWatch.prettyPrint());
}
控制台输出
StopWatch 'myWatch': running time (millis) = 6002
-----------------------------------------
ms % Task name
-----------------------------------------
02001 033% task1
03000 050% task2
01001 017% task3
Process finished with exit code 0
代码优点
- 短小精悍
- 命名严谨
- 考录周到(异常、输出美观)
启动加载器案例演示
-
方式一
- 实现CommandLineRunner接口
- 重写run方法
- 通过order排序
-
方式二
- 实现ApplicationRunner接口
- 重写run方法
- 通过order排序
-
排序规则:
- 通过order值指定排序
- order值相同ApplicationRunner实现优先
代码演示
@Component
@Order(1)
public class FirstCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("\u001B[32m >>>>> start first runner <<<<<");
}
}
@Component
@Order(2)
public class SecondCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("\u001B[32m >>>>> start second runner <<<<<");
}
}
@Component
@Order(1)
public class FirstApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("\u001B[32m >>>>> start first ApplicationRunner runner <<<<<");
}
}
@Component
@Order(2)
public class SecondApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("\u001B[32m >>>>> start second ApplicationRunner runner <<<<<");
}
}
控制台输出
>>>>> start first ApplicationRunner runner <<<<<
>>>>> start first runner <<<<<
>>>>> start second ApplicationRunner runner <<<<<
>>>>> start second runner <<<<<
启动加载器原理解析
org.springframework.boot.SpringApplication#run(java.lang.String…)
public ConfigurableApplicationContext run(String... args) {
/*......*/
try {
/*......*/
//2
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
/*......*/
//1
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
return context;
}
//1
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
//获取ApplicationRunner接口的实现
//1.1 context.getBeansOfType方法
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
//获取CommandLineRunner接口的实现
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
//通过order注解的值进行排序
AnnotationAwareOrderComparator.sort(runners);
//遍历runners
for (Object runner : new LinkedHashSet<>(runners)) {
//如果为ApplicationRunner则调用其run方法
if (runner instanceof ApplicationRunner) {
//1.2
callRunner((ApplicationRunner) runner, args);
}
//如果为CommandLineRunner则调用其run方法
if (runner instanceof CommandLineRunner) {
//1.3
callRunner((CommandLineRunner) runner, args);
}
}
}
//1.1
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException {
//1.1.1
assertBeanFactoryActive();
//getBeanFactory()调用后获得DefaultListableBeanFactory对象,在调用其getBeansOfType方法获得指定类型的bean实例
return getBeanFactory().getBeansOfType(type);
}
//1.1.1
protected void assertBeanFactoryActive() {
//该状态是在调用refre方法时,通过调用org.springframework.context.support.AbstractApplicationContext#prepareRefresh 方法进行设置的
if (!this.active.get()) {
if (this.closed.get()) {
throw new IllegalStateException(getDisplayName() + " has been closed already");
}
else {
throw new IllegalStateException(getDisplayName() + " has not been refreshed yet");
}
}
}
//1.2
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
//2、 argus
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
//1.3
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
//2、 argus
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
//2
public DefaultApplicationArguments(String... args) {
Assert.notNull(args, "Args must not be null");
//2.1
this.source = new Source(args);
this.args = args;
}
//2.1
Source(String[] args) {
//2.1.1
super(args);
}
//2.1.1
public SimpleCommandLinePropertySource(String... args) {
//2.1.1.1 parse
super(new SimpleCommandLineArgsParser().parse(args));
}
//2.1.1.1
public CommandLineArgs parse(String... args) {
//对argus参数进行解析,将 --key=value形式的字符串解析成key、value的形式
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
String optionText = arg.substring(2);
String optionName;
String optionValue = null;
int indexOfEqualsSign = optionText.indexOf('=');
if (indexOfEqualsSign > -1) {
optionName = optionText.substring(0, indexOfEqualsSign);
optionValue = optionText.substring(indexOfEqualsSign + 1);
}
else {
optionName = optionText;
}
if (optionName.isEmpty()) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
两个Runner接口实现类差异点
- 执行优先级差异
- run方法入参不一致
两个Runner接口实现类相同点
- 调用点一样
- 实现方法名一样
面试题
- SpringBoot计时器的实现?它有哪些优点?
- 让你去设计实现一个计时器,你的思路?
- 怎么实现在SpringBoot启动后执行程序?
- 启动加载如何实现
- 启动加载器的实现有什么异同点?
- 启动加载器的调用时机?