文章目录
前言
首先,自动装配包含了哪些的装配?
- 自动将包下的被打上@Component注解以及其派生注解的类作为Bean注册到IOC容器中
- 自动注册jar包中需要被注册的Bean到IOC容器中
这篇文章将从底层源码分析,SpringBoot是如何做到自动装配Bean的。其中起到关键作用的两大关键注解是
- @EnableAutoConfiguration(关于该注解的分析可以查看 Enable模式手动挡装配Bean)
- @ComponentScan
而以上两大关键注解都元标注了@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 {
// ...
}
我们通常会将被标注该注解的类注册到Spring容器中,之后,就会自动帮我们装配jar中配置的一些需要的Bean了。就像下面这样
@SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {
// 将UserServiceApplication作为Bean注册到Spring中
SpringApplication.run(UserServiceApplication.class, args);
}
}
这样,就开启了自动挡,自动装配Bean到Spring中。
希望读者具有ConfigurationClassPostProcessor配置注解处理类的基础,才能更好的了解自动装配的魔法。
关于ConfigurationClassPostProcessor的分析可以查看 配置注解驱动的处理
自动装配的魔法
开头的分析就给了我们一个入口,也就是@EnableAutoConfiguration、@ComponentScan这两大注解,接下来我们就以这两个注解为入口,分析自动装配的魔法。
@ComponentScan扫描并注册
首先,来一个开胃小菜,这块内容相对比较简单,所以先来分析Bean的扫描并注册的过程。
我们知道,在SpringBoot中我们需要将引导类放在包的外层,然后那些@Service、@Controller等等@Component的派生注解都会被注册到Spring中,作为SpringBean。例如下面这样的结构
那么这是为什么呢?Spring解析配置注解驱动类ConfigurationClassPostProcessor负责@ComponentScan注解的解析工作,在解析时,由如下逻辑进行处理
其实这段逻辑在 配置注解驱动的处理 这篇文章中就已经讲述过,这里只做大致的描述。
// Process any @ComponentScan annotations
// 这里主要拿到@ComponentScan注解的元信息,里面包含注解中的属性值
// 如果是ComponentScans,也会分解成一个个的ComponentScan
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
// 从这里可以看出,这里也会进行@Conditional的条件过滤
// 并且阶段是REGISTER_BEAN,表示要开始注册Bean了
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
// 扫描并注册Bean
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 这里还会判断是否需要像配置类解析那样进行解析
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 如果需要,则解析之,此方法就是刚开始的方法
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
这里最为关键的是componentScanParser的parse方法,扫描并注册Bean
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
// 主要扫描的类
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
// ...略过一些ComponentScan注解属性的配置
Set<String> basePackages = new LinkedHashSet<>();
// 获取basePackages属性值
// 下面主要针对属性值,获取需要被扫描的包名
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
// 获取需要被注册的Class
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
//