Перейти к основному содержимому

Псевдоним для аннотации весной

· 5 мин. чтения

1. Обзор

В этом уроке мы узнаем об аннотации @AliasFor в Spring `` .

Во-первых, мы увидим примеры внутри фреймворка, где он используется. Далее мы рассмотрим несколько индивидуальных примеров.

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 {};
...
}

Как мы видим, значение здесь определено явно как псевдоним для basePackages и наоборот. Это означает , что мы можем использовать их взаимозаменяемо .

Таким образом, эти два использования похожи:

@ComponentScan(basePackages = "com.foreach.aliasfor")

@ComponentScan(value = "com.foreach.aliasfor")

Кроме того, поскольку два атрибута также помечены как значения по умолчанию , давайте напишем это более кратко:

@ComponentScan("com.foreach.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 действие является явным псевдонимом для метода атрибута в @RequestMapping . То есть действие в нашей составленной аннотации переопределяет метод в мета-аннотации .

Подобно псевдонимам в аннотации, псевдонимы атрибутов метааннотаций также должны иметь один и тот же возвращаемый тип. Например, RequestMethod[] в нашем случае. Кроме того, аннотация атрибута должна ссылаться на мета-аннотацию, как в нашем использовании annotation = 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 .

6. Заключение

В этом уроке мы узнали об аннотации @AliasFor в Spring Framework . В наших примерах мы рассмотрели как явные, так и неявные сценарии использования.

Как всегда, исходный код доступен на GitHub .