简介
SpringBoot是当今比较火的一门技术,同时也是我们必须要掌握的技能项,它的出现得益于’‘约定大于配置’'的理念,它不像Spring框架那样需要繁琐的xml配bean的配置,没有难以集成的内容,我们使用SpringBoot能够快速进行Web开发,同时SpringBoot的自动配置功能也是真的香,但是使用框架给我们带来方便的同时,也不能忘记了底层的实现,往往一个技术让我们开发更加快捷的同时,也让我们更容易忽略底层的实现,接下来我们就来分析一下,关于SpringBoot自动配置的底层源码是怎么实现的。
SpringBoot的配置文件
学习了SpringBoot的伙伴们都知道SpringBoot有一个全局配置文件:application.properties/yml,我们需要配置的东西基本上都是在该文件中进行配置,比如最常用的配置有启动时的端口:server.port等等,在SpringBoot的官方文档中关于配置文件给出了超级多的配置选项,详情请看SpringBoot的官方文档。
超级多的配置,难道要全部背下来?估计背到明年都背不下来,这么多的配置,背是不可能背,最重要的还是要理解,这么多的配置我们基本上不会全部用到,但是对于这些配置就有疑问了,为什么SpringBoot在这里配置就能够自动的生效呢?它们是如何在SpringBoot项目中生效的?就下来我们就开始通过源码进行详细的介绍。
SpringBoot的自动配置的源码位置为:
SpringBoot关于自动配置的源码位置为:spring-boot-autoconfigure-X.X.X.RELEASE.jar中
SpringBoot自动配置原理源码分析
@SpringBootApplication
首先,我们都知道,新建一个SpringBoot项目,都会有一个启动类,而启动类上面就有一个@SpringBootApplication
注解,它是SpringBoot的核心注解,它和SpringBoot的自动配置原理有着很大的联系,我们可以点进去这个注解查看一下这个注解底层。
@EnableAutoConfiguration
我们点进@SpringBootApplication
注解里面发现,它原来是一个复合注解(派生注解),其中里面有一个@EnableAutoConfiguration
注解,按照我们中文式翻译来说其实就是能够(开启)自动配置的意思,这里我们先记住这个注解,后面的源码中就说到这个注解,我们再点进这个 @EnableAutoConfiguration注解查看它的底层。
@Import(AutoConfigurationImportSelector.class)
我们发现,这个@EnableAutoConfiguration注解也是一个派生注解,但是里面的关键功能是有@Import提供的,它导入了AutoConfigurationImportSelector.class,这个是什么呢?
我们依次根据源码去查看一下,首先找到该类中的selectImports方法
该方法里面又调用了getAutoConfigurationEntry(annotationMetadata)方法去获取自动配置的实例,我们继续去该方法中查看,
- 发现
getAutoConfigurationEntry(annotationMetadata)
方法调用了getCandidateConfigurations(annotationMetadata, attributes)
去获取候选的配置; - 并且在
getCandidateConfigurations(annotationMetadata, attributes)
;调用了SpringFactoriesLoader.loadFactoryNames().loadSpringFactories()
方法去加载Spring的工厂; - 在
loadSpringFactories()
方法的源码中,调用了classLoader.getResources(FACTORIES_RESOURCE_LOCATION)
方法去加载资源,其中FACTORIES_RESOURCE_LOCATION
在源码中的定义就是:public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
- 最终加载了这个位置的文件,同时扫描了具有META-INF/spring.factories的jar包。
以上的总结为:导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。
我们打开spring.factories文件,发现里面是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:
而关于@EnableAutoConfiguration注解,首先与getCandidateConfigurations中的SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader())方法相关,其中getSpringFactoriesLoaderFactoryClass()这个方法中就返回了EnableAutoConfiguration.class,如下如所示:
意思就是返回了@EnableAutoConfiguration注解标注的类下的所有资源,而@EnableAutoConfiguration注解被@SpringBootApplication注解继承,最终被被间接的标注到在Springboot的启动类上,同时在SpringApplication.run(…)的内部执行到了AutoConfigurationImportSelector中的selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有的自动配置类加载到Spring容器中。
关于自动配置的生效
在spring.factories文件中的每一个XxxxAutoConfiguration自动配置类的上面都标注了一些条件判断的注解,意思就是这些配置类都是在满足某些条件下才会生效的,而这些条件的限制在SpringBoot中以注解的形式体现,常见的条件注解如下所示:
条件注解 | 说明 |
---|---|
ConditionalOnJava | 系统的java版本是否符合要求 |
ConditionalOnBean | 当容器里有指定的bean的条件下。 |
ConditionalOnMissingBean | 当容器里不存在指定bean的条件下。 |
ConditionalOnExpression | 满足SpEL表达式指定 |
ConditionalOnClass | 当类路径下有指定类的条件下。 |
ConditionalOnMissingClass | 当类路径下不存在指定类的条件下。 |
ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
ConditionalOnProperty | 指定的属性是否有指定的值,例如ConditionalOnProperty(prefix=“xxx.xxx”,value=“enable”,matchlfMissing=ture),代表当xxx.xxx为enable时条件布尔值为true时,如果没有设置的情况下也为true。系统中指定的属性是否有指定的值 |
ConditionalOnResource | 类路径下是否存在指定资源 |
ConditionalOnWebApplication | 当前是web环境 |
ConditionalOnNotWebApplication | 当前不是web环境 |
ConditionalOnOnJndi | JNDI存在指定项 |
举个例子,就拿我们配置Tomcat启动的端口为例,比如:修改端口server: 8081
是如何生效的,修改端口的自动配置类是spring.factories文件中的ServletWebServerFactoryAutoConfiguration类,这个端口如果不配置的话就是8080,来源于org.apache.catalina.startup.Tomcat。
我们通过spring.factories文件中的ServletWebServerFactoryAutoConfiguration类查看其源码,可以看见上面标注了很多注解,其中有一个@EnableConfigurationProperties注解(开启配置属性),它的作用就是负责导入这个已经绑定了属性的bean到spring容器中,它后面的参数是一个ServerProperties的类,我们可以点进去看一下:
在这个类上标注了@ConfiguratioonProperties注解,它的作用就是从配置文件中绑定属性到对应的bean上,那么所有其他的和这个类相关的属性都可以在全局配置文件中定义,也就是说,真正“限制”我们可以在全局配置文件中配置哪些属性的类就是这些XxxxProperties类,它与配置文件中定义的prefix关键字开头的一组属性是唯一对应的。
当我们在yml配置文件中配置server:port :8081时,相当于将ServerProperties中的port属性赋值为8081。
总结
到此,我们就可以大致的了解到:在全局配置的属性中,server.port等等,通过@ConfiguratioonProperties注解绑定到对应的Xxxx.Properties配置实体类上封装成一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring的容器中。
而诸多的XxxxAutoConfiguration自动配置类,就是Spring容器的JavaConfig形式,作用就是为Spring 容器导入bean,而所有导入的bean所需要的属性都通过xxxxProperties的bean来获得。
Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的,当我们在application.yml/properties文件中进行设置时就相当于调用了对应配置类中的属性进行赋值。