简介
不知道大家还记不记得第一次搭建SpringBoot环境的时候,有没有觉得非常简单,非常方便,无须各种的配置文件,无需各种繁杂的pom坐标,一个main方法,就能run起来了.与其他的框架整合也狠方便,再启动类上加一个EnableXXX就可以了.今天看一下SpringBoot是如何实现自动配置的
源码的话就先送启动类入手:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplication:SpringBoot应用中,这个注解标注再那个类上,就说明这个类是当前应用的主配置类,SpringBoot需要运行主配置类的main方法来启动SpringBoot应用
- SpringBootApplication
//设置当前注解可以标注在哪
@Target(ElementType.TYPE)
//当前注解标注的类编译以什么方式保留
//RetentionPolicy.RUNTIME::会被java虚拟机所加载
@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 {
//...
}
- @SpringBootApplication是一个组合注解,上面包含着三个注解
- SpringBootConfiguration:标注再某个类上,这个类就是SpringBoot的配置类;SpringBootConfiguration类上只有一个注解@Configuration,作用等同于Configuration;
- @EnableAutoConfiguration:开启自动配置,以前我们需要配置的东西,现在SpringBoot可以帮我们去配置,@EnableAutoConfiguration告诉SpringBoot开启自动配置,SpringBoot会自动帮我们去加载自动配置类
- @ComponentScan:相当于Spring.xml文件中context:comonent-scan,但是没有指定basePackage,如果没有指定当前的扫描包,SpringBoot会默认扫描启动类所再的包下的所有类(含子包下的类)
TypeExcludeFilter:这个是SpringBoot对外提供的扩展类,可以供我们去按照自己的方式进行排除
AutoConfigurationExcludeFilter:排除所有的配置类并且是自动配置类里面的其中一个
- @EnableAutoConfiguration
这个注解是@SpringBootApplication中的重中之重,如此见名知意的名字,一看就知道SpringBoot要开始操作了,于是默默点进EnableAutoConfiguration的源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//将当前配置类所在的包保存再BasePackage的Bean中,供Spring内部使用
@AutoConfigurationPackage
//完成导入配置功能
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//...
}
- @AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//保存扫描路径;就是注册了一个保存当前配置类所在包的一个bean
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
//...
}
- @Import(AutoConfigurationImportSelector.class)
可以看到@EnableAutoConfiguration注解内使用了@Import注解完成导入功能,而AutoConfigurationImportSelector内部再解析@Import注解时会调用getAutoConfigurationEntry方法,
- getAutoConfigurationEntry方法进行扫描具有META-INF/spring.factories文件的jar包
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
//...
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//去重,点进源码发现就是将当前集合放置到LinkedHashSet中进行去重,然后再转换成List
configurations = removeDuplicates(configurations);
//根据EnableAutoConfiguration注解中属性,获取不需要自动装配的类名单
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 根据:@EnableAutoConfiguration.exclude,excludeName和spring.autoconfigure.exclude进行删除
checkExcludedClasses(configurations, exclusions);
//exclusions也进行删除
configurations.removeAll(exclusions);
/**
*通过读取spring.factories所在jar中的
*org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition进行删除
*/
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
//...
/**
* 从META-INF/spring.factories中获得候选的自动配置类
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
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;
}
}
//SpringFactoriesLoader部分源码
public final class SpringFactoriesLoader {
/**
*工厂位置
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//...
}
- 任何一个springboot应用,都会引入spring-boot-autoconfigure,而spring.factories文件就在该包下面。spring.factories文件是 Key=Value形式,多个Value时使用,隔开,该文件中定义了关于初始化,监听器等信息,而真正使自动配置生效的key是org.springframework.boot.autoconfigure.EnableAutoConfiguration
- 最后:@EnableAutoConfiguration注解通过@SpringBootApplication间接的标注再SpringBoot的启动类上面,在执行SpringApplication.run(DemoApplication.class, args)时;找到所有JavaConfig自动配置类的全限定名对应的 class,然后将所有自动配置类加载到Spring容器中