Spring中类解析神器:MetadataReader,熟悉ClassMetadata、AnnotationMetadata

@[TOC]

一、认识MetadataReader

我们平时对一个类的解析,通常会用到反射的技术,比如:

java 复制代码
Class<?> aClass = Class.forName("com.qstcloud.qcard.portal.context.domain.Test");
aClass.getMethods();
aClass.getDeclaredFields();
// ...

在Spring中需要去解析类的信息,比如类名、类中的方法、类上的注解,这些都可以称之为类的元数据,所以Spring中对类的元数据做了抽象,并提供了一些工具类。

MetadataReader表示类的元数据读取器,默认实现类为SimpleMetadataReader

需要注意的是,SimpleMetadataReader去解析类时,使用的ASM技术。 我们都知道,Class.forName执行之后,就会将这个类加载到JVM中,哪怕我们根本用不到这个类,也会被加载。 ​ Spring启动的时候需要去扫描所有的类,如果指定的包路径比较宽泛,那么扫描的类是非常多的,那如果在Spring启动时就把这些类全部加载进JVM了,这样对应用非常不友好,所以使用了ASM技术。

后续我们进一步分析MetadataReader的源码,就知道Spring的处理比我们自己处理到底高明在哪里了。

二、使用MetadataReader

java 复制代码
SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();

// 构造一个MetadataReader
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.test.Test");

// 得到一个ClassMetadata,并获取了类名
ClassMetadata classMetadata = metadataReader.getClassMetadata();

System.out.println(classMetadata.getClassName());

// 获取一个AnnotationMetadata,并获取类上的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
for (String annotationType : annotationMetadata.getAnnotationTypes()) {
    System.out.println(annotationType);
}

我们可以看到,使用MetadataReader对类进行解析,非常的方便,不需要我们编写大量的反射代码,Spring直接给我们处理好了。

三、源码分析

1、认识MetadataReaderFactory

MetadataReaderFactory是一个创建MetadataReader的工厂,有两个方法,可以通过类的全限定名或者类的Resource进行获取MetadataReader:

java 复制代码
public interface MetadataReaderFactory {

	// 类全限定名
	MetadataReader getMetadataReader(String className) throws IOException;

	// 类的Resource
	MetadataReader getMetadataReader(Resource resource) throws IOException;

}

2、认识MetadataReader

MetadataReader接口有三个方法:

java 复制代码
public interface MetadataReader {

	// 获取资源
	Resource getResource();

	// 获取ClassMetadata,里面包含一个类的基本信息
	ClassMetadata getClassMetadata();

	// 获取AnnotationMetadata ,里面包含一个类中注解的信息
	AnnotationMetadata getAnnotationMetadata();

}

3、认识ClassMetadata

ClassMetadata是一个接口,它的优势是以一种不需要加载特定类的形式定义该类的抽象元数据。 也就是说,通过ClassMetadata获取类的信息,并不需要加载该类的JVM中。

里面包含了获取类基本属性的所有方法,包括类名、类的接口、父类、内部类等等信息。

4、认识AnnotationMetadata

AnnotationMetadata是一个接口,它的优势是以不需要加载特定类的形式定义对该类注解内容。 也就是说,通过AnnotationMetadata获取类的注解信息,并不需要加载该类的JVM中。

里面包含了获取类中注解的信息。

5、MetadataReader在Spring的应用

Spring在进行包扫描时,就用到了MetadataReader。

因为扫描需要读取包路径下所有的类,所以使用MetadataReader,不需要加载额外的类到JVM中。

6、举一反三:扫描标注了我们自定义注解的所有类

通过Spring的扫描器,我们可以自定义我们自己的扫描器,扫描我们想要的结果:

java 复制代码
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Set;


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
}

@MyAnnotation
public class Test {

    public static void main(String[] args) throws IOException {
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        provider.addIncludeFilter(new TypeFilter() {
            /**
             * 自定义过滤器,只扫描标注@MyAnnotation的类
             */
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                if (metadataReader.getAnnotationMetadata().hasAnnotation(MyAnnotation.class.getName())) {
                    return true;
                }

                return false;
            }
        });

        // 获取扫描结果,并并且会自动封装到BeanDefinition
        Set<BeanDefinition> candidateComponents = provider.findCandidateComponents("com.test");

        for (BeanDefinition candidateComponent : candidateComponents) {
        	// 输出我们Test类
            System.out.println(candidateComponent.getBeanClassName());
        }
    }
}
相关推荐
2401_8543910810 分钟前
城镇住房保障:SpringBoot系统功能概览
java·spring boot·后端
陈随易14 分钟前
兔小巢收费引发的论坛调研Node和Deno有感
前端·后端·程序员
聪明的墨菲特i20 分钟前
Django前后端分离基本流程
后端·python·django·web3
hlsd#1 小时前
go mod 依赖管理
开发语言·后端·golang
陈大爷(有低保)1 小时前
三层架构和MVC以及它们的融合
后端·mvc
亦世凡华、1 小时前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
河西石头1 小时前
一步一步从asp.net core mvc中访问asp.net core WebApi
后端·asp.net·mvc·.net core访问api·httpclient的使用
2401_857439691 小时前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧6661 小时前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
阿华的代码王国2 小时前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话