【走近Spring】Spring貢獻的多個注解相關的工具類:AnnotationUtils、AnnotatedElementUtils、AnnotationConfigUtils...


本文主要聊聊Spring提供的多個關於注解相關的工具類:AnnotationUtils和AnnotatedElementUtils等等。

因為很多邏輯都封裝在了工具類里面,因此要理解Spring的深層意思,有時候不了解工具類也是比較麻煩的。雖然說都是Spring內部去使用,但是有些工具類是public的(下面會有介紹),所以我們平時若有需要,也是可以使用的。

在这里插入图片描述

本文要說明的工具類如上圖,一共會講述9個。(ParserStrategyUtils忽略,只有一個方法處理Aware的注入。AnnotationReadingVisitorUtils也忽略,它和ASM字節碼相關)

AnnotationUtils(最重要)

從類名就能看出來。這是Spring提供的獲取、處理注解的工具類。

可能有小伙伴就非常好奇了:JDK已經提供給我們獲取注解的方法了,Spring為何要多此一舉呢?如下:

@MyAnno
interface Eat {
}

class Parent implements Eat {
}

class Child extends Parent {
}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping // 特意注解上放一個注解,方面測試看結果
@Inherited
@interface MyAnno {

}

	// 現在我們來獲取類上面的注解如下
    public static void main(String[] args) {
        MyAnno anno1 = Eat.class.getAnnotation(MyAnno.class);
        MyAnno anno2 = Parent.class.getAnnotation(MyAnno.class);
        MyAnno anno3 = Child.class.getAnnotation(MyAnno.class);
        System.out.println(anno1); //@com.fsx.maintest.MyAnno()
        System.out.println(anno2); //null
        System.out.println(anno3); //null
    }

注意:我們的注解標注了@Inherited表示該注解可以被繼承,但是anno2和anno3還是null。需要注意:@Inherited繼承只能發生在類上,而不能發生在接口上(也就是說標注在接口上仍然是不能被繼承的)

介紹Class提供的獲取注解相關方法:

  • <A extends Annotation>A getAnnotation(Class<A>annotationClass):獲取該class對象對應類上指定類型的Annotation,如果該類型注解不存在,則返回null
  • Annotation[] getAnnotations():返回修飾該class對象對應類上存在的所有Annotation
  • <A extends Annotation>A getDeclaredAnnotation(Class<A>annotationClass):這是Java 8中新增的,該方法獲取直接修飾該class對象對應類的指定類型的Annotation,如果不存在,則返回null(也就說只找自己的,繼承過來的注解這個方法就不管了)
  • Annotation[] getDeclaredAnnotations():返回修飾該Class對象對應類上存在的所有Annotation(同上,繼承的不管
  • <A extends Annotation>A[] getAnnotationByType(Class<A>annotationClass):該方法的功能與前面介紹的getAnnotation()方法基本相似,但由於Java8增加了重復注解功能,因此需要使用該方法獲取修飾該類的指定類型的多個Annotation(會考慮繼承的注解)
  • <A extends Annotation>A[] getDeclaredAnnotationByType(Class<A>annotationClass)

既然JDK提供給了我們這么多的注解相關方法,乍一看是夠用了呢?為何Spring還自己寫個工具類呢?我覺得這也是Spring的強大之處,往往寫出來的東西比JDK的還強大。比如試想一下下面兩個場景,你就沒覺得疑惑?

  • @AliasFor(Spring4.2之后才有的)為何能生效呢?大大的方便了我們注解的使用
  • @RequestMapping注解明明不能繼承(即使有@Inherited也不能寫在接口上嘛),但為何我們把它寫在接口上時,用Controller去實現的時候卻像是被繼承了一樣呢?

注解支持的數據類型:

  • 所有基本類型(int,float,boolean,byte,double,char,long,short) 注意:包裝類型不支持
  • String
  • Class
  • enum
  • Annotation
  • 上述類型的數組

注解是不支持繼承的,因此不能使用關鍵字extends來繼承某個@interface,但注解在編譯后,編譯器會自動繼承java.lang.annotation.Annotation接口(從反編譯代碼里可以看出,類似於Enum)

AnnotationUtils方法介紹

AnnotationUtils內部使用了很多ConcurrentReferenceHashMap(弱、軟引用)作為緩存,來各種提高效率

開門見山,先直接上一個最強的方法,也是該工具類的核心:synthesizeAnnotation

static <A extends Annotation> A synthesizeAnnotation(A annotation) {
	return synthesizeAnnotation(annotation, null);
}
public static <A extends Annotation> A synthesizeAnnotation(
		A annotation, @Nullable AnnotatedElement annotatedElement) {
	return synthesizeAnnotation(annotation, (Object) annotatedElement);
}
static <A extends Annotation> A synthesizeAnnotation(A annotation, @Nullable Object annotatedElement) {
	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};
	return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler);
}

