这是一个很好的问题,涉及到Spring注解的元注解处理和属性别名机制。让我详细解释一下Spring JMS是如何获取到你自定义注解中的destination值的。
核心机制:Spring的注解属性别名(Attribute Aliasing)
当你在自定义注解中使用@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 {@Overridepublic 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的处理过程:
- 发现注解:扫描到
@MyCustomJmsListener
注解 - 识别元注解:发现
@MyCustomJmsListener
被@JmsListener
元注解标记 - 属性合并:通过
@AliasFor
将value="my-queue"
映射到destination
属性 - 创建监听器:使用合成后的
@JmsListener(destination = "my-queue")
创建JMS监听容器
6. 验证方法
你可以通过以下方式验证这个机制:
@SpringBootTest
class JmsListenerTest {@Autowiredprivate JmsListenerEndpointRegistry endpointRegistry;@Testvoid 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);}}}
}
总结
Spring通过AnnotatedElementUtils
和注解属性别名机制,能够正确识别你自定义注解中通过@AliasFor
映射的属性值。这种设计使得注解组合和自定义变得非常灵活,是Spring框架强大的元编程能力的体现。