Spring源码系列二:扫描Bean
更多请关注:https://t.zsxq.com/fhroW
扫描
找到需要spring管理的类,并存入到beanDefinitionMap中。主要步骤如下:
- 步骤一:扫描出需要spring管理的类,封装为BeanDefinition
- 步骤二:填充BeanDefinition的属性
- 步骤三:判断BeanName是否重复
- 步骤四:注册Bean
源码查看
去除了日志打印、参数非空判断、异常抛出
- 扫描入口:
ClassPathBeanDefinitionScanner.scan(String... basePackages)
- scan()
传入包扫描路径,返回最后扫描到的Bean数量,主要看doScan方法
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registr);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
- doScan()
- 步骤一:见下文
- 步骤二:
经过步骤一之后BeanDefinition中只有一个class名称,没有其他属性,步骤二就是填充属性,如BeanName、是单例还是多例、是否懒加载等
步骤三:
检查Spring容器中是否已经存在该beanName,如果存在相同的BeanName且两个类不相同则抛出异常,开发中常见的BeanName重复就是在这里抛出的
步骤四:
注册Bean ,即将BeanDefinition添加到beanDefinitionMap中
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
//步骤一:扫描出所有的BeanDefinition
Set<BeanDefinition> candidates =findCandidateComponents(basePackage);
//步骤二:填充BeanDefinition的属性
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata =this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//从Component中取value值,如果没有就调用JDK的方法生成名字
String beanName =this.beanNameGenerator.generateBeanName(candidate,this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate,beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// 解析@Lazy、@Primary、@DependsOn、@Role、@Description
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnotatedBeanDefinition) candidate);
}
//步骤三:判断BeanName是否重复
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = newBeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetaata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//步骤四:注册Bean,这里的this.registery就是scan方法中的registry
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
步骤一,扫描出需要spring管理的类,封装为BeanDefinition
- 1.1: 加载basePackage下所有的文件资源,得到resources
- 1.2: 遍历resources创建每个类的元数据读取器
元数据读取器中包含:类名称、类上的注解、方法上的注解、实现的口名称、继承的类名称、内部类列表等信息 - 1.3: 判断该类是否匹配排除过滤器和包含过滤器
spring默认添加了一个包含过滤器,如果该类有Component注解,就会匹配包含过滤器 - 1.4 判断是否为独立的类、接口、抽象类。非独立类、接口、抽象类都不创建Bean
其中抽象类中如果有方法贴了注解@Lookup注解,该接口也会创建Bean(见拓展)
什么是独立的类?最顶层的类和静态内部类是独立的类,其他不,比如非静态的内部类
判断有无/resources/META-INF/spring.components索引文件。如果存在索引文则从改文件中扫描Bean,就不从Target中去扫描了,这样更快
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex,basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
没有索引文件的情况,从Target中去扫描
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
//(1.1)获取basePackage下所有的文件资源
String packageSearchPath = "classpath*:" +resolveBasePackage(basePackage) + "/**/*.class";
Resource[] resources =getResourcePatternResolver().getResources(packageSearchPath); //D:/package/workspace/.../com/xxx/AppConfig.class
for (Resource resource : resources) {
if (resource.isReadable()) {
//(1.2):遍历resources创建每个类的元数据读取器
MetadataReader metadataReader =getMetadataReaderFactory().getMetadataReader(resource);
// (1.3):excludeFilters、includeFilters判断
if (isCandidateComponent(metadataReader)) {
//生成BeanDefinition,通过扫描生成的就是ScannedGenericBeanDeinition,还有其他类型,比如通过XML配置的就是XmlBeanDefinitioReader
ScannedGenericBeanDefinition sbd = newScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
//(1.4):判断是否为独立的类,是否为接口、抽象类。
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
}
}
}
return candidates;
}
拓展
- JFR:jdk提供的监控程序运行的性能参数,例如执行时间
- @Lookup注解
使用场景:
@Component
@Scope("prototype")
public class User{}
@Component
public class Person(){
@Autowire
private User user;
public void test(){
System.out.println(user);
}
}
public class Test {
public static void main(String[] args) {
// 创建一个Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = (Person) applicationContext.getBean("person");
person.test();
person.test();
person.test();
}
}
以下代码打印出的三个user是同一个对象还是三个对象?是一个对象。尽管User是一个多例对象,但是Person是单例的,创建Person进行依赖注入时已经把User创建好了,所以使用user属性时不会再创建
要想实现多例的效果,就可以使用@Lookup注解:
@Component
public class Person(){
@Autowire
private User user;
public void test(){
System.out.println(anyName());
}
@Lookup
public User anyName(){
return any;
}
}