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(),它其实返回了当前主程序类的 同级以及子级 的包组件。
}
HelloWorldApplication启动加载的Bean中,并不会加载Test,这也就是为什么,我们要把DemoApplication放在项目的最高级中。
3.2@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector 继承了 DeferredImportSelector 继承了 ImportSelector,ImportSelector有一个方法为:selectImports。
getCandidateConfigurations
它其实是去加载 public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”,这个外部文件,有很多自动配置的类。
@EnableAutoConfiguration
借助AutoConfigurationImportSelector.class
,可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。
3.3SpringFactoriesLoader
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实例初始化的时候,它会提前做几件事情:
2.1springApplication构造化执行流程
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));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
从以上三个资源文件得到数据。————————————————————————————————————————————————————
//listener的加载与之前的Initializers加载同理,之后就能找到已经提前写好的相关监听配置了。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//该方法是通过遍历栈,找到main方法,从而找到启动类,并将其赋值给mainApplicationClass属性。
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2springApplication运行执行流程
(2) SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。
(3) 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)
(4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
(5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。
(6) 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。(7) ApplicationContext创建好之后,SpringApplication会再次借助SpringFactoriesLoader,查找并加载classpath中所有可用的ApplicationContextInitializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。
(8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。
(9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
private void prepareAnalyzer(ConfigurableApplicationContext context,FailureAnalyzer analyzer) {
if (analyzer instanceof BeanFactoryAware) {
((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());
}
}
(10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
(11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。
(12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
(13) 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)
去除事件通知点后,整个流程如下:
public void finished(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFinishedListener(listener, context, exception);
}
}