欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

28.SpringBoot入口类原理解析

程序员文章站 2022-04-15 23:22:31
1.SpringBoot入口类通过Spring Initializr新建项目,SpringBoot会自动生成如下所示的入口启动类。@SpringBootApplicationpublic class AppApplication {public static void main(String[] args) {SpringApplication.run(AppApplication.class, args);}}2.@SpringBootApplication原理@Sprin...

1.SpringBoot入口类
通过Spring Initializr新建项目,SpringBoot会自动生成如下所示的入口启动类。

@SpringBootApplication
public class AppApplication {

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

}

2.@SpringBootApplication原理
@SpringBootApplication开启了Spring的组件扫描和SpringBoot自动配置功能。实际上,它是一个复合注解,包含3个重要的注解@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。在SpringBoot早期版本中,需要在入口类中同时添加这3个注解,但是从1.2.0版本之后,只需要在入口类添加@SpringBootApplication注解即可,其源代码如下。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
	excludeFilters = {
		@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
		@Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})
	}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "nameGenerator"
    )
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
  • @SpringBootConfiguration:表明该类使用Spring基于Java的注解,SpringBoot推荐使用基于Java的注解而不是XML配置。查看@SpringBootConfiguration的源代码可知,其是对@Configuration进行的简单包装,然后取名为SpringBootConfiguration。
  • @EnableAutoConfiguration:开启自动配置的功能。查看@EnableAutoConfiguration的源代码可知,其包含@Import注解。@Import注解的主要作用是借助EnableAutoConfigurationImportSelector将SpringBoot应用所有符合条件的@Configuration配置加载到当前SpringBoot创建并使用的IOC容器中(Spring应用程序上下文ApplicationContext)。Spring框架提供了很多@Enable开头的注解,这些注解都是借助@Import的支持,来收集和注册特定场景相关的bean定义。
  • @ComponentScan:启动组件扫描注解,开发的组件或bean定义能自动发现并注入到Spring应用程序上下文。如控制层注解@Controller、服务层注解@Service和@Component等,这些注解都可以被@ComponentScan注解扫描到。

3.SpringApplication的run方法
除了@SpringBootConfiguration注解,入口类中还有一个重要的内容是SpringApplication.run方法。在run方法中,首先创建一个SpringApplication的对象实例,然后调用SpringApplication的run方法,其源码如下所示。

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);
	}
}

从源代码中可以看出,SpringBoot首先开启了一个SpringApplicationRunListeners监听器,然后通过createApplicationContext、prepareContext和refreshContext方法创建、准备和刷新应用上下文ConfigurableApplicationContext,通过应用上下文加载应用所需要的类和各种配置环境等,最后启动一个应用实例。

4.SpringApplicationRunListeners监听器
SpringApplicationRunListeners接口规定了SpringBoot的生命周期,在各个生命周期广播相应的ApplicationEvent事件,调用实际的是ApplicationListener,其源代码如下所示。

public interface SpringApplicationRunListener {
	//执行run方法时触发
    default void starting() {
    }

	//环境创建完成时触发
    default void environmentPrepared(ConfigurableEnvironment environment) {
    }

	//上下文建立完成时触发
    default void contextPrepared(ConfigurableApplicationContext context) {
    }

	//上下文载入配置时触发
    default void contextLoaded(ConfigurableApplicationContext context) {
    }

    default void started(ConfigurableApplicationContext context) {
    }

    default void running(ConfigurableApplicationContext context) {
    }

    default void failed(ConfigurableApplicationContext context, Throwable exception) {
    }
}

ApplicationListener是Spring框架对Java中监听器模式的一种框架实现,其源代码如下所示。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}

ApplicationListener接口只有一个方法onApplicationEvent。如果在上下文中部署实现了一个ApplicationListener接口的监听器,每当ApplicationEvent事件发布到ApplicationContext时,该监听器就会得到通知。如果要为SpringBoot应用添加自定义的ApplicationListener,可通过SpringApplication.addListeners()或者SpringApplication.setListeners()方法添加一个或者多个自定义的ApplicationListener。

5.ApplicationContextInitializer接口
在SpringBoot准备上下文prepareContext的时候,会对ConfigurableApplicationContext实例做进一步的设置或处理,prepareContext源码如下所示。

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	context.setEnvironment(environment);
	this.postProcessApplicationContext(context);
	//对上下文进行设置和处理
	this.applyInitializers(context);
	listeners.contextPrepared(context);
	if (this.logStartupInfo) {
	    this.logStartupInfo(context.getParent() == null);
	    this.logStartupProfileInfo(context);
	}
	
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
	    beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	
	if (beanFactory instanceof DefaultListableBeanFactory) {
	    ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	
	if (this.lazyInitialization) {
	    context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	
	Set<Object> sources = this.getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	this.load(context, sources.toArray(new Object[0]));
	listeners.contextLoaded(context);
}

在准备上下文prepareContext方法中,通过applyInitializers方法对context上下文进行设置和处理,applyInitializers的源码如下所示。

protected void applyInitializers(ConfigurableApplicationContext context) {
	for (ApplicationContextInitializer initializer : getInitializers()) {
		Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
				ApplicationContextInitializer.class);
		Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
		initializer.initialize(context);
	}
}

在applyInitializers方法中,主要是调用ApplicationContextInitializer的initialize方法对应用上下文进行设置和处理。ApplicationContextInitializer本质上是一个回调接口,用于在ConfigurableApplicationContext执行refresh操作之前对它进行一些初始化操作。一般情况下,开发者无需自定义一个ApplicationContextInitializer,如果需要自定义一个ApplicationContextInitializer,则可以通过SpringApplication.addInitializers()设置。

6.ApplicationRunner与CommandLineRunner
ApplicationRunner与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;
}
@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;
}

在ApplicationRunner与CommandLineRunner类中,只有一个run方法,但它们的入参不一样,分别是ApplicationArguments和可变String数组。如果有多个ApplicationRunner与CommandLineRunner实现类,并且需要按照一定的顺序执行时,可以通过在实现类上添加@Order(value=整数值)来实现,那么SpringBoot就会按照@Order中value值从小到大依次执行。
如果想再SpringBoot启动的时候完成一些特定的代码,则可以通过实现ApplicationRunner与CommandLineRunner接口来完成,这两个接口实现方式一样,如实现CommandLineRunner的接口。

public class MyCommandRunner implements CommandLineRunner {

	@Override
	public void run(String... args) throws Exception{
		//todo
	}
}

本文地址:https://blog.csdn.net/Jgx1214/article/details/107328642