Spring的@AliasFor原理

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

}

猜你喜欢

转载自blog.csdn.net/qq_43621091/article/details/114000240
今日推荐