框架计时器、启动加载器

计时器源码

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

1
2
3

代码优点

  1. 短小精悍
  2. 命名严谨
  3. 考录周到(异常、输出美观)

启动加载器案例演示

  • 方式一

    1. 实现CommandLineRunner接口
    2. 重写run方法
    3. 通过order排序
  • 方式二

    1. 实现ApplicationRunner接口
    2. 重写run方法
    3. 通过order排序
  • 排序规则:

    1. 通过order值指定排序
    2. 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启动后执行程序?
  • 启动加载如何实现
  • 启动加载器的实现有什么异同点?
  • 启动加载器的调用时机?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值