springboot2 自动装配原理
springboot自动装配
Spring支持两种bean配置方式:XML配置、JavaConfig配置
@SpringBootApplication 注解
我们创建一个springboot项目后,一般要用该注解,然后在springbootApplication.run方法传入标注了该注解的类,这样就可以去加载spring的相关操作
@SpringBootApplication
public class SFastAdminApplication {
public static void main(String[] args) {
SpringApplication.run(SFastAdminApplication.class, args);
}
}
内部结构
内部注释了三个注解:
@SpringBootConfiguration
配置类注解
@EnableAutoConfiguration
开启自动装配的注解
@ComponentScan
进行包扫描,默认是扫描当前类所在的包下的所有组件
@SpringBootConfiguration(Spring 配置类,Bean)
该注解简单来说就是把当前类作为一个配置类,因为它内部就是一个@configuration注解,它也是一个@Component(组件)
它内部还有个@Indexed
注解。
@Indexed注解
在应用中有大量使用@ComponentScan
扫描的package包含的类越多的时候,Spring模式注解解析耗时就越长。因为需要通过反射扫描包嘛,然后一个一个遍历。
那么它在这里就体现出了优势,我们只需要在类或者接口(当然是有注解标注当前类或者接口是组件的)加上该注解即可提升启动性能。
内部做了什么:
在项目中使用了@Indexed
之后,编译打包的时候会在项目中自动生成META-INT/spring.components
文件。
当Spring应用上下文执行ComponentScan
扫描时,META-INT/spring.components
将会被CandidateComponentsIndexLoader
读取并加载,转换为CandidateComponentsIndex
对象,这样的话@ComponentScan
不在扫描指定的package,而是读取CandidateComponentsIndex
对象,从而达到提升性能的目的。
@EnableAutoConfiguration(启用自动化装配, 重点)
该注解就是开启自动装配。
内部注释了三个注解:
@AutoConfigurationPackage
自动配置包注解,扫描第三方的依赖注解
@Import(AutoConfigurationImportSelector.class)
导入自动化配置类
@AutoConfigurationPackage(自动配置包)
@AutoConfigurationPackage是自动配置的提醒,是Spring Boot中的注解
该注解跟@ComponentScan一样,都是将Spring Boot启动类所在的包及其子包里面的组件扫描到IOC容器中。
但是区别是:
-
@AutoConfigurationPackage扫描@Enitity、@Mapper等第三方依赖的注解,
-
@ComponentScan只扫描@Controller/@Service/@Component/@Repository这些常见注解。
@Import(AutoConfigurationImportSelector.class)(导入自动化配置类)
AutoConfigurationImportSelector类实现了ImportSelector接口。
AutoConfigurationImportSelector
有一个方法叫selectImports()
就是我们到底要给容器中导哪些类。
然后所有的东西都是调用getAutoConfigurationEntry()
方法得到的,然后再通过调用该方法得到的autoConfigurationEntry
对象去获取Configurations集合,并且返回出去,最终交给容器。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
getAutoConfigurationEntry(annotationMetadata):
该方法用于获取自动配置类的元数据。
注: 自动配置类是通过@Configuration和@Conditional注解来实现的。
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);
}
该方法代码解析:
-
isEnabled(annotationMetadata)是判断是否启用自动装配,启用就继续,不起用则直接返回空的AutoConfigurationEntry
-
getAttributes(annotationMetadata)是获取EnableAutoConfiguration注解的元数据,里面包含exclude(排除特定的自动配置类)和excludeName(排除特定的自动配置类名)
-
getCandidateConfigurations(annotationMetadata, attributes);是获取自动装配配置类的,内部有SpringFactoriesLoader.loadFactoryNames获取,获取
"META-INF/spring.factories"
资源里的配置类。(注意的是,该文件是properties文件,我们在使用的时候全类名要写在org.springframework.boot.autoconfigure.EnableAutoConfiguration key名的后面) -
removeDuplicates(configurations);通过该方法去重,避免重复的全类名
-
getExclusions(annotationMetadata, attributes);然后再对他们进行一个排除,排除条件就是通过getAttributes()方法获取的排除类或者类名
-
checkExcludedClasses(configurations, exclusions);排除额外的类,不适合自动装配的类,若发现有这样的类则会抛出异常,让程序员处理。
-
getConfigurationClassFilter().filter(configurations);过滤到不符合条件的类,只有符合条件的类才能在configurations里。
-
fireAutoConfigurationImportEvents(configurations, exclusions);它通知所有监听器,自动装配的过程已经完成,并且告诉它们哪些AutoConfiguration类被应用了,哪些被排除了。
-
最终new AutoConfigurationEntry(configurations, exclusions);封装成AutoConfigurationEntry对象。
@ComponentScan(将指定路径上扫描)
@ComponentScan
用于类或接口上主要是指定扫描路径,spring会把指定路径下带有指定注解的类自动装配到bean容器里。会被自动装配的注解包括@Controller
、@Service
、@Component
、@Repository
等等。与ComponentScan
注解相对应的XML配置就是<context:component-scan/>
, 根据指定的配置自动扫描package,将符合条件的组件加入到IOC容器中;
该注解的常用属性:
-
basePackages和value:指定要扫描的路径(package),如果为空则以-@ComponentScan注解的类所在的包为基本的扫描路径。
-
basePackageClasses:指定具体扫描的类。
-
includeFilters:指定满足Filter条件的类。
-
excludeFilters:指定排除Filter条件的类。
-
useDefaultFilters=true/false:指定是否需要使用Spring默认的扫描规则:被@Component, @Repository, @Service, @Controller或者已经声明过@Component自定义注解标记的组件;
扩展
对本文里面提到的一些内容的补充。
@Import注解
import注解传入类可是单个类,也可指定多个类(数组形式)
三种用法:
-
@Import一个普通类 spring会将该类加载到spring容器中
-
@Import一个类,该类实现了
ImportBeanDefinitionRegistrar
接口,在重写的registerBeanDefinitions
方法里面,能拿到BeanDefinitionRegistry
的注册器,能手工往beanDefinitionMap
中注册beanDefinition
-
@Import一个类 该类实现了
ImportSelector
重写selectImports
方法该方法返回了String[]数组的对象,数组里面的类都会注入到spring容器当中