spring源码分析7----注册@Bean修饰的bean

作者:shihuaping0918@163.com转载请注明作者

前面的文章分析了bean的实例化,bean字段/成员变量的填充。这一篇来看一下@Bean注解。最早分析的是读xml中的<bean></bean>定义,现在来看一下代码中的@Bean又是怎么生效的。文章的篇幅越来越大,因为代码贴上去占了太多空间,但是只摘取一段代码,这样又对读者不友好,读文章的时候又要不停返回去看代码对照。篇幅大就大吧,我尽量不贴图片,这样的话,文字再多,占的体积也是有限的。这一篇文章又会很长。

从AnnotationConfigApplicationContext讲起,基于xml配置时,一般使用ClassPathXmlApplication进行spring应用上下文加载。基于java配置时,就使用AnnotationConfigApplicationContext进行spring应用上下文加载。它会注册@Bean注释的方法所返回的bean。这里有一个要注意的地方,就是要使用这两个构造方法,才会去注册bean。

/**
 * Create a new AnnotationConfigApplicationContext, deriving bean definitions
 * from the given component classes and automatically refreshing the context.
 * @param componentClasses one or more component classes &mdash; for example,
 * {@link Configuration @Configuration} classes
 */
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
   this();
   register(componentClasses); 
   refresh(); //熟悉吧
}

/**
 * Create a new AnnotationConfigApplicationContext, scanning for components
 * in the given packages, registering bean definitions for those components,
 * and automatically refreshing the context.
 * @param basePackages the packages to scan for component classes
 */
public AnnotationConfigApplicationContext(String... basePackages) {
   this();
   scan(basePackages);
   refresh(); //很眼熟
}

refresh这里就不讲了,这个类也只是为了提供一个入口,下面来看scan方法。

/**
 * Perform a scan within the specified base packages.
 * <p>Note that {@link #refresh()} must be called in order for the context
 * to fully process the new classes.
 * @param basePackages the packages to scan for component classes
 * @see #register(Class...)
 * @see #refresh()
 */
@Override
public void scan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   this.scanner.scan(basePackages);
}

再看一下scanner,是在无参构造方法里赋值的。

/**
 * Create a new AnnotationConfigApplicationContext that needs to be populated
 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
 */
public AnnotationConfigApplicationContext() {
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

/**
 * Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory.
 * @param beanFactory the DefaultListableBeanFactory instance to use for this context
 */
public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
   super(beanFactory);
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

好,下面去ClassPathBeanDefinitionScanner里看看scan方法。注意一下scan方法参数传的是package路径,包路径。

/**
 * Perform a scan within the specified base packages.
 * @param basePackages the packages to check for annotated classes
 * @return number of beans registered
 */
public int scan(String... basePackages) {
   int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

   doScan(basePackages); //扫描开始

   // Register annotation config processors, if necessary.
   if (this.includeAnnotationConfig) {
      AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
   }

   return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

去doScan里看看

/**
 * Perform a scan within the specified base packages,
 * returning the registered bean definitions.
 * <p>This method does <i>not</i> register an annotation config processor
 * but rather leaves this up to the caller.
 * @param basePackages the packages to check for annotated classes
 * @return set of beans registered if any for tooling registration purposes (never {@code null})
 */
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { //这里传参是a,b,c,d,e,f,g这种形式
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   for (String basePackage : basePackages) { //遍历路径
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage); //取bean
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         if (checkCandidate(beanName, candidate)) { //检查当前bean和已注册的有没有冲突
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            registerBeanDefinition(definitionHolder, this.registry); //注册bean
         }
      }
   }
   return beanDefinitions;
}

从代码看,就是遍历package,然后取出里面的bean。再对bean进行遍历,判定没冲突就注册,一共两个for循环,代码不复杂,要继续关注的是怎么从package取bean。再提一句,这一篇分析所有的scan相关的代码是在refresh之前的,也就是完全不涉及bean的实例化,属性初始化。

/**
 * Scan the class path for candidate components.
 * @param basePackage the package to check for annotated classes
 * @return a corresponding Set of autodetected bean definitions
 */
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   if (this.componentsIndex != null && indexSupportsIncludeFilters()) { //不进这里componentsIndex为null
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else {
      return scanCandidateComponents(basePackage); //进这里
   }
}

