首先,我们直接追踪@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}
)}
)
这些注解虽然看起来很多,但是除去元注解,真正起作用的注解只有以下三个注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
?可以看到,除去元注解,剩下的@Configuration
注解,它的作用就是将当前类申明为配置类,同时还可以使用@bean
注解将类以方法的形式实例化到spring容器,而方法名就是实例名,springboot靠这个注解去除了xml配置。
@ComponentScan
作用就是扫描当前包以及子包,将有@Component
,@Controller
,@Service
,@Repository
等注解的类注册到容器中,以便调用。
注:如果@ComponentScan
不指定basePackages
,那么默认扫描当前包以及其子包,而@SpringBootApplication
里的@ComponentScan
就是默认扫描,所以我们一般都是把springboot启动类放在最外层,以便扫描所有的类。
@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 {};
}
@EnableAutoConfiguration是借助@Import的帮助导入AutoConfigurationImportSelector,将所有符合自动配置条件的bean定义加载到IoC容器
注:在AutoConfigurationImportSelector中使用SpringFactoriesLoader加载bean
?上图就是从SpringBoot的autoconfigure依赖包中的META-INF/spring.factories配置文件中摘录的一段内容,可以很好地说明问题。
从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
?
创建一个SpringApplication对象,并调用了run方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
//获取所有初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//获取所有监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//定位main方法
this.mainApplicationClass = this.deduceMainApplicationClass();
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
//获取所有初始化器的名称集合
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
? //根据名称集合实例化这些初始化器(通过反射)
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
从源码可以看出,该配置模块的主要使用到了SpringFactoriesLoader,即Spring工厂加载器,该对象提供了loadFactoryNames方法,SpringFactoriesLoader在META-INF/spring.factories配置文件里收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。
同样跟踪源码,发现其实监听器和初始化的操作是基本一样的,这里就不细说了
跟踪源码进入deduceMainApplicationClass
方法
private Class<?> deduceMainApplicationClass() {
try {
//通过创建运行时异常的方式获取栈
StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace.length;
//遍历获取main方法所在的类并且返回
for(int var4 = 0; var4 < var3; ++var4) {
StackTraceElement stackTraceElement = var2[var4];
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException var6) {
}
return null;
}