作用:來獲取一個動態代理注解(相當於調用者傳進來的注解會被代理掉),該方法是別名注解@AliasFor的核心原理。

public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType)

public static void main(String[] args) {
	MyAnno anno1 = Eat.class.getAnnotation(MyAnno.class);

	// 注解交給這么一處理  相當於就會被Spring代理了  這就是優勢
	MyAnno sAnno1 = AnnotationUtils.getAnnotation(anno1, MyAnno.class);
	System.out.println(sAnno1); //@com.fsx.maintest.MyAnno()

	// 這樣前后類型不一致的話,會把這個注解上面的注解給獲取出來
	RequestMapping annotation = AnnotationUtils.getAnnotation(anno1, RequestMapping.class);
	System.out.println(annotation); //@org.springframework.web.bind.annotation.RequestMapping
}

public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType)

重載方法。上面annotation.annotationType();其實就是annotatedElement了

public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType)

強大之處在於:它連橋接方法(BridgeMethod)都支持。

public static Annotation[] getAnnotations(AnnotatedElement annotatedElement)

獲取指定類型上所有注解。

public static void main(String[] args) {
	MyAnno anno1 = Eat.class.getAnnotation(MyAnno.class);

	// 注意這兩種寫法的區別:
	// 這個相當於是獲取Child.class的它上面的所有注解, 所以只有繼承過來的一個@MyAnno
	Annotation[] sAnno1 = AnnotationUtils.getAnnotations(Child.class);
	// 而這里傳入的為anno1.annotationType,所以相當於獲取該注解上面的注解  所以使用的時候需要注意
	Annotation[] sAnno2 = AnnotationUtils.getAnnotations(anno1.annotationType());
	System.out.println(sAnno1);
	System.out.println(sAnno2);
}

public static Annotation[] getAnnotations(Method method)

支持橋接

public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType)

支持了Java8的重復注解

getDeclaredRepeatableAnnotations

重載方法

public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType)

它的特點就是,會遞歸去你的父類、接口里把注解找到,找到既返回,返回第一個匹配的注解信息 順序是:先接口后父類

public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType)

重載方法

@MyAnno
interface Eat {
}

class Parent implements Eat {
}

//本來,接口上的注解我們無論如何都繼承不了了,但用了Spring的,你就可以
    public static void main(String[] args) {
        MyAnno annotation = AnnotationUtils.findAnnotation(Child.class, MyAnno.class);
        System.out.println(annotation);
    }
//備注:哪怕@MyAnno上沒有標注@Inherited,也是能找出來的(這是后面講解@RequestMapping為何能被子類繼承的重要原因)

public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, @Nullable Class<?> clazz)

找到第一個(自己有就自己了,否則去父類繼續找)有這個注解的。Class~~(有可能是自己,有可能是父類) 備注:接口不找

public static void main(String[] args) {
	Class<?> annotationDeclaringClass = AnnotationUtils.findAnnotationDeclaringClass(MyAnno.class, Child.class);
	System.out.println(annotationDeclaringClass);
}

public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz)

annotationTypes和上面相比,只有類里面有一個這個注解,就return了

public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz)

簡單的說就是自己本身Class是否含有指定的這個注解

public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz)

判斷該Class上指定的注解是否是繼承過來的。

public static void main(String[] args) {
	System.out.println(AnnotationUtils.isAnnotationInherited(MyAnno.class, Parent.class)); //false

	// 說明一下:clazz.isAnnotationPresent(annotationType) JDK的。表示注解存在就行,不管你是自己的還是繼承來的
	System.out.println(AnnotationUtils.isAnnotationInherited(MyAnno.class, Child.class)); //true 很顯然,Child的這個注解是繼承來的

	// Child的MyAnno注解是父類的,但這里還是會返回true
	System.out.println(Child.class.isAnnotationPresent(MyAnno.class)); //true
	System.out.println(Child.class.getAnnotation(MyAnno.class)); // @com.fsx.maintest.MyAnno(c...
}

