SpringBoot -- SpringBoot项目启动成功后执行一段代码

  因为业务需要,做了一个小项目,不让植入太多依赖,所以集成了H2用着很爽,但是在配置多数据源时因为修改了加载数据源的方式,导致初始化sql总是不成功,所以就想着在SpringBoot启动后自动执行一段逻辑,加载sql文件,进行表的初始化。
  SpringBoot启动后执行一段逻辑主要有两种方式:实现ApplicationRunner接口、实现CommandLineRunner。两种实现方式的不同之处在于run方法中接收的参数类型不一样。

1. 实现ApplicationRunner接口

@Component
public class ApplicationRunnerService implements ApplicationRunner {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRunnerService.class);

    @Autowired
    @Qualifier("h2Template")
    private JdbcTemplate h2Template;

    @Value("${invoke.schema.location}")
    private String schema;

    @Value("${invoke.data.location}")
    private String data;

    /**
     * @Author: TheBigBlue
     * @Description: 项目启动,执行sql文件初始化
     * @Date: 2019/9/19
     * @Param args:
     * @Return:
     **/
    @Override
    public void run(ApplicationArguments args) {
        String schemaContent = FileUtil.readFileFromClassPath(schema);
        String dataContent = FileUtil.readFileFromClassPath(data);
        //获取所有表名
        List<String> tableList = h2Template.queryForList("show tables")
                .stream().map(meta -> (String) meta.get("TABLE_NAME"))
                .collect(Collectors.toList());
        //如果H2中不存在任何表,则创建,如果已经存在了,则只初始化数据
        if (IsNullUtil.isBlank(tableList)) {
            LOGGER.info("初始化{}文件数据", data);
            h2Template.execute(schemaContent);
        }
        LOGGER.info("初始化{}文件数据", data);
        //初始化数据
        h2Template.execute(dataContent);
    }


}

2. 实现CommandLineRunner接口

@Component
public class CommandLineRunnerImpl implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("通过实现CommandLineRunner接口,在spring boot项目启动后打印参数");
        for (String arg : args) {
            System.out.print(arg + " ");
        }
        System.out.println();
    }
}

指定执行顺序

  当项目中同时实现了ApplicationRunner和CommondLineRunner接口时,可使用Order注解或实现Ordered接口来指定执行顺序,值越小越先执行。

@Component
@Order(1000)
public class ApplicationRunnerService implements ApplicationRunner {}
@Component
public class ApplicationRunnerService implements ApplicationRunner, Ordered {
    @Override
    public int getOrder() {
        return 1000;
    }
}

原理

  Debug可以看到,都是在 org.springframework.boot.SpringApplication#run(java.lang.String…)方法内的afterRefresh(上下文刷新后处理)方法后,会执行callRunners方法。

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			//刷新上下文
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			//调用runners
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

  callRunners方法具体实现如下,可以看出上下文完成刷新后,依次调用注册的runners, runners的类型为 ApplicationRunner 或 CommandLineRunner。

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

案例地址

  https://github.com/linj6/springboot-learn/tree/master/springboot-runner

发布了66 篇原创文章 · 获赞 18 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Aeve_imp/article/details/101218758