大家好,我是丁甲,很久沒有寫博客了,最近由於工作原因,需要重新面試,所以最近會寫幾篇關於面試的博客。
Spring Boot的運行機制是什麼?
這個問題通常會被問到,之前丁甲也只是背書應付面試,但這樣始終是好的,於是我決定看看Spring Boot註解的源碼,並從源碼層面上來看看Spring Boot的運行機制究竟是什麼。
首先,我們隨便打開一個Spring Boot項目,我們能夠發現他的啓動類是都是有一個非常重要的註解:
@SpringBootApplication public class Demo123Application { public static void main(String[] args) { SpringApplication.run(Demo123Application.class, args); } }
這個非常重要的註解就是@SpringBootApplicaiton。
其次,任意打開一個SpringBoot項目,不難發現他的啓動類(使用@SpringBootApplication註解的類)目錄層級非常高,是頂級目錄,這是爲什麼呢?今天我們從源碼的角度出發,進一步的解析SpringBoot項目的運行機制。
@SpringBootApplication註解是一個複合註解
@SpringBootApplication是一個複合註解,並非一個單一的註解,我們點開一個@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; }
從這段源碼中不難看出,@SpringBootApplication註解是由多個註解組合而成的,而在這幾個組合註解中其中最重要的核心註解當屬@EnableAutoConfiguration、@ComponentScan、@Configuration。
我們從這三個核心註解來分下SpringBoot項目的運行機制。
@Configuration註解
源碼:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { @AliasFor( annotation = Component.class ) String value() default ""; boolean proxyBeanMethods() default true; }
@coofiguration註解是一個十分常用的註解,經常都會用到。它的作用通過java類將寫好的類在Spring容器初始化的時候將其加載到Spring容器中去,摒棄了xml配置文件來初始化Bean,十分的方便,和他一起使用的註解爲@Bean
@ComponentScan
源碼:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; String resourcePattern() default "**/*.class"; boolean useDefaultFilters() default true; ComponentScan.Filter[] includeFilters() default {}; ComponentScan.Filter[] excludeFilters() default {}; boolean lazyInit() default false; @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Filter { FilterType type() default FilterType.ANNOTATION; @AliasFor("classes") Class<?>[] value() default {}; @AliasFor("value") Class<?>[] classes() default {}; String[] pattern() default {}; } }
@componentScan註解,是一個掃描包的註解。在Spring容器初始化的時候,invokeBeanFactoryPostProcessor()方法中會將所有的@componentScan下面的包掃描並將類通過一系列的操作(java -> class ->BeanDefinitonMap ->Singleton)加載入Spring容器中。
@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 {}; }
@EnableAotuConfiguration類中的核心註解爲@import以及AutoConfigurationImportSelector類。@Import註解也是我們平時用得非常多的一個註解,同一模塊下的引包,以及JDK自帶包的引用都是需要用@Import註解使用的,這個註解是非常好理解的,然後我們再來說一說@Import的AutoConfigurationimportSelector類。
首先我們來看看autoSonfigurationImportSelector類的源碼:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { private static final AutoConfigurationImportSelector.AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationImportSelector.AutoConfigurationEntry(); private static final String[] NO_IMPORTS = new String[0]; private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class); private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude"; private ConfigurableListableBeanFactory beanFactory; private Environment environment; private ClassLoader beanClassLoader; private ResourceLoader resourceLoader; private AutoConfigurationImportSelector.ConfigurationClassFilter configurationClassFilter; public AutoConfigurationImportSelector() { }
首先這個類是實現了DeferredImportSelector接口,然後在看來他的核心方法 :
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct."); return configurations; }
他的核心方法就是這個SpringFactoriesLoader.loadFactoryNames(),作用就是將META-INF/spring.factories的文件信息讀取出來。然後作爲Bean加載到Spring容器中。
總結:
1、SpringBoot的運行機制要從他的核心註解@SpringBootAppliccation說起
2、@SpringBootApplication的核心註解爲:@ComponentScan、@Configuration、@EnabledAutoConfiguration
3、@ComponentScan發現掃描包並將包下面的類加載進Spring容器作爲Spring Bean
4、@Configuration + @Bean將自定義的配置類也加載入Spring容器中作爲SpringBean
5、@EnabledAutoConfiguration、@Import(AutoConfigurationIpmortSelector)、@AutoConfigurationPackage三個註解將Spring容器裏面的Bean實現發現與加載