大家好,我是丁甲,很久没有写博客了,最近由于工作原因,需要重新面试,所以最近会写几篇关于面试的博客。
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实现发现与加载