返回的是BeanDefinition的集合。根据前面的代码,结合ClassPathScanningCandidateComponentProvider的构造方法,可以确定this.componentsIndex为空。

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); //获取resource
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      for (Resource resource : resources) { //遍历resource
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }
         if (resource.isReadable()) { //可读取
            try {
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); //从resource里取bean信息
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                     }
                     candidates.add(sbd);
                  }
                  else {
                     if (debugEnabled) {
                        logger.debug("Ignored because not a concrete top-level class: " + resource);
                     }
                  }
               }
               else {
                  if (traceEnabled) {
                     logger.trace("Ignored because not matching any filter: " + resource);
                  }
               }
            }
            catch (Throwable ex) {
               throw new BeanDefinitionStoreException(
                     "Failed to read candidate component class: " + resource, ex);
            }
         }
         else {
            if (traceEnabled) {
               logger.trace("Ignored because not readable: " + resource);
            }
         }
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
   }
   return candidates;
}

看看ScannerGenericBeanDefinition

/**
 * Create a new ScannedGenericBeanDefinition for the class that the
 * given MetadataReader describes.
 * @param metadataReader the MetadataReader for the scanned target class
 */
public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
   Assert.notNull(metadataReader, "MetadataReader must not be null");
   this.metadata = metadataReader.getAnnotationMetadata();
   setBeanClassName(this.metadata.getClassName());
}

这个类得到的信息都是从metadataReader里获取的,取了类名,还有元数据。还是去看看metadataReader做了什么。metadataReader来源于CachingMetadataReaderFactory。

/**
 * Return the MetadataReaderFactory used by this component provider.
 */
public final MetadataReaderFactory getMetadataReaderFactory() {
   if (this.metadataReaderFactory == null) {
      this.metadataReaderFactory = new CachingMetadataReaderFactory();
   }
   return this.metadataReaderFactory;
}

CachingMetadataReaderFactory是使用无参构造方法创建的。去看看构造方法。

/**
 * Create a new CachingMetadataReaderFactory for the default class loader,
 * using a local resource cache.
 */
public CachingMetadataReaderFactory() {
   super();
   setCacheLimit(DEFAULT_CACHE_LIMIT);
}

可以看到这个构造方法没有做其它的事情,只有一个方法调用。分析代码的时候一定要随时注意参数值,参数类型。这个参数是

public static final int DEFAULT_CACHE_LIMIT = 256;
/**
 * Specify the maximum number of entries for the MetadataReader cache.
 * <p>Default is 256 for a local cache, whereas a shared cache is
 * typically unbounded. This method enforces a local resource cache,
 * even if the {@link ResourceLoader} supports a shared resource cache.
 */
public void setCacheLimit(int cacheLimit) {
   if (cacheLimit <= 0) {
      this.metadataReaderCache = null;
   }
   else if (this.metadataReaderCache instanceof LocalResourceCache) {
      ((LocalResourceCache) this.metadataReaderCache).setCacheLimit(cacheLimit);
   }
   else { //走到这里了
      this.metadataReaderCache = new LocalResourceCache(cacheLimit);
   }
}

而LocalResourceCache的代码是

@SuppressWarnings("serial")
private static class LocalResourceCache extends LinkedHashMap<Resource, MetadataReader> {

   private volatile int cacheLimit;

   public LocalResourceCache(int cacheLimit) {
      super(cacheLimit, 0.75f, true);
      this.cacheLimit = cacheLimit;
   }

   public void setCacheLimit(int cacheLimit) {
      this.cacheLimit = cacheLimit;
   }

   public int getCacheLimit() {
      return this.cacheLimit;
   }

   @Override
   protected boolean removeEldestEntry(Map.Entry<Resource, MetadataReader> eldest) {
      return size() > this.cacheLimit;
   }
}

上面看的代码实际上都是为了好分析下面这个方法

@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
   if (this.metadataReaderCache instanceof ConcurrentMap) { //不进这里
      // No synchronization necessary...
      MetadataReader metadataReader = this.metadataReaderCache.get(resource);
      if (metadataReader == null) {
         metadataReader = super.getMetadataReader(resource);
         this.metadataReaderCache.put(resource, metadataReader);
      }
      return metadataReader;
   }
   else if (this.metadataReaderCache != null) { //进了这里
      synchronized (this.metadataReaderCache) {
         MetadataReader metadataReader = this.metadataReaderCache.get(resource);
         if (metadataReader == null) { //新的resource
            metadataReader = super.getMetadataReader(resource); //进了这里
            this.metadataReaderCache.put(resource, metadataReader); //缓存起来
         }
         return metadataReader;
      }
   }
   else {
      return super.getMetadataReader(resource);
   }
}

看看super.getMetadataReader

@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
   return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}

继续看SimpleMetadataReader这个类

SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
   SimpleAnnotationMetadataReadingVisitor visitor = new SimpleAnnotationMetadataReadingVisitor(classLoader);
   getClassReader(resource).accept(visitor, PARSING_OPTIONS);
   this.resource = resource;
   this.annotationMetadata = visitor.getMetadata(); //metaData来自于visitor
}

任务是在SimpleAnnotationMetadataReadingVisitor中完成的。但是class的二进制格式读取是ClassReader完成的,这个类读class的二进制,将二进制分解。分解过程中,使用visitor去转换。

private static ClassReader getClassReader(Resource resource) throws IOException {
   try (InputStream is = new BufferedInputStream(resource.getInputStream())) {
      try {
         return new ClassReader(is); //这个是asm下的ClassReader,解析class二进制
      }
      catch (IllegalArgumentException ex) {
         throw new NestedIOException("ASM ClassReader failed to parse class file - " +
               "probably due to a new Java class file version that isn't supported yet: " + resource, ex);
      }
   }
}

然后看ClassReader.accept,这个方法非常长,感兴趣的可以去看一下java虚拟机里对class文件格式的描述,也有专门的书《深入java虚拟机》,代码我就不一一解释了,主要标注有visitor的地方。


public void accept(
    final ClassVisitor classVisitor, //visitor传入
    final Attribute[] attributePrototypes,
    final int parsingOptions) {
  Context context = new Context();
  context.attributePrototypes = attributePrototypes;
  context.parsingOptions = parsingOptions;
  context.charBuffer = new char[maxStringLength];

  // Read the access_flags, this_class, super_class, interface_count and interfaces fields.
  char[] charBuffer = context.charBuffer;
  int currentOffset = header; //跳过文件头
  int accessFlags = readUnsignedShort(currentOffset);
  String thisClass = readClass(currentOffset + 2, charBuffer); //读自己
  String superClass = readClass(currentOffset + 4, charBuffer); //读父类
  String[] interfaces = new String[readUnsignedShort(currentOffset + 6)];
  currentOffset += 8;
  for (int i = 0; i < interfaces.length; ++i) {
    interfaces[i] = readClass(currentOffset, charBuffer);
    currentOffset += 2; 
  } //读接口

  // Read the class attributes (the variables are ordered as in Section 4.7 of the JVMS).
  // Attribute offsets exclude the attribute_name_index and attribute_length fields.
  // - The offset of the InnerClasses attribute, or 0.
  int innerClassesOffset = 0;
  // - The offset of the EnclosingMethod attribute, or 0.
  int enclosingMethodOffset = 0;
  // - The string corresponding to the Signature attribute, or null.
  String signature = null;
  // - The string corresponding to the SourceFile attribute, or null.
  String sourceFile = null;
  // - The string corresponding to the SourceDebugExtension attribute, or null.
  String sourceDebugExtension = null;
  // - The offset of the RuntimeVisibleAnnotations attribute, or 0.
  int runtimeVisibleAnnotationsOffset = 0;
  // - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
  int runtimeInvisibleAnnotationsOffset = 0;
  // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
  int runtimeVisibleTypeAnnotationsOffset = 0;
  // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
  int runtimeInvisibleTypeAnnotationsOffset = 0;
  // - The offset of the Module attribute, or 0.
  int moduleOffset = 0;
  // - The offset of the ModulePackages attribute, or 0.
  int modulePackagesOffset = 0;
  // - The string corresponding to the ModuleMainClass attribute, or null.
  String moduleMainClass = null;
  // - The string corresponding to the NestHost attribute, or null.
  String nestHostClass = null;
  // - The offset of the NestMembers attribute, or 0.
  int nestMembersOffset = 0;
  // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
  //   This list in the <i>reverse order</i> or their order in the ClassFile structure.
  Attribute attributes = null;

  int currentAttributeOffset = getFirstAttributeOffset();
  for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
    // Read the attribute_info's attribute_name and attribute_length fields.
    String attributeName = readUTF8(currentAttributeOffset, charBuffer);
    int attributeLength = readInt(currentAttributeOffset + 2);
    currentAttributeOffset += 6;
    // The tests are sorted in decreasing frequency order (based on frequencies observed on
    // typical classes).
    if (Constants.SOURCE_FILE.equals(attributeName)) {
      sourceFile = readUTF8(currentAttributeOffset, charBuffer);
    } else if (Constants.INNER_CLASSES.equals(attributeName)) {
      innerClassesOffset = currentAttributeOffset;
    } else if (Constants.ENCLOSING_METHOD.equals(attributeName)) {
      enclosingMethodOffset = currentAttributeOffset;
    } else if (Constants.NEST_HOST.equals(attributeName)) {
      nestHostClass = readClass(currentAttributeOffset, charBuffer);
    } else if (Constants.NEST_MEMBERS.equals(attributeName)) {
      nestMembersOffset = currentAttributeOffset;
    } else if (Constants.SIGNATURE.equals(attributeName)) {
      signature = readUTF8(currentAttributeOffset, charBuffer);
    } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
      runtimeVisibleAnnotationsOffset = currentAttributeOffset;
    } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
      runtimeVisibleTypeAnnotationsOffset = currentAttributeOffset;
    } else if (Constants.DEPRECATED.equals(attributeName)) {
      accessFlags |= Opcodes.ACC_DEPRECATED;
    } else if (Constants.SYNTHETIC.equals(attributeName)) {
      accessFlags |= Opcodes.ACC_SYNTHETIC;
    } else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
      sourceDebugExtension =
          readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]);
    } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
      runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
    } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
      runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset;
    } else if (Constants.MODULE.equals(attributeName)) {
      moduleOffset = currentAttributeOffset;
    } else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) {
      moduleMainClass = readClass(currentAttributeOffset, charBuffer);
    } else if (Constants.MODULE_PACKAGES.equals(attributeName)) {
      modulePackagesOffset = currentAttributeOffset;
    } else if (!Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
      // The BootstrapMethods attribute is read in the constructor.
      Attribute attribute =
          readAttribute(
              attributePrototypes,
              attributeName,
              currentAttributeOffset,
              attributeLength,
              charBuffer,
              -1,
              null);
      attribute.nextAttribute = attributes;
      attributes = attribute;
    }
    currentAttributeOffset += attributeLength;
  }

  // Visit the class declaration. The minor_version and major_version fields start 6 bytes before
  // the first constant pool entry, which itself starts at cpInfoOffsets[1] - 1 (by definition).
  classVisitor.visit(
      readInt(cpInfoOffsets[1] - 7), accessFlags, thisClass, signature, superClass, interfaces);

  // Visit the SourceFile and SourceDebugExtenstion attributes.
  if ((parsingOptions & SKIP_DEBUG) == 0
      && (sourceFile != null || sourceDebugExtension != null)) {
    classVisitor.visitSource(sourceFile, sourceDebugExtension);
  }

  // Visit the Module, ModulePackages and ModuleMainClass attributes.
  if (moduleOffset != 0) {
    readModuleAttributes(
        classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass);
  }

  // Visit the NestHost attribute.
  if (nestHostClass != null) {
    classVisitor.visitNestHost(nestHostClass);
  }

  // Visit the EnclosingMethod attribute.
  if (enclosingMethodOffset != 0) {
    String className = readClass(enclosingMethodOffset, charBuffer);
    int methodIndex = readUnsignedShort(enclosingMethodOffset + 2);
    String name = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex], charBuffer);
    String type = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex] + 2, charBuffer);
    classVisitor.visitOuterClass(className, name, type);
  }

  // Visit the RuntimeVisibleAnnotations attribute.
  if (runtimeVisibleAnnotationsOffset != 0) {
    int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
    int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
    while (numAnnotations-- > 0) {
      // Parse the type_index field.
      String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
      currentAnnotationOffset += 2;
      // Parse num_element_value_pairs and element_value_pairs and visit these values.
      currentAnnotationOffset =
          readElementValues(
              classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
              currentAnnotationOffset,
              /* named = */ true,
              charBuffer); //读取annotation,注解
    }
  }

  // Visit the RuntimeInvisibleAnnotations attribute.
  if (runtimeInvisibleAnnotationsOffset != 0) {
    int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
    int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
    while (numAnnotations-- > 0) {
      // Parse the type_index field.
      String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
      currentAnnotationOffset += 2;
      // Parse num_element_value_pairs and element_value_pairs and visit these values.
      currentAnnotationOffset =
          readElementValues(
              classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
              currentAnnotationOffset,
              /* named = */ true,
              charBuffer);
    }
  }

  // Visit the RuntimeVisibleTypeAnnotations attribute.
  if (runtimeVisibleTypeAnnotationsOffset != 0) {
    int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
    int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
    while (numAnnotations-- > 0) {
      // Parse the target_type, target_info and target_path fields.
      currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
      // Parse the type_index field.
      String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
      currentAnnotationOffset += 2;
      // Parse num_element_value_pairs and element_value_pairs and visit these values.
      currentAnnotationOffset =
          readElementValues(
              classVisitor.visitTypeAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  annotationDescriptor,
                  /* visible = */ true),
              currentAnnotationOffset,
              /* named = */ true,
              charBuffer);
    }
  }

  // Visit the RuntimeInvisibleTypeAnnotations attribute.
  if (runtimeInvisibleTypeAnnotationsOffset != 0) {
    int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
    int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
    while (numAnnotations-- > 0) {
      // Parse the target_type, target_info and target_path fields.
      currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
      // Parse the type_index field.
      String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
      currentAnnotationOffset += 2;
      // Parse num_element_value_pairs and element_value_pairs and visit these values.
      currentAnnotationOffset =
          readElementValues(
              classVisitor.visitTypeAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  annotationDescriptor,
                  /* visible = */ false),
              currentAnnotationOffset,
              /* named = */ true,
              charBuffer);
    }
  }

  // Visit the non standard attributes.
  while (attributes != null) {
    // Copy and reset the nextAttribute field so that it can also be used in ClassWriter.
    Attribute nextAttribute = attributes.nextAttribute;
    attributes.nextAttribute = null;
    classVisitor.visitAttribute(attributes);
    attributes = nextAttribute;
  }

  // Visit the NestedMembers attribute.
  if (nestMembersOffset != 0) {
    int numberOfNestMembers = readUnsignedShort(nestMembersOffset);
    int currentNestMemberOffset = nestMembersOffset + 2;
    while (numberOfNestMembers-- > 0) {
      classVisitor.visitNestMember(readClass(currentNestMemberOffset, charBuffer));
      currentNestMemberOffset += 2;
    }
  }

  // Visit the InnerClasses attribute.
  if (innerClassesOffset != 0) {
    int numberOfClasses = readUnsignedShort(innerClassesOffset);
    int currentClassesOffset = innerClassesOffset + 2;
    while (numberOfClasses-- > 0) {
      classVisitor.visitInnerClass(
          readClass(currentClassesOffset, charBuffer),
          readClass(currentClassesOffset + 2, charBuffer),
          readUTF8(currentClassesOffset + 4, charBuffer),
          readUnsignedShort(currentClassesOffset + 6));
      currentClassesOffset += 8;
    }
  }

  // Visit the fields and methods.
  int fieldsCount = readUnsignedShort(currentOffset);
  currentOffset += 2;
  while (fieldsCount-- > 0) {
    currentOffset = readField(classVisitor, context, currentOffset);
  }
  int methodsCount = readUnsignedShort(currentOffset);
  currentOffset += 2;
  while (methodsCount-- > 0) {
    currentOffset = readMethod(classVisitor, context, currentOffset);
  }

  // Visit the end of the class.
  classVisitor.visitEnd();
}

