写过Spring Boot都知道需要有一个入口类,就是本例子中的TutorialApplication,而这个类上面必不可上的需要有一个@SpringBootApplication注解。
点击进入该注解,我们可以发现其是一个复合注解,包括@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。
@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 {};
}@SpringBootConfiguration
该注解底层其实就是@Configuration注解。熟悉Spring的发展里程碑就知道这是Java Config的配置形式。
通过该注解修饰,表示该类是一个配置类。
@EnableAutoConfiguration
该注解其实也是一个复合注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@documented @Inherited @AutoConfigurationPackage @import(AutoConfigurationimportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class>[] exclude() default {}; String[] excludeName() default {};
}其实现也是通过类似@import的方式注入AutoConfigurationimportSelector类,并借助该类将所有符合条件的Configuration注解修饰的配置类加载到Spring Boot容器中。从classpath中搜索所有的meta-INF/spring.factories配置文件,将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应配置项通过反射的形式实例化为标注了@Configuration和javaconfig形式的IOC容器配置类,然后汇总为一个并加载到ioc容器中。
image
@ComponentScan
这个注解就不需要多介绍了吧,其作用自动扫描加载符合条件的bean。
SpringApplication从项目的入口第一个碰到的就是SpringApplication类。
@SpringBootApplication public class TutorialApplication { public static void main(String[] args) {
SpringApplication.run(TutorialApplication.class, args);
}
}
进入该类的静态方法run,可以看到其在构造SpringApplication对象
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);
}进入SpringApplication构造方法,可以看到
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new linkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}主要做了如下几件事:
加载Source,这里只有只有Application
image
推断WebApplicationType,该枚举有三种类型NONE、SERVLET、REACTIVE。
设置初始化器变量setInitializers,初始化后得到6个初始化变量,这些类在上面提到的spring.factories中可以找到
image
设置监听器,与上面setInitializers实现类似,最终得到如下10个listeners
image
最后推断带有main函数的所在类,即入口类,这里就是TutorialApplication
private Class> deduceMainApplicationClass() { try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException ex) { // Swallow and continue
} return null;
}
作者:Jackie_Zheng
链接:https://www.jianshu.com/p/f225708caf9e