public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType, @Nullable Class<? extends Annotation> metaAnnotationType)

簡單的說:就是annotationType這個注解類型上面,是否標注有metaAnnotationType這個類型的注解

public static void main(String[] args) {
	System.out.println(AnnotationUtils.isAnnotationMetaPresent(MyAnno.class, RequestMapping.class)); //true
	System.out.println(AnnotationUtils.isAnnotationMetaPresent(MyAnno.class, Component.class)); //false
}

public static boolean isInJavaLangAnnotationPackage(@Nullable Annotation annotation)

是否是JDK的注解

public static void main(String[] args) {
	MyAnno myAnno = Eat.class.getAnnotation(MyAnno.class);
	Target target = MyAnno.class.getAnnotation(Target.class);

	System.out.println(AnnotationUtils.isInJavaLangAnnotationPackage(myAnno)); //false
	System.out.println(AnnotationUtils.isInJavaLangAnnotationPackage(target)); //true
}

public static Map<String, Object> getAnnotationAttributes(Annotation annotation)

獲取這個注解的所有屬性值們,用map保存(非常重要)

public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap)

最全的一個方法 classValuesAsString:true表示把Class類型的都轉換為String類型,nestedAnnotationsAsMap:true表示連內嵌的注解也解析出來(默認都是false的) 注意此處返回的是AnnotationAttributes,它其實就是個Map,提供了各種更便捷的獲取方法:getString、getBoolean等等

public static AnnotationAttributes getAnnotationAttributes(@Nullable AnnotatedElement annotatedElement, Annotation annotation)

annotatedElement表示被標注了后面這個注解的元素,如果不知道,你就傳null吧

// 給注解增加屬性、有Class屬性  也有嵌套屬性
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping
@Inherited
@interface MyAnno {
    String value() default "this is mine";
    Class<? extends Number> clazz() default Integer.class;
    // 注解類型的屬性
    Component anno() default @Component;
}

    public static void main(String[] args) {
        MyAnno myAnno = Eat.class.getAnnotation(MyAnno.class);

        // 它原理是調用了下面的底層方法  傳值為兩個false
        Map<String, Object> myAnnoAttrs = AnnotationUtils.getAnnotationAttributes(myAnno);
        // 此處可以看到clazz輸出的是class類型,並不是字符串。anno也是調用了toString方法,並沒有解析內嵌的
        System.out.println(myAnnoAttrs); //{clazz=class java.lang.Integer, value=this is mine, anno=@org.springframework.stereotype.Component(value=mytest)}

        // =====傳雙true
        AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes(myAnno, true, true);
        System.out.println(annotationAttributes); //{clazz=java.lang.Integer, value=this is mine, anno={value=mytest}}

        // 經過我測試,第一個參數寫Object.class都無所謂 結果和上面一樣的
        // 若哪位小伙伴知道這個參數有不同的解釋,請告知哈
        AnnotationAttributes classAnno = AnnotationUtils.getAnnotationAttributes(Object.class, myAnno, true, true);
        System.out.println(classAnno);
    }

public static Object getValue(Annotation annotation)

獲取注解內指定屬性的值

public static void main(String[] args) {
	MyAnno myAnno = Eat.class.getAnnotation(MyAnno.class);

	// 個人覺得還不如直接:myAnno.value()呢  哈哈(Spring底層用的Method做的)
	System.out.println(AnnotationUtils.getValue(myAnno)); //this is mine
	System.out.println(AnnotationUtils.getValue(myAnno, "clazz")); //class java.lang.Integer
	System.out.println(AnnotationUtils.getValue(myAnno, "aaa")); //null
}

public static Object getDefaultValue(@Nullable Annotation annotation, @Nullable String attributeName)

和上面相比,這里只拿默認值。若沒有設置默認值,那就返回null

public static <A extends Annotation> A synthesizeAnnotation(A annotation, @Nullable AnnotatedElement annotatedElement)