到了这里,可以知道,所有的metadata,其实都是从class二进制流里得来的。看一下metadata到底都有什么内容。

@Override
public void visitEnd() {
   String[] memberClassNames = StringUtils.toStringArray(this.memberClassNames);
   MethodMetadata[] annotatedMethods = this.annotatedMethods.toArray(new MethodMetadata[0]);
   MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
   this.metadata = new SimpleAnnotationMetadata(this.className, this.access,
         this.enclosingClassName, this.superClassName, this.independentInnerClass,
         this.interfaceNames, memberClassNames, annotatedMethods, annotations);
} //类名,access privilege,inner class,interfaces,annotions,annotatedMethos(注意@Bean修饰的方法就是一种)

public SimpleAnnotationMetadata getMetadata() {
   Assert.state(this.metadata != null, "AnnotationMetadata not initialized");
   return this.metadata;
}

到此,一个bean的class读取,注册就分析完了。还遗留了一个问题,如果是扫描一个包下面的所有类,那么spring是怎么过滤掉接口,抽象类,内部类这些它不需要的东西。

/**
 * Determine whether the given bean definition qualifies as candidate.
 * <p>The default implementation checks whether the class is not an interface
 * and not dependent on an enclosing class.
 * <p>Can be overridden in subclasses.
 * @param beanDefinition the bean definition to check
 * @return whether the bean definition qualifies as a candidate component
 */
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
   AnnotationMetadata metadata = beanDefinition.getMetadata();
   return (metadata.isIndependent() && (metadata.isConcrete() ||
         (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

简单分析一下,第一种:不是接口,不是抽象类,还必须是顶层类(比如,不是内部嵌套类)。第二种:是抽象类,但是有注解修饰的方法。到这里,整个流程分析完了,spring对于bean的加载,是扫描式的,先全部扫进来。扫描进来以后,再做过滤。过滤完了以后,再注册bean。

写在最后:接单,有后台活java/cpp/lua/go联系shihuaping0918@163.com。不上班了也要有点收入才行。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容