@AliasFor 註解
- 2021 年 2 月 7 日
- 筆記
- 08-框架:Spring
Spring 框架提供了很豐富的註解可以讓我們很方便的進行 Spring 配置,今天要講的註解——@AliasFor之前你可能並沒有關注過,因為平時開發時我們的確不太會用到。
我關注到這個註解是因為我經常翻看 Spring 的源程式碼,在 Spring 提供的註解中大量的用到了這個註解,對這個註解不熟悉的話會影響你對程式碼的判斷,而且有些程式碼看的總是似懂非懂的,很難受(強迫症,哈哈),比如說下面這段程式碼。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
上面的程式碼大致能「猜測」出 @AliasFor 是為了屬性起別名,但是 @AliasFor 的使用場景,使用方式,實現原理是什麼?這部落格就簡單介紹下。
@AliasFor 註解的幾種使用方式
1. 在同一個註解中顯示使用,將註解中的多個屬性互相設置別名
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
//...
}
為什麼要給 value 屬性和 path 屬相互相設置別名也是有原因的。我們知道在 Spring 中給 value 屬性設置值是可以省略屬性的,比如可以寫成:
RequestMapping("/foo")
這樣寫比較簡潔,但是這樣可讀性不高,我們並不知道 value 屬性代表什麼意思。如果給這個屬相設置一個 path 別名的話我們就知道這個是在設置路徑。
但是要注意一點,@AliasFor 標籤有一些使用限制:
- 互為別名的屬性屬性值類型,默認值,都是相同的;
- 互為別名的註解必須成對出現,比如 value 屬性添加了@AliasFor(「path」),那麼 path 屬性就必須添加@AliasFor(「value」);
- 另外還有一點,互為別名的屬性必須定義默認值。
那麼如果違反了別名的定義,在使用過程中就會報錯。
2. 給元註解中的屬性設定別名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
//...
}
我們來看 @SpringBootApplication 這個註解,這個註解是有其他幾個註解「組合」而成的。下面的程式碼就是在給@ComponentScan 註解的basePackages屬性設置別名scanBasePackages。如果不設置attribute屬性的話就是在給元註解的同名屬性設置別名。
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
這種使用方式的好處是可以將幾個註解的功能組合成一個新的註解。
@AliasFor 的實現程式碼
貼大片程式碼的事就不幹了。@AliasFor 的具體實現在AnnotationUtils.findAnnotation 中,程式碼大家自己翻看吧。