1. 概述
在本教程中, 我们将了解 Spring 中的 @AliasFor 注解 。
首先,我们将看到使用它的框架内的示例。接下来,我们将看一些定制示例。
2. 注释
@AliasFor 从 4.2 版开始就成为框架的一部分。现在,几个核心Spring 注释已更新为包含此注释。
我们可以使用它来装饰单个注释或由元注释组成的注释中的属性。即,元注释是可以应用于另一个注释的注释。
在同一个注释中, 我们使用 @AliasFor 来声明属性的别名,以便我们可以互换地应用它们 。或者,我们可以在组合注释中使用它来覆盖其元注释中的属性。换句话说,当我们使用 @AliasFor 修饰组合注释中的属性时,它会覆盖其元注释中指定的属性 。
有趣的是,许多核心 Spring 注解(例如 @Bean 、 @ComponentScan 、 @Scope 、 @RequestMapping 和 @RestController ) 现在使用 @AliasFor 来配置其内部属性别名。
这是注释的定义:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {
@AliasFor("attribute")
String value() default "";
@AliasFor("value")
String attribute() default "";
Class<? extends Annotation> annotation() default Annotation.class;
}
重要的是, 我们可以隐式和显式地使用这个注释 。隐式使用仅限于注释中的别名。相比之下,也可以对元注释中的属性进行显式使用。
我们将通过以下部分中的示例详细了解这一点。
3. 注释中的显式别名
让我们考虑一个核心 Spring 注释 @ComponentScan 来理解单个注释中的显式别名:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
...
}
正如我们所看到的, value 在这里被明确定义为 basePackages 的别名,反之亦然。这意味着 我们可以互换使用它们 。
实际上,这两种用法是相似的:
@ComponentScan(basePackages = "com.baeldung.aliasfor")
@ComponentScan(value = "com.baeldung.aliasfor")
此外,由于这两个属性也被标记为 default ,所以让我们更简洁地写一下:
@ComponentScan("com.baeldung.aliasfor")
此外,Spring 针对此场景强制规定了一些实现要求。首先,别名属性应该声明相同的默认值。此外,它们应该具有相同的返回类型。如果我们违反任何这些约束,框架将抛出 AnnotationConfigurationException 。
4. 元注释中属性的显式别名
接下来,让我们看一个元注释的示例,并从中创建一个组合注释。然后, 我们将在自定义中看到别名的显式用法 。
首先,让我们将框架注释 RequestMapping 视为我们的元注释:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
...
}
接下来,我们将从中创建一个组合注释 MyMapping :
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping
public @interface MyMapping {
@AliasFor(annotation = RequestMapping.class, attribute = "method")
RequestMethod[] action() default {};
}
我们可以看到,在 @MyMapping 中, action 是 @RequestMapping 中属性 方法 的显式别名。也就是说, 我们的组合注释中的 操作 会覆盖元注释中的 方法 。
与注释中的别名类似,元注释属性别名也必须具有相同的返回类型。例如,我们例子中的 RequestMethod[] 。此外,属性 注释 应该引用元注释,就像我们使用 注释 = RequestMapping.class 一样。
为了进行演示,我们添加一个名为 MyMappingController 的 控制器类。我们将使用自定义注释来装饰它的方法。
具体来说,这里我们将仅向 @MyMapping 添加两个属性: route 和 action :
@Controller
public class MyMappingController {
@MyMapping(action = RequestMethod.PATCH, route = "/test")
public void mappingMethod() {}
}
最后,为了了解显式别名的行为方式,让我们添加一个简单的测试:
@Test
public void givenComposedAnnotation_whenExplicitAlias_thenMetaAnnotationAttributeOverridden() {
for (Method method : controllerClass.getMethods()) {
if (method.isAnnotationPresent(MyMapping.class)) {
MyMapping annotation = AnnotationUtils.findAnnotation(method, MyMapping.class);
RequestMapping metaAnnotation =
AnnotationUtils.findAnnotation(method, RequestMapping.class);
assertEquals(RequestMethod.PATCH, annotation.action()[0]);
assertEquals(0, metaAnnotation.method().length);
}
}
}
正如我们所看到的,我们的自定义注释的属性 操作 已经覆盖了元注释 @RequestMapping 的属性 方法 。
5. 注释中的隐式别名
为了理解这一点, 让我们在 @MyMapping 中添加更多别名 :
@AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] value() default {};
@AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] mapping() default {};
@AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] route() default {};
在这种情况下, value 、 mapping 和 route 是 @RequestMapping 中 路径 的显式元注释覆盖。因此,它们也是彼此的隐式别名。换句话说, 对于 @MyMapping ,我们可以互换使用这三个属性。
为了演示这一点,我们将使用与上一节相同的控制器。这是另一个测试:
@Test
public void givenComposedAnnotation_whenImplictAlias_thenAttributesEqual() {
for (Method method : controllerClass.getMethods()) {
if (method.isAnnotationPresent(MyMapping.class)) {
MyMapping annotationOnBean =
AnnotationUtils.findAnnotation(method, MyMapping.class);
assertEquals(annotationOnBean.mapping()[0], annotationOnBean.route()[0]);
assertEquals(annotationOnBean.value()[0], annotationOnBean.route()[0]);
}
}
}
值得注意的是,我们没有在控制器方法的注释中定义属性 值 和 映射 。然而, 它们仍然隐式地带有与 route 相同的值 。
六,结论
在本教程中, 我们了解了 Spring 框架中的 @AliasFor 注解 。在我们的示例中,我们研究了显式和隐式使用场景。
与往常一样,源代码可在 GitHub 上获取。