前言
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(); }
‘
它的继承树如下:
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); } }
它有个非常重要的子类:StandardAnnotationMetadata
它和注解密切相关,在文章下半部分重点分析。
MethodsMetadata:新增访问方法元数据的接口
它是子接口,主要增加了从Class里获取到MethodMetadata
们的方法:
// @since 2.1 可以看到它出现得更早一些 public interface MethodsMetadata extends ClassMetadata { // 返回该class所有的方法 Set<MethodMetadata> getMethods(); // 方法指定方法名的方法们(因为有重载嘛~) Set<MethodMetadata> getMethods(String name); }
名字上请不要和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); }
它的继承树如下:
两个子接口相应的都提供了标准实现以及基于ASM的Visitor模式实现。
ASM 是一个通用的 Java 字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。 ASM 虽然提供与其他 Java 字节码框架如 Javassist,CGLIB类似的功能,但是其设计与实现小而快,且性能足够高。
Spring 直接将 ASM 框架核心源码内嵌于 Spring-core中,目前`Spring 5.1使用ASM 7 版本。