一,BeanDefinitionScanner加载流程
1,源码分析前准备
在分析这个源码的时候,首先需要去官网下载这个spring的源码包,建议下载5.x.x的版本
我这里安装的是:https://github.com/spring-projects/spring-framework/tree/5.2.x
2,源码分析
1,ApplicationContext是参与了整个springIoc的加载流程,因此ApplicationContext也是作为SpringIoc的一个入口了。由于ApplicationContext接口有很多的实现类,因此这里使用注解的方式来获取上下文的内容。
首先通过这个注解类AnnotationConfigApplicationContext获取这个上下文的全部信息,然后加载里面的配置信息,环境等。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
2,然后进入这个AnnotationConfigApplicationContext类里面,可以发现有一个无参构造方法,注册配置类的方法和一个refresh刷新IOC容器的方法,这里的话主要先看这个无参的构造方法。
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { //调用构造函数 this(); //注册我们的配置类 register(annotatedClasses); //IOC容器刷新接口 refresh(); }
3,在这个构造函数里面,会实例化一个BeanDefinitionReader的读取器和一个BeanDefinitionScanner的扫描器。读取器就是为了读取注解,扫描器是为了扫描这个包和类,最后注册成一个BeanDefinition,这个BeanDefinitionScanner 也是本篇文章的核心内容
public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
4,在这个ClassPathBeanDefinitionScanner类的构造方法里面,首先会设置一下这个当前的环境以及资源加载器,还有一个重要的就是有一个初始化一个默认的过滤器的方法。
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { this.registry = registry; if (useDefaultFilters) { registerDefaultFilters(); } //设置环境对象 setEnvironment(environment); //设置资源加载器 setResourceLoader(resourceLoader); }
然后可以来查看一下这个registerDefaultFilters的方法,里面会初始化这个includeFilter的这个包含过滤器,其底层就是一个List集合。这个包含过滤器里面会去添加所有的加了@Component注解的类,并且在spring中,这个@Component的这个注解就是在这个阶段进行扫描的。这个包含过滤器会在后面是否成为一个候选的bean的时候起到作用。
private final List<TypeFilter> includeFilters = new LinkedList<>(); protected void registerDefaultFilters() { //加入扫描我们的@Component的 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); }
5,接下来进入这个ClassPathBeanDefinitionScanner扫描类里面,会有一个scan方法,然后开始进行真正的对这个包路径的扫描。
public int scan(String... basePackages) { //对这些pachage的这些包进行扫描 doScan(basePackages); }
接下来进入这个doScan的这个方法里面,就是开始扫描包。
protected Set < BeanDefinitionHolder > doScan(String...basePackages) { //创建bean定义的holder对象用于保存扫描后生成的bean定义对象 Set <BeanDefinitionHolder> beanDefinitions = new LinkedHashSet <> (); //循环我们的包路径集合 for (String basePackage: basePackages) { //找到候选的Components Set < BeanDefinition > candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate: candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); //设置我们的beanName String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); //这是默认配置 autowire-candidate if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //获取@Lazy @DependsOn等注解的数据设置到BeanDefinition中 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } //把我们解析出来的组件bean定义注册到我们的IOC容器中(容器中没有才注册) if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
6,在上面的doScan方法里面,会有一个findCandidateComponents(basePackage) 的方法,主要是为了找到需要生成beanDefinition的候选者。进入这个方法,里面会有一个这个scanCandidateComponents方法用于扫描全部的候选者。并且里面有一个componentsIndex的一个索引,主要是为了增加这个查询的效率
public Set < BeanDefinition > findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { return scanCandidateComponents(basePackage); } }
7,接下来在查看这个scanCandidateComponents方法,就是用来扫描这些候选的Component配置类的。这里面的basePackage就是具体的包路径,比如说com.zhs.study,最后会将这个包路径转化为资源路径com/zhs/study。然后会去遍历这个资源集合,最后判断这些包路径下面的类是不是一个候选的component
private Set <BeanDefinition> scanCandidateComponents(String basePackage) { Set <BeanDefinition> candidates = new LinkedHashSet<>(); try { //把我们的包路径转为资源路径 com/zhs/study String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; //扫描指定包路径下面的所有.class文件 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); //遍历这个resources集合 for (Resource resource : resources) { try { //获取这个当前类的一个读取器,就可以读取当前类的类名,注解等 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); //判断当前类是不是一个候选的bean if (isCandidateComponent(metadataReader)) { //如果这个类是一个有效的bean ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); //判断类的属性,如是否是接口,抽象类,内部类等,如果是也不能注册 if (isCandidateComponent(sbd)) { //加入到集合中 candidates.add(sbd); } } } } } }
8,判断这个类是不是一个有效的bean的方法如下,主要是在这个 isCandidateComponent 方法里面实现。
首先会判断一下这个类在不在这个excludeFilters排除过滤器里面,如果在里面,那么直接返回false;
如果不在排除过滤器里面,那么会判断在不在这个includeFilters包含过滤器里面,就是判断一下这个类上面有没有这个@Component的这个注解。
如果有这个@Component注解,又会去判断一下这个类上面有没有这个@Conditional这个条件注解,如果有这个注解,则会判断是否符合里面的条件,如果符合条件,那么可以成为一个BeanDefinition;如果没有这个注解,则可以通过这个过滤器,可以成为一个BeanDefinition
如果都不在这两个过滤器里面,那么也会返回一个false,表示不符合条件
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { //通过excludeFilters 进行是否需要排除的 for (TypeFilter tf: this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } //includeFilters 是否需要进行包含的 for (TypeFilter tf: this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; } //如果有这个@Component的这个注解,又会再判断一下这个这个类上面有没有加这个@Conditional的这个注解 private boolean isConditionMatch(MetadataReader metadataReader) { if (this.conditionEvaluator == null) { this.conditionEvaluator = new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver); } return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata()); }
9,如果这个类是一个有效的bean,那么里面又内嵌了一个 isCandidateComponent 的布尔类型的方法,主要是判断一下这个有效bean的类是一个什么类型的类。如果里面不是一些接口,抽象类,内部类等,那么才能将这个有效的BeanDefinition对象加入set集合给返回。
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { AnnotationMetadata metadata = beanDefinition.getMetadata(); // metadata.isIndependent()=顶级类、嵌套类、静态内部类 // metadata.isConcrete() =非接口、非抽象类 // metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName() = 抽象类并且必须方法中有@LookUp return (metadata.isIndependent() && (metadata.isConcrete() || (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName())))); }
10,回到5里面的doScan方法,再获取到这个全部需要生成的BeanDefinition之后,就会去给这个BeanDefinition进行一个初始的赋值。比如说设置一些作用域,bean的名字,是否懒加载等。
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); //这是默认配置 autowire-candidate if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //获取@Lazy @DependsOn等注解的数据设置到BeanDefinition中 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); }
11,依旧是回到5里面的doScan的方法里面,再设置完一些属性之后,就会开始将这个BeanDefinition注册到这个springIoc的容器里面了。首先会判断一下这个BeanDefinition在这个容器里面是否存在,如果不存在,那么就会将这个BeanDefinition注册到这个springIoc的容器里面。
//把我们解析出来的组件bean定义注册到我们的IOC容器中(容器中没有才注册) if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); }
二,总结
1,BeanDifinitionScanner执行流程总结
1,首先会调用一个scan的一个方法,然后调用里面的doscan方法开始真正的扫描。
2,首先会扫描所有的包路径,会获取包下面所有的类,然后会这些类都会成为一个被候选的类,如果满足条件那么就可以成为最终的BeanDefinition。
3,候选的规则如下,首先会判断一下这个类在不在一个excludeFilters的排除过滤器里面,如果在里面,那么直接返回;再判断一下这个类在不在一个includeFilters的包含过滤器里面,就是这个类上面有没有一个@Component这个类的注解,没有则直接return返回;有的话则继续判断一下这个类上面有没有一个@Conditional的条件注解,如果有的话看一下这个类是否满足里里面的条件表达式,如果不满足则直接return返回,如果满足的话就可以成为一个有效的BeanDifinition,如果这个类上面没有这个@Conditional的这个注解,那么也会成为一个有效的BeanDifinition
4,在成为一个有效的BeanDefinition之后,会判断一下这个BeanDefinition的类是一个什么类型,如果是接口,抽象类,内部类等,那么直接return ;如果不是接口,抽象类,内部类等,那么会将这个有效类加入到这个set集合里面,最后返回这个set集合
5,在获取到所有的BeanDefinition之后,会设置一些BeanDefinition的一些属性,如一些作用域、是否懒加载等,并且这些BeanDefinition都会加入到一个BeanDefinitionMap里面
6,最后会去判断一下这个Ioc容器里面是否存在这个BeanDefinition,如果不存在,那么会通过这个beanDefinitionRegistry将这个BeanDefinition注册到Spring的Ioc容器里面
2,@Component注解总结
就是在spring启动时,这个加了@Component这个注解上面的类,是在这个@IncludeFilter包含过滤器里面被创建和加载的。在创建这个@IncludeFilter的时候,就会去获取所有加了这个@Component这个注解的类,会把这些类加载到这个Spring的容器里面。