本文主要聊聊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... |