前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【小家Spring】Spring贡献的多个注解相关的工具类:AnnotationUtils、AnnotatedElementUtils、AnnotationConfigUtils...

【小家Spring】Spring贡献的多个注解相关的工具类:AnnotationUtils、AnnotatedElementUtils、AnnotationConfigUtils...

作者头像
YourBatman
发布2019-09-03 16:28:13
3.7K0
发布2019-09-03 16:28:13
举报
文章被收录于专栏:BAT的乌托邦
前言

本文主要聊聊Spring提供的多个关于注解相关的工具类:AnnotationUtilsAnnotatedElementUtils等等 因为很多逻辑都封装在了工具类里面,因此要理解Spring的深层意思,有时候不了解工具类也是比较麻烦的

虽然说都是Spring内部去使用,但是有些工具类是public的(下面会有介绍),所以我们平时若有需要,也是可以使用的

本文要说明的工具类如上图,一共会讲述9个(排名不分先后)。(ParserStrategyUtils忽略,没什么好说的,只有一个方法处理Aware的注入。AnnotationReadingVisitorUtils也忽略,它和ASM字节码相关,后面再讨论)

AnnotationUtils(最重要)

总之,从类名就能看出来。这是Spring提供的获取、处理注解的工具类。 可能有小伙伴就非常好奇了:JDK已经提供给我们获取注解的方法了,Spring为何要多此一举呢?如下:

代码语言:javascript
复制
@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):相信不解释你也懂

更多Class中的方法使用,请参阅:Java反射获取类和对象信息全解析

既然JDK提供给了我们这么多的注解相关方法,乍一看是够用了呢?为何Spring还自己写个工具类呢?我觉得这也是Spring的强大之处,往往写出来的东西比JDK的还强大。比如试想一下下面两个场景,你就没觉得疑惑?

  1. @AliasFor(Spring4.2之后才有的)为何能生效呢?大大的方便了我们注解的使用
  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

代码语言:javascript
复制
	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):
代码语言:javascript
复制
    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)都支持。参考;java中什么是bridge method(桥接方法)
  • public static Annotation[] getAnnotations(AnnotatedElement annotatedElement):获取指定类型上所有注解
代码语言:javascript
复制
    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):(其它重载方法略)
代码语言:javascript
复制
@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~~(有可能是自己,有可能是父类) 备注:接口不找
代码语言:javascript
复制
    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上指定的注解是否是继承过来的。
代码语言:javascript
复制
    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这个类型的注解
代码语言:javascript
复制
    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的注解(String的重载方法,省略)
代码语言:javascript
复制
    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吧
代码语言:javascript
复制
// 给注解增加属性、有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):获取注解内指定属性的值
代码语言:javascript
复制
    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 finding annotations, meta-annotations, and repeatable annotations(@since 4.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
代码语言:javascript
复制
    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
    }
  • 关于getMerge之类的方法,在后面讲@AliasFor的时候再详说吧

AnnotationBeanUtils:拷贝注解值到指定的Bean中

它在org.springframework.beans.annotation包下,并且这个包下只有它一个类。该类只有一个核心方法。

代码语言:javascript
复制
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注册有关)

代码语言:javascript
复制
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工厂设置了:setDependencyComparatorsetAutowireCandidateResolver
  • public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd):处理通用的Bean定义上的注解,该方法从原始bean定义的元数据中获取那些通用的注解信息:@Lazy,@DependsOn,@Role,@Description,然后设置AnnotatedBeanDefinition实例相应的属性

基本上这个工具类是Spring内部使用的,我们用不着。它在org.springframework.context.annotation包内

ConfigurationClassUtils:Spring内部处理@Configuration的工具类

这个工具类只处理@Configuration配置类的。是Full模式还是Lite模式

代码语言:javascript
复制
	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
代码语言:javascript
复制
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);
	}
}

BeanFactoryAnnotationUtils:提供与注解相关的查找Bean的方法(比如@Qualifier

讲解它之前,先看看这个工具类:BeanFactoryUtils

BeanFactoryUtils:方便在Bean工厂上操作的工具类,特别针对于ListableBeanFactory这个工厂
代码语言:javascript
复制
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
代码语言:javascript
复制
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的使用场景,它是可以直接标注在类上的。看下面例子解说:

代码语言:javascript
复制
@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 上加了此注解,但是它在Bean工厂里的BeanName可不会变。但是它在匹配的时候就能匹配上了,这就是BeanFactoryAnnotationUtils#isQualifierMatch的功劳



最后再讨论讨论注解中常常使用的AnnotationAttributes

AnnotationAttributes:Map的封装,在实际使用时会有更好的体验

它的获取一般这么来:

  • AnnotatedTypeMetadata#getAnnotationAttributes
  • AnnotationAttributes#fromMap
  • AnnotatedElementUtils#getMergedAnnotationAttributes等系列方法
  • new AnnotationAttributes
代码语言:javascript
复制
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);
	}
}
总结

本文主要讲解了一下Spirng体系里和注解相关的工具类。因为在注解驱动大行其道的今天,个人认为有必要去了解Spring解析注解的一些方式、方法等。 Spring易学难精是得以与他优秀的设计:分层、封装、扩展、包访问权限管理等等,在各处都有所体现

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年03月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • AnnotationUtils(最重要)
    • AnnotationUtils方法介绍
    • AnnotatedElementUtils:在AnnotatedElement finding annotations, meta-annotations, and repeatable annotations(@since 4.0)
    • AnnotationBeanUtils:拷贝注解值到指定的Bean中
    • AnnotationConfigUtils:(重要),和Config配置类有关的注解工具类
    • BeanFactoryAnnotationUtils:提供与注解相关的查找Bean的方法(比如@Qualifier)
      • BeanFactoryUtils:方便在Bean工厂上操作的工具类,特别针对于ListableBeanFactory这个工厂
        • BeanFactoryAnnotationUtils
          • AnnotationAttributes:Map的封装,在实际使用时会有更好的体验
            • 总结
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档