• Spring元数据Metadata的使用,注解编程之AnnotationMetadata,ClassMetadata、MetadataReaderFactory【享学Spring】


    每篇一句
    不要担心你此次此刻的付出得不到回报,因为你现在的付出是为了扎根

    前言
    Spring在2.0的时候就支持了基于XML Schema的扩展机制,让我们可以自定义的对xml配置文件进行扩展(四大步骤,有兴趣的可以自己学习),比如鼎鼎大名的Dubbo它就扩展了xml,用它来引入服务或者导出服务。
    随着Spring3.0+的发展,xml慢慢的淡出了我们的视野,特别是Spring Boot的流行让xml彻底消失,所有的xml配置都使用注解的方式进行了代替。有的人说注解编程源码难度是 XML 扩展好几倍,其实我并不这么认为,本文就着眼于大多数小伙伴比较费解的元数据(Metadata)进行分析,给深入理解注解编程铺路。

    元数据
    元数据:数据的数据。比如Class就是一种元数据。Metadata在org.springframework.core.type包名下,还有用于读取的子包classreading也是重要知识点。此体系大致的类结构列出如下图:

    可以看到顶层接口有两个:ClassMetadata和AnnotatedTypeMetadata

    注解上的注解,Spring将其定义为元注解(meta-annotation),如 @Component标注在 @Service上,@Component就被称作为元注解。后面我们就将注解的注解称为元注解。

    ClassMetadata:对Class的抽象和适配
    此接口的所有方法,基本上都跟Class有关。

    // @since 2.5
    public interface ClassMetadata {

    // 返回类名(注意返回的是最原始的那个className)
    String getClassName();
    boolean isInterface();
    // 是否是注解
    boolean isAnnotation();
    boolean isAbstract();
    // 是否允许创建 不是接口且不是抽象类 这里就返回true了
    boolean isConcrete();
    boolean isFinal();
    // 是否是独立的(能够创建对象的) 比如是Class、或者内部类、静态内部类
    boolean isIndependent();
    // 是否有内部类之类的东东
    boolean hasEnclosingClass();
    @Nullable
    String getEnclosingClassName();
    boolean hasSuperClass();
    @Nullable
    String getSuperClassName();
    // 会把实现的所有接口名称都返回 具体依赖于Class#getSuperclass
    String[] getInterfaceNames();

    // 基于:Class#getDeclaredClasses 返回类中定义的公共、私有、保护的内部类
    String[] getMemberClassNames();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    它的继承树如下:


    StandardClassMetadata
    基于Java标准的(Standard)反射实现元数据的获取。

    // @since 2.5
    public class StandardClassMetadata implements ClassMetadata {
    // 用于内省的Class类
    private final Class<?> introspectedClass;

    // 唯一构造函数:传进来的Class,作为内部的内省对象
    public StandardClassMetadata(Class<?> introspectedClass) {
    Assert.notNull(introspectedClass, "Class must not be null");
    this.introspectedClass = introspectedClass;
    }
    ... // 后面所有的方法实现,都是基于introspectedClass,类似代理模式。举例如下:

    public final Class<?> getIntrospectedClass() {
    return this.introspectedClass;
    }
    @Override
    public boolean isInterface() {
    return this.introspectedClass.isInterface();
    }
    @Override
    public String[] getMemberClassNames() {
    LinkedHashSet<String> memberClassNames = new LinkedHashSet<>(4);
    for (Class<?> nestedClass : this.introspectedClass.getDeclaredClasses()) {
    memberClassNames.add(nestedClass.getName());
    }
    return StringUtils.toStringArray(memberClassNames);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    它有个非常重要的子类:StandardAnnotationMetadata它和注解密切相关,在文章下半部分重点分析。

    MethodsMetadata:新增访问方法元数据的接口
    它是子接口,主要增加了从Class里获取到MethodMetadata们的方法:

    // @since 2.1 可以看到它出现得更早一些
    public interface MethodsMetadata extends ClassMetadata {
    // 返回该class所有的方法
    Set<MethodMetadata> getMethods();
    // 方法指定方法名的方法们(因为有重载嘛~)
    Set<MethodMetadata> getMethods(String name);
    }
    1
    2
    3
    4
    5
    6
    7
    名字上请不要和MethodMetadata搞混了,MethodMetadata是AnnotatedTypeMetadata的子接口,代表具体某一个Type(方法上的注解);而此类是个ClassMetadata,它能获取到本类里所有的方法Method(MethodMetadata)~

    AnnotatedTypeMetadata:对注解元素的封装适配
    什么叫注解元素(AnnotatedElement)?比如我们常见的Class、Method、Constructor、Parameter等等都属于它的子类都属于注解元素。简单理解:只要能在上面标注注解都属于这种元素。Spring4.0新增的这个接口提供了对注解统一的、便捷的访问,使用起来更加的方便高效了。

    // @since 4.0
    public interface AnnotatedTypeMetadata {

    // 此元素是否标注有此注解~~~~
    // annotationName:注解全类名
    boolean isAnnotated(String annotationName);

    // 这个就厉害了:取得指定类型注解的所有的属性 - 值(k-v)
    // annotationName:注解全类名
    // classValuesAsString:若是true表示 Class用它的字符串的全类名来表示。这样可以避免Class被提前加载
    @Nullable
    Map<String, Object> getAnnotationAttributes(String annotationName);
    @Nullable
    Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);

    // 参见这个方法的含义:AnnotatedElementUtils.getAllAnnotationAttributes
    @Nullable
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
    @Nullable
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    它的继承树如下:

    两个子接口相应的都提供了标准实现以及基于ASM的Visitor模式实现。

    ASM 是一个通用的 Java 字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。 ASM 虽然提供与其他 Java 字节码框架如 Javassist,CGLIB类似的功能,但是其设计与实现小而快,且性能足够高。

    Spring 直接将 ASM 框架核心源码内嵌于 Spring-core中,目前`Spring 5.1使用ASM 7 版本。

    MethodMetadata:方法描述
    子接口,用来描述java.lang.reflect.Method。

    // @since 3.0
    public interface MethodMetadata extends AnnotatedTypeMetadata {
    String getMethodName();
    String getDeclaringClassName();
    String getReturnTypeName();
    boolean isAbstract();
    boolean isStatic();
    boolean isFinal();
    boolean isOverridable();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    StandardMethodMetadata
    基于反射的标准实现,略

    MethodMetadataReadingVisitor
    基于ASM的实现的,继承自ASM``的org.springframework.asm.MethodVisitor采用Visitor的方式读取到元数据。

    // @since 3.0
    public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata {
    ...
    }
    1
    2
    3
    4
    AnnotationMetadata(重要)
    这是理解Spring注解编程的必备知识,它是ClassMetadata和AnnotatedTypeMetadata的子接口,具有两者共同能力,并且新增了访问注解的相关方法。可以简单理解为它是对注解的抽象。

    经常这么使用得到注解里面所有的属性值:
    AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annoMetadata, annType);

    // @since 2.5
    public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {

    //拿到当前类上所有的注解的全类名(注意是全类名)
    Set<String> getAnnotationTypes();
    // 拿到指定的注解类型
    //annotationName:注解类型的全类名
    Set<String> getMetaAnnotationTypes(String annotationName);

    // 是否包含指定注解 (annotationName:全类名)
    boolean hasAnnotation(String annotationName);
    //这个厉害了,用于判断注解类型自己是否被某个元注解类型所标注
    //依赖于AnnotatedElementUtils#hasMetaAnnotationTypes
    boolean hasMetaAnnotation(String metaAnnotationName);

    // 类里面只有有一个方法标注有指定注解,就返回true
    //getDeclaredMethods获得所有方法, AnnotatedElementUtils.isAnnotated是否标注有指定注解
    boolean hasAnnotatedMethods(String annotationName);
    // 返回所有的标注有指定注解的方法元信息。注意返回的是MethodMetadata 原理基本同上
    Set<MethodMetadata> getAnnotatedMethods(String annotationName);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    同样的它提供了两种实现方式。

    StandardAnnotationMetadata
    继承了StandardClassMetadata,很明显关于ClassMetadata的实现部分就交给此父类了,自己只关注于AnnotationMetadata接口的实现。

    // @since 2.5
    public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {

    // 很显然它是基于标准反射类型:java.lang.annotation.Annotation
    // this.annotations = introspectedClass.getAnnotations()
    private final Annotation[] annotations;
    private final boolean nestedAnnotationsAsMap;
    ...


    // 获取本Class类上的注解的元注解们
    @Override
    public Set<String> getMetaAnnotationTypes(String annotationName) {
    return (this.annotations.length > 0 ?
    AnnotatedElementUtils.getMetaAnnotationTypes(getIntrospectedClass(), annotationName) : Collections.emptySet());
    }

    @Override
    public boolean hasAnnotation(String annotationName) {
    for (Annotation ann : this.annotations) {
    if (ann.annotationType().getName().equals(annotationName)) {
    return true;
    }
    }
    return false;
    }
    ...
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    AnnotationMetadataReadingVisitor
    继承自ClassMetadataReadingVisitor,同样的ClassMetadata部分实现交给了它。

    说明:ClassMetadataReadingVisitor是org.springframework.core.type.classreading包下的类,同包的还有我下面重点讲述的MetadataReader。此实现类最终委托给AnnotationMetadataReadingVisitor来做的,而它便是ClassMetadataReadingVisitor的子类(MetadataReader的底层实现就是它,使用的ASM的ClassVisitor模式读取元数据)。

    // @since 2.5
    public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
    ...
    }
    1
    2
    3
    4
    MetadataReader接口
    你是否有疑问:为何Spring要提供一个标准实现和一个ASM的实现呢?这里就能给你答案。
    此接口是一个访问ClassMetadata等的简单门面,实现是委托给org.springframework.asm.ClassReader、ClassVisitor来处理的,它不用把Class加载进JVM就可以拿到元数据,因为它读取的是资源:Resource,这是它最大的优势所在。

    // @since 2.5
    public interface MetadataReader {
    // 返回此Class文件的来源(资源)
    Resource getResource();
    // 返回此Class的元数据信息
    ClassMetadata getClassMetadata();
    // 返回此类的注解元信息(包括方法的)
    AnnotationMetadata getAnnotationMetadata();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    它的继承树如下:


    SimpleMetadataReader
    它是基于ASM的org.springframework.asm.ClassReader的简单实现。请注意:此类是非public的,而是default包访问权限。

    final class SimpleMetadataReader implements MetadataReader {
    private final Resource resource;
    private final ClassMetadata classMetadata;
    private final AnnotationMetadata annotationMetadata;

    // 唯一构造函数:给上面三个私有属性赋值,下面就只需提供get方法即可
    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,咀咒读取从而获取到各种信息
    // 它实现了ClassVisitor,所以可以作为入参传给ClassReader ASM去解析
    AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
    classReader.accept(visitor, ClassReader.SKIP_DEBUG);

    this.annotationMetadata = visitor;
    // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
    this.classMetadata = visitor;
    this.resource = resource;
    }
    ... // 省略三个get方法
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    MethodsMetadataReader
    子接口,新增接口方法:获取到本类所有的方法元数据们。

    public interface MethodsMetadataReader extends MetadataReader {
    MethodsMetadata getMethodsMetadata();
    ...
    }
    1
    2
    3
    4
    它所有的实现都是委托给静态内部类MethodsMetadataReadingVisitor去做的,它继承自上面的AnnotationMetadataReadingVisitor并且实现了接口MethodsMetadata的相关方法。

    它的唯一实现类DefaultMethodsMetadataReader的访问权限也是包级别非public,略。

    MetadataReaderFactory
    MetadataReader的实现都并未public暴露出来,所以我们若想得到它的实例,就只能通过此工厂。

    // @since 2.5
    public interface MetadataReaderFactory {
    //className: the class name (to be resolved to a ".class" file)
    MetadataReader getMetadataReader(String className) throws IOException;
    MetadataReader getMetadataReader(Resource resource) throws IOException;
    }
    1
    2
    3
    4
    5
    6
    继承树如下:


    SimpleMetadataReaderFactory
    利用ResourceLoader的简单实现,加载进资源后,new SimpleMetadataReader(resource)交给此实例分析即可。

    public class SimpleMetadataReaderFactory implements MetadataReaderFactory {
    // ResourceLoader这个资源加载类应该不陌生了吧
    // 默认使用的是DefaultResourceLoader,当然你可以通过构造器指定
    private final ResourceLoader resourceLoader;

    // 根据类名找到一个Resource
    @Override
    public MetadataReader getMetadataReader(String className) throws IOException {
    try {
    // 把..形式换成//.class形式。使用前缀是:classpath: 在类路径里找哦
    String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
    Resource resource = this.resourceLoader.getResource(resourcePath);
    return getMetadataReader(resource); // 调用重载方法
    } catch (FileNotFoundException ex) {
    // Maybe an inner class name using the dot name syntax? Need to use the dollar syntax here...
    // ClassUtils.forName has an equivalent check for resolution into Class references later on.
    ... // 此处是兼容内部类形式,代码略
    }
    }

    // 默认使用的是SimpleMetadataReader哦~~~
    @Override
    public MetadataReader getMetadataReader(Resource resource) throws IOException {
    return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    此工厂生产的是SimpleMetadataReader。

    CachingMetadataReaderFactory
    它继承自SimpleMetadataReaderFactory,没有其它特殊的,就是提供了缓存能力private Map<Resource, MetadataReader> metadataReaderCache,提高访问效率。
    因为有了它,所以SimpleMetadataReaderFactory就不需要被直接使用了,用它代替。Spring内自然也使用的便是效率更高的它喽~

    MethodsMetadataReaderFactory
    它继承自SimpleMetadataReaderFactory,唯一区别是它生产的是一个MethodsMetadataReader(DefaultMethodsMetadataReader),从而具有了读取MethodsMetadata的能力。
    此类可认为从没有被Spring内部使用过,暂且可忽略(spring-data工程有用)

    Factory工厂的实现都是非常简单的,毕竟只是为了生产一个实例而已。

    Spring注解编程中AnnotationMetadata的使用
    Spring从3.0开始就大量的使用到了注解编程模式,所以可想而知它对元数据(特别是注解元数据)的使用是非常多的,此处我只给出非常简单的总结。

    对于MetadataReaderFactory的应用主要体现在几个地方:

    ConfigurationClassPostProcessor:该属性值最终会传给ConfigurationClassParser,用于@EnableXXX / @Import等注解的解析上~
    // 私有成员变量,默认使用的CachingMetadataReaderFactory
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
    1
    2
    ClassPathScanningCandidateComponentProvider:它用于@ComponentScan的时候解析,拿到元数据判断是否是@Component的派生注解
    public final MetadataReaderFactory getMetadataReaderFactory() {
    if (this.metadataReaderFactory == null) {
    this.metadataReaderFactory = new CachingMetadataReaderFactory();
    }
    return this.metadataReaderFactory;
    }
    1
    2
    3
    4
    5
    6
    Mybatis的SqlSessionFactoryBean:它在使用上非常简单,只是为了从Resouece里拿到ClassName而已。classMetadata.getClassName()
    private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();

    private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) {
    ...
    ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata();
    Class<?> clazz = Resources.classForName(classMetadata.getClassName());
    ...
    }
    1
    2
    3
    4
    5
    6
    7
    8
    SourceClass:它是对source对象一个轻量级的包装,持有AnnotationMetadata 元数据,如下一般实际为一个StandardAnnotationMetadata,比如@EnableTransactionManagement用的就是它
    private class SourceClass implements Ordered {
    private final Object source; // Class or MetadataReader
    private final AnnotationMetadata metadata;
    public SourceClass(Object source) {
    this.source = source;
    if (source instanceof Class) {
    this.metadata = new StandardAnnotationMetadata((Class<?>) source, true);
    } else {
    this.metadata = ((MetadataReader) source).getAnnotationMetadata();
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    说明:Spring的@EnableXXX模块注解很多都使用到了ImportSelector这个接口,此接口的回调方法参数第一个便是AnnotationMetadata代表着@Import所在类的注解的一些元数据们。通常我们会这样使用它:

    // 1、转换成AnnotationAttributes(LinkedHashMap),模糊掉注解类型(常用)
    AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);

    // 2、拿到指定类型注解的元数据信息(也较为常用)
    AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true))

    // 3、直接使用MetaData
    MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(), false);
    1
    2
    3
    4
    5
    6
    7
    8
    使用示例
    仅通过文字的说明,我相信很多小伙伴还云里雾里。正所谓任何文字上的理解都比不上自己动手一试,书是别人的,动手后才是自己的。因此,此处我贴出使用示例代码,方便你做理解参考:

    // 准备一个Class类 作为Demo演示
    @Repository("repositoryName")
    @Service("serviceName")
    @EnableAsync
    class MetaDemo extends HashMap<String, String> implements Serializable {
    private static class InnerClass {
    }

    @Autowired
    private String getName() {
    return "demo";
    }
    }


    public static void main(String[] args) {
    StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(MetaDemo.class, true);

    // 演示ClassMetadata的效果
    System.out.println("==============ClassMetadata==============");
    ClassMetadata classMetadata = metadata;
    System.out.println(classMetadata.getClassName()); //com.fsx.maintest.MetaDemo
    System.out.println(classMetadata.getEnclosingClassName()); //null 如果自己是内部类此处就有值了
    System.out.println(StringUtils.arrayToCommaDelimitedString(classMetadata.getMemberClassNames())); //com.fsx.maintest.MetaDemo$InnerClass 若木有内部类返回空数组[]
    System.out.println(StringUtils.arrayToCommaDelimitedString(classMetadata.getInterfaceNames())); // java.io.Serializable
    System.out.println(classMetadata.hasSuperClass()); // true(只有Object这里是false)
    System.out.println(classMetadata.getSuperClassName()); // java.util.HashMap

    System.out.println(classMetadata.isAnnotation()); // false(是否是注解类型的Class,这里显然是false)
    System.out.println(classMetadata.isFinal()); // false
    System.out.println(classMetadata.isIndependent()); // true(top class或者static inner class,就是独立可new的)
    // 演示AnnotatedTypeMetadata的效果
    System.out.println("==============AnnotatedTypeMetadata==============");
    AnnotatedTypeMetadata annotatedTypeMetadata = metadata;
    System.out.println(annotatedTypeMetadata.isAnnotated(Service.class.getName())); // true(依赖的AnnotatedElementUtils.isAnnotated这个方法)
    System.out.println(annotatedTypeMetadata.isAnnotated(Component.class.getName())); // true

    System.out.println(annotatedTypeMetadata.getAnnotationAttributes(Service.class.getName())); //{value=serviceName}
    System.out.println(annotatedTypeMetadata.getAnnotationAttributes(Component.class.getName())); // {value=repositoryName}(@Repository的value值覆盖了@Service的)
    System.out.println(annotatedTypeMetadata.getAnnotationAttributes(EnableAsync.class.getName())); // {order=2147483647, annotation=interface java.lang.annotation.Annotation, proxyTargetClass=false, mode=PROXY}

    // 看看getAll的区别:value都是数组的形式
    System.out.println(annotatedTypeMetadata.getAllAnnotationAttributes(Service.class.getName())); // {value=[serviceName]}
    System.out.println(annotatedTypeMetadata.getAllAnnotationAttributes(Component.class.getName())); // {value=[, ]} --> 两个Component的value值都拿到了,只是都是空串而已
    System.out.println(annotatedTypeMetadata.getAllAnnotationAttributes(EnableAsync.class.getName())); //{order=[2147483647], annotation=[interface java.lang.annotation.Annotation], proxyTargetClass=[false], mode=[PROXY]}

    // 演示AnnotationMetadata子接口的效果(重要)
    System.out.println("==============AnnotationMetadata==============");
    AnnotationMetadata annotationMetadata = metadata;
    System.out.println(annotationMetadata.getAnnotationTypes()); // [org.springframework.stereotype.Repository, org.springframework.stereotype.Service, org.springframework.scheduling.annotation.EnableAsync]
    System.out.println(annotationMetadata.getMetaAnnotationTypes(Service.class.getName())); // [org.springframework.stereotype.Component, org.springframework.stereotype.Indexed]
    System.out.println(annotationMetadata.getMetaAnnotationTypes(Component.class.getName())); // [](meta就是获取注解上面的注解,会排除掉java.lang这些注解们)

    System.out.println(annotationMetadata.hasAnnotation(Service.class.getName())); // true
    System.out.println(annotationMetadata.hasAnnotation(Component.class.getName())); // false(注意这里返回的是false)

    System.out.println(annotationMetadata.hasMetaAnnotation(Service.class.getName())); // false(注意这一组的结果和上面相反,因为它看的是meta)
    System.out.println(annotationMetadata.hasMetaAnnotation(Component.class.getName())); // true

    System.out.println(annotationMetadata.hasAnnotatedMethods(Autowired.class.getName())); // true
    annotationMetadata.getAnnotatedMethods(Autowired.class.getName()).forEach(methodMetadata -> {
    System.out.println(methodMetadata.getClass()); // class org.springframework.core.type.StandardMethodMetadata
    System.out.println(methodMetadata.getMethodName()); // getName
    System.out.println(methodMetadata.getReturnTypeName()); // java.lang.String
    });
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    像这些元数据,在框架设计时候很多时候我们都希望从File(Resource)里得到,而不是从Class文件里获取,所以就是MetadataReader和MetadataReaderFactory。下面我也给出使用案例:

    因为MetadataReader的实现类都是包级别的访问权限,所以它的实例只能来自工厂

    public static void main(String[] args) throws IOException {
    CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
    // 下面两种初始化方式都可,效果一样
    //MetadataReader metadataReader = readerFactory.getMetadataReader(MetaDemo.class.getName());
    MetadataReader metadataReader = readerFactory.getMetadataReader(new ClassPathResource("com/fsx/maintest/MetaDemo.class"));

    ClassMetadata classMetadata = metadataReader.getClassMetadata();
    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
    Resource resource = metadataReader.getResource();

    System.out.println(classMetadata); // org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor@79079097
    System.out.println(annotationMetadata); // org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor@79079097
    System.out.println(resource); // class path resource [com/fsx/maintest/MetaDemo.class]

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    总结
    元数据,是框架设计中必须的一个概念,所有的流行框架里都能看到它的影子,包括且不限于Spring、SpringBoot、SpringCloud、MyBatis、Hibernate等。它的作用肯定是大大的,它能模糊掉具体的类型,能让数据输出变得统一,能解决Java抽象解决不了的问题,比如运用得最广的便是注解,因为它不能继承无法抽象,所以用元数据方式就可以完美行成统一的向上抽取让它变得与类型无关,也就是常说的模糊效果,这便是框架的核心设计思想。

    不管是ClassMetadata还是AnnotatedTypeMetadata都会有基于反射和基于ASM的两种解决方案,他们能使用于不同的场景:

    标准反射:它依赖于Class,优点是实现简单,缺点是使用时必须把Class加载进来。
    ASM:无需提前加载Class入JVM,所有特别特别适用于形如Spring应用扫描的场景(扫描所有资源,但并不是加载所有进JVM/容器~)
    ————————————————
    版权声明:本文为CSDN博主「_YourBatman」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/f641385712/article/details/88765470

  • 相关阅读:
    为什么需要配置环境变量
    Highcharts使用简例 + 异步动态读取数据
    使用SQL检测死锁
    2015.12.21-2015.12.25单词
    SQL Server锁定【2015.12.17】
    SQL SERVER 并发【2015.12.16】
    SQL闲杂知识点汇总【2015年12月】
    键和约束【2015.12.11】
    [笔记]聚集索引和非聚集索引相关知识点
    论Top与ROW_NUMBER读取第一页的效率问题
  • 原文地址:https://www.cnblogs.com/zhoading/p/12195075.html
Copyright © 2020-2023  润新知