前置说明
- 以
spring-boot-starter-activemq
为例子来说明
springboot
提供JmsListener
用以支持对消息的监听。但有些时候为了提高消费效率,需要对同一个queue
或者topic
使用多个监听器进行消费。而通过JmsListener
注解,只能支持单线程消费,如果要做多个消费者,需要多次使用JmsListener
,代码如下:
/**
* 单线程监听Queue
* @param message
*/
@JmsListener(destination = "test.queue1")
public void consumer4(String message) {
System.out.println("==queue消息:" + message);
}
/**
* 单线程监听Queue
* @param message
*/
@JmsListener(destination = "test.queue1")
public void consumer5(String message) {
System.out.println("==queue消息:" + message);
}
- 本文基于
springboot-2.1.4
自定义注解MultiJmsListener
- 本文采用
spring-boot-starter-activemq
整合activeMQ
源码清单
MultiJmsListener
注解
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@MessageMapping
public @interface MultiJmsListener {
//用以定义监听器beanName的前缀(保留字段)
String prefixId() default "";
String destination();
//定义监听线程的个数
int num() default 1;
//定义消息的ContainerFactory,实际值参考ActiveMQConstant
String containerFactory() default "";
}
传统消息监听器
public class ActiveMQMessageListener implements MessageListener {
private final Logger logger = LoggerFactory.getLogger(ActiveMQMessageListener.class);
//消息监听ID
private int id;
//执行方法
private Method executeMethod;
//执行类
private Object executeObject;
//监听目标
private String dest;
private Class executeClass;
/** 有参构造器 */
public ActiveMQMessageListener(Class executeClass, Method executeMethod, int id, String dest) {
this.executeMethod = executeMethod;
this.dest = dest;
this.id = id;
init(executeClass);
this.executeClass = executeClass;
}
/**
* 初始化操作
* @param executeClass
*/
private void init(Class executeClass) {
try {
this.executeObject = executeClass.newInstance();
} catch (Exception e) {
throw new JmsException("方法反射异常:", e);
}
}
public ActiveMQMessageListener() {}
@Override
public void onMessage(Message message) {
if (logger.isDebugEnabled()) {
logger.debug("监听器[{}]监听到消息", id);
}
TextMessage textMessage = (TextMessage) message;
try {
//反射的方式调用方法,用以执行实际消费流程
executeMethod.invoke(executeObject, textMessage.getText());
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error("消息监听异常:", e);
}
throw new JmsException("消息监听异常:", e);
}
}
//省略掉get/set方法
}
自动配置
- 自动配置类,引入
JmsListenerContainerFactory
用以同时支持Queue
和Topic
/**
* 配置topic模式下的JmsListenerContainerFactory
* @param activeMQConnectionFactory
* @return
*/
@Bean(name = ActiveMQConsumerConstant.CONTAINER_FACTORY_TOPIC)
public JmsListenerContainerFactory> jmsListenerContainerTopic(ConnectionFactory activeMQConnectionFactory) {
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
//支持Topic模式
bean.setPubSubDomain(true);
bean.setConnectionFactory(activeMQConnectionFactory);
FrameworkAutoConfigureManager.addAutoConfigureBeanMap("jmsListenerContainerTopic", JmsListenerContainerFactory.class);
return bean;
}
/**
* 配置queue模式下的JmsListenerContainerFactory
* @param activeMQConnectionFactory
* @return
*/
@Bean(name = ActiveMQConsumerConstant.CONTAINER_FACTORY_QUEUE)
public JmsListenerContainerFactory> jmsListenerContainerQueue(ConnectionFactory activeMQConnectionFactory) {
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
bean.setConnectionFactory(activeMQConnectionFactory);
FrameworkAutoConfigureManager.addAutoConfigureBeanMap("jmsListenerContainerQueue", JmsListenerContainerFactory.class);
return bean;
}
/**
* 用以支持MultiJmsListener注解的后置处理器
* @return
*/
@Bean
public MultiJmsListenerAnnotationBeanPostProcessor multiJmsListenerAnnotationBeanPostProcessor() {
MultiJmsListenerAnnotationBeanPostProcessor multiJmsBeanPostProcessor =
new MultiJmsListenerAnnotationBeanPostProcessor();
FrameworkAutoConfigureManager.addAutoConfigureBeanMap("multiJmsListenerAnnotationBeanPostProcessor", MultiJmsListenerAnnotationBeanPostProcessor.class);
return multiJmsBeanPostProcessor;
}
常量类
- 定义
ActiveMQConsumerConstant
常量,用以配置ContainerFactory
对应的BeanName
public interface ActiveMQConsumerConstant {
/** Queue模式下的JmsListenerContainerFactory的Bean名称 */
String CONTAINER_FACTORY_QUEUE = "jmsListenerContainerQueue";
/** Topic模式下的JmsListenerContainerFactory的Bean名称 */
String CONTAINER_FACTORY_TOPIC = "jmsListenerContainerTopic";
}
后置处理器
- 定义一个
Bean
后置处理器处理MultiJmsListener
对应的方法
public class MultiJmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton {
//用以缓存方法中没有MultiJmsListener注解的
private final Set> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));
//JMS 注册机
private JmsListenerEndpointRegistrar jmsListenerEndpointRegistrar = new JmsListenerEndpointRegistrar();
private BeanFactory beanFactory;
@Autowired
public void setEndpointRegistry(JmsListenerEndpointRegistry endpointRegistry) {
jmsListenerEndpointRegistrar.setEndpointRegistry(endpointRegistry);
}
@Autowired
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
jmsListenerEndpointRegistrar.setBeanFactory(beanFactory);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//特殊bean过滤
if((bean instanceof AopInfrastructureBean)
|| (bean instanceof JmsListenerContainerFactory)
|| (bean instanceof JmsListenerEndpointRegistry)) {
return bean;
}
//获取bean对象的最终类型
Class> targetClass = AopProxyUtils.ultimateTargetClass(bean);
if (this.nonAnnotatedClasses.contains(targetClass)) {
return bean;
}
//选出该bean中包含MultiJmsListener注解的方法
Map annotatedMethods = MethodIntrospector.selectMethods(targetClass, new MethodIntrospector.MetadataLookup () {
@Override
public MultiJmsListener inspect(Method method) {
return AnnotationUtils.getAnnotation(method, MultiJmsListener.class);
}
});
// 若当前类中未找到含该注解的方法,直接return bean
if (annotatedMethods.isEmpty()) {
this.nonAnnotatedClasses.add(targetClass);
return bean;
}
//对每个方法做处理,添加监听
annotatedMethods.forEach((method, multiJmsListener) -> processMultiListener(multiJmsListener, method, targetClass));
//返回bean对象
return bean;
}
/*
* 提交监听器到进程
* @param multiJmsListener 注解
* @param method 方法
* @param targetClass bean对应的class类
* @param
*/
private void processMultiListener(MultiJmsListener multiJmsListener, Method method, Class targetClass) {
//线程数
int threadNum = multiJmsListener.num();
if (threadNum <= 0) {
numthreadNum= 1;
}
//for循环
for (int i = 1; i <= threadNum; i ++) {
//多少个线程数,开启多少个监听
ActiveMQMessageListener activeMQMessageListener = new ActiveMQMessageListener<>(targetClass, method, i, multiJmsListener.destination());
//将监听器注册到JMSEndpointRegistrar
register(multiJmsListener, activeMQMessageListener);
}
}
/*
* 向JMSEndpointRegistrar注册Endpoint
* @param activeMQMessageListener 消息监听器
*/
private void register(MultiJmsListener multiJmsListener, ActiveMQMessageListener activeMQMessageListener) {
SimpleJmsListenerEndpoint simpleJmsListenerEndpoint = new SimpleJmsListenerEndpoint();
//endpointId = 类名+方法名+id
simpleJmsListenerEndpoint.setId(buildEndpointId(activeMQMessageListener));
simpleJmsListenerEndpoint.setDestination(activeMQMessageListener.getDest());
simpleJmsListenerEndpoint.setMessageListener(activeMQMessageListener);
jmsListenerEndpointRegistrar.setBeanFactory(this.beanFactory);
//不同的模式,引用的containerFactory不同
String containFactory = multiJmsListener.containerFactory();
if (StringUtils.isBlank(containFactory)) {
//默认使用Queue
containFactory = ActiveMQConsumerConstant.CONTAINER_FACTORY_QUEUE;
}
JmsListenerContainerFactory> factory = (JmsListenerContainerFactory)this.beanFactory.getBean(containFactory, JmsListenerContainerFactory.class);
jmsListenerEndpointRegistrar.setContainerFactoryBeanName(ActiveMQConsumerConstant.CONTAINER_FACTORY_QUEUE);
jmsListenerEndpointRegistrar.registerEndpoint(simpleJmsListenerEndpoint, factory);
}
/*
* 生成端点Endpoint的ID
* @param activeMQMessageListener
* @return
*/
private String buildEndpointId(ActiveMQMessageListener activeMQMessageListener) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(activeMQMessageListener.getExecuteClass().getName())
.append(MarkConstant.DOT)
.append(activeMQMessageListener.getExecuteMethod().getName())
.append(activeMQMessageListener.getId());
return stringBuilder.toString();
}
/**
* 在类初始化后,执行后续操作
*/
@Override
public void afterSingletonsInstantiated() {
//执行registrar的后置操作
jmsListenerEndpointRegistrar.afterPropertiesSet();
}
@Override
public int getOrder() {
return Integer.MAX_VALUE;
}
}
测试
注解示例代码
@MultiJmsListener(destination = "test.queue0", num = 4)
public void consumer(String message) {
System.out.println("==============================");
System.out.println(message);
System.out.println("==============================");
}
输出结果
测试queue
==============================
id=2
==============================
测试queue
==============================
id=1
==============================
测试queue
==============================
id=3
==============================
测试queue
==============================
总结
- 方法层级的注解实现,常见的有两个方式,一种是直接通过
AOP
切面去实现,还有一种是通过BeanPostProcessor
,在Bean
初始化后,做后置处理。