Spring 注解属性别名与覆盖

Spring 注解属性别名与覆盖

在Spring 中将多个注解组合注解到一个注解上,这个注解就可以发挥被注解的多个注解功能。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	......
}

一直以为这是Java注解自带的功能, 但是发现这是Spring封装后的功能;

@SpringBootApplication
public class AnnotatedElementUtilsTest  {

	public static void main(String[] args) {
		Annotation[] annotations = AnnotatedElementUtilsTest.class.getAnnotations();
		for(Annotation annotation : annotations) {
			System.err.println(annotation.annotationType());
		}
	}
}

最后发现打印结果只有
interface org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
public class AnnotatedElementUtilsTest {

	public static void main(String[] args) {
		MergedAnnotations annotations = MergedAnnotations.from(AnnotatedElementUtilsTest.class, SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none());
		annotations.stream().map(MergedAnnotation::synthesize).forEach(k -> {
			System.err.println(k.annotationType());
		});
	}
}

打印结果为:

interface org.springframework.boot.autoconfigure.SpringBootApplication
interface org.springframework.boot.SpringBootConfiguration
interface org.springframework.boot.autoconfigure.EnableAutoConfiguration
interface org.springframework.context.annotation.ComponentScan
interface org.springframework.context.annotation.Configuration
interface org.springframework.boot.autoconfigure.AutoConfigurationPackage
interface org.springframework.context.annotation.Import
interface org.springframework.stereotype.Component
interface org.springframework.context.annotation.Import
interface org.springframework.stereotype.Indexed

可以看出, Spring 会把所有注解都找到。
在这里插入图片描述
那么,组合注解之间如何传值的呢?比如,一个类标有SpringBootApplication注解,属性只能填到SpringBootApplication注解相应字段中,那SpringBootApplication注解属性值如何传到SpringBootConfiguration,EnableAutoConfiguration,ComponentScan等其他注解中呢?
组合注解的传值主要通过注解属性别名与覆盖机制实现。
通过org.springframework.core.annotation.AliasFor可以将注解中一个属性声明成另外一个属性的别名。

例如,定义注解MarkAnnotation:

public @interface MarkAnnotation {	
	String[] value() default {};
	
	@AliasFor("value")
	String[] path() default {};
}
@MarkAnnotation(value="k")
public class AnnotatedElementUtilsTest {

	public static void main(String[] args) {
		MarkAnnotation markAnnotation = AnnotatedElementUtils.findMergedAnnotation(AnnotatedElementUtilsTest.class, MarkAnnotation.class);
		
		System.err.println(markAnnotation.value()[0]);  // k
		System.err.println(markAnnotation.path()[0]);  // k
	}
}

但是,注解属性之间互为别名需要满足一下条件:

  1. 属性类型相同
  2. 属性方法必须存在默认值
  3. 属性默认值必须相同

否则,运行过程中会报错。
去掉MarkAnnotation注解value属性的默认值,再次运行以上代码,会报
Exception in thread "main" org.springframework.core.annotation.AnnotationConfigurationException: Misconfigured aliases: attribute 'path' in annotation [MarkAnnotation] and attribute 'value' in annotation [MarkAnnotation] must declare default values. 。
那么,注解属性如何覆盖被注解注解属性呢?

属性覆盖可以分为三类:

  1. 隐式覆盖(Implicit Overrides)
  2. 显示覆盖(Explicit Overrides)
  3. 传递式显式覆盖(Transitive Explicit Overrides)
隐式覆盖

当一个注解 @One 被元注解 @Two 标注,两个注解存在同样的属性方法 name。@Two#name 将会被 @One#name 属性覆盖。

再定义注解MarkedAnnotation,其具有和MarkAnnotation相同类型及默认值的属性path, 并注解MarkAnnotation。

@MarkAnnotation
public @interface MarkedAnnotation {
	String[] path() default {};
}

如果一个类标有MarkedAnnotation, 通过以上内容我们知道,这个类会获得MarkAnnotation,且其path属性的值和MarkedAnnotation的path属性值相同。

@MarkedAnnotation(path="k")
public class AnnotatedElementUtilsTest {

	public static void main(String[] args) {
		MarkAnnotation markAnnotation = AnnotatedElementUtils.findMergedAnnotation(AnnotatedElementUtilsTest.class, MarkAnnotation.class);
		System.err.println(markAnnotation.path()[0]); // k
	}
}
显示覆盖

AliasFor除了可以在同一个注解之间声明别名, 还可以将一个注解的属性声明为另一个注解属性的别名。
当一个注解 @One 被元注解 @Two 标注,同时在@one中存在属性nameA使用AliasFor声明为注解@Two注解中nameB的别名,那么@Two#nameB 将会被 @One#nameA 属性覆盖。

修改MarkedAnnotation注解如下:

@MarkAnnotation
public @interface MarkedAnnotation {
	@AliasFor(annotation=MarkAnnotation.class, value="path")
	String[] url() default {};
}

@MarkedAnnotation(url="k")
public class AnnotatedElementUtilsTest {

	public static void main(String[] args) {
		MarkAnnotation markAnnotation = AnnotatedElementUtils.findMergedAnnotation(AnnotatedElementUtilsTest.class, MarkAnnotation.class);
		System.err.println(markAnnotation.path()[0]); // k
	}
}
传递式显式覆盖

如果注解 @One#name 显示覆盖了 @Two#nameAlias,而 @Two#nameAlias 显示覆盖了 @Three#nameAlias,最后因为传递性,@One#name 实际覆盖了@Three#nameAlias。

新增注解BaseAnnotation

@MarkedAnnotation
public @interface BaseAnnotation {
	@AliasFor(annotation=MarkedAnnotation.class, value="url")
	String[] value() default {};
}
@BaseAnnotation("k")
public class AnnotatedElementUtilsTest {

	public static void main(String[] args) {
		MarkAnnotation markAnnotation = AnnotatedElementUtils.findMergedAnnotation(AnnotatedElementUtilsTest.class, MarkAnnotation.class);
		System.err.println(markAnnotation.path()[0]); // k
		MarkedAnnotation markedAnnotation = AnnotatedElementUtils.findMergedAnnotation(AnnotatedElementUtilsTest.class, MarkedAnnotation.class);
		System.err.println(markedAnnotation.url()[0]); // k
	}
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值