Spring构建BeanDefinition的全流程解析,选择asm而非反射的原因

1,761 阅读3分钟

代码从ClassPathBeanDefinitionScanner类的doScan方法开始阅读。

处理流程整体说来并不复杂,下面我们就看下流程图。

1.流程图

流程图

2.时序图

3.asm读取class文件的信息

我们看下如何利用asm读取class文件的信息的:

SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
   InputStream is = new BufferedInputStream(resource.getInputStream());
   ClassReader classReader;
   try {
      classReader = new ClassReader(is);
   }
   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);
   }
   finally {
      is.close();
   }
   //下面会重点讲解
   AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
   classReader.accept(visitor, ClassReader.SKIP_DEBUG);

   this.annotationMetadata = visitor;
   // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
   this.classMetadata = visitor;
   this.resource = resource;
}

AnnotationMetadataReadingVisitor,这个继承了ClassVisitor抽象类,这个visitor里面定义了一堆的回调方法:

public abstract class ClassVisitor { 
  public ClassVisitor(int api); 
  public ClassVisitor(int api, ClassVisitor cv);
  public void visit(int version, int access, String name, String signature, String superName, String[] interfaces); 
  public void visitSource(String source, String debug); 
  public void visitOuterClass(String owner, String name, String desc); 
 // 解析到class文件中的注解时回调本方法 
 AnnotationVisitor visitAnnotation(String desc, boolean visible); 
 public void visitAttribute(Attribute attr); 
 public void visitInnerClass(String name, String outerName,String innerName,int access); 
 // 解析到field时回调 
 public FieldVisitor visitField(int access, String name, String desc,String signature, Object value); 
 // 解析到method时回调 
  public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions); 
  void visitEnd(); 
}

这其中,方法的访问顺序如下:

visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*
( visitInnerClass | visitField | visitMethod )*
visitEnd

代表:
visit必须最先访问;
接着是最多一次的visitSource,再接着是最多一次的visitOuterClass;
接着是任意多次的visitAnnotation | visitAttribute ,这两个,顺序随意;
再接着是,任意多次的visitInnerClass | visitField | visitMethod ,顺序随意
最后,visitEnd

这个顺序的? * () 等符号,其实类似于正则表达式的语法,对吧,还是比较好理解的。

AnnotationMetadataReadingVisitor还实现了AnnotationMetadata接口。

AnnotationMetadata接口方法如下:

下面我们看下,两个非常重要的方法:

1.获取在当前class上的直接注解:getAnnotationTypes();

2.获取某个直接注解的父注解,比如你这里传个controller进去,就能给你拿到controller这个注解的父注解:getMetaAnnotationTypes(String annotationName);

方法1:

@Override
public Set<String> getAnnotationTypes() {
   return this.annotationSet;
}

annotationSet里的数据是如何来的呢?

这里每访问到一个注解,就会回调visitAnnotation方法,放入到annotationSet中。

@Override
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
   String className = Type.getType(desc).getClassName();
   this.annotationSet.add(className);
   return new AnnotationAttributesReadingVisitor(
         className, this.attributesMap, this.metaAnnotationMap, this.classLoader);
}

方法2:

@Override
public Set<String> getMetaAnnotationTypes(String annotationName) {
   return this.metaAnnotationMap.get(annotationName);
}

metaAnnotationMap中的数据是从哪里来的呢:

AnnotationAttributesReadingVisitor,这个visitor会在:asm访问注解的具体属性时,其中的如下方法被回调。

@Override
public void visitEnd() {
   super.visitEnd();

   Class<?> annotationClass = this.attributes.annotationType();
   if (annotationClass != null) {
      List<AnnotationAttributes> attributeList = this.attributesMap.get(this.annotationType);
      if (attributeList == null) {
         this.attributesMap.add(this.annotationType, this.attributes);
      }
      else {
         attributeList.add(0, this.attributes);
      }
      Set<Annotation> visited = new LinkedHashSet<>();
      //获取所有的父注解
      Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass);
      if (!ObjectUtils.isEmpty(metaAnnotations)) {
         for (Annotation metaAnnotation : metaAnnotations) {
            //非元注解
           if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) {
              recursivelyCollectMetaAnnotations(visited, metaAnnotation);
            }
         }
      }
      Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());
      for (Annotation ann : visited) {
         metaAnnotationTypeNames.add(ann.annotationType().getName());
      }
      this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
   }
}

private void recursivelyCollectMetaAnnotations(Set<Annotation> visited, Annotation annotation) {
   Class<? extends Annotation> annotationType = annotation.annotationType();
   String annotationName = annotationType.getName();
   if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName) && visited.add(annotation)) {
      try {
         // Only do attribute scanning for public annotations; we'd run into
         // IllegalAccessExceptions otherwise, and we don't want to mess with
         // accessibility in a SecurityManager environment.
         if (Modifier.isPublic(annotationType.getModifiers())) {
            this.attributesMap.add(annotationName,
                  AnnotationUtils.getAnnotationAttributes(annotation, false, true));
         }
         for (Annotation metaMetaAnnotation : annotationType.getAnnotations()) {
            // 递归调用自己
           recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation);
         }
      }
      catch (Throwable ex) {
         if (logger.isDebugEnabled()) {
            logger.debug("Failed to introspect meta-annotations on [" + annotation + "]: " + ex);
         }
      }
   }
}

通过asm的解析,最终返回MetadataReader的对象,通过MetadataReader可以读取到类的元数据和注解的元数据。

再利用MetadataReader,创建出ScannedGenericBeanDefinition。

4.为什么使用asm,而不是反射来获取元数据

1.asm方式的性能,远高于反射实现,因为无需加载class,直接解析class文件的字节码。

asm是一个字节码操作和分析的框架,能够用来修改已存在的class,或者动态生成class,直接以二进制的形式。ASM提供一些通用的字节码转换和分析算法,通过这些算法,可以构建复杂的字节码转换和代码分析工具。ASM提供和其他字节码框架类似的功能,但是其专注于性能。因为它被设计和实现为,尽可能的小,尽可能的快。

2.Class.forName()方法在执行时,类中static代码块会执行,这会让静态代码块过早的执行,对执行时机造成了干扰。