为什么写这篇文章
Spring 支持类型注入,并且可以通过Qualifier 或者Mate 调整类型注入的范围。但是通过自定义注解结合现有的 @Qualifier 使用起来有种种困难。
- 将 @Qualifier 融合在自定义注解中,在使用 @AliasFor 遇到问题
- 仅仅检查注解中的一部分内容是否匹配即可,Spring 不支持
添加自定义 Qualifier
package ann;
import java.lang.annotation.*;
/**
* @author Jay
*/
@Target({ElementType.PARAMETER,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandlerSelect {
String value() default "";
}
组合 Qualifier 注解
package ann;
import java.lang.annotation.*;
/**
* @author Jay
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MessageHandlerAnn {
HandlerSelect qualifier();
/**
* 通过SpEL 表达式计算 #messageExt 的相关信息是否满足所有条件
*
* @return
*/
String[] eLFilter() default "";
}
注册自定义限定符
@Bean
public CustomAutowireConfigurer customAutowireConfigurer() {
CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
configurer.setCustomQualifierTypes(Collections.singleton(HandlerSelect.class));
return configurer;
}
自定义注解处理工厂处理器
package config;
import ann.HandlerSelect;
import ann.MessageHandlerAnn;
import message.handler.MessageHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.stereotype.Component;
/**
* @author Jay
*/
@Component
public class HandlerSelectQualifierProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 检查 实现了 MessageHandler 接口的 SpringBean 检查其是否标注有 MessageHandlerAnn 注解
String[] beanNamesForType = beanFactory.getBeanNamesForType(MessageHandler.class);
for (String beanName : beanNamesForType) {
// 获取 Bean 定义对象
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
Class<?> beanClass = beanFactory.getType(beanName);
// 检查是否有 MessageHandlerAnn 注解
if (beanClass != null && beanClass.isAnnotationPresent(MessageHandlerAnn.class)) {
// 创建并添加自定义的 qualifier 元素
addCustomQualifier((AbstractBeanDefinition) beanDefinition, beanClass);
}
}
}
private static void addCustomQualifier(AbstractBeanDefinition beanDefinition, Class<?> beanClass) {
// 创建 AutowireCandidateQualifier 实例(HandlerSelect 已经加入到 QualifierType 中)
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(HandlerSelect.class);
// 设置 qualifier 的值,例如:
HandlerSelect qualifierValue = beanClass.getAnnotation(MessageHandlerAnn.class).qualifier();
// Attribute 的 Key 固定为 value, 其 value 值取自 HandlerSelect.qualifier.value
qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, qualifierValue.value());
// TODO 以上处理仅考虑 HandlerSelect 仅仅有一个限定描述符的场景。 如果需要拓展 HandlerSelect 的属性, 需要重新设计以上代码
// 添加限定符
beanDefinition.addQualifier(qualifier);
}
}
MyMessageListener & MessageHandler
public interface MessageHandler {
}
public class MyMessageListener implements MessageListenerConcurrently, InitializingBean {
final private List<MessageHandler<?>> messageHandlerList;
public MyMessageListener(List<MessageHandler<?>> messageHandlerList) {
this.messageHandlerList = messageHandlerList;
}
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt messageExt : msgs) {
// ...
}
}
@Override
public void afterPropertiesSet() {
System.out.println("MyMessageListener afterPropertiesSet");
// messageHandlerList 不能为空
if (ObjectUtils.isEmpty(this.messageHandlerList)) {
log.error("messageHandlerList is empty");
throw new RuntimeException("messageHandlerList is empty");
} else {
messageHandlerList.forEach(messageHandler -> {
System.out.println(messageHandler.getClass().getName());
});
}
}
}
配置&效果
待完善的地方
- 用于解析 @HandlerSelect 的 HandlerSelectQualifierProcessor 不能随着 HandlerSelect 的属性增加而 解析属性并增加对应的限定符
- IDEA 工具无法预估 使用自定义的限定符号 还有哪些Bean
(截图红框显示为三个, 但实际myMessageListener0 依赖注入了T0T0、ToT1 而myMessageListener1 依赖注入了T1T0 )
相关博客: rocketmq Listener 还可以这样配置(基于SPEL\限定符号\自动连线)http://t.csdnimg.cn/sE1Dk