如何判断某个类是否有某个注解?

开发过程中,我们经常通过在类上打注解的方式来给它做标记,以便后期对其进行相应操作。

比如Springboot在启动的时候,会对标有@Component注解的类进行加载和初始化。

那么,如何判断某个类是否有某个注解呢?

很多人会说,这还不简单,不就是利用反射检索该类的注解集合里有没有这个注解不就好了吗。

但这样是不全面的,因为类可能继承自某个父类或者接口,而其父类或者接口上存在该注解。

同时,该注解可能是个复合注解,即该注解也是被其他注解所注解的。

所以,判断某个类是否有某个注解的完善流程应该包含4个方面:

  • 检索该类是否直接被该注解标记;
  • 递归检索该类注解的注解中是否存在目标注解;
  • 递归检索该类的接口中是否存在目标注解;
  • 递归检索该类的父类是否存在目标注解。

Spring框架的AnnotationUtils.findAnnotation方法就是基于上述流程来实现的。

private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
	try {
		// 判断该类是否被该注解直接标记,对应第①种情况
		A annotation = clazz.getDeclaredAnnotation(annotationType);
		if (annotation != null) {
			return annotation;
		}
		// 递归检索该类注解的注解中是否存在目标注解,对应第②种情况
		for (Annotation declaredAnn : clazz.getDeclaredAnnotations()) {
			Class<? extends Annotation> declaredType = declaredAnn.annotationType();
			if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
				// 进入递归
				annotation = findAnnotation(declaredType, annotationType, visited);
				if (annotation != null) {
					return annotation;
				}
			}
		}
	}
	catch (Throwable ex) {
		handleIntrospectionFailure(clazz, ex);
		return null;
	}
	// 递归检索该类的接口中是否存在目标注解,对应第③种情况
	for (Class<?> ifc : clazz.getInterfaces()) {
		A annotation = findAnnotation(ifc, annotationType, visited);
		if (annotation != null) {
			return annotation;
		}
	}
	// 递归检索该类的父类是否存在目标注解,对应第④种情况
	Class<?> superclass = clazz.getSuperclass();
	// 如果父类不存在或者父类为Object,则直接返回null
	if (superclass == null || Object.class == superclass) {
		return null;
	}
	return findAnnotation(superclass, annotationType, visited);
}

同时,AnnotationUtils会通过缓存来加快检索速度。

private static <A extends Annotation> A findAnnotation(
		Class<?> clazz, @Nullable Class<A> annotationType, boolean synthesize) {

	Assert.notNull(clazz, "Class must not be null");
	if (annotationType == null) {
		return null;
	}

	AnnotationCacheKey cacheKey = new AnnotationCacheKey(clazz, annotationType);
	// 若缓存中存在,则直接返回
	A result = (A) findAnnotationCache.get(cacheKey);
	if (result == null) {
		// 否则执行注解检索,即上面讲过的4个过程
		result = findAnnotation(clazz, annotationType, new HashSet<>());
		if (result != null && synthesize) {
			result = synthesizeAnnotation(result, clazz);
			// 将结果更新到缓存中
			findAnnotationCache.put(cacheKey, result);
		}
	}
	return result;
}

缓存是使用ConcurrentReferenceHashMap来作为容器的,其和ConcurrentHashMap不同的是,ConcurrentReferenceHashMap可以声明容器对象的引用强度,默认使用的是软连接,即当内存不足时,会被GC强制回收,避免OOM。

可以看出,仅仅判断某个类是否有某个注解这样1个简单的需求,我们想要做的完善,代码写的健壮,也不是一件简单的事情。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值