1springboot的启动类重要注解的解读

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

其中@SpringBootApplication包括以下注解:

@SpringBootConfiguration //继承了Configuration,表示当前是注解类

//@EnableAutoConfiguration则是开启Spring Boot的自动配置功能,
//Spring Boot根据依赖中的jar包,自动选择实例化某些配置,其借助@import的帮助。
@EnableAutoConfiguration 

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

1@Configuration

@Configuration注解,在springboot中我们大多用配置类来解决配置问题,配置bean方式的不同:a)xml配置文件的形式配置bean;b)javaconfiguration的配置形式配置bean。

@Configuration
public class MockConfiguration{
    //bean定义
}

注入bean方式的不同:a)xml配置文件的形式注入bean;b)javaconfiguration的配置形式注入bean。

@Configuration
public class MockConfiguration{
    @Bean //任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。
    public MockService mockService(){
        return new MockServiceImpl();
    }
}

表达bean之间依赖关系的不同:a)xml配置文件的形式表达依赖关系;b)javaconfiguration配置的形式表达依赖关系

@Configuration
public class MockConfiguration{
 @Bean
 public MockService mockService(){
     return new MockServiceImpl(dependencyService());//如果一个bean的定义依赖其他bean,则直接调用对应的JavaConfig类中依赖bean的创建方法就可以了。
 }
 @Bean
 public DependencyService dependencyService(){
     return new DependencyServiceImpl();
 }
}

2@ComponentScan

@ComponentScan注解,定义从扫描的路径从中找出标识了需要装配的类,自动装配到spring的bean容器中。我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

3@EnableAutoConfiguration

@EnableAutoConfiguration自动配置,开启Spring Boot的自动配置功能,Spring Boot根据依赖中的jar包自动选择实例化某些配置,其借助@import的帮助。

@AutoConfigurationPackage //【重点注解】
@Import(AutoConfigurationImportSelector.class) //【重点注解】,当然还有其中比较重要的一个类就是:AutoConfigurationImportSelector.class
public @interface EnableAutoConfiguration {}

3.1@AutoConfigurationPackage

AutoConfigurationPackage注解:从哪里加载Bean

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
 register(registry, new PackageImport(metadata).getPackageName()); //new PackageImport(metadata).getPackageName(),它其实返回了当前主程序类的 同级以及子级 的包组件。
}

spring boot 远程new file springboot启动远离_实例化


HelloWorldApplication启动加载的Bean中,并不会加载Test,这也就是为什么,我们要把DemoApplication放在项目的最高级中。

3.2@Import(AutoConfigurationImportSelector.class)

spring boot 远程new file springboot启动远离_spring_02


AutoConfigurationImportSelector 继承了 DeferredImportSelector 继承了 ImportSelector,ImportSelector有一个方法为:selectImports。

spring boot 远程new file springboot启动远离_实例化_03


spring boot 远程new file springboot启动远离_spring_04


spring boot 远程new file springboot启动远离_实例化_05


getCandidateConfigurations它其实是去加载 public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”,这个外部文件,有很多自动配置的类。

spring boot 远程new file springboot启动远离_spring_06


@EnableAutoConfiguration借助AutoConfigurationImportSelector.class,可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。

3.3SpringFactoriesLoader

spring boot 远程new file springboot启动远离_加载_07


SpringFactoriesLoader从指定的配置文件META-INF/spring.factories加载配置。即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类

List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());

2SpringBoot的启动流程

(1) 如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

spring boot 远程new file springboot启动远离_加载_08

2.1springApplication构造化执行流程

spring boot 远程new file springboot启动远离_spring_09

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
//首先,如果primarySources长度大于0(即是数组或容器),则把这些primarySources全部传到内置的hashset中。
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		
//判断整个环境中是否存在javax.servlet.Servlet类和
//org.springframework.web.context.ConfigurableWebApplicationContext类这两个类,
//如果存在话,则表示web环境正常,返回true,否则返回false。
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		
//首先会调getSpringFactoriesInstances方法,获取资源文件【三个META-INF/spring.factories文件见下面】的实例,
//加载ApplicationContextInitializer类<ApplicationContextInitializer,value>,然后createSpringFactoriesInstances实例化7个value,返回这7个实例化对象的集合;
//最后给this.initializers = new ArrayList<>(initializers);赋值
		this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));		
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

spring boot 远程new file springboot启动远离_实例化_10


spring boot 远程new file springboot启动远离_加载_11


spring boot 远程new file springboot启动远离_加载_12


spring boot 远程new file springboot启动远离_加载_13


spring boot 远程new file springboot启动远离_加载_14


spring boot 远程new file springboot启动远离_实例化_15


setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));从以上三个资源文件得到数据。————————————————————————————————————————————————————

spring boot 远程new file springboot启动远离_spring_16

//listener的加载与之前的Initializers加载同理,之后就能找到已经提前写好的相关监听配置了。
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		
//该方法是通过遍历栈,找到main方法,从而找到启动类,并将其赋值给mainApplicationClass属性。
		this.mainApplicationClass = deduceMainApplicationClass();
	}

2.2springApplication运行执行流程

(2) SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。

spring boot 远程new file springboot启动远离_实例化_17


spring boot 远程new file springboot启动远离_spring_18


spring boot 远程new file springboot启动远离_spring_19


spring boot 远程new file springboot启动远离_实例化_20


spring boot 远程new file springboot启动远离_spring_21


(3) 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)

spring boot 远程new file springboot启动远离_spring_22


spring boot 远程new file springboot启动远离_实例化_23


(4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。

spring boot 远程new file springboot启动远离_实例化_24


(5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。

spring boot 远程new file springboot启动远离_实例化_25


(6) 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。(7) ApplicationContext创建好之后,SpringApplication会再次借助SpringFactoriesLoader,查找并加载classpath中所有可用的ApplicationContextInitializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。

spring boot 远程new file springboot启动远离_spring_26


(8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。

spring boot 远程new file springboot启动远离_加载_27


(9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。

private void prepareAnalyzer(ConfigurableApplicationContext context,FailureAnalyzer analyzer) {
 if (analyzer instanceof BeanFactoryAware) {
 ((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());
 }
}

(10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

spring boot 远程new file springboot启动远离_实例化_28


(11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。

spring boot 远程new file springboot启动远离_spring_29


(12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。

spring boot 远程new file springboot启动远离_实例化_30


(13) 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)

去除事件通知点后,整个流程如下:

public void finished(ConfigurableApplicationContext context, Throwable exception) {
 for (SpringApplicationRunListener listener : this.listeners) {
 callFinishedListener(listener, context, exception);
 }
}

spring boot 远程new file springboot启动远离_实例化_31