Spring Boot自动装配原理
- 1.Spring Boot 入口
- 2.@SpringBootApplication
- @SpringBootConfiguration
- @ComponentScan
- @EnableAutoConfiguration
- 判断自动装配开关是否打开
- 获取EnableAutoConfiguration注解中的 exclude 和 excludeName
- 获取需要自动装配的所有配置类
- 最后
- 3.总结
1.Spring Boot 入口
Spring Boot 的入口是如下的main方法:
@SpringBootApplication
public class TestBootApplication {
public static void main(String[] args) {
SpringApplication.run(TestBootApplication.class, args);
}
}
其中:
@SpringBootApplication
标注这个应用是一个SpringBoot应用,是一个完全必要的注解
SpringApplication.run
,用于将SpringBoot应用启动!
2.@SpringBootApplication
这个注解是SpringBoot 的核心注解 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 {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
可以看到SpringBootApplication
注解的源码还包含了其他三个重要注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
大概可以把@SpringBootApplication
看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan
注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:
@Configuration
:允许在上下文中注册额外的 bean 或导入其他配置类@EnableAutoConfiguration
:启用 SpringBoot 的自动配置机制@ComponentScan
: 扫描被@Component (@Service,@Controller)
注解的bean,注解默认会扫描启动类所在的包下所有的类
@SpringBootConfiguration
源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@SpringBootConfiguration
是继承自Spring的@Configuration
注解@SpringBootConfiguration
作用相当于@Configuration
总结,
@Configuration
相当于一个spring的xml文件,配合@Bean
注解,可以在里面配置需要Spring容器管理的bean
@ComponentScan
@ComponentScan
通常与@Configuration
一起配合使用,相当于xml里面的<context:component-scan>
,用来告诉Spring需要扫描哪些包或类。如果不设值的话默认扫描@ComponentScan
注解所在类的同级类和同级目录下的所有类,所以对于一个Spring Boot项目,一般会把入口类放在顶层目录中,这样就能够保证源码目录下的所有类都能够被扫描到。
@EnableAutoConfiguration
@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 {};
}
其中,@AutoConfigurationPackage
的作用是自动配置包
@EnableAutoConfiguration
注解的核心是@Import(AutoConfigurationImportSelector.class)
里面导入的AutoConfigurationImportSelector.class
我们现在重点分析下
AutoConfigurationImportSelector
类到底做了什么?🙌
AutoConfigurationImportSelector
类的继承体系如下:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}
public interface DeferredImportSelector extends ImportSelector {
}
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
可以看出,AutoConfigurationImportSelector
类实现了 ImportSelector
接口,也就实现了这个接口中的 selectImports
方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断自动装配开关是否打开
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取所有需要装配的bean
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
这里我们需要重点关注一下getAutoConfigurationEntry()
方法,这个方法主要负责加载自动配置类的。
现在我们结合getAutoConfigurationEntry()
的源码来详细分析一下:
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);
}
注意了!重头戏来了!
判断自动装配开关是否打开
默认spring.boot.enableautoconfiguration=true
,可在 application.properties 或 application.yml 中设置:
获取EnableAutoConfiguration注解中的 exclude 和 excludeName
获取需要自动装配的所有配置类
获取需要自动装配的所有配置类,读取META-INF/spring.factories
先看一下META-INF/spring.factories
文件所在的位置:
从下图可以看到这个文件的配置内容都被我们读取到了。XXXAutoConfiguration
的作用就是按需加载组件。
不光是这个依赖下的META-INF/spring.factories
被读取到,所有 Spring Boot Starter 下的META-INF/spring.factories
都会被读取到。
所以,你可以清楚滴看到, druid 数据库连接池的 Spring Boot Starter 就创建了META-INF/spring.factories文件。
如果,我们自己要创建一个 Spring Boot Starter,这一步是必不可少的。
最后
到这里可能面试官会问你:“spring.factories
中这么多配置,每次启动都要全部加载么?”。
很明显,这是不现实的。我们 debug 到后面你会发现,configurations 的值变小了。
因为,这一步又经历了一遍筛选,@ConditionalOnXXX
中的所有条件都满足,该类才会生效。
@Configuration
// 检查相关的类:RabbitTemplate 和 Channel是否存在
// 存在才会加载
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
}
3.总结
- SpringBoot在启动的时候,从类路径下
/META-lNF/spring.factories
获取指定的值 - 将这些自动配置的类导入容器,自动配置就会生效,帮我们进行自动配置
- 以前我们需要自动配置的东西,现在SpringBoot帮我们做了
- 整合javaEE,解决方案和自动配置的东西都在
spring-boot-autoconfigure-2.2.0.RELEASE.jar
这个包下 - 它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器
- 容器中也会存在非常多的
xxAutoConfiguration
的文件(@Bean
),就是这些类给容器中导入了这个场录需要的所有组件:并自动配置,@Configuration
,JavaConfig
! - 有了自动配置类,免去了我们手动编写配置文件的工作!