提供出一個public的方法,外部也能調用代理指定的注解了

public static void clearCache()

我們也可以手動調用此方法,清除內部的緩存

順便提一句,其內部有一個私有的靜態內部類private static class AliasDescriptor:專程用來處理@AliasFor注解

AnnotatedElementUtils

在AnnotatedElement查找注釋、元注釋和可重復注釋(Spring4.0開始)

public static AnnotatedElement forAnnotations(final Annotation... annotations)

給這么多的Annos提供一個適配器(內部就是new了一個AnnotatedElement匿名內部類,沒啥特殊的)

public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName)

簡單的說,就是返回指定Class上面這個注解上的注解(若沒有,返回null) 備注:不包含Java的元注解哦~

public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType)

public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType)

不管是注解,還是有注解的注解標注了,都返回true

public static void main(String[] args) {
	Set<String> metaAnnotationTypes = AnnotatedElementUtils.getMetaAnnotationTypes(Child.class, MyAnno.class);
	System.out.println(metaAnnotationTypes); //[org.springframework.web.bind.annotation.RequestMapping, org.springframework.web.bind.annotation.Mapping]

	// 請注意此處:因為是元注解types,所以第一個是false,第二個是true
	System.out.println(AnnotatedElementUtils.hasMetaAnnotationTypes(Child.class, MyAnno.class)); // false
	System.out.println(AnnotatedElementUtils.hasMetaAnnotationTypes(Child.class, RequestMapping.class)); // true

	// 注意此處  兩個都是true哦~~~
	System.out.println(AnnotatedElementUtils.isAnnotated(Child.class, MyAnno.class)); // true
	System.out.println(AnnotatedElementUtils.isAnnotated(Child.class, RequestMapping.class)); //true
}

AnnotationBeanUtils

拷貝注解值到指定的Bean中。

它在org.springframework.beans.annotation包下,並且這個包下只有它一個類。該類只有一個核心方法。


public abstract class AnnotationBeanUtils {
	// excludedProperties 排除的屬性值不拷貝
	public static void copyPropertiesToBean(Annotation ann, Object bean, String... excludedProperties) {
		copyPropertiesToBean(ann, bean, null, excludedProperties);
	}
	
	public static void copyPropertiesToBean(Annotation ann, Object bean, @Nullable StringValueResolver valueResolver, String... excludedProperties) {
		
		// 用set去重
		Set<String> excluded = new HashSet<>(Arrays.asList(excludedProperties));
		
		// 這是拿到該注解的所有屬性(編譯后都是通過method的方式存儲的)
		Method[] annotationProperties = ann.annotationType().getDeclaredMethods();
		// 吧這個Bean包裝成一個BeanWrapper 
		BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(bean);
		for (Method annotationProperty : annotationProperties) {
			String propertyName = annotationProperty.getName();
			if (!excluded.contains(propertyName) && bw.isWritableProperty(propertyName)) {
				// 拿到注解的值
				Object value = ReflectionUtils.invokeMethod(annotationProperty, ann);
				// 若是字符串類型,還可以處理(也就值支持到了{}這種占位符形式)
				if (valueResolver != null && value instanceof String) {
					value = valueResolver.resolveStringValue((String) value);
				}
				// 把該Value值設置進去
				bw.setPropertyValue(propertyName, value);
			}
		}
	}
}

這個我們一般用不着,Spring內在JMX相關類中使用。比如AnnotationJmxAttributeSource。

AnnotationConfigUtils

重要類,和Config配置類有關的注解工具類。

AnnotationConfigUtils是一個Spring內部工具類,用於識別注解配置類中的bean定義。(和Bean注冊有關)

public class AnnotationConfigUtils {

	// 這個常量在我們AnnotationConfigApplicationContext#setBeanNameGenerator的時候會執行這么一句話:
	//getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator)
	//  然后在處理@Configuration里的@Bean的時候,會get出來處理名字
	public static final String CONFIGURATION_BEAN_NAME_GENERATOR =
			"org.springframework.context.annotation.internalConfigurationBeanNameGenerator";

