自定义注解那些事
文章目录
一、自定义注解
- Operate
package com.example.demo.enumeration;
public enum Operate {
INSERT, DELETE, UPDATE, SELECT, OTHER;
}
- LogPoint
package com.example.demo.annotation;
import com.example.demo.enumeration.Operate;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface LogPoint {
/**
* 模块
*
* @return 模块
*/
String module();
/**
* 操作类型
*
* @return 操作类型
*/
Operate type() default Operate.OTHER;
}
二、自定义注解可以被继承吗?
注解 | 无Inherited元注解 | 有Inherited元注解 |
---|---|---|
父类注解 | ❌ | ✔️ |
父类方法注解-重写父类方法(含抽象类抽象方法实现) | ❌ | ❌ |
父类方法注解-不重写父类方法① | ✔️ | ✔️ |
接口注解 | ❌ | ❌ |
接口方法注解 | ❌ | ❌ |
接口方法注解(接口默认方法default)同①情况 | ✔️ | ✔️ |
注:以上能否获取到父类中的注解,指的是通过常规的方式获取,如aClass.getAnnotation(LogPoint.class)
、method.getAnnotation(LogPoint.class)
。
三、动态代理会导致自定义注解丢失吗?
注解 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
类注解(实现类) | 不管自定义注解包不包含Inherited元注解,常规方法、AnnotationUtils 、AnnotatedElementUtils 工具类均无法获取自定义注解。❌ | 若自定义注解包含Inherited元注解,常规方法可获取,若不包含Inherited元注解常规方法无法获取,通过AnnotationUtils 、AnnotatedElementUtils 工具类可获取。✔️❌ |
方法注解(实现类) | 不管自定义注解包不包含Inherited元注解,常规方法、AnnotationUtils 、AnnotatedElementUtils 工具类均无法获取自定义注解。❌ | 不管自定义注解包不包含Inherited元注解,常规方法均无法获取,通过AnnotationUtils 、AnnotatedElementUtils 工具类可获取。✔️❌ |
类注解(接口类) | 不管自定义注解包不包含Inherited元注解,常规方法无法获取,通过AnnotationUtils 、AnnotatedElementUtils 工具类可获取。✔️❌ | 不管自定义注解包不包含Inherited元注解,常规方法无法获取,通过AnnotationUtils 、AnnotatedElementUtils 工具类可获取。✔️❌ |
方法注解(接口类) | 不管自定义注解包不包含Inherited元注解,常规方法无法获取,通过AnnotationUtils 、AnnotatedElementUtils 工具类可获取。✔️❌ | 不管自定义注解包不包含Inherited元注解,常规方法无法获取,通过AnnotationUtils 、AnnotatedElementUtils 工具类可获取。✔️❌ |
1.获取自定义注解的可靠方式
- 类注解
final Class<? extends TestService> aClass = testService.getClass();
final LogPoint annotation = AnnotatedElementUtils.findMergedAnnotation(aClass, LogPoint.class);
- 方法注解
final Class<? extends TestService> aClass = testService.getClass();
final Method method = aClass.getMethod("test");
final LogPoint annotation = AnnotatedElementUtils.findMergedAnnotation(method, LogPoint.class);
2.AnnotatedElementUtils工具类源码解析
- 调用链
- 核心代码
@Nullable
private static <C, R> R processClassHierarchy(C context, int[] aggregateIndex, Class<?> source,
AnnotationsProcessor<C, R> processor, boolean includeInterfaces, boolean includeEnclosing) {
try {
R result = processor.doWithAggregate(context, aggregateIndex[0]);
if (result != null) {
return result;
}
if (hasPlainJavaAnnotationsOnly(source)) {
return null;
}
Annotation[] annotations = getDeclaredAnnotations(source, false);
result = processor.doWithAnnotations(context, aggregateIndex[0], source, annotations);
if (result != null) {
return result;
}
aggregateIndex[0]++;
if (includeInterfaces) {
for (Class<?> interfaceType : source.getInterfaces()) {
R interfacesResult = processClassHierarchy(context, aggregateIndex,
interfaceType, processor, true, includeEnclosing);
if (interfacesResult != null) {
return interfacesResult;
}
}
}
Class<?> superclass = source.getSuperclass();
if (superclass != Object.class && superclass != null) {
R superclassResult = processClassHierarchy(context, aggregateIndex,
superclass, processor, includeInterfaces, includeEnclosing);
if (superclassResult != null) {
return superclassResult;
}
}
if (includeEnclosing) {
// Since merely attempting to load the enclosing class may result in
// automatic loading of sibling nested classes that in turn results
// in an exception such as NoClassDefFoundError, we wrap the following
// in its own dedicated try-catch block in order not to preemptively
// halt the annotation scanning process.
try {
Class<?> enclosingClass = source.getEnclosingClass();
if (enclosingClass != null) {
R enclosingResult = processClassHierarchy(context, aggregateIndex,
enclosingClass, processor, includeInterfaces, true);
if (enclosingResult != null) {
return enclosingResult;
}
}
}
catch (Throwable ex) {
AnnotationUtils.handleIntrospectionFailure(source, ex);
}
}
}
catch (Throwable ex) {
AnnotationUtils.handleIntrospectionFailure(source, ex);
}
return null;
}
3.AnnotationUtils和AnnotatedElementUtils工具类区别
常用方法 | 搜索范围 | 属性合并① |
---|---|---|
AnnotationUtils#getAnnotation(java.lang.reflect.AnnotatedElement, java.lang.Class) | SearchStrategy.INHERITED_ANNOTATIONS(基本等同于JDK常规获取方式,但功能更强) | 否 |
AnnotationUtils#findAnnotation(java.lang.Class<?>, java.lang.Class) | SearchStrategy.INHERITED_ANNOTATIONS(基本等同于JDK常规获取方式,但功能更强) | 否 |
AnnotatedElementUtils#getMergedAnnotation | SearchStrategy.TYPE_HIERARCHY(包含父类及接口的注解) | 是 |
AnnotatedElementUtils#findMergedAnnotation | SearchStrategy.TYPE_HIERARCHY(包含父类及接口的注解) | 是 |
注:属性合并=>org.springframework.core.annotation.MergedAnnotations
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping {
@AliasFor(attribute = "path")
String[] value() default {};
@AliasFor(attribute = "value")
String[] path() default {};
}
四、扩展
1.Controller可增加事务注解吗?
五、参考
Java元注解与Spring组合注解使用
【小家Spring】Spring贡献的多个注解相关的工具类:AnnotationUtils、AnnotatedElementUtils、AnnotationConfigUtils…