《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标签的传递性也是在这里体现;如果不相同,直接抛出异常;否则正常返回属性值;
相关文章
|
4月前
|
缓存 监控 Java
SpringBoot @Scheduled 注解详解
使用`@Scheduled`注解实现方法周期性执行,支持固定间隔、延迟或Cron表达式触发,基于Spring Task,适用于日志清理、数据同步等定时任务场景。需启用`@EnableScheduling`,注意线程阻塞与分布式重复问题,推荐结合`@Async`异步处理,提升任务调度效率。
704 128
|
3月前
|
前端开发 Java 应用服务中间件
《深入理解Spring》 Spring Boot——约定优于配置的革命者
Spring Boot基于“约定优于配置”理念,通过自动配置、起步依赖、嵌入式容器和Actuator四大特性,简化Spring应用的开发与部署,提升效率,降低门槛,成为现代Java开发的事实标准。
|
3月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
3月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
3月前
|
JavaScript Java Maven
【SpringBoot(二)】带你认识Yaml配置文件类型、SpringMVC的资源访问路径 和 静态资源配置的原理!
SpringBoot专栏第二章,从本章开始正式进入SpringBoot的WEB阶段开发,本章先带你认识yaml配置文件和资源的路径配置原理,以方便在后面的文章中打下基础
379 3
|
3月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
480 2
|
4月前
|
XML Java 数据格式
常用SpringBoot注解汇总与用法说明
这些注解的使用和组合是Spring Boot快速开发和微服务实现的基础,通过它们,可以有效地指导Spring容器进行类发现、自动装配、配置、代理和管理等核心功能。开发者应当根据项目实际需求,运用这些注解来优化代码结构和服务逻辑。
356 12
|
缓存 安全 前端开发
SpringBoot整合SpringSecurity带图片验证码简单实现
针对把code码放到httpServletRequest中易引发并发问题,考虑之后,实现把code码放入到login的提交表单内,与用户名和密码一起发送。
SpringBoot整合SpringSecurity带图片验证码简单实现
|
3月前
|
JavaScript Java 关系型数据库
基于springboot的项目管理系统
本文探讨项目管理系统在现代企业中的应用与实现,分析其研究背景、意义及现状,阐述基于SSM、Java、MySQL和Vue等技术构建系统的关键方法,展现其在提升管理效率、协同水平与风险管控方面的价值。