	//============= Spring默認會注冊進去的7個Bean(若沒導包,有可能是5個哦) ===========================
	public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
			"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
	public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
			"org.springframework.context.annotation.internalAutowiredAnnotationProcessor";
	public static final String REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
			"org.springframework.context.annotation.internalRequiredAnnotationProcessor";
	public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME =
			"org.springframework.context.annotation.internalCommonAnnotationProcessor";
	public static final String PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME =
			"org.springframework.context.annotation.internalPersistenceAnnotationProcessor";
	public static final String EVENT_LISTENER_PROCESSOR_BEAN_NAME =
			"org.springframework.context.event.internalEventListenerProcessor";
	public static final String EVENT_LISTENER_FACTORY_BEAN_NAME =
			"org.springframework.context.event.internalEventListenerFactory";

	
	private static final String PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME =
			"org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor";
	private static final boolean jsr250Present =
			ClassUtils.isPresent("javax.annotation.Resource", AnnotationConfigUtils.class.getClassLoader());
	private static final boolean jpaPresent =
			ClassUtils.isPresent("javax.persistence.EntityManagerFactory", AnnotationConfigUtils.class.getClassLoader()) &&
			ClassUtils.isPresent(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader());
}
  • public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source):該方法主要是向容器注冊了一組基礎設施PostProcessor bean定義,這些bean定義生成的PostProcessor實例被框架自己用於識別注解配置類中的bean定義(就是我們上面說的7大默認Bean定義,role均為:BeanDefinition.ROLE_INFRASTRUCTURE表示框架自己用的)。 它還為Bean工廠設置了:setDependencyComparator和setAutowireCandidateResolver
  • public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd):處理通用的Bean定義上的注解,該方法從原始bean定義的元數據中獲取那些通用的注解信息:@Lazy,@DependsOn,@Role,@Description,然后設置AnnotatedBeanDefinition實例相應的屬性

基本上這個工具類是Spring內部使用的,我們用不着。它在org.springframework.context.annotation包內

ConfigurationClassUtils

Spring內部處理@Configuration的工具類。

這個工具類只處理@Configuration配置類的。是Full模式還是Lite模式

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
	return metadata.isAnnotated(Configuration.class.getName());
}
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata){
//有@Component、@Component、@Import、@ImportResource標注的
// 或者類內部有@Bean標注的方法  都屬於Lite模式的配置類
...
}

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
	return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));
}

/
public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
	return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
	return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}

// 獲取@Order的值 since Spring5.0
@Nullable
public static Integer getOrder(AnnotationMetadata metadata) {
	Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
	return (orderAttributes != null ? ((Integer) orderAttributes.get(AnnotationUtils.VALUE)) : null);
}
// since Spring4.2
public static int getOrder(BeanDefinition beanDef) {
	Integer order = (Integer) beanDef.getAttribute(ORDER_ATTRIBUTE);
	return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
}

OrderUtils

處理@Order和javax.annotation.Priority

public abstract class OrderUtils {
	@Nullable
	public static Integer getOrder(Class<?> type) {
		Order order = AnnotationUtils.findAnnotation(type, Order.class);
		if (order != null) {
			return order.value();
		}
		// 兼容到了JDK6提供的javax.annotation.Priority這個注解(需要額外導包)
		Integer priorityOrder = getPriority(type);
		if (priorityOrder != null) {
			return priorityOrder;
		}
		return null;
	}
	@Nullable
	public static Integer getPriority(Class<?> type) {
		if (priorityAnnotationType != null) {
			Annotation priority = AnnotationUtils.findAnnotation(type, priorityAnnotationType);
			if (priority != null) {
				return (Integer) AnnotationUtils.getValue(priority);
			}
		}
		return null;
	}


// 最常調用的還是下列方法:
	public static int getOrder(Class<?> type, int defaultOrder) {
		Integer order = getOrder(type);
		return (order != null ? order : defaultOrder);
	}
	@Nullable
	public static Integer getOrder(Class<?> type, @Nullable Integer defaultOrder) {
		Integer order = getOrder(type);
		return (order != null ? order : defaultOrder);
	}
}

BeanFactoryUtils

方便在Bean工廠上操作的工具類,特別針對於ListableBeanFactory這個工廠

public abstract class BeanFactoryUtils {
	// 內部生成BeanName的分隔符,如果不唯一后面會一直加這個符號
	public static final String GENERATED_BEAN_NAME_SEPARATOR = "#";

