@AliasFor注解
顾名思义,@AliasFor表示别名,它可以添加到自定义注解的两个属性上,表示这两个属性互为别名。也就是说,这两个属性其实是同一个含义。
通常所有注解都会有一个属性value,在使用注解时,如果给value进行赋值,默认可以将value省略,如:@RequestMapping(value="name") 就可以写成 @RequestMapping("name") 。
自定义注解
若自定义注解已有一个属性,但是我们想要定义一个能够描述业务的属性,就可以使用@AliasFor与之前已有的属性互为别名。如下代码,name和value就互为别名。
1 @Retention(RetentionPolicy.RUNTIME) 2 @Target(ElementType.TYPE) 3 public @interface MyAnnotation { 4 @AliasFor("value") 5 String name() default ""; 6 7 @AliasFor("name") 8 String value() default ""; 9 }
若自定义注解继承了另一个注解,要想使用继承过来的属性值,就必须在自定义注解中重新定义一个属性,同时声明该属性是继承过来注解的某个属性的别名。例如:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Component 4 public @interface MyComponent { 5 @AliasFor(annotation = Component.class, attribute = "value") 6 String name() default ""; 7 }
自定义注解@MyComponent继承了注解@Component(继承了哪个注解,就需要在自定义注解上引入该注解),name属性与@Component中的value属性互为别名。
拓展
因为我们的自定义注解继承了@Component注解,按理说@MyComponent的作用应该和@Component作用一样,但是此处有一点特殊。如果我们的自定义注解中没有采用value这个属性,而是定义为其它名称,例如name。spring在初始化扫描bean时并不会像使用@Component注解一样,读取注解中自定义的beanName。示例如下,spring加载UserServiceImpl后生成的beanName并不是“userService”,而是“userServiceImpl”。
1 // 将userServiceImpl重新命名为userService 2 @MyComponent(name = "userService") 3 public class UserServiceImpl { 4 public void getName(){ 5 System.out.println("userServiceImpl"); 6 } 7 } 8 @ComponentScan("com.lr.interfaces.aliasFor") 9 public class AppConfig { 10 public static void main(String[] args) { 11 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); 12 // 无法获取beanName为userService的实例,只能通过userServiceImpl获取 13 UserServiceImpl userService = (UserServiceImpl) context.getBean("userService"); 14 userService.getName(); 15 } 16 }
查看spring源码,发现spring在生成bean对应的beanName时,会先从注解@Component里获取value对应的名称(@Controller等注解本质上也是@Component,具体解释可以参考本文)。所以在自定义注解中如果将value替换为其它属性name,就无法采用自定义的beanName,只能生成默认的别名也就是类名。因此,在自定义注解时,最好采用默认的value。
建议自定义注解将name()修改为value():
1 @Documented 2 @Target(ElementType.TYPE) 3 @Retention(RetentionPolicy.RUNTIME) 4 @Component 5 public @interface MyComponent { 6 @AliasFor(annotation = Component.class, attribute = "value") 7 String value() default ""; 8 }
spring中源码判断核心逻辑
org.springframework.context.annotation.AnnotationBeanNameGenerator#isStereotypeWithNameValue
1 protected boolean isStereotypeWithNameValue(String annotationType, 2 Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) { 3 4 // 判断注解中是否包含@Component 5 boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) || 6 metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) || 7 annotationType.equals("javax.annotation.ManagedBean") || 8 annotationType.equals("javax.inject.Named"); 9 // 判断注解中是否包含属性value 10 return (isStereotype && attributes != null && attributes.containsKey("value")); 11 }
参考:
https://zhuanlan.zhihu.com/p/454946128
https://blog.csdn.net/wolfcode_cn/article/details/80654730
https://blog.csdn.net/weixin_43564627/article/details/93871075