为什么写这篇文章
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 "";}
注册自定义限定符
@Beanpublic 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 {@Overridepublic 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.valuequalifier.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) {// ...}}@Overridepublic 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