	// 判斷這個Bean是不是工廠Bean  FactoryBean
	public static boolean isFactoryDereference(@Nullable String name) {
		return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
	}
	// 得到真實的Bean的名稱(兼容工廠Bean的情況)
	public static String transformedBeanName(String name) {
		Assert.notNull(name, "'name' must not be null");
		String beanName = name;
		while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
			beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
		}
		return beanName;
	}
	// 是否是BeanDefinitionReaderUtils#generateBeanName生成出來的Bean名字
	public static boolean isGeneratedBeanName(@Nullable String name) {
		return (name != null && name.contains(GENERATED_BEAN_NAME_SEPARATOR));
	}

	// 包含祖先(父工廠)  bean的總數目
	public static int countBeansIncludingAncestors(ListableBeanFactory lbf) {
		return beanNamesIncludingAncestors(lbf).length;
	}
	// bean的所有的名稱  會做去重處理
	public static String[] beanNamesIncludingAncestors(ListableBeanFactory lbf) {
		return beanNamesForTypeIncludingAncestors(lbf, Object.class);
	}
	// 顯然依賴的方法都是ListableBeanFactory#getBeanNamesForType
	public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, ResolvableType type) {
		Assert.notNull(lbf, "ListableBeanFactory must not be null");
		String[] result = lbf.getBeanNamesForType(type);
		if (lbf instanceof HierarchicalBeanFactory) {
			HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
			if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
				// 遞歸去獲取
				String[] parentResult = beanNamesForTypeIncludingAncestors(
						(ListableBeanFactory) hbf.getParentBeanFactory(), type);
				// 做名字的合並、去重處理
				result = mergeNamesWithParent(result, parentResult, hbf);
			}
		}
		return result;
	}

public static <T> T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type){ ... }

	// 這個方法是關於注解的,需要注意一些========Spring5.0后才有的
	public static String[] beanNamesForAnnotationIncludingAncestors(
			ListableBeanFactory lbf, Class<? extends Annotation> annotationType) {

		Assert.notNull(lbf, "ListableBeanFactory must not be null");
		String[] result = lbf.getBeanNamesForAnnotation(annotationType);
		if (lbf instanceof HierarchicalBeanFactory) {
			HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
			if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
				String[] parentResult = beanNamesForAnnotationIncludingAncestors(
						(ListableBeanFactory) hbf.getParentBeanFactory(), annotationType);
				result = mergeNamesWithParent(result, parentResult, hbf);
			}
		}
		return result;
	}
	public static <T> T beanOfType(ListableBeanFactory lbf, Class<T> type){ ... }
}

總之這個Util里面就是提供了一些便捷從ListableBeanFactory里面獲取Bean的方法(包含了父容器,默認實現都是不去父容器里找的)

BeanFactoryAnnotationUtils

提供與注解相關的查找Bean的方法(比如@Qualifier)

public abstract class BeanFactoryAnnotationUtils {

	// 檢查beanName這個Bean是否匹配。或者標注了@Qualifier注解,名稱是否匹配
	public static boolean isQualifierMatch(Predicate<String> qualifier, String beanName,
			@Nullable BeanFactory beanFactory) {

		// Try quick bean name or alias match first...
		// 若BeanName匹配,那就快速返回
		if (qualifier.test(beanName)) {
			return true;
		}
		if (beanFactory != null) {
			// 若有alias別名匹配上了,也可以快速返回
			for (String alias : beanFactory.getAliases(beanName)) {
				if (qualifier.test(alias)) {
					return true;
				}
			}
			try {
				if (beanFactory instanceof ConfigurableBeanFactory) {
				
					// 拿到和父類(若存在)合並后的定義信息
					BeanDefinition bd = ((ConfigurableBeanFactory) beanFactory).getMergedBeanDefinition(beanName);
					// Explicit qualifier metadata on bean definition? (typically in XML definition)
					if (bd instanceof AbstractBeanDefinition) {
						AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
						AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
						
						// 如果有@Qualifier 並且匹配上了  就返回true
						if (candidate != null) {
							Object value = candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY);
							if (value != null && qualifier.test(value.toString())) {
								return true;
							}
						}
					}
					// Corresponding qualifier on factory method? (typically in configuration class)
					// 若FactoryMethod工廠方法里有此注解,匹配上了也返回true
					if (bd instanceof RootBeanDefinition) {
						Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
						if (factoryMethod != null) {
							Qualifier targetAnnotation = AnnotationUtils.getAnnotation(factoryMethod, Qualifier.class);
							if (targetAnnotation != null) {
								return qualifier.test(targetAnnotation.value());
							}
						}
					}
				}
				// Corresponding qualifier on bean implementation class? (for custom user types)
				// 若自己本類上標注了此注解,匹配上了  肯定也是返回true的...
				//上面的解析相當於配置的情況,而這種情況就是在本類上直接標注~~~(備注:@Qualifier可以標注在類上,作用就體現在這里了)
				Class<?> beanType = beanFactory.getType(beanName);
				if (beanType != null) {
					Qualifier targetAnnotation = AnnotationUtils.getAnnotation(beanType, Qualifier.class);
					if (targetAnnotation != null) {
						return qualifier.test(targetAnnotation.value());
					}
				}
			} catch (NoSuchBeanDefinitionException ex) {
			}
		}
		return false;
	}
}

