自动配置原理入门
针对已经学习到的SpringBoot帮我们完成的各种任务,今天来简单看一看它是如何帮助我们实现自动配置流程的。
首先还是从启动类出发,这里我们发现这个入口上只有一个注解@SpringBootApplication
,这个注解标记了我们项目的启动类,当我们点击进入这个注解后可以看到:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
除去一些元注解,这里列出了一些这个注解需要实现的功能注解,首先是我们前面所了解过的@ComponentScan
包扫描,这个我们很熟悉了就不再多说。
然后是@SpringBootConfiguration
,这个注解看名字其实就是SpringBoot的配置注解,当我们点入就可以看到是几个元注解和我们之前学到的@Configuration
配置注解,也就是证明这个类是一个配置类:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
这个@Indexed
其实就是在类逐渐增多的情况下,为了加快启动的解析速度所设置的,项目启动会优先从索引开始,而不是全局的包扫描。
最后就是这个@EnableAutoConfiguration
注解,字面翻译的话大概就是确保自动配置,当我点入这个注解后会发现:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
它是除了其他元注解外剩余的注解,类中的其他代码我暂时删掉了,这里面@Import
我们之前已经了解过,同时我们点开@AutoConfigurationPackage
也可以发现它其实也包含了一个@Import
,不同的是导入的类是AutoConfigurationPackages.Registrar.class
。
这里我们先看AutoConfigurationImportSelector.class
这个类的实现,这个类中方法非常多,我们先看这一个就可以:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
首先我们可以很清楚的看到,它首先获取到的配置的元数据,通过元数据又设置了一个方法getAttributes
获取其中的注解参数,获取到注解参数后,调用了getCandidateConfigurations
方法,这个方法我们点击可以发现,他是实现了一个把元数据中不同的参数存入了一个ArrayList列表,并且校验了集合产生是否为空,如果为空会输出日志参数。这里它的loadFactoryNames
方法将从META-INF/spring.factories
位置来加载一个文件,所有的配置都会被写死在这个文件下,每个导入的Spring模块都有这个路径文件。之后它对这个列表进行了去重,并又放入了LinkedHashSet中,进一步去重。之后又读取了参数中所存在的Conditional
判断是否存在需要去除的类,如果存在,将会从建立好的集合中将其去除,最后返回一个内部类格式AutoConfigurationEntry
。这个内部类中的所有参数都将被加入到容器中,作为后续调用的组件。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
接下来是这个AutoConfigurationPackages.Registrar.class
,这个里面其实就相对来说简单一些,主要是负责包的扫描和导入,基本方法和上述差不多,也是读取到注解信息后,通过new一个final的私有静态类:
PackageImports(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
packageNames.add(basePackageClass.getPackage().getName());
}
if (packageNames.isEmpty()) {
packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
}
this.packageNames = Collections.unmodifiableList(packageNames);
}
同样的转为一个集合交给容器管理。
此时我们就可以发现,其实SpringBoot在开始的时候会把所有的写好的配置信息,全部加载进来,每个配置信息都会根据条件选择是否再进行注入生效,默认的所有配置类都有它默认的配置信息,如果有自定义的用户配置,会优先使用用户的配置,覆盖掉默认信息。
配置信息到这里就差不多了,更多详细想要的信息可以从官方文档中根据自己的喜好进行查阅。
开发小工具
在众多的开发工具中我感觉这个Spring Initailizr(项目初始化向导)
应该是对于新手来说最为实用的了,我们可以直接选择我们所需要的场景。
这里我们点击Next:
这里可以自定义自己的包名和Java版本,点击Next:
可以看到基本数所有的配置都可以进行勾选,包括SpringBoot的版本号也可以选择,包括一些小插件,在Template Engines
中可以自己选择,再点击Next后选择创建的文件目录后就可以打开项目了,创建好项目后,他会帮助我们生成固定的文件格式:
当然这里的Config和Pojo是我自己创建的,大家创建的时候是没有的,其他的目录格式都是系统帮助我们完成的,而且我们学习的配置信息,也根据我们需要的场景都写入了Pom文件,我们无需再关心,非常的方便快捷。
这里的application.yml
是我单独创建的配置文件,yml和properties都是很不错的配置文件,大家哪个熟悉就可以使用哪一个。
Lombok
关于Lombok
这个插件大家可能也有听过,但是我还是不是很推荐大家使用,因为缺少了get\set函数的配置,会使得我们在调试的过程中出现问题,追溯路径过程可能会有点麻烦,不太适合新手上来的学习,而且她默认的生成所有属性的方法,可能会导致某些我们不想让外部访问的数据泄露,所以大家还是尽量使用自己写的JavaBean开发吧。