Java 的 AnnotatedElement API 提供的方法通常不提供在不加載注釋本身的情況下內(nèi)省注釋的能力。它們也不提供任何內(nèi)省注釋構(gòu)造型的能力(通常稱(chēng)為元注釋?zhuān)蛔⑨寴?gòu)造型是用另一個(gè)注釋對(duì)一個(gè)注釋進(jìn)行注釋的地方,本質(zhì)上是繼承它的行為)。
為了解決這個(gè)問(wèn)題,許多框架生成運(yùn)行時(shí)元數(shù)據(jù)或執(zhí)行昂貴的反射來(lái)分析類(lèi)的注釋。
Micronaut 改為在編譯時(shí)生成此注解元數(shù)據(jù),從而避免了昂貴的反射并節(jié)省了內(nèi)存。
BeanContext API 可用于獲取對(duì)實(shí)現(xiàn) AnnotationMetadata 接口的 BeanDefinition 的引用。
例如,以下代碼獲取所有帶有特定構(gòu)造型注釋的 bean 定義:
按構(gòu)造型查找 Bean 定義
BeanContext beanContext = ... // obtain the bean context
Collection<BeanDefinition> definitions =
beanContext.getBeanDefinitions(Qualifiers.byStereotype(Controller.class))
for (BeanDefinition definition : definitions) {
AnnotationValue<Controller> controllerAnn = definition.getAnnotation(Controller.class);
// do something with the annotation
}
上面的示例找到所有用 @Controller 注釋的 BeanDefinition 實(shí)例,無(wú)論 @Controller 是直接使用還是通過(guò)注釋構(gòu)造型繼承。
請(qǐng)注意,getAnnotation 方法和該方法的變體返回 AnnotationValue 類(lèi)型而不是 Java 注釋。這是設(shè)計(jì)使然,您通常應(yīng)該在讀取注釋值時(shí)嘗試使用此 API,因?yàn)閺男阅芎蛢?nèi)存消耗的角度來(lái)看,合成代理實(shí)現(xiàn)更糟糕。
如果您需要對(duì)注釋實(shí)例的引用,您可以使用 synthesize 方法,它創(chuàng)建一個(gè)實(shí)現(xiàn)注釋接口的運(yùn)行時(shí)代理:
合成注解實(shí)例
Controller controllerAnn = definition.synthesize(Controller.class);
但是,不推薦使用這種方法,因?yàn)樗枰瓷洳⒂捎谑褂眠\(yùn)行時(shí)生成的代理而增加內(nèi)存消耗,并且應(yīng)該作為最后的手段使用,例如,如果您需要注釋的實(shí)例來(lái)與第三方集成圖書(shū)館。
注解繼承
Micronaut 將遵守 Java 的 AnnotatedElement API 中定義的關(guān)于注解繼承的規(guī)則:
使用 Inherited 進(jìn)行元注釋的注釋將通過(guò) AnnotationMetadata API 的 getAnnotation* 方法提供,而直接聲明的注釋則通過(guò) getDeclaredAnnotation* 方法提供。
未使用 Inherited 進(jìn)行元注釋的注釋將不會(huì)包含在元數(shù)據(jù)中
Micronaut 與 AnnotatedElement API 的不同之處在于它將這些規(guī)則擴(kuò)展到方法和方法參數(shù),以便:
任何用 Inherited 注釋并出現(xiàn)在被子接口或類(lèi) B 覆蓋的接口或超類(lèi) A 的方法上的注釋都將繼承到可通過(guò) ExecutableMethod API 從 BeanDefinition 或 AOP 攔截器檢索的 AnnotationMetadata 中。
任何用 Inherited 注釋并出現(xiàn)在被子接口或類(lèi) B 覆蓋的接口或超類(lèi) A 的方法參數(shù)上的注釋都將繼承到可通過(guò) Argument 接口從 ExecutableMethod API 的 getArguments 方法檢索的 AnnotationMetadata 中。
通常,您可能希望覆蓋的一般行為默認(rèn)情況下不會(huì)繼承,包括 Bean 范圍、Bean 限定符、Bean 條件、驗(yàn)證規(guī)則等。
如果您希望在子類(lèi)化時(shí)繼承特定的范圍、限定符或一組要求,那么您可以定義一個(gè)用@Inherited 注釋的元注釋。例如:
定義繼承的元注解
Java | Groovy | Kotlin |
|
|
|
注解聲明為@Inherited
Bean Conditions 將被子類(lèi)繼承
Bean 限定符將由子類(lèi)繼承
Bean Scopes 將被子類(lèi)繼承
你也可以給注解起別名,它們會(huì)被繼承
使用此元注釋?zhuān)梢詫⒆⑨屘砑拥匠?lèi):
在超類(lèi)上使用繼承的元注釋
Java | Groovy | Kotlin |
|
|
|
然后子類(lèi)將繼承所有注釋?zhuān)?
在子類(lèi)中繼承注解
Java | Groovy | Kotlin |
|
|
|
子類(lèi)必須至少有一個(gè) bean 定義注釋?zhuān)绶秶蛳薅ǚ?/p>
別名/映射注釋
有時(shí)您可能希望將注釋成員的值作為另一個(gè)注釋成員的值的別名。為此,請(qǐng)使用@AliasFor 注釋。
例如,一個(gè)常見(jiàn)的用例是注釋定義了 value() 成員,但也支持其他成員。例如 @Client 注解:
@Client 注解
public @interface Client {
/**
* @return The URL or service ID of the remote service
*/
@AliasFor(member = "id") (1)
String value() default "";
/**
* @return The ID of the client
*/
@AliasFor(member = "value") (2)
String id() default "";
}
value 成員也設(shè)置了 id 成員
id 成員也設(shè)置了 value 成員
有了這些別名,無(wú)論您定義@Client("foo") 還是@Client(id="foo"),value 和 id 成員都將被設(shè)置,從而更容易解析和使用注釋。
如果您無(wú)法控制注釋?zhuān)硪环N方法是使用 AnnotationMapper。要?jiǎng)?chuàng)建 AnnotationMapper,請(qǐng)執(zhí)行以下操作:
實(shí)現(xiàn) AnnotationMapper 接口
定義一個(gè) META-INF/services/io.micronaut.inject.annotation.AnnotationMapper 文件引用實(shí)現(xiàn)類(lèi)
將包含實(shí)現(xiàn)的 JAR 文件添加到 annotationProcessor 類(lèi)路徑(Kotlin 的 kapt)
因?yàn)?nbsp;AnnotationMapper 實(shí)現(xiàn)必須在注釋處理器類(lèi)路徑上,所以它們通常應(yīng)該在一個(gè)包含很少外部依賴項(xiàng)的項(xiàng)目中,以避免污染注釋處理器類(lèi)路徑。
以下是改進(jìn) JPA 實(shí)體的內(nèi)省功能的示例 AnnotationMapper。
EntityIntrospectedAnnotationMapper 映射器示例
public class EntityIntrospectedAnnotationMapper implements NamedAnnotationMapper {
@NonNull
@Override
public String getName() {
return "javax.persistence.Entity";
}
@Override
public List<AnnotationValue<?>> map(AnnotationValue<Annotation> annotation, VisitorContext visitorContext) { (1)
final AnnotationValueBuilder<Introspected> builder = AnnotationValue.builder(Introspected.class)
// don't bother with transients properties
.member("excludedAnnotations", "javax.persistence.Transient"); (2)
return Arrays.asList(
builder.build(),
AnnotationValue.builder(ReflectiveAccess.class).build()
);
}
}
map 方法接收帶有注釋值的 AnnotationValue。
可以返回一個(gè)或多個(gè)注釋?zhuān)诒纠袨锧Transient。
上面的示例實(shí)現(xiàn)了 NamedAnnotationMapper 接口,該接口允許將注釋與運(yùn)行時(shí)代碼混合。要針對(duì)具體注釋類(lèi)型進(jìn)行操作,請(qǐng)改用 TypedAnnotationMapper,但請(qǐng)注意,它需要注釋類(lèi)本身位于注釋處理器類(lèi)路徑中。
更多建議: