如何让SpringBoot项目启动时执行特定代码

前言

(其实直接在main方法里写也不是执行不了) 如果只是简单的一些语句,写在main中可能会方便一些

但如果需要调用spring容器中的对象可能会要吃瘪,因为main方法是static的,而获取ioc对象不能使用static直接获取(会报错) 当调用@AutoWired获得ioc容器中的对象时

	@Autowired
    private static TestService testService;
复制代码

Exception in thread "main" java.lang.NullPointerException

当调用@Resource获得ioc容器中的对象时

	@Resource
    private static TestService testService;
复制代码

Caused by: java.lang.IllegalStateException: @Resource annotation is not supported on static fields

两个函数接口

有两个函数接口类都可以实现

1.ApplicationRunner接口

源码

package org.springframework.boot;

@FunctionalInterface
public interface ApplicationRunner {
    void run(ApplicationArguments args) throws Exception;
}
复制代码

使用方法

使用@SpringBootApplication注释的ApplicationMain启动类实现ApplicationRunner接口,并实现run方法即可 在main方法将spring启动完成后会执行run方法中的程序

@SpringBootApplication
public class BootTestApplication implements ApplicationRunner{

    public static void main(String[] args) {
        SpringApplication.run(BootTestApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner.run");
    }
}
复制代码

2.CommandLineRunner接口

源码

package org.springframework.boot;

@FunctionalInterface
public interface CommandLineRunner {
    void run(String... args) throws Exception;
}
复制代码

使用方法

其实CommandLineRunnerApplicationRunner使用方法差不多

使用@SpringBootApplication注释的ApplicationMain启动类实现接口,并实现run方法即可 在main方法将spring启动完成后会执行run方法中的程序

@SpringBootApplication
public class BootTestApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(BootTestApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner.run");
    }
}
复制代码

为什么会被执行?两个接口谁先执行?为什么?

举个栗子

我们来看个例子 现在我们有一个service类

@Service
public class TestService {
    public void test(String name){
        System.out.println(name);
    }
}
复制代码

然后main启动类中同时实现ApplicationRunnerCommandLineRunner接口,并调用这个service的方法 看看是谁先被输出

@SpringBootApplication
public class BootTestApplication implements ApplicationRunner , CommandLineRunner {

    @Autowired
    private TestService service;

    public static void main(String[] args) {
        SpringApplication.run(BootTestApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        service.test("ApplicationRunner.run");
    }

    @Override
    public void run(String... args) throws Exception {
        service.test("CommandLineRunner.run");
    }
}
复制代码

结果

可以看到,无论运行几次,结果都是ApplicationRunner先执行,CommandLineRunner后执行

ApplicationRunner.run

CommandLineRunner.run

如图

简单源码分析

我们查看SpringApplication.run(BootTestApplication.class, args) 一层一层被调用到

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }
复制代码

其中有一条this.callRunners(context, applicationArguments);就是回调方法了,我们点开可以看到

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);
        Iterator var4 = (new LinkedHashSet(runners)).iterator();

        while(var4.hasNext()) {
            Object runner = var4.next();
            if (runner instanceof ApplicationRunner) {
                this.callRunner((ApplicationRunner)runner, args);
            }

            if (runner instanceof CommandLineRunner) {
                this.callRunner((CommandLineRunner)runner, args);
            }
        }
    }
复制代码

如果启动类是ApplicationRunner的实现类,那么会调用runner.run(args); 也就是启动类中实现了ApplicationRunner的run方法

	if(runner instanceof ApplicationRunner) {
		this.callRunner((ApplicationRunner)runner, args);
	}

	private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
        try {
            runner.run(args);
        } catch (Exception var4) {
            throw new IllegalStateException("Failed to execute ApplicationRunner", var4);
        }
    }
复制代码

如果启动类是CommandLineRunner 的实现类,那么会调用runner.run(args.getSourceArgs()); 也就是启动类中实现了CommandLineRunner的run方法

	if (runner instanceof CommandLineRunner) {
    	this.callRunner((CommandLineRunner)runner, args);
    }

	private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
        try {
            runner.run(args.getSourceArgs());
        } catch (Exception var4) {
            throw new IllegalStateException("Failed to execute CommandLineRunner", var4);
        }
    }
全部评论

相关推荐

工作基本定了,比较满意。这深度学习真是难搞啊,论文看不懂,代码看不懂,不会改,创新点???&nbsp;还有一个月中期,怎么搞,已经抑郁了,没有小论文要求,我真是非常幸运了,不然就我这科研能力…………&nbsp;感觉自己对自己失去信心,无时无刻不在否定自己取得一些成绩都是运气好罢了,真的能顺利毕业吗?严重内耗、无力感爆棚啊…………
AI牛可乐:哎呀,听起来你最近压力好大呀,毕业论文和工作的双重压力一定让你感觉像是背了两座山呢。深度学习确实挺复杂的,就像是要在一片迷雾中找到出路一样。但是,你已经很棒了,工作都定好了,这是很大的成就呢! 论文的事情,别太担心。每个人都有看不懂代码和论文的时候,这是正常的。至于创新点嘛,有时候灵感就像小猫一样,需要一点一点地哄出来的。你可以先试试把你看过的资料整理一下,说不定就能找到新的思路呢。 中期答辩前,你可以列个计划,一步一步来,把大问题分解成小问题,一点一点克服。记得,你不是一个人在战斗,你的导师、同学都是你的战友哦。 至于信心嘛,别否定自己,每个人都会有低谷的时候。你已经取得的成绩,都是你实力的体现。毕业不是问题,只要你不放弃,就一定能顺利走过去的。加油哦,我相信你可以的! 对了,如果你愿意的话,可以点击我的头像,我们私信聊,也许我能帮你出出主意,或者就是简单地聊聊天,分散一下你的注意力也好呀。🐮💪🌟
点赞 评论 收藏
分享
点赞 评论 收藏
分享
三年之期已到我的offer快到碗里来:9硕都比不上9本
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务