概念:能够在我们添加jar包依赖的时候,自动为我们配置一些组件的相关配置,我们无需配置或者只需要少量配置就能运行编写的项目·
一.在聊自动配置之前先说说boot的热部署:
- 在开发过程中,通常会对一段业务代码不断地修改测试,在修改之后往往需要重启服务,有些服务
需要加载很久才能启动成功,这种不必要的重复操作极大的降低了程序开发效率。为此,Spring Boot框
架专门提供了进行热部署的依赖启动器,用于进行项目热部署,而无需手动重启项目 。 - 热部署:在修改完代码之后,不需要重新启动容器,就可以实现更新。
1.添加spring-boot-devtools热部署依赖启动器
在Spring Boot项目进行热部署测试之前,需要先在项目的pom.xml文件中添加spring-boot-devtools热
部署依赖启动器:
<!-- 引入热部署依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
- IDEA工具热部署设置
选择IDEA工具界面的【File】->【Settings】选项,打开Compiler面板设置页面
选择IDEA工具界面的【File】->【Settings】选项,打开Compiler面板设置页面
- 选择Build下的Compiler选项,在右侧勾选“Build project
automatically”选项将项目设置为自动编译,单击【Apply】→【OK】按钮保存设置 - 在项目任意页面中使用组合快捷键“Ctrl+Shift+Alt+/”打开Maintenance选项框,选中并打开
Registry页面,具体如图所示
3.热部署效果测试
二.自动配置
概念:能够在我们添加jar包依赖的时候,自动为我们配置一些组件的相关配置,我们无需配置或者只需要少量配置就能运行编写的项目
问题:Spring Boot到底是如何进行自动配置的,都把哪些组件进行了自动配置?
- Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法,
@SpringBootApplication : SpringBoot 应用标注在某个类上说明这个类是 SpringBoot 的主配置
类, SpringBoot 就应该运行这个类的 main() 方法启动 SpringBoot 应用。
@SpringBootApplication
public class SpringbootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDemoApplication.class, args);
}
}
进入到 @SpringBootApplication 内,观察其做了哪些工作:
@Target({ElementType.TYPE}) //注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时
@Documented //表示注解可以记录在javadoc中
@Inherited //表示可以被子类继承该注解
@SpringBootConfiguration // 标明该类为配置类
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes =
TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes =
AutoConfigurationExcludeFilter.class) })
从上述源码可以看出,@SpringBootApplication注解是一个组合注解,前面 4 个是注解的元数据
信息, 我们主要看后面 3 个注解:@SpringBootConfiguration、@EnableAutoConfiguration、
@ComponentScan三个核心注解,关于这三个核心注解的相关说明具体如下:
1.@SpringBootConfiguration注解
@SpringBootConfiguration : SpringBoot 的配置类,标注在某个类上,表示这是一个 SpringBoot
的配置类。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 配置类的作用等同于配置文件,配置类也是容器中的一个对象
public @interface SpringBootConfiguration {
}
从上述源码可以看出,@SpringBootConfiguration注解内部有一个核心注解@Configuration,该注解是Spring框架提供的,表示当前类为一个配置类(XML配置文件的注解表现形式),并可以被组件扫描器扫描。由此可见,@SpringBootConfiguration注解的作用与@Configuration注解相同,都是标识一个可以被组件扫描器扫描的配置类,只不过@SpringBootConfiguration是被Spring Boot进行了重新封装命名而已
2.@EnableAutoConfiguration注解·
@EnableAutoConfiguration :开启自动配置功能,以前由我们需要配置的东西,现在由 SpringBoot
帮我们自动配置,这个注解就是 Springboot 能实现自动配置的关键。
// 自动配置包
@AutoConfigurationPackage
// Spring的底层注解@Import,给容器中导入一个组件;
// 导入的组件是AutoConfigurationPackages.Registrar.class
@Import(AutoConfigurationImportSelector.class)
// 告诉SpringBoot开启自动配置功能,这样自动配置才能生效。
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 返回不会被导入到 Spring 容器中的类
Class<?>[] exclude() default {};
// 返回不会被导入到 Spring 容器中的类名
String[] excludeName() default {};
}
可以发现它是一个组合注解, Spring 中有很多以 Enable 开头的注解,其作用就是借助 @Import
来收集并注册特定场景相关的 Bean ,并加载到 IOC 容器。@EnableAutoConfiguration就是借助
@Import来收集所有符合自动配置条件的bean定义,并加载到IoC容器。
查看@AutoConfigurationPackage注解内部源码信息,核心代码具体如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class}) // 导入Registrar中注册的组件
public @interface AutoConfigurationPackage {
}
从上述源码可以看出,@AutoConfigurationPackage注解的功能是由@Import注解实现的,它是
spring框架的底层注解,它的作用就是给容器中导入某个组件类,例如
@Import(AutoConfigurationPackages.Registrar.class),它就是将Registrar这个组件类导入到容器
中,可查看Registrar类中registerBeanDefinitions方法,这个方法就是导入组件类的具体实现 :
从上述源码可以看出,在Registrar类中有一个registerBeanDefinitions()方法,使用Debug模式启
动项目,可以看到选中的部分就是com.ywc。也就是说,@AutoConfigurationPackage注解的主要作
用就是将主程序类所在包及所有子包下的组件到扫描到spring容器中。
因此 在定义项目包结构时,要求定义的包结构非常规范,项目主程序启动类要定义在最外层的根目录位
置,然后在根目录位置内部建立子包和类进行业务开发,这样才能够保证定义的类能够被组件扫描器扫
描
@Import({AutoConfigurationImportSelector.class})注解
将 AutoConfigurationImportSelector 这个类导入到 Spring 容器中,
AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration
配置都加载到当前 SpringBoot 创建并使用的 IOC 容器( ApplicationContext )中。
继续研究AutoConfigurationImportSelector这个类,通过源码分析这个类中是通过selectImports这
个方法告诉springboot都需要导入那些组件:
深入研究loadMetadata方法
AutoConfigurationImportSelector类 getAutoConfigurationEntry方法
protected AutoConfigurationEntry
getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
//判断EnabledAutoConfiguration注解有没有开启,默认开启
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//获得注解的属性信息
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取默认支持的自动配置类列表
List<String> configurations =
getCandidateConfigurations(annotationMetadata, attributes);
//去重
configurations = removeDuplicates(configurations);
//去除一些多余的配置类,根据EnabledAutoConfiguratio的exclusions属性进行排除
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//根据pom文件中加入的依赖文件筛选中最终符合当前项目运行环境对应的自动配置类
configurations = filter(configurations, autoConfigurationMetadata);
//触发自动配置导入监听事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
深入getCandidateConfigurations方法
继续点开loadFactory方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable
ClassLoader classLoader) {
//获取出入的键
String factoryClassName = factoryClass.getName();
return
(List)loadSpringFactories(classLoader).getOrDefault(factoryClassName,
Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable
ClassLoader classLoader) {
MultiValueMap<String, String> result =
(MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
//如果类加载器不为null,则加载类路径下spring.factories文件,将其中设置的
配置类的全路径信息封装 为Enumeration类对象
Enumeration<URL> urls = classLoader != null ?
classLoader.getResources("META-INF/spring.factories") :
ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
//循环Enumeration类对象,根据相应的节点信息生成Properties对象,通过传入的
键获取值,在将值切割为一个个小的字符串转化为Array,方法result集合中
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties =
PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName =
((String)entry.getKey()).trim();
String[] var9 =
StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
会去读取一个 spring.factories 的文件,读取不到会表这个错误,我们继续根据会看到,最终路径的长
这样,而这个是spring提供的一个工具类
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "METAINF/spring.factories";
}
它其实是去加载一个外部的文件,而这文件是在
@EnableAutoConfiguration就是从classpath中搜寻META-INF/spring.factories配置文件,并将其中
org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(Java
Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的配置类,并加载到IOC容器中
以刚刚的项目为例,在项目中加入了Web环境依赖启动器,对应的WebMvcAutoConfiguration自动配
置类就会生效,打开该自动配置类会发现,在该配置类中通过全注解配置类的方式对Spring MVC运行所
需环境进行了默认配置,包括默认前缀、默认后缀、视图解析器、MVC校验器等。而这些自动配置类的
本质是传统Spring MVC框架中对应的XML配置文件,只不过在Spring Boot中以自动配置类的形式进行
了预先配置。因此,在Spring Boot项目中加入相关依赖启动器后,基本上不需要任何配置就可以运行程
序,当然,我们也可以对这些自动配置类中默认的配置进行更改
总结
因此springboot底层实现自动配置的步骤是:
- springboot应用启动;
- @SpringBootApplication起作用;
- @EnableAutoConfiguration;
- @AutoConfigurationPackage:这个组合注解主要是
@Import(AutoConfigurationPackages.Registrar.class),它通过将Registrar类导入到容器中,而
Registrar类作用是扫描主配置类同级目录以及子包,并将相应的组件导入到springboot创建管理的
容器中; - @Import(AutoConfigurationImportSelector.class):它通过将AutoConfigurationImportSelector
类导入到容器中,AutoConfigurationImportSelector类作用是通过selectImports方法执行的过程
中,会使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的METAINF/spring.factories进行加载,实现将配置类信息交给SpringFactory加载器进行一系列的容器创
建过程