《SpringBoot系列八》:Spring注解别名@AliasFor和覆盖(含原理)

简介: 《SpringBoot系列八》:Spring注解别名@AliasFor和覆盖(含原理)

一、@AliasFor概述和使用

所有注解均实现Annotation接口。较底层注解能够覆盖其元注解的同名属性,并且AnnotationAttributes采用注解就近覆盖的设计原则。

覆盖的分类:

  • 隐性覆盖:元注解的层次高低关系、Override
  • 显性覆盖:当A @AliasFor B时,属性A显性覆盖了属性B的内容。

@AliasFor可建立在不同注解层次的属性之间。

1. 同一注解内显式使用:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
 
    @AliasFor("path")
    String[] value() default {};
 
    @AliasFor("value")
    String[] path() default {};
 
    //...
}

value和path互为别名,互为别名的限制条件如下:

  • 互为别名的属性 其属性值类型、默认值,都是相同的。
  • 互为别名的属性必须定义默认值
  • 互为别名的注解必须成对出现

2. 显示的覆盖元注解中的属性:

先来看一段代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AopConfig.class)
public class AopUtilsTest {

定义一个标签:

@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration
public @interface STC {
    @AliasFor(value = "classes", annotation = ContextConfiguration.class)
    Class<?>[] cs() default {};

}
  • 因为@ContextConfiguration注解被定义为@Inherited的,所以@STC注解可理解为继承了@ContextConfiguration注解;
  • @STC注解使用cs属性替换@ContextConfiguration注解中的classes属性。
  • 使用@AliasFor标签,分别设置了value(即作为哪个属性的别名)和annotation(即作为哪个注解);

使用@STC注解替换@ContextConfiguration:

@RunWith(SpringJUnit4ClassRunner.class)
@STC(cs = AopConfig.class)
public class AopUtilsTest {
    @Autowired
    private IEmployeeService service;
}

3. 注解中隐式声明别名

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

因为value,groovyScripts,xmlFiles都定义了别名@AliasFor(annotation = ContextConfiguration.class, attribute = “locations”),所以value、groovyScripts和xmlFiles也互为别名。

4. 别名的传递

@AliasFor注解是允许别名之间的传递的,简单理解,如果A是B的别名,并且B是C的别名,那么A是C的别名。

@MyTestConfig
public @interface GroovyOrXmlTestConfig {

   @AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")
   String[] groovy() default {};

   @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
   String[] xml() default {};
}

@ContextConfiguration
public @interface MyTestConfig {

   @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
   String[] groovyScripts() default {};
}
  • @GroovyOrXmlTestConfig把 @MyTestConfig作为元注解;
  • groovy属性作为@MyTestConfig中的groovyScripts属性的别名;
  • xml属性作为@ContextConfiguration中的locations属性的别名;
  • 因为MyTestConfig中的groovyScripts属性是ContextConfiguration中的locations属性的别名;所以xml属性和groovy属性也互为别名。

二、@AliasFor在SpringBoot源码中的使用

public @interface SpringBootApplication {

    ....
    
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
    
    .....
}
@SpringBootApplication(scanBasePackages = {"com.saint.spring.controller"})

@SpringBootApplication的scanBasePackages属性将 @ComponentScan扫描的位置重定向到我们指定的位置;即@SpringBootApplication利用@AliasFor注解别名了@CompoentScan注解的basePackages()属性。

三、@AliasFor实现原理

使用@AliasFor最需要注意一点的,就是只能使用Spring的AnnotationUtils工具类来获取;因为AliasFor是Spring定义的标签。

  • AnnotationUtils工具类中的<A extends Annotation> A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement)方法来处理@AliasFor注解;
  • 其返回一个经过处理(处理@AliasFor标签)之后的注解对象,即把A注解对象处理为支持@AliasFor的A注解对象;

    public static <A extends Annotation> A synthesizeAnnotation(
                A annotation, @Nullable AnnotatedElement annotatedElement) {
    
            if (annotation instanceof SynthesizedAnnotation || AnnotationFilter.PLAIN.matches(annotation)) {
                return annotation;
            }
            return MergedAnnotation.from(annotatedElement, annotation).synthesize();
        }
  • 本质原理就是使用AOP来对A注解对象做动态代理,而用于处理代理的对象为SynthesizedMergedAnnotationInvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) {
    if (ReflectionUtils.isEqualsMethod(method)) {
        return annotationEquals(args[0]);
    }
    if (ReflectionUtils.isHashCodeMethod(method)) {
        return annotationHashCode();
    }
    if (ReflectionUtils.isToStringMethod(method)) {
        return annotationToString();
    }
    if (isAnnotationTypeMethod(method)) {
        return this.type;
    }
    if (this.attributes.indexOf(method.getName()) != -1) {
        return getAttributeValue(method);
    }
    throw new AnnotationConfigurationException(String.format(
            "Method [%s] is unsupported for synthesized annotation type [%s]", method, this.type));
}
  • 再看getAttributeValue(method)方法逻辑:

    • 首先正常获取当前属性的值;
    • 然后得到所有的标记为别名的属性名称;
    • 接着遍历获取所有别名属性的值;
    • 判断attributeValue、aliasValue、defaultValue是否相同,@AliasFor标签的传递性也是在这里体现;如果不相同,直接抛出异常;否则正常返回属性值;
相关文章
|
26天前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
47 0
|
22天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
28 0
|
1月前
|
XML JSON Java
SpringBoot必须掌握的常用注解!
SpringBoot必须掌握的常用注解!
56 4
SpringBoot必须掌握的常用注解!
|
12天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
26 2
|
1月前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
127 2
|
1月前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
42 1
|
1月前
|
存储 安全 Java
springboot当中ConfigurationProperties注解作用跟数据库存入有啥区别
`@ConfigurationProperties`注解和数据库存储配置信息各有优劣,适用于不同的应用场景。`@ConfigurationProperties`提供了类型安全和模块化的配置管理方式,适合静态和简单配置。而数据库存储配置信息提供了动态更新和集中管理的能力,适合需要频繁变化和集中管理的配置需求。在实际项目中,可以根据具体需求选择合适的配置管理方式,或者结合使用这两种方式,实现灵活高效的配置管理。
17 0
|
Java Spring
Spring原理学习系列之五:IOC原理之Bean加载
其实很多同学都想通过阅读框架的源码以汲取框架设计思想以及编程营养,Spring框架其实就是个很好的框架源码学习对象。我们都知道Bean是Spring框架的最小操作单元,Spring框架通过对于Bean的统一管理实现其IOC以及AOP等核心的框架功能,那么Spring框架是如何把Bean加载到环境中来进行管理的呢?本文将围绕这个话题进行详细的阐述,并配合Spring框架的源码解析。
Spring原理学习系列之五:IOC原理之Bean加载
|
3月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
193 2