不是我要一键是骂我者开一键 發表於 2025-9-23 17:40:00

springboot~获取原注解的方法findMergedAnnotation使用场景

<h2 id="一-重要知识点">一 重要知识点</h2>
<p>在Spring框架中,<code>AnnotationUtils.findAnnotation()</code>和<code>AnnotatedElementUtils.findMergedAnnotation()</code>是两种不同的注解查找方式,主要区别如下:</p>
<h3 id="1-annotationutilsfindannotation">1. <strong><code>AnnotationUtils.findAnnotation()</code></strong></h3>
<ul>
<li><strong>特点</strong>:直接查找原始注解</li>
<li><strong>局限性</strong>:
<ul>
<li>无法获取被元注解(如<code>@AliasFor</code>)覆盖的属性值</li>
<li>无法处理注解属性覆盖(Annotation Attribute Overrides)场景</li>
<li>若注解是通过元注解(如<code>@Component</code>派生出的<code>@Service</code>)间接存在,可能无法正确获取属性值</li>
</ul>
</li>
</ul>
<h3 id="2-annotatedelementutilsfindmergedannotation">2. <strong><code>AnnotatedElementUtils.findMergedAnnotation()</code></strong></h3>
<ul>
<li><strong>特点</strong>:查找"合并后的"注解</li>
<li><strong>优势</strong>:
<ul>
<li>支持Spring的注解属性覆盖机制(通过<code>@AliasFor</code>)</li>
<li>会递归处理元注解,合并属性值</li>
<li>能正确获取经过覆盖后的最终属性值</li>
<li>支持查找接口/父类上的注解(通过<code>@Inherited</code>)</li>
</ul>
</li>
</ul>
<h3 id="示例场景差异">示例场景差异</h3>
<pre><code class="language-java">@Spec(name = "defaultName") // 元注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Spec {
    String name() default "";
}

@Spec(name = "customName") // 实际使用的注解
public void someMethod() {}

// 测试结果:
AnnotationUtils.findAnnotation() -&gt; 可能返回原始元注解的name="defaultName"
AnnotatedElementUtils.findMergedAnnotation() -&gt; 会返回合并后的name="customName"
</code></pre>
<h3 id="何时使用">何时使用?</h3>
<ul>
<li>需要原始注解时 → <code>AnnotationUtils</code></li>
<li>需要实际生效的注解属性时 → <code>AnnotatedElementUtils</code></li>
<li>Spring注解处理(如<code>@Transactional</code>等组合注解) → 优先使用<code>AnnotatedElementUtils</code></li>
</ul>
<p>建议在Spring环境下优先使用<code>AnnotatedElementUtils</code>,除非明确需要访问未经处理的原始注解。</p>
<h2 id="二-spring的注解属性别名的应用">二 Spring的注解属性别名的应用</h2>
<p>当你在自定义注解中使用<code>@AliasFor</code>为<code>@JmsListener</code>的destination属性赋值时,Spring通过以下步骤处理:</p>
<h3 id="1-注解处理流程">1. 注解处理流程</h3>
<pre><code class="language-java">// 你的自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@JmsListener
public @interface MyCustomJmsListener {
   
    @AliasFor(annotation = JmsListener.class, attribute = "destination")
    String value() default "";
   
    // 其他属性...
}
</code></pre>
<h3 id="2-spring-jms的内部处理机制">2. Spring JMS的内部处理机制</h3>
<p><code>JmsListenerAnnotationBeanPostProcessor</code>是处理<code>@JmsListener</code>的核心类:</p>
<pre><code class="language-java">// 简化的处理逻辑
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();
      // 创建监听器容器...
    }
}
</code></pre>
<h3 id="3-关键方法annotatedelementutilsfindmergedannotation">3. 关键方法:AnnotatedElementUtils.findMergedAnnotation()</h3>
<p>这是Spring处理注解属性的核心方法:</p>
<pre><code class="language-java">// Spring内部的处理逻辑
public static &lt;A extends Annotation&gt; A findMergedAnnotation(
      AnnotatedElement element, Class&lt;A&gt; 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;
}
</code></pre>
<h3 id="4-属性别名解析过程">4. 属性别名解析过程</h3>
<pre><code class="language-java">private static &lt;A extends Annotation&gt; A synthesizeAnnotation(
      Annotation sourceAnnotation,
      Annotation metaAnnotation,
      AnnotatedElement element) {
   
    Map&lt;String, Object&gt; attributeMap = new HashMap&lt;&gt;();
   
    // 获取元注解的属性
    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);
}
</code></pre>
<h3 id="5-实际示例">5. 实际示例</h3>
<p>假设你的使用方式如下:</p>
<pre><code class="language-java">@Component
public class MyMessageListener {
   
    @MyCustomJmsListener("my-queue")
    public void handleMessage(String message) {
      // 处理消息
    }
}
</code></pre>
<p>三 Spring JMS的处理过程:</p>
<ol>
<li><strong>发现注解</strong>:扫描到<code>@MyCustomJmsListener</code>注解</li>
<li><strong>识别元注解</strong>:发现<code>@MyCustomJmsListener</code>被<code>@JmsListener</code>元注解标记</li>
<li><strong>属性合并</strong>:通过<code>@AliasFor</code>将<code>value="my-queue"</code>映射到<code>destination</code>属性</li>
<li><strong>创建监听器</strong>:使用合成后的<code>@JmsListener(destination = "my-queue")</code>创建JMS监听容器</li>
</ol>
<h3 id="验证方法">验证方法</h3>
<p>你可以通过以下方式验证这个机制:</p>
<pre><code class="language-java">@SpringBootTest
class JmsListenerTest {
   
    @Autowired
    private JmsListenerEndpointRegistry endpointRegistry;
   
    @Test
    void testCustomAnnotation() {
      // 检查监听器容器是否创建成功
      Collection&lt;MessageListenerContainer&gt; containers =
            endpointRegistry.getListenerContainers();
      
      for (MessageListenerContainer container : containers) {
            if (container instanceof JmsListenerEndpointRegistry) {
                // 验证destination是否正确设置
                String destination = ((AbstractJmsListenerContainer) container)
                  .getDestination();
                System.out.println("监听的destination: " + destination);
            }
      }
    }
}
</code></pre>
<h2 id="四-总结">四 总结</h2>
<p>Spring通过<code>AnnotatedElementUtils</code>和注解属性别名机制,能够正确识别你自定义注解中通过<code>@AliasFor</code>映射的属性值。这种设计使得注解组合和自定义变得非常灵活,是Spring框架强大的元编程能力的体现。</p>


</div>
<div id="MySignature" role="contentinfo">
    <p></p>
<div class="navgood">
<p>作者:仓储大叔,张占岭,<br>
荣誉:微软MVP<br>QQ:853066980</p>

<p><strong>支付宝扫一扫,为大叔打赏!</strong>
<br><img src="https://images.cnblogs.com/cnblogs_com/lori/237884/o_IMG_7144.JPG"></p>
</div><br><br>
来源:https://www.cnblogs.com/lori/p/19107625
頁: [1]
查看完整版本: springboot~获取原注解的方法findMergedAnnotation使用场景