1.引言
springboot框架同样有四个基础核心,分别是:
- 依赖管理
- 自动装配
- 监控Actuator
- CLI工具
2.自动装配
springboot应用,从启动类开始,通常启动类位于src/main/java目录下。像这样:
通常启动类的内容,看着非常简单:
/**
* 启动类
*
* @author ThinkPad
* @version 1.0
*/
@SpringBootApplication
public class EduCommonApplication {
public static void main(String[] args) {
SpringApplication.run(EduCommonApplication.class, args);
}
}
一个注解,一个run方法,即完成了springboot应用的两个关键流程事项:
- @SpringBootApplication:开启组件扫描,和自动装配
- SpringApplication.run():引导启动应用程序
@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 {
..............省略属性定义..................
}
这里有关于java注解的基础知识,简单解释下:
- @Target({ElementType.TYPE}):表示该注解,可以标注在哪些地方。ElementType.TYPE表示可以用在类,接口上。常见的取值:FIELD、METHOD、PARAMETER、CONSTRUCTOR、PACKAGE等,见文知意,很好理解
- @Retention(RetentionPolicy.RUNTIME):表示该注解,在JVM处理类的过程(著名的家宴准备了西式菜)中的哪些地方会保留。RetentionPolicy.RUNTIME表示保留到运行时,通过反射机制可以拿到它。常见的取值还有:SOURCE、CLASS,见文知意,很好理解。
铺垫完java注解的基础知识,回到重点关注点,分别是:
- @ComponentScan子注解:启用组件扫描,默认会从启动类所在的包开始扫描至所有子包,这也是为什么通常建议将启动类,放置在顶层包的原因
- @SpringBootConfiguration子注解:它就是@Configuration,是spring框架提供的注解,表示这是一个javaconfig配置类,关于javaconfig的相关知识,你应该还没有忘记吧!
- @EnableAutoConfiguration子注解:表示开启springboot应用的自动配置能力,它会依据应用的依赖、classpath有没有某个类,以及自定义的一系列bean。来决定应用最终的装配需求,并注册到IoC容器中。
下面我们就来详细分析注解:@EnableAutoConfiguration,是如何懂得你的需求的?它真的这么厉害吗!
看它的定义:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
我们把关注点,放到这行代码上:
@Import({AutoConfigurationImportSelector.class})
@Import注解的作用是导入类,并将目标导入类的bean注入到IoC容器中。
这里是将AutoConfigurationImportSelector的bean注入到IoC容器,AutoConfigurationImportSelector中有一个方法,值得关注:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 前面的知识点:spring factories 即spi机制在spring中的引用
// 它会加载META-INF/spring.factories文件,从中找到需要自动装配的需求
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
getAutoConfigurationEntry方法:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 从META-INF/spring.factorie配置中,获取自动需求
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
getCandidateConfigurations方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 终于看到我们熟悉的:SpringFactoriesLoader了
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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.loadFactoryNames方法:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
loadSpringFactories方法:
META-INF/spring.factories文件,内容太多,我就截图了,关注关键点即可:
从上面的截图,我们看到spring.factories文件中,有一个配置属性:org.springframework.boot.autoconfigure.EnableAutoConfiguration,指定了一系列自动装配的需求。挑几个我们熟悉的看看
以上就是springboot应用的自动装配。下一篇文章,我们再来看启动流程,即run方法内部的行为。