这篇文章既介绍一个技术,又记录一个逐步摸索发现的过程,以供大家参考。
缘起
留神到 Java 的依赖注入 DI 标准(起初认为是 CDI 标准,而后发现是 DI 标准)有个叫 @Qualifier 的注解,用于当一个 interface 或 base class 有多个实现类时,能抉择其中一个实现。如不必这一注解,个别的(按类型)注入就会报错说“不晓得要在多个实现当选哪一个”。这一注解能够放在一个自定义注解上(例如 @MyPreferredImplementation),从而将自定义注解变成一个 qualifier annotation(限定符注解),而后只有在某一个实现类上放上这个自定义注解,也在注入处放上这个自定义注解,就能起到连通单方的作用,指定注入这个实现类了,很不便也很语义化。(大家能够搜寻学习 @Qualifier 的教程。)
Spring 反对 DI 标准,而它本人也有一个叫 @Qualifier 注解(包名不雷同,在 spring 的 package 里),岂但反对以上性能,还能够间接放在待注入的变量上,用 name 参数(例如 @Qualifier(name =“myBeanName”))来指定要注入的那个实现类的 bean name。Spring 的这个性能如同更罕用,至多在某公司就是这样,DI 标准的 qualifier 性能反而有些不为人所知了。
我认为 DI 标准的更好,更加语义化。而这种把一个注解放在另一个注解上,是什么 Java 个性呢?起初不晓得正确的关键词,用“annotation on annotation”之类的词语左查右查也查不到。而后看 JDK 的 Javadoc,看哪一个呢,看已知的几个“annotation on annotation”,懂的敌人可能想到了,@Retention @Target @Inherited 这些 JDK 内置的用来放在另一个注解上的注解,Javadoc 说它们叫做元注解 meta-annotation。JDK 的这几个元注解有很多文章解说,我就不讲了,这一篇专讲元注解。
摸索
我就好奇了,依赖注入框架所用的元注解是怎么实现的?大家有想过吗?比如说,框架怎么晓得哪些注解被标了 @Qualifier 元注解?第一反馈是 Java 内置了这方面的反对,因为单元测试框架的 @Test 等注解也有元注解性能,这么罕用的性能或者是 Java 原生反对的?
因而我就做了试验,写两个自定义注解,一个叫 @Virtual 元注解,一个叫 @Real 注解,把 @Virtual 放在 @Real 上,把 @Real 放到一个 User 类上,看看编译后果,而后用反射从这个类上取 @Virtual,看 @Real 能不能主动疏导到 @Virtual 上。示例代码如下:
@Retention(RetentionPolicy.RUNTIME)
public @interface Virtual {
}
@Virtual
@Retention(RetentionPolicy.RUNTIME)
public @interface Real {
}
@Real
public class User {}
编译后用 IDE 查看 class 文件,发现 @Virtual 元注解依然只标在 @Real 上,User 类上只标有 @Real 注解,可证实编译器没有为元注解做什么工作。而后反射的后果也是不能从 User 类拿到 @Virtual,可证实 JVM runtime 也没有为元注解做什么工作。因而 @Qualifier 的元注解个性极有可能是相干框架自行实现的。
要怎么实现呢?咱们能够本人动脑筋想一想。思考到,Spring 框架扫描所有的 class 文件(之所以要扫描 class 文件而非 class 对象,是因为 Java 不提供遍历所有 class 对象的性能,使框架不得不反复实现对 class 文件的解析工作),将其中有相应注解的 class 转化为 BeanDefinition 注册到 BeanFactory。那么 @Qualifier 也能够相似地解决,对于扫描到的 class,如果它具备 @Qualifer 注解,并且本身也是注解 (实现了 java.lang.Annotation interface),就作为一个自定义注解注册到框架里(比如说,QualifierAnnotationRegistry?),如此一来框架就意识所有的蕴含 @Qualifier 元注解的自定义注解了,之后要应用就牵强附会了。
发现
那么 Spring 实际上是怎么实现的呢?咱们能够查源码。到 GitHub 上找到 spring-framework 这我的项目,搜寻代码关键词 Qualifer 或 javax.inject.Qualifier,查到 90 多个 Java 文件,再在页面中高亮关键词”main”以过滤掉单元测试,凭教训翻阅,在前 3 页就能找到实现代码了:
- QualifierAnnotationAutowireCandidateResolver https://github.com/spring-pro… 用于注册那些蕴含 javax.inject.Qualifer 的自定义注解。
- CustomAutowireConfigurer https://github.com/spring-pro… 顺便发现这个类容许用户手动注册自定义注解,无需元注解。