- 配置在META-INF/spring.factories
ApplicationContextInitializer
/** * Callback interface for initializing a Spring {@link ConfigurableApplicationContext} * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}. * *
Typically used within web applications that require some programmatic initialization * of the application context. For example, registering property sources or activating * profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment() * context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support * for declaring a "contextInitializerClasses" context-param and init-param, respectively. * *
{@code ApplicationContextInitializer} processors are encouraged to detect * whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been * implemented or if the @{@link org.springframework.core.annotation.Order Order} * annotation is present and to sort instances accordingly if so prior to invocation. * * @author Chris Beams * @since 3.1 * @param
the application context type * @see org.springframework.web.context.ContextLoader#customizeContext * @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM * @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses * @see org.springframework.web.servlet.FrameworkServlet#applyInitializers */ public interface ApplicationContextInitializer { /** * Initialize the given application context. * @param applicationContext the application to configure */ void initialize(C applicationContext); }
SpringApplicationRunListener
public interface SpringApplicationRunListener { /** * 回调 * Called once the {@link ApplicationContext} has been created and prepared, but * before sources have been loaded. * @param context the application context */ default void contextPrepared(ConfigurableApplicationContext context) { } /** * 回调 * Called once the application context has been loaded but before it has been * refreshed. * @param context the application context */ default void contextLoaded(ConfigurableApplicationContext context) { } }
只需要放在ioc容器中
ApplicationRunner
/** * Interface used to indicate that a bean should run when it is contained within * a {@link SpringApplication}. Multiple {@link ApplicationRunner} beans can be defined * within the same application context and can be ordered using the {@link Ordered} * interface or {@link Order @Order} annotation. * * @author Phillip Webb * @since 1.3.0 * @see CommandLineRunner */ @FunctionalInterface public interface ApplicationRunner { /** * Callback used to run the bean. * @param args incoming application arguments * @throws Exception on error */ void run(ApplicationArguments args) throws Exception; }
CommandLineRunner
/** * Interface used to indicate that a bean should run when it is contained within * a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined * within the same application context and can be ordered using the {@link Ordered} * interface or {@link Order @Order} annotation. *
* If you need access to {@link ApplicationArguments} instead of the raw String array * consider using {@link ApplicationRunner}. * * @author Dave Syer * @since 1.0.0 * @see ApplicationRunner */ @FunctionalInterface public interface CommandLineRunner { /** * Callback used to run the bean. * @param args incoming main method arguments * @throws Exception on error */ void run(String... args) throws Exception; }
- 测试:只要创建四个类分别实现四个接口即可
- 还需要配置一个文件 resources/META-INF/spring.factories
# Initializers org.springframework.context.ApplicationContextInitializer=\ com.atguigu.springboot07.listener.HelloApplicationContextInitializer org.springframework.boot.SpringApplicationRunListener=\ com.atguigu.springboot07.listener.HelloSpringApplicationRunListener
public class HelloApplicationContextInitializer implements ApplicationContextInitializer
{ @Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println("ApplicationContextInitializer...initialize"+applicationContext); } } public class HelloSpringApplicationRunListener implements SpringApplicationRunListener { /** * 如果没有这个有参构造,那么会报错, * Exception in thread "main" java.lang.IllegalArgumentException: Cannot instantiate interface org.springframework.boot.SpringApplicationRunListener : com.atguigu.springboot07.listener.HelloSpringApplicationRunListener * 意思就是: 不能实例化这个类SpringApplicationRunListener,进入这个类发现这个类没有无参构造,只有有参构造 */ public HelloSpringApplicationRunListener(SpringApplication application, String[] args){ } @Override public void starting() { System.out.println("SpringApplicationRunListener...starting..."); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { Object o = environment.getSystemProperties().get("os.name"); System.out.println("SpringApplicationRunListener...environmentPrepared..."+o); } @Override public void contextPrepared(ConfigurableApplicationContext context) { System.out.println("SpringApplicationRunListener...contextPrepared..."); } @Override public void contextLoaded(ConfigurableApplicationContext context) { System.out.println("SpringApplicationRunListener...contextLoaded..."); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { System.out.println("SpringApplicationRunListener...failed..."); } } - HelloApplicationRunner 和 HelloCommandLineRunner 两个类都需要添加一个注解 @Component
@Component public class HelloApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner...run..."); } } @Component public class HelloCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("CommandLineRunner...run..."+ Arrays.asList(args)); } }
- 这里有个问题:
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener { /** * 如果没有这个有参构造,那么会报错, * Exception in thread "main" java.lang.IllegalArgumentException: Cannot instantiate interface org.springframework.boot.SpringApplicationRunListener : com.atguigu.springboot07.listener.HelloSpringApplicationRunListener * 意思就是: 不能实例化这个类SpringApplicationRunListener,进入这个类发现这个类没有无参构造,只有有参构造 */ public HelloSpringApplicationRunListener(SpringApplication application, String[] args){ }
- Spring Boot 的入口类
@SpringBootApplication public class SpringBootBestPracticeApplication { public static void main(String[] args) { SpringApplication.run(SpringBoot07Application.class, args); } }
做过 Spring Boot 项目的都知道,上面是 Spring Boot 最简单通用的入口类。入口类的要求是最顶层包下面第一个含有 main 方法的类,使用注解
@SpringBootApplication
来启用 Spring Boot 特性,使用SpringApplication.run
方法来启动 Spring Boot 项目。来看一下这个类的
run
方法调用关系源码:/** * Static helper that can be used to run a {@link SpringApplication} from the * specified source using default settings. * @param primarySource the primary source to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class> primarySource, String... args) { return run(new Class>[] { primarySource }, args); } /** * Static helper that can be used to run a {@link SpringApplication} from the * specified sources using default settings and user supplied arguments. * @param primarySources the primary sources to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
第一个参数
primarySource
:加载的主要资源类第二个参数
args
:传递给应用的应用参数先用主要资源类来实例化一个
SpringApplication
对象,再调用这个对象的run
方法,所以我们分两步来分析这个启动源码。SpringApplication 的实例化过程:
接着上面的
SpringApplication
构造方法进入以下源码:/** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified primary sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param primarySources the primary bean sources * @see #run(Class, String[]) * @see #SpringApplication(ResourceLoader, Class...) * @see #setSources(Set) */ public SpringApplication(Class>... primarySources) { this(null, primarySources); } /** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified primary sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param resourceLoader the resource loader to use * @param primarySources the primary bean sources * @see #run(Class, String[]) * @see #setSources(Set) */ public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) { // 1、资源初始化资源加载器为 null this.resourceLoader = resourceLoader; // 2、断言主要加载资源类不能为 null,否则报错 Assert.notNull(primarySources, "PrimarySources must not be null"); // 3、初始化主要加载资源类集合并去重 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 4、推断当前 WEB 应用类型(REACTIVE、NONE、SERVLET) /** * NONE: 非 WEB 项目 * SERVLET: servlet WEB 项目 * REACTIVE: 响应式 WEB 项目 */ this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 5、设置初始化器,得到Spring工厂实例 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 6、设置监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 7、推断主入口应用类 this.mainApplicationClass = deduceMainApplicationClass(); }
调用这个对象的
run
方法:
上面分析了
SpringApplication
实例对象构造方法初始化过程,下面继续来看下这个SpringApplication
对象的run
方法的源码和运行流程。/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { // 1、创建并启动计时监控类 StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 2、初始化应用上下文和异常报告集合 ConfigurableApplicationContext context = null; Collection
exceptionReporters = new ArrayList<>(); // 3、设置系统属性 `java.awt.headless` 的值,默认值为:true configureHeadlessProperty(); // 4、创建所有 Spring 运行监听器并发布应用启动事件 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { // 5、初始化默认应用参数类 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 6、根据运行监听器和应用参数来准备 Spring 环境 ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); configureIgnoreBeanInfo(environment); // 7、创建 Banner 打印类 Banner printedBanner = printBanner(environment); // 8、创建应用上下文 context = createApplicationContext(); // 9、准备异常报告器 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 10、准备应用上下文 prepareContext(context, environment, listeners, applicationArguments,printedBanner); // 11、刷新应用上下文 refreshContext(context); // 12、应用上下文刷新后置处理 afterRefresh(context, applicationArguments); // 13、停止计时监控类 stopWatch.stop(); // 14、输出日志记录执行主类名、时间信息 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 15、发布应用上下文启动完成事件 listeners.started(context); // 16、执行所有 Runner 运行器 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { // 17、发布应用上下文就绪事件 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } // 18、返回应用上下文 return context; } 这里只是描述的不详细没有深入方法进行说明,我找了一篇博客详细描述了整个方法调用
https://www.jianshu.com/p/414d3e2f04e9