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

如何让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);
        }
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot项目中,可以使用初始化代码来实现项目启动时的一些初始化操作。下面以示例代码进行解释: 1. 创建一个类,命名为ApplicationRunnerImpl实现ApplicationRunner接口,该接口继承了CommandLineRunner接口,用于在Spring Boot项目启动之后执行特定代码。 ```java import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class ApplicationRunnerImpl implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { // 这里写入初始化代码,例如加载配置、数据库连接等操作 System.out.println("Spring Boot项目启动初始化代码执行!"); } } ``` 2. 在启动类中,使用@SpringBootApplication注解启动Spring Boot应用程序。 ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 在上述示例代码中,当Spring Boot项目启动之后,ApplicationRunnerImpl类中的run方法会被执行,可以在此方法中编写一些初始化的代码。比如加载配置文件、初始化数据库连接等。上面的例子中,run方法内只打印了一条信息。实际应用中,可以根据需要编写具体的初始化逻辑。 当代码执行时,控制台会打印出"Spring Boot项目启动初始化代码执行!"的信息,表示初始化代码成功执行。 这种方式非常适用于需要在项目启动时执行一些初始化操作的场景,可以方便地集成到Spring Boot框架中,实现项目的自动初始化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值