SpringBoot 中的 @AliasFor注解

@AliasFor是一个注解,用于为注解属性声明别名。
代码如下:它有两个属性value和attribute @AliasFor注解注释了
自身,并且value和attribute互为别名。

@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;
}

使用案例介绍:


1.把多个元注解的属性组合在一起形成新的注解
如我们熟知的@SpringBootApplication 代码如下:

@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 = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
}

如上所示@SpringBootApplication并没有定义新的属性而是复用其他注解已有的注解属性并对其进行组合
形成新的注解从而到达到便捷的目的。这样的注解我们可以称之为复合注解。
所以在使用SpringBoot 时我们只需要@SpringBootApplication一个注解就能开启
自动配置,自动扫描的功能。
而不再需要使下面三个注解来达到同样的目的。
@Configuration
@ComponentSan
@EnnableAutoConfiguration

2.继承注解的功能

如@Controller,@Service,@Repository都继承了@Component的功能
他们的基本作用和@Component完全一样都是标明某个类是
Spring的Bean,需要Spring容器进行管理。不同之处在于对Spring bean进行了归类,从而能对不同类型的Bean进行不同的处理。

示例代码如下 @RequiresAction,@ReadAction,

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Component
public @interface RequiresAction{
    String code() default "";

    RequiresMethod type()  default RequiresMethod.Read;

    enum RequiresMethod {
         Read,
         Write;
    }
}    
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//指定一个tpye类型值,如果不需要指定,可以不用加 @RequiresAction(type
=RequiresMethod.Read) public @interface ReadAction{ @AliasFor(annotation = RequiresMethod.class) String code() default ""; }

测试类

@ReadAction(code = "serviceAlias")
public class ServiceAlias {
    public static void main(String[] args) {
         RequriesAction requriesAction = AnnotationUtils.getAnnotation(ServiceAlias.class, RequriesAction .class);
        System.out.println("code="+requriesAction.code());
        
        RequriesAction requriesAction2 = AnnotatedElementUtils.getMergedAnnotation(ServiceAlias.class, RequriesAction .class);
        System.out.println("code="+requriesAction2.code());
    }
}

输出

code=
code=serviceAlias

可以看到,虽然ServiceAlias上只有@Service,但通过AnnotationUtils.getAnnotation方法会解析得到@Component,而通过AnnotatedElementUtils.getMergedAnnotation方法还可以将@Service#value的值赋给@Component#value。

 

3.在同个注解中为同一个功能定义两个名称不一样的属性,那么这两个属性彼此互为别名

如@RequestMapping注解中的value和path它们两互为别名。如下所示:

 

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

    String name() default "";

    @AliasFor("path")
    String[] value() default {};

    @AliasFor("value")
    String[] path() default {};
  
    RequestMethod[] method() default {};
    
    String[] params() default {};
  
    String[] headers() default {};
 
    String[] consumes() default {};
 
    String[] produces() default {};
 
}

 

这么做的目的在于
1.更便捷
当我们只定义一个属性的时候往往可以省略属性名如:
@RequestMapping(“/user”)
2.顾名思义
当我门定义多个属性时为了能做到顾名思义
使之达到一目了然的效果我们需要选择一个更加贴合特定场景的名称。
@RequestMapping(path = “/user”,method = RequestMethod.GET)
当然你也可以这样:
@RequestMapping(value = “/user”,method = RequestMethod.GET)
只是这样子的定义value = “/user” 不能很准确地传达代码的意图

 

根据@AliasFor的使用形式我们可以将它分为三类:
1.注解内部的显性别名
在单个注解中,可以把@AliasFor声明在一对属性上标明它们彼此之间互无别名如下所示: ContextConfiguration中的value和locations是彼此的显性别名

public @interface ContextConfiguration {
 
    @AliasFor("locations")
    String[] value() default {};
 
    @AliasFor("value")
    String[] locations() default {};
 }

实现要求:
1.组成别名对的每个属性都必须用@AliasFor进行注释,并且AliasFor中的值
必须指向别名对中的另一个属性
2.别名化的属性必须声明相同的返回类型
3.别名化的属性必须声明默认值
4.别名化的属性默认值必须相同

2.用于元注解属性的显性别名

如果被@AliasFor注释的属性指向的是它所在注解之外的其他注解,
那么这个属性被解释成元注解属性的别名。(称之为显性的元注解属性重写)
我们可以通过重写继承一个或多个其他注解的功能从而
使得可以更细粒度精准地控制注解层级中属性的重写,
不像Java中继承必须继承父类的所有功能。
实际上,使用@AliasFor甚至可以为元注解的value属性声明别名.
如下所示:@MyTestConfig下的xmlFiles指向的是一个元注解@ContextConfiguration的属性locations

@ContextConfiguration
 public @interface MyTestConfig {
   @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xmlFiles();

}

实现要求:
1 如果一个属性是一个元注解属性的别名,那么这个属性必须用@AliasFor进行注释并且
该属性必须指向元注解属性。
2 别名化的属性必须声明相同的返回结果
3.@AliasFor的annotation属性必须引用元注解
4.被引用的元注解必须放置在声明了@AliasFor的注解类上

3 注解中的隐性别名
如果注解中的一个或多个属性声明为同一个元注解属性的属性重写(直接地或传递地重写)
那么这些注解会被当作彼此的隐性别名集来对待
结果是它们的行为类似于注解中的显性别名
如下所示:@MyTestConfig中,value,groovyScripts和xmlFiles
都是复写了@ContextConfiguration中locations属性的,因此这
三个属性是彼此的隐性别名。
如下所示:

@ContextConfiguration
 public @interface MyTestConfig {
 
    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] value() default {};
 
    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] groovyScripts() default {};
 
    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xmlFiles() default {};

}

3.1 注解中的可传递隐性别名
如下所示:在GroovyOrXmlTestConfig中,groovy是对上面的MyTestConfig中的groovyScripts属性显示的复写,
而xml是对@ContextConfiguration中locations属性的显示的复写,
我们就可以称groovy和xml是彼此的可传递隐性别名,因为它们实际上只是复写ContextConfiguration中的locations属性。

public @interface GroovyOrXmlTestConfig {
 
    @AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")
    String[] groovy() default {};
 
    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xml() default {};

}

实现要求:
1.属于隐性别名组中的每一个属性必须使用@AliasFor进行注释,并且attribute必须引用相同元注解中的同一个属性
2.别名化的属性必须声明相同的返回类型
3.别名化的属性必须定义默认值
4.别名化的属性必须声明相同的默认值
5.注解必须引用合适的元注解
6.被引用的元注解必须放置在声明了@AliasFor的注解上

文档参考链接:https://blog.csdn.net/u012043390/article/details/89391518

posted @ 2022-03-22 09:49  随风秋叶  阅读(723)  评论(0编辑  收藏  举报