文章目录
1、@AliasFor
如果自定义一个和它类似的注解并没有下列效果,因为这些效果是通过Spring处理后才达到的。
一般有两种用途
- 1、将两个属性互为别名。一个值和另一个的值同步,修改一个同时更改另一个。
- 2、可以对注解上使用的注解的值进行重写,类似于Override。比如@SpringBootApplication
的定义上有@EnableAutoConfiguration:然后
@SpringBootApplication的exclude()就会重写@EnableAutoConfiguration的exclude()值。所以@SpringBootApplication实际上是对多注解的配置。
2、原理
2.1、定义使用AliasFor的注解
package cn.annotation;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* @author wenei
* @date 2021-02-02 22:34
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TestAlias {
@AliasFor("location")
String value() default "";
@AliasFor("value")
String location() default "";
}
2.2、使用
通过调用AnnotationUtilsSpring获取注解才会对AliasFor注解进行解析。
package cn;
import cn.annotation.OverrideAlias;
import cn.annotation.TestAlias;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotationUtils;
/**
* @author: huu
* @date: 2020/4/9 23:48
* @description:
*/
@ComponentScan("cn")
@TestAlias(value = "123")
public class Application {
public static void main(String[] args) {
TestAlias annotation = AnnotationUtils.
getAnnotation(Application.class, TestAlias.class);
assert annotation != null;
System.out.println(annotation.value());
System.out.println(annotation.location());
}
}
2.3、源码解析
1、AnnotationUtils#getAnnotation方法首先从被注解的元素上获取到注解实例。
2、然后调用synthesizeAnnotation方法进行合成注解。
3、在synthesizeAnnotation方法中,对注解实例使用SynthesizedAnnotationInvocationHandler
进行动态代理完成解析。
2.3.1、AnnotationUtils#getAnnotation
@Nullable
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
try {
// 从被注解元素上获取到注解实例
A annotation = annotatedElement.getAnnotation(annotationType);
// 如果注解实例没有直接获取到,那么从被注解的元素上的所有注解中获取
if (annotation == null) {
// 遍历被注解元素上的所有注解
for (Annotation metaAnn : annotatedElement.getAnnotations()) {
// 被注解的元素上的注解中的注解
annotation = metaAnn.annotationType().getAnnotation(annotationType);
if (annotation != null) {
break;
}
}
}
// 合成注解
return (annotation != null ? synthesizeAnnotation(annotation, annotatedElement) : null);
}
catch (Throwable ex) {
handleIntrospectionFailure(annotatedElement, ex);
return null;
}
}
2.3.2、AnnotationUtils#synthesizeAnnotation
public static <A extends Annotation> A synthesizeAnnotation(
A annotation, @Nullable AnnotatedElement annotatedElement) {
return synthesizeAnnotation(annotation, (Object) annotatedElement);
}
2.3.3、AnnotationUtils#synthesizeAnnotation(annotation,annotatedElement)
static <A extends Annotation> A synthesizeAnnotation(A annotation, @Nullable Object annotatedElement) {
Assert.notNull(annotation, "Annotation must not be null");
// 如果注解的实例已经是SynthesizedAnnotation的实例,表示已经是合成后的注解了
if (annotation instanceof SynthesizedAnnotation) {
return annotation;
}
Class<? extends Annotation> annotationType = annotation.annotationType();
// 判断注解的类型能否合成
if (!isSynthesizable(annotationType)) {
return annotation;
}
// 默认注解属性提取器
DefaultAnnotationAttributeExtractor attributeExtractor =
new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
// Can always expose Spring's SynthesizedAnnotation marker since we explicitly check for a
// synthesizable annotation before (which needs to declare @AliasFor from the same package)
Class<?>[] exposedInterfaces = new Class<?>[] {
annotationType, SynthesizedAnnotation.class};
// 进行JDK动态代理
return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler);
}
2.3.4、SynthesizedAnnotationInvocationHandler处理器源码
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* {@link InvocationHandler} for an {@link Annotation} that Spring has
* <em>synthesized</em> (i.e., wrapped in a dynamic proxy) with additional
* functionality.
*
* @author Sam Brannen
* @since 4.2
* @see Annotation
* @see AnnotationAttributeExtractor
* @see AnnotationUtils#synthesizeAnnotation(Annotation, AnnotatedElement)
*/
class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
private final AnnotationAttributeExtractor<?> attributeExtractor;
private final Map<String, Object> valueCache = new ConcurrentHashMap<>(8);
/**
* Construct a new {@code SynthesizedAnnotationInvocationHandler} for
* the supplied {@link AnnotationAttributeExtractor}.
* @param attributeExtractor the extractor to delegate to
*/
SynthesizedAnnotationInvocationHandler(AnnotationAttributeExtractor<?> attributeExtractor) {
Assert.notNull(attributeExtractor, "AnnotationAttributeExtractor must not be null");
this.attributeExtractor = attributeExtractor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 通用方法调用时处理
if (ReflectionUtils.isEqualsMethod(method)) {
return annotationEquals(args[0]);
}
if (ReflectionUtils.isHashCodeMethod(method)) {
return annotationHashCode();
}
if (ReflectionUtils.isToStringMethod(method)) {
return annotationToString();
}
if (AnnotationUtils.isAnnotationTypeMethod(method)) {
return annotationType();
}
if (!AnnotationUtils.isAttributeMethod(method)) {
throw new AnnotationConfigurationException(String.format(
"Method [%s] is unsupported for synthesized annotation type [%s]", method, annotationType()));
}
// 获取属性值
return getAttributeValue(method);
}
private Class<? extends Annotation> annotationType() {
return this.attributeExtractor.getAnnotationType();
}
private Object getAttributeValue(Method attributeMethod) {
String attributeName = attributeMethod.getName();
// 先从缓存中取
Object value = this.valueCache.get(attributeName);
if (value == null) {
// 从属性提取器中提取属性值
value = this.attributeExtractor.getAttributeValue(attributeMethod);
if (value == null) {
String msg = String.format("%s returned null for attribute name [%s] from attribute source [%s]",
this.attributeExtractor.getClass().getName(), attributeName, this.attributeExtractor.getSource());
throw new IllegalStateException(msg);
}
// 返回的值如果是注解类型将再次合成
// Synthesize nested annotations before returning them.
if (value instanceof Annotation) {
value = AnnotationUtils.synthesizeAnnotation((Annotation) value, this.attributeExtractor.getAnnotatedElement());
}
// 返回的值如果是注解数组类型将再次合成
else if (value instanceof Annotation[]) {
value = AnnotationUtils.synthesizeAnnotationArray((Annotation[]) value, this.attributeExtractor.getAnnotatedElement());
}
this.valueCache.put(attributeName, value);
}
// Clone arrays so that users cannot alter the contents of values in our cache.
if (value.getClass().isArray()) {
value = cloneArray(value);
}
return value;
}
/**
* Clone the provided array, ensuring that original component type is
* retained.
* @param array the array to clone
*/
private Object cloneArray(Object array) {
if (array instanceof boolean[]) {
return ((boolean[]) array).clone();
}
if (array instanceof byte[]) {
return ((byte[]) array).clone();
}
if (array instanceof char[]) {
return ((char[]) array).clone();
}
if (array instanceof double[]) {
return ((double[]) array).clone();
}
if (array instanceof float[]) {
return ((float[]) array).clone();
}
if (array instanceof int[]) {
return ((int[]) array).clone();
}
if (array instanceof long[]) {
return ((long[]) array).clone();
}
if (array instanceof short[]) {
return ((short[]) array).clone();
}
// else
return ((Object[]) array).clone();
}
/**
* See {@link Annotation#equals(Object)} for a definition of the required algorithm.
* @param other the other object to compare against
*/
private boolean annotationEquals(Object other) {
if (this == other) {
return true;
}
if (!annotationType().isInstance(other)) {
return false;
}
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotationType())) {
Object thisValue = getAttributeValue(attributeMethod);
Object otherValue = ReflectionUtils.invokeMethod(attributeMethod, other);
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
return false;
}
}
return true;
}
/**
* See {@link Annotation#hashCode()} for a definition of the required algorithm.
*/
private int annotationHashCode() {
int result = 0;
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotationType())) {
Object value = getAttributeValue(attributeMethod);
int hashCode;
if (value.getClass().isArray()) {
hashCode = hashCodeForArray(value);
}
else {
hashCode = value.hashCode();
}
result += (127 * attributeMethod.getName().hashCode()) ^ hashCode;
}
return result;
}
/**
* WARNING: we can NOT use any of the {@code nullSafeHashCode()} methods
* in Spring's {@link ObjectUtils} because those hash code generation
* algorithms do not comply with the requirements specified in
* {@link Annotation#hashCode()}.
* @param array the array to compute the hash code for
*/
private int hashCodeForArray(Object array) {
if (array instanceof boolean[]) {
return Arrays.hashCode((boolean[]) array);
}
if (array instanceof byte[]) {
return Arrays.hashCode((byte[]) array);
}
if (array instanceof char[]) {
return Arrays.hashCode((char[]) array);
}
if (array instanceof double[]) {
return Arrays.hashCode((double[]) array);
}
if (array instanceof float[]) {
return Arrays.hashCode((float[]) array);
}
if (array instanceof int[]) {
return Arrays.hashCode((int[]) array);
}
if (array instanceof long[]) {
return Arrays.hashCode((long[]) array);
}
if (array instanceof short[]) {
return Arrays.hashCode((short[]) array);
}
// else
return Arrays.hashCode((Object[]) array);
}
/**
* See {@link Annotation#toString()} for guidelines on the recommended format.
*/
private String annotationToString() {
StringBuilder sb = new StringBuilder("@").append(annotationType().getName()).append("(");
Iterator<Method> iterator = AnnotationUtils.getAttributeMethods(annotationType()).iterator();
while (iterator.hasNext()) {
Method attributeMethod = iterator.next();
sb.append(attributeMethod.getName());
sb.append('=');
sb.append(attributeValueToString(getAttributeValue(attributeMethod)));
sb.append(iterator.hasNext() ? ", " : "");
}
return sb.append(")").toString();
}
private String attributeValueToString(Object value) {
if (value instanceof Object[]) {
return "[" + StringUtils.arrayToDelimitedString((Object[]) value, ", ") + "]";
}
return String.valueOf(value);
}
}