// 就根據beanType、qualifier找到一個唯一的Bean(因為該type的可能有很多)
// 目前只有CacheAspectSupport#getBean等少量用到
public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier){ ... }

此處一定注意@Qualifier的使用場景,它是可以直接標注在類上的。

@Service
public class HelloServiceImpl implements HelloService {
}

@Controller
public class HelloController {
    @Autowired
    @Qualifier("aaa") // 因為不存在aaa的bean,所以肯定會報錯的
    private HelloService helloService;
}

但是,我們只需要在HelloServiceImpl 也加上對應的注解`@Qualifier`就不會報錯了(但強烈不建議這么寫,需要注意)
@Service
@Qualifier("aaa") // 這樣上面的HelloService 注入就不會報錯了
public class HelloServiceImpl implements HelloService {
}

需要注意的是,雖然HelloServiceImpl 上加了@Qualifier注解,但是它在Bean工廠里的BeanName可不會變。但是它在匹配的時候就能匹配上了,這就是BeanFactoryAnnotationUtils#isQualifierMatch的功勞

AnnotationAttributes

Map的封裝,在實際使用時會有更好的體驗。

它的獲取一般這么來:

  • AnnotatedTypeMetadata#getAnnotationAttributes
  • AnnotationAttributes#fromMap
  • AnnotatedElementUtils#getMergedAnnotationAttributes等系列方法
  • new AnnotationAttributes
public class AnnotationAttributes extends LinkedHashMap<String, Object> {
	// 持有對應注解的類型的引用
	@Nullable
	private final Class<? extends Annotation> annotationType;
	final String displayName;

// 多個構造函數
	public AnnotationAttributes(AnnotationAttributes other) {
		super(other);
		this.annotationType = other.annotationType;
		this.displayName = other.displayName;
		this.validated = other.validated;
	}
	// 把一個注解類型Class,直接包裝成AnnotationAttributes
	public AnnotationAttributes(Class<? extends Annotation> annotationType) {
		Assert.notNull(annotationType, "'annotationType' must not be null");
		this.annotationType = annotationType;
		this.displayName = annotationType.getName();
	}
	...
	// 獲得它所屬的注解類型
	@Nullable
	public Class<? extends Annotation> annotationType() {
		return this.annotationType;
	}
	public String getString(String attributeName) {
		return getRequiredAttribute(attributeName, String.class);
	}
	public String[] getStringArray(String attributeName) {
		return getRequiredAttribute(attributeName, String[].class);
	}
	...

	// 這個使用得比較多,因為AnnotationMetadata#getAnnotationAttributes返回的是Map值
	@Nullable
	public static AnnotationAttributes fromMap(@Nullable Map<String, Object> map) {
		if (map == null) {
			return null;
		}
		//大部分情況在整理就return了,否則繼續下面,annotationType就為null,那就是普通的Map里
		if (map instanceof AnnotationAttributes) {
			return (AnnotationAttributes) map;
		}
		return new AnnotationAttributes(map);
	}
}

 

參考鏈接:

【小家Spring】Spring貢獻的多個注解相關的工具類:AnnotationUtils、AnnotatedElementUtils、AnnotationConfigUtils...

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM