前置说明
以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<T> 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<T> executeClass;
/** 有参构造器 */
public ActiveMQMessageListener(Class<T> 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<T> 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";
}
public class MarkConstant {
public static final String DOT = ".";
}
后置处理器
定义一个Bean后置处理器处理MultiJmsListener对应的方法
public class MultiJmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton {
//用以缓存方法中没有MultiJmsListener注解的
private final Set<Class<?>> 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<Method, MultiJmsListener> annotatedMethods = MethodIntrospector.selectMethods(targetClass, new MethodIntrospector.MetadataLookup<MultiJmsListener> () {
@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 <T>
*/
private <T> void processMultiListener(MultiJmsListener multiJmsListener, Method method, Class<T> 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;
}
}
测试
@Service
public class AlarmMqCustomer {
@Autowired
private AlarmMapper alarmMapper;
//注解示例代码
@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
==============================
如果想把数据存入数据库,需要注意的地方是在
AlarmMqCustomer类里是不能通过@Autowired注入对象的,必须通过手动注入。
例如:
public class AlarmMqCustomer {
@Autowired
private AlarmMapper alarmMapper;
//@JmsListener(destination = "foo.bar")
@MultiJmsListener(destination = "foo.bar",num=10)
public void getQueueMessage(String message) {
System.out.println("队列");
doMessage(message);
}
public void doMessage(String message) {
Map<String, Object> map = new HashMap<>();
//逻辑处理
。。。
if(alarmMapper == null ){
alarmMapper = (AlarmMapper) SpringContextUtil.getBean("alarmMapper");
}
alarmMapper.insertFaultAlarmInfo(map);
}
}
springboot实现手动获取bean对象,首先启动类得实现接口ApplicationListener<ContextRefreshedEvent> 并实现方法
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
SpringContextUtil.setApplicationContext(event.getApplicationContext());
}
SpringContextUtil 类代码
/**
* 获取spring容器,以访问容器中定义的其他bean
*
*/
public class SpringContextUtil {
//spring上下文
private static ApplicationContext applicationContext;
/**
*实现ApplicationContextAware接口的回调方法,设置上下文环境
*@param applicationContext
*
*/
public static void setApplicationContext(ApplicationContext applicationContext) {
if (null == SpringContextUtil.applicationContext) {
SpringContextUtil.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
*通过name获取 Bean.
*
*@param name
*@return
*
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
*通过name获取 Bean.
*
*@param clazz
*@return
*
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
*通过name,以及Clazz返回指定的Bean
*
*@param name
* @param clazz
*@return
*
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
说明
id不同表示监听线程不同
总结
方法层级的注解实现,常见的有两个方式,一种是直接通过AOP切面去实现,还有一种是通过BeanPostProcessor,在Bean初始化后,做后置处理。