大家好,我是丁甲,很久沒有寫部落格了,最近由于工作原因,需要重新面試,是以最近會寫幾篇關于面試的部落格。
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實作發現與加載