spring源碼分析7----注冊(cè)@Bean修飾的bean

作者:shihuaping0918@163.com轉(zhuǎn)載請(qǐng)注明作者

前面的文章分析了bean的實(shí)例化馋劈,bean字段/成員變量的填充。這一篇來看一下@Bean注解。最早分析的是讀xml中的<bean></bean>定義,現(xiàn)在來看一下代碼中的@Bean又是怎么生效的猴凹。文章的篇幅越來越大,因?yàn)榇a貼上去占了太多空間岭皂,但是只摘取一段代碼郊霎,這樣又對(duì)讀者不友好,讀文章的時(shí)候又要不停返回去看代碼對(duì)照爷绘。篇幅大就大吧歹篓,我盡量不貼圖片,這樣的話揉阎,文字再多庄撮,占的體積也是有限的。這一篇文章又會(huì)很長(zhǎng)毙籽。

從AnnotationConfigApplicationContext講起洞斯,基于xml配置時(shí),一般使用ClassPathXmlApplication進(jìn)行spring應(yīng)用上下文加載坑赡±尤纾基于java配置時(shí),就使用AnnotationConfigApplicationContext進(jìn)行spring應(yīng)用上下文加載毅否。它會(huì)注冊(cè)@Bean注釋的方法所返回的bean亚铁。這里有一個(gè)要注意的地方,就是要使用這兩個(gè)構(gòu)造方法螟加,才會(huì)去注冊(cè)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這里就不講了吞琐,這個(gè)類也只是為了提供一個(gè)入口,下面來看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站粟,是在無參構(gòu)造方法里賦值的。

/**
 * 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方法參數(shù)傳的是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)) { //檢查當(dāng)前bean和已注冊(cè)的有沒有沖突
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            registerBeanDefinition(definitionHolder, this.registry); //注冊(cè)bean
         }
      }
   }
   return beanDefinitions;
}

從代碼看切诀,就是遍歷package,然后取出里面的bean搔弄。再對(duì)bean進(jìn)行遍歷幅虑,判定沒沖突就注冊(cè),一共兩個(gè)for循環(huán)肯污,代碼不復(fù)雜翘单,要繼續(xù)關(guān)注的是怎么從package取bean吨枉。再提一句蹦渣,這一篇分析所有的scan相關(guān)的代碼是在refresh之前的,也就是完全不涉及bean的實(shí)例化貌亭,屬性初始化柬唯。

/**
 * 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()) { //不進(jìn)這里componentsIndex為null
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else {
      return scanCandidateComponents(basePackage); //進(jìn)這里
   }
}

返回的是BeanDefinition的集合。根據(jù)前面的代碼圃庭,結(jié)合ClassPathScanningCandidateComponentProvider的構(gòu)造方法锄奢,可以確定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());
}

這個(gè)類得到的信息都是從metadataReader里獲取的剧腻,取了類名拘央,還有元數(shù)據(jù)。還是去看看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是使用無參構(gòu)造方法創(chuàng)建的。去看看構(gòu)造方法儒旬。

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

可以看到這個(gè)構(gòu)造方法沒有做其它的事情栏账,只有一個(gè)方法調(diào)用。分析代碼的時(shí)候一定要隨時(shí)注意參數(shù)值栈源,參數(shù)類型挡爵。這個(gè)參數(shù)是

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;
   }
}

上面看的代碼實(shí)際上都是為了好分析下面這個(gè)方法

@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
   if (this.metadataReaderCache instanceof ConcurrentMap) { //不進(jìn)這里
      // 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) { //進(jìn)了這里
      synchronized (this.metadataReaderCache) {
         MetadataReader metadataReader = this.metadataReaderCache.get(resource);
         if (metadataReader == null) { //新的resource
            metadataReader = super.getMetadataReader(resource); //進(jìn)了這里
            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());
}

繼續(xù)看SimpleMetadataReader這個(gè)類

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
}

任務(wù)是在SimpleAnnotationMetadataReadingVisitor中完成的。但是class的二進(jìn)制格式讀取是ClassReader完成的甚垦,這個(gè)類讀class的二進(jìn)制茶鹃,將二進(jìn)制分解涣雕。分解過程中,使用visitor去轉(zhuǎn)換前计。

private static ClassReader getClassReader(Resource resource) throws IOException {
   try (InputStream is = new BufferedInputStream(resource.getInputStream())) {
      try {
         return new ClassReader(is); //這個(gè)是asm下的ClassReader,解析class二進(jìn)制
      }
      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胞谭,這個(gè)方法非常長(zhǎng),感興趣的可以去看一下java虛擬機(jī)里對(duì)class文件格式的描述男杈,也有專門的書《深入java虛擬機(jī)》丈屹,代碼我就不一一解釋了,主要標(biāo)注有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先蒋,其實(shí)都是從class二進(jìn)制流里得來的⊥鸾ィ看一下metadata到底都有什么內(nèi)容竞漾。

@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;
}

到此业岁,一個(gè)bean的class讀取,注冊(cè)就分析完了寇蚊。還遺留了一個(gè)問題笔时,如果是掃描一個(gè)包下面的所有類,那么spring是怎么過濾掉接口仗岸,抽象類允耿,內(nèi)部類這些它不需要的東西。

/**
 * 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()))));
}

簡(jiǎn)單分析一下扒怖,第一種:不是接口较锡,不是抽象類,還必須是頂層類(比如盗痒,不是內(nèi)部嵌套類)蚂蕴。第二種:是抽象類,但是有注解修飾的方法积糯。到這里掂墓,整個(gè)流程分析完了,spring對(duì)于bean的加載看成,是掃描式的君编,先全部掃進(jìn)來。掃描進(jìn)來以后川慌,再做過濾吃嘿。過濾完了以后祠乃,再注冊(cè)bean。

寫在最后:接單兑燥,有后臺(tái)活java/cpp/lua/go聯(lián)系shihuaping0918@163.com亮瓷。不上班了也要有點(diǎn)收入才行。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末降瞳,一起剝皮案震驚了整個(gè)濱河市嘱支,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挣饥,老刑警劉巖除师,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異扔枫,居然都是意外死亡汛聚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門短荐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倚舀,“玉大人,你說我怎么就攤上這事忍宋『勖玻” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵讶踪,是天一觀的道長(zhǎng)芯侥。 經(jīng)常有香客問我泊交,道長(zhǎng)乳讥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任廓俭,我火速辦了婚禮云石,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘研乒。我一直安慰自己汹忠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布雹熬。 她就那樣靜靜地躺著宽菜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪竿报。 梳的紋絲不亂的頭發(fā)上铅乡,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音烈菌,去河邊找鬼阵幸。 笑死花履,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的挚赊。 我是一名探鬼主播诡壁,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼荠割!你這毒婦竟也來了妹卿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蔑鹦,失蹤者是張志新(化名)和其女友劉穎纽帖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體举反,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡懊直,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了火鼻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片室囊。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖魁索,靈堂內(nèi)的尸體忽然破棺而出融撞,到底是詐尸還是另有隱情,我是刑警寧澤粗蔚,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布尝偎,位于F島的核電站,受9級(jí)特大地震影響鹏控,放射性物質(zhì)發(fā)生泄漏致扯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一当辐、第九天 我趴在偏房一處隱蔽的房頂上張望抖僵。 院中可真熱鬧,春花似錦缘揪、人聲如沸耍群。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹈垢。三九已至,卻和暖如春袖裕,著一層夾襖步出監(jiān)牢的瞬間曹抬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工陆赋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沐祷,地道東北人嚷闭。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像赖临,于是被迫代替她去往敵國(guó)和親胞锰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容