springboot~获取原注解的方法findMergedAnnotation使用场景
  • 特点:直接查找原始注解
  • 局限性
    • 无法获取被元注解(如@AliasFor)覆盖的属性值
    • 无法处理注解属性覆盖(Annotation Attribute Overrides)场景
    • 若注解是通过元注解(如@Component派生出的@Service)间接存在,可能无法正确获取属性值

2.AnnotatedElementUtils.findMergedAnnotation()

  • 特点:查找"合并后的"注解
  • 优势
    • 支持Spring的注解属性覆盖机制(通过@AliasFor
    • 会递归处理元注解,合并属性值
    • 能正确获取经过覆盖后的最终属性值
    • 支持查找接口/父类上的注解(通过@Inherited

示例场景差异

@Spec(name = "defaultName") // 元注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Spec { String name() default ""; } @Spec(name = "customName") // 实际使用的注解 public void someMethod() {} // 测试结果: AnnotationUtils.findAnnotation() -> 可能返回原始元注解的name="defaultName" AnnotatedElementUtils.findMergedAnnotation() -> 会返回合并后的name="customName"

何时使用?

  • 需要原始注解时 →AnnotationUtils
  • 需要实际生效的注解属性时 →AnnotatedElementUtils
  • Spring注解处理(如@Transactional等组合注解) → 优先使用AnnotatedElementUtils

建议在Spring环境下优先使用AnnotatedElementUtils,除非明确需要访问未经处理的原始注解。

二 Spring的注解属性别名的应用

当你在自定义注解中使用@AliasFor@JmsListener的destination属性赋值时,Spring通过以下步骤处理:

1. 注解处理流程

// 你的自定义注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @JmsListener public @interface MyCustomJmsListener { @AliasFor(annotation = JmsListener.class, attribute = "destination") String value() default ""; // 其他属性... }

2. Spring JMS的内部处理机制

JmsListenerAnnotationBeanPostProcessor是处理@JmsListener的核心类:

// 简化的处理逻辑 public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) { // 扫描bean的方法,查找JmsListener注解 for (Method method : bean.getClass().getMethods()) { // 这里会使用Spring的AnnotationUtils找到注解 JmsListener jmsListener = AnnotatedElementUtils.findMergedAnnotation( method, JmsListener.class); if (jmsListener != null) { processJmsListener(jmsListener, method, bean); } } return bean; } private void processJmsListener(JmsListener jmsListener, Method method, Object bean) { // 获取destination值 String destination = jmsListener.destination(); // 创建监听器容器... } }

3. 关键方法:AnnotatedElementUtils.findMergedAnnotation()

这是Spring处理注解属性的核心方法:

// Spring内部的处理逻辑 public static <A extends Annotation> A findMergedAnnotation( AnnotatedElement element, Class<A> annotationType) { // 1. 查找直接或元注解 Annotation[] annotations = element.getAnnotations(); for (Annotation ann : annotations) { // 2. 如果是目标注解直接返回 if (annotationType.isInstance(ann)) { return (A) ann; } // 3. 递归处理元注解 Annotation[] metaAnnotations = ann.annotationType().getAnnotations(); for (Annotation metaAnn : metaAnnotations) { if (annotationType.isInstance(metaAnn)) { // 4. 处理属性别名映射 return synthesizeAnnotation(ann, metaAnn, element); } } } return null; }

4. 属性别名解析过程

private static <A extends Annotation> A synthesizeAnnotation( Annotation sourceAnnotation, Annotation metaAnnotation, AnnotatedElement element) { Map<String, Object> attributeMap = new HashMap<>(); // 获取元注解的属性 Method[] metaMethods = metaAnnotation.annotationType().getDeclaredMethods(); for (Method metaMethod : metaMethods) { String attributeName = metaMethod.getName(); // 检查源注解是否有对应的别名属性 Method sourceMethod = findAliasMethod(sourceAnnotation, attributeName); if (sourceMethod != null) { // 使用源注解的值覆盖元注解的值 Object value = invokeMethod(sourceMethod, sourceAnnotation); attributeMap.put(attributeName, value); } else { // 使用元注解的默认值 Object value = invokeMethod(metaMethod, metaAnnotation); attributeMap.put(attributeName, value); } } // 创建合成注解 return AnnotationUtils.synthesizeAnnotation(attributeMap, metaAnnotation.annotationType(), element); }

5. 实际示例

假设你的使用方式如下:

@Component public class MyMessageListener { @MyCustomJmsListener("my-queue") public void handleMessage(String message) { // 处理消息 } }

三 Spring JMS的处理过程:

  1. 发现注解:扫描到@MyCustomJmsListener注解
  2. 识别元注解:发现@MyCustomJmsListener@JmsListener元注解标记
  3. 属性合并:通过@AliasForvalue="my-queue"映射到destination属性
  4. 创建监听器:使用合成后的@JmsListener(destination = "my-queue")创建JMS监听容器

验证方法

你可以通过以下方式验证这个机制:

@SpringBootTest class JmsListenerTest { @Autowired private JmsListenerEndpointRegistry endpointRegistry; @Test void testCustomAnnotation() { // 检查监听器容器是否创建成功 Collection<MessageListenerContainer> containers = endpointRegistry.getListenerContainers(); for (MessageListenerContainer container : containers) { if (container instanceof JmsListenerEndpointRegistry) { // 验证destination是否正确设置 String destination = ((AbstractJmsListenerContainer) container) .getDestination(); System.out.println("监听的destination: " + destination); } } } }