本文如有任何纰漏、错误,还请不吝指出!html
注:本文提到的Spring容器或者Bean容器,或者Spring Bean容器,都是指同一个事情,那就是代指BeanFactory。关于BeanFactory,后面有机会会再说下。java
几年前接触过SpringBoot
,跑过Demo,当时刚入行,连Spring
都没搞明白,更别说SpringBoot
了,就是以为,哇塞,好厉害,而后一脸懵逼。web
工做中没有用到,又没有去主动学习它。以为很恐惧,这么厉害的东西,确定是很深奥,很复杂吧!。spring
这种心理也形成了必定程度上,对某些事物的望而却步,其实只要向前迈出了步子,一步步慢慢来,才发现,之前的那种恐惧心理是多么的幼稚、胆怯、好笑!编程
SpringBoot
自己并无多大的花样,全部的知识点其实还都是Spring Framework
的。数组
在SpringBoot
以前,使用Spring
能够说,并非那么的方便,其实也主要是在搭建一个基于Spring Framework
的项目时这个困扰。Spring
自己的配置,整合SpringMVC
,整合Struts2
,整合mybatis
,整合Hibernate
,整合SpringSecurity
等等,若是是Web
应用还有个web.xml
须要配置。什么都要你去配置一下,第一步就是去找怎么配置,记住这么配置是如何配的,其实并无切实的意义,毕竟又不是常常须要去搭建一个项目。正由于不常这么配置,不值得记住如何配置,致使每次实际用到时,很麻烦,处处去找如何配置的XML
配置文件。mybatis
SpringBoot
的出现,正是为了解决这个问题,让你能够不去作任何配置的状况下,运行一个Spring
应用,或者Web
应用。须要作的仅仅是引入SpringBoot
的maven
或者gradle
依赖便可。app
SpringBoot
要作的就是,让你开箱即用!框架
将使用Spring
的成本降到尽量低,为用户带来了极大的便利。maven
固然SpringBoot
作的也不只仅只有这些,不过这里仅讨论下它的自动化配置,不讨论其余的。
若是了解Spring
对@Configuration
这个注解的处理过程,会更加容易理解SpringBoot
的自动化配置。
若是没有,能够参考这篇解释
这第一件事,就是找门,门都找不到,那不是没门
吗!
既然想找门,就得从程序的启动入口去找,任何SpringBoot
程序都会用到这么两个
@SpringBootApplication public class Application{ public static void main(String[] args){ SpringApplication.run(Application.class, args); } }
看到这个后,若是好奇其实现,应该会首先查看SpringApplication#run
方法,实际调用的是这个重载的静态方法。
// org.springframework.boot.SpringApplication public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } public ConfigurableApplicationContext run(String... args) { ···省略··· try { ···省略··· context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context); // 真正启动应用程序上下文前的一些准备动做 // 这里会去将Application.class,注册到org.springframework.context.annotation.AnnotatedBeanDefinitionReader // 也就是去把Application.class注册成一个BeanDefinition实例 // 不过Application必需要是一个@Component prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新上下文,这个过程当中主要就是Bean的实例化和属性赋值绑定 // 若是是Web环境,涉及到Web相关的一些东西,可是本质上仍是各类Bean的实例化 // 和Bean之间依赖关系的处理,代理Bean的生成(涉及到AspectJ的Advice处理)等等 refreshContext(context); } return context; }
BeanDefinition
实例有了,就能去启动上下文,处理Bean
容器了,容器启动完成后,整个SpringBoot
程序基本启动完成!
等等! 是否是少了什么?
这里就注册了一个BeanDefinition
,那么多@Component
、@Configuration
、@Service
、@Controller
怎么办?
先留着疑问,且待后面解答!
林尽水源,便得一山,山有小口,仿佛如有光。
注意到上面的准备阶段,被注册的Bean
必需要被@Component
注解,如今Application.class
仅有一个注解@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 { ···省略··· }
挨个查看几个注解的定义后,会发现@SpringBootConfiguration
被@Component
所注解,这就解释了为何被@SpringBootApplication
所注解的Application.class
类能够被做为一个Bean
注册到BeanDefinitionRegistry
。
除此以外,还有个使人惊喜的名称:@EnableAutoConfiguration
,看名字就看出来它是作啥的了。
没错,SpringBoot
的所谓自动配置,就是它在起做用。
这里暂时不讨论@ComponentScan
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ···省略··· }
这个注解又使用了两个注解,分别是@AutoConfigurationPackage
和@Import
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
能够发现,这两个注解最终都指向了同一个注解@Import
@Import
是Annotation
时代的<import/>
,做用是向BeanDefinitionRegistry
注册Bean
的。
因此@EnableAutoConfiguration
这个注解一共注册了两个Bean
,分别是:AutoConfigurationPackages.Registrar.class
和AutoConfigurationImportSelector.class
先说说AutoConfigurationPackages.Registrar
的用处
这个类就干一个事,注册一个Bean
,这个Bean
就是org.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages
,它有一个参数,这个参数是使用了@AutoConfigurationPackage
这个注解的类所在的包路径。有了这个包路径后,就会扫描这个包下的全部class
文件,而后将须要注册到Bean
容器的类,给注册进去。
具体能够参见这里
org.springframework.boot.autoconfigure.AutoConfigurationPackages#register
这里就解释了为何有时候主配置类放的位置不对,致使有些类没被Spring容器归入管理
经历了一番折腾,就要进入桃花源了
AutoConfigurationImportSelector
就是那最后一层窗户纸
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 为了加载spring-boot-autoconfiguration包下的配置文件META-INF/spring-autoconfigure-metadata.properties // 这里配置的主要是一些SpringBoot启动时用到的一些@ConditionOnClass的配置 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); // 这里的AutoConfigurationEntry,就包含了全部的导入的须要被实例化的Bean AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); // 返回这些被导入Bean的类全限定名数组 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } ··· 省略 ··· // 获取全部的须要导入的Bean,这些被导入的Bean就是各个组件须要自动化配置的启动点 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); ··· 省略 ··· return new AutoConfigurationEntry(configurations, exclusions); } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 使用SpringFactoriesLoader#loadFactoryNames方法,从全部的包及classpath目录下, // 查找META-INF/spring.factories文件,且名称为org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 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; } }
最终这些配置在META-INF/spring.factories
中须要自动配置的类,就会被注册到Spring Bean
容器中,而后被实例化,调用初始化方法等!
这些作自动配置的类,基本都会经过实现各类Aware
接口,获取到Spring Framework
中的BeanFactory
,ApplicationContext
等等全部的一些框架内的组件,用于后面使用。
以后完成本身框架的一些初始化工做,主要就是将原先和Spring
整合时,须要手动配置的那些,在这里经过编程式的方式
,给作了。
这样,就完成了所谓的自动化配置,全程不须要咱们的任何参与。
PS: 这个仅仅是作了一个通用的配置,让用户能够在不作任何配置的状况下能直接使用。可是一些个性化的配置,仍是须要经过配置文件的方式,写入配置。对于这部分配置的处理,
SpringBoot
也都给揽下了
总体看下来,SpringBoot
干的这些,更像是一个体力活,将于Spring
集成的那么多三方库的配置,使用代码所有实现了一遍,其使用的核心功能,依然是Spring Framework
的那些东西。
可是这个体力活是为使用者省下的,也让Spring Framework
更加的具备活力了。
同时微服务的兴起,也是Spring
为了顺势而必须做出的一个改变,也能够说为Spring
在微服务领域立下了汗马功劳!