Spring源码:bean的生命周期(一)

打印 上一主题 下一主题

主题 922|帖子 922|积分 2766

前言

本节将正式介绍Spring源码细节,将讲解Bean生命周期。请注意,虽然我们不希望过于繁琐地理解Spring源码,但也不要认为Spring源码很简单。在本节中,我们将主要讲解Spring 5.3.10版本的源代码。如果您看到的代码与我讲解的不同,也没有关系,因为其中的原理和业务逻辑基本相同。为了更好地理解,我们将先讲解Bean的生命周期,再讲解Spring的启动原理和流程,因为启动是准备工作的一部分。
题外话

目前在该版本中,引入了一个名为jfr的JDK技术,类似于Java飞行日志(JFL),也称为飞行数据记录器(Black Box)技术。具体作用不再详细阐述,读者可以参考此文:JFR介绍
如果您看到以下代码,请直接跳过,因为它并没有太大的作用:
  1.     public AnnotationConfigApplicationContext() {
  2.         StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
  3.         // 额外会创建StandardEnvironment
  4.         this.reader = new AnnotatedBeanDefinitionReader(this);
  5.         createAnnotatedBeanDefReader.end();
  6.         this.scanner = new ClassPathBeanDefinitionScanner(this);
  7.     }
复制代码
需要注意的是,其中的 StartupStep 关于默认实现并没有什么实际作用。但是,还有一种实现方式是 FlightRecorderStartupStep,它是JDK的JFR技术。
Bean的生成过程

生成BeanDefinition

BeanDefinition的作用大家基本通过前面的文章也知道了大概,就是用来描述bean的。
那么它是如何加载的呢?首先我们看一下  ClassPathBeanDefinitionScanner  类,它是用于扫描的。其中有一个属性是  BeanDefinitionRegistry,即Bean定义的注册类。默认实现是  DefaultListableBeanFactory,但是在  ClassPathBeanDefinitionScanner  类中并没有直接使用该类作为属性,而是使用了它的父接口  BeanDefinitionRegistry。这是因为  ClassPathBeanDefinitionScanner  类实际上并没有使用  BeanDefinitionRegistry  接口中的许多方法来注册Bean定义。
接下来,我们来分析  ClassPathBeanDefinitionScanner  类的  scan  方法:
  1. protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
  2.         Assert.notEmpty(basePackages, "At least one base package must be specified");
  3.         Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
  4.         for (String basePackage : basePackages) {
  5.             Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
  6.             for (BeanDefinition candidate : candidates) {
  7.                 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
  8.                 candidate.setScope(scopeMetadata.getScopeName());
  9.                 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
  10.                 if (candidate instanceof AbstractBeanDefinition) {
  11.                     postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
  12.                 }
  13.                 if (candidate instanceof AnnotatedBeanDefinition) {
  14.                     // 解析@Lazy、@Primary、@DependsOn、@Role、@Description
  15.                     AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
  16.                 }
  17.                 // 检查Spring容器中是否已经存在该beanName
  18.                 if (checkCandidate(beanName, candidate)) {
  19.                     BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
  20.                     definitionHolder =
  21.                             AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
  22.                     beanDefinitions.add(definitionHolder);
  23.                     // 注册
  24.                     registerBeanDefinition(definitionHolder, this.registry);
  25.                 }
  26.             }
  27.         }
  28.         return beanDefinitions;
  29.     }
复制代码
大致逻辑如下:

  • 获取扫描包路径
  • findCandidateComponents:获取符合条件的bean
  • 遍历candidate(候选bean),由于第二部使用了ASM技术,所以并没有真正获取beanclass而是使用了beanname替代所以,遍历的做法就是将符合条件的bean定义进行注册。
  • scopeMetadata解析scope注解。
  • beanNameGenerator构建当前bean的唯一名字。
  • postProcessBeanDefinition这里其实就是进行默认值赋值。
  • processCommonDefinitionAnnotations进行解析@Lazy、@Primary、@DependsOn、@Role、@Description
  • checkCandidate(beanName, candidate)再次检查是否该beanName已经注册过。
  • registerBeanDefinition,注册到我们的DefaultListableBeanFactory的BeanDefinitionMap中。
其实这里基本就已经大概了解 的差不多了,然后再继续讲解下每一个流程里面都走了那些逻辑:
findCandidateComponents

其主要逻辑会进入如下源码:
  1. private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
  2.         Set<BeanDefinition> candidates = new LinkedHashSet<>();
  3.         try {
  4.             // 获取basePackage下所有的文件资源
  5.             String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
  6.                     resolveBasePackage(basePackage) + '/' + this.resourcePattern;
  7.             Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
  8.             boolean traceEnabled = logger.isTraceEnabled();
  9.             boolean debugEnabled = logger.isDebugEnabled();
  10.             for (Resource resource : resources) {
  11.                 if (traceEnabled) {
  12.                     logger.trace("Scanning " + resource);
  13.                 }
  14.                 if (resource.isReadable()) {
  15.                     try {
  16.                         MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
  17.                         // excludeFilters、includeFilters判断
  18.                         if (isCandidateComponent(metadataReader)) { // @Component-->includeFilters判断
  19.                             ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
  20.                             sbd.setSource(resource);
  21.                             if (isCandidateComponent(sbd)) {
  22.                                 if (debugEnabled) {
  23.                                     logger.debug("Identified candidate component class: " + resource);
  24.                                 }
  25.                                 candidates.add(sbd);
  26.                             }
  27.                             //此处省略部分代码
  28.                             ......
  29.         }
  30.         return candidates;
  31.     }
复制代码
让我们来深入了解一下Bean扫描的具体细节。以下是主要流程:

  • 获取basePackage下所有的文件资源。以下分析注解信息等都是用到了ASM技术,并没有真正的去加载这个类。
  • isCandidateComponent(metadataReader),进行判断是否当前类具有@component注解。
  • isCandidateComponent(sbd),进行判断是否当前类属于内部类、接口、抽象类
  • 符合上述条件则会加入到bean定义候选集合中。
ASM技术这里不做多解释,主要看下Spring是如何进行判断校验当前bean是否符合条件的,第一个 isCandidateComponent(metadataReader)方法:
  1.         protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
  2.                 for (TypeFilter tf : this.excludeFilters) {
  3.                         if (tf.match(metadataReader, getMetadataReaderFactory())) {
  4.                                 return false;
  5.                         }
  6.                 }
  7.                 // 符合includeFilters的会进行条件匹配,通过了才是Bean,也就是先看有没有@Component,再看是否符合@Conditional
  8.                 for (TypeFilter tf : this.includeFilters) {
  9.                         if (tf.match(metadataReader, getMetadataReaderFactory())) {
  10.                                 return isConditionMatch(metadataReader);
  11.                         }
  12.                 }
  13.                 return false;
  14.         }
复制代码
那么includeFilters默认会在启动AnnotationConfigApplicationContext时就会默认注册一个解析Component注解的filter,代码如下:
[code]protected void registerDefaultFilters() {       // 注册@Component对应的AnnotationTypeFilter    this.includeFilters.add(new AnnotationTypeFilter(Component.class));       ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();       try {        this.includeFilters.add(new AnnotationTypeFilter(              ((Class
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

吴旭华

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表