Spring整合RocketMQ

RocketMQ在使用的时候需要定义一些列的信息,然后注册一个MessageListener,不是很方便。能不能通过Spring整合RocketMQ,像整合RabbitMQ,Kafka这样通过注解@RabbitListener,@KafkaListener的标识在方法上的形式直接生成一个客户端,简单方便。

有了这样的想法,稍微构思了下花了一天多的时间写了一个初版,具备了基本的功能,通过注解@RocketListener标识方法的形式直接生成一个消费者客户端,项目运行后能订阅指定的Broker消费消息。花的时间不多,写的仓促,如果以后有大的调整或者改进会重新放出。

下面看整合的代码结构:
Spring整合RocketMQ_第1张图片

红框内是整合的代码,另外两个类和一个配置文件是使用的形式。

下面逐个的看类的结构和相关的说明:

EnableRocket:引入RocketMQ整合框架的注解,在配置类上添加此注解就启动了整合解析,最主要的作用就是引入了整合的配置类RocketBootstrapConfiguration。

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.bob.intergrate.rocket.config.RocketBootstrapConfiguration;
import org.springframework.context.annotation.Import;

/**
 * RocketMQ整合注解
 *
 * @author wb-jjb318191
 * @create 2018-03-20 9:23
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Import(RocketBootstrapConfiguration.class)
public @interface EnableRocket {
}

RocketListener :标识方法的注解,将指定方法标识为一个RocketMQ的消费器,框架启动时将此方法封装成一个MessageListener,配合注解上的信息和环境配置生成一个Consumer。注解上的可配置选项可以根据实际需要自行添加,在解析时相应加上就可以了,看流程很容易明白的。

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.apache.rocketmq.common.consumer.ConsumeFromWhere;

import static org.apache.rocketmq.common.consumer.ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET;

/**
 * RocketMQ消费者监听
 *
 * @author wb-jjb318191
 * @create 2018-03-20 9:19
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface RocketListener {

    /**
     * 消费者组
     *
     * @return
     */
    String consumerGroup();

    /**
     * @return
     */
    String topic();

    /**
     * @return
     */
    String tag() default "*";

    /**
     * @return
     */
    String namesrvAddr() default "";

    /**
     * 从哪里开始消费
     *
     * @return
     */
    ConsumeFromWhere consumeFromWhere() default CONSUME_FROM_LAST_OFFSET;

}

RocketBootstrapConfiguration:Spring整合RocketMQ的配置类,定义了3个Bean,在Spring启动过程中依次发挥作用。

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;

/**
 * Rocket整合配置类
 *
 * @author wb-jjb318191
 * @create 2018-03-20 9:24
 */
@Configuration
public class RocketBootstrapConfiguration {

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public RocketListenerAnnotationPostProcessor rocketListenerAnnotationProcessor() {
        return new RocketListenerAnnotationPostProcessor();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public RocketListenerAnnotationBeanPostProcessor rocketListenerAnnotationBeanPostProcessor() {
        return new RocketListenerAnnotationBeanPostProcessor();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public RocketConsumerLifecycleProcessor rocketConsumerLifecycleProcessor() {
        return new RocketConsumerLifecycleProcessor();
    }

}

RocketListenerAnnotationPostProcessor:检测BeanClass中是否含有@RocketListener标识的方法,若有则注册一个DefaultMQPushConsumer类型的BeanDefinition,提取注解上的信息放入其中,以备后续实例化Bean时使用。

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.bob.intergrate.rocket.ann.RocketListener;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.MethodIntrospector.MetadataLookup;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.CONSUMER_GROUP;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.CONSUME_BEAN_NAME;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.CONSUME_FROM_WHERE;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.CONSUME_METHOD;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.NAMESRV_ADDR;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.ROCKETMQ_CONSUMER_BEAN_NAME_SUFFIX;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.TAG;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.TOPIC;

/**
 * {@link RocketListener}注解解析器
 *
 * @author wb-jjb318191
 * @create 2018-03-20 9:25
 */
public class RocketListenerAnnotationPostProcessor implements BeanDefinitionRegistryPostProcessor {

    private static final Logger LOGGER = LoggerFactory.getLogger(RocketListenerAnnotationPostProcessor.class);

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        String[] beanDefinitionNames = beanDefinitionRegistry.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(name);
            Class beanClass;
            try {
                beanClass = resolveBeanClass(beanDefinition, beanDefinitionRegistry);
            } catch (ClassNotFoundException e) {
                throw new BeanDefinitionStoreException(String.format("解析[%s]BeanDefinition出现异常", beanDefinition.toString()), e);
            }
            Map annotatedMethods = introspectorRocketAnnotatedMethod(beanClass);
            if (annotatedMethods.isEmpty()) {
                continue;
            }
            for (Map.Entry entry : annotatedMethods.entrySet()) {
                RocketListener listener = entry.getValue();
                BeanDefinition rocketConsumer = new RootBeanDefinition(DefaultMQPushConsumer.class);
                //让@RocketListener的定义Bean先实例化
                rocketConsumer.setDependsOn(beanDefinition.getFactoryBeanName());
                MutablePropertyValues mpv = rocketConsumer.getPropertyValues();
                mpv.add(CONSUMER_GROUP, listener.consumerGroup());
                mpv.add(TOPIC, listener.topic());
                mpv.add(TAG, listener.tag());
                mpv.add(NAMESRV_ADDR, listener.namesrvAddr());
                mpv.add(CONSUME_BEAN_NAME, name);
                mpv.add(CONSUME_METHOD, entry.getKey());
                mpv.add(CONSUME_FROM_WHERE, listener.consumeFromWhere());
                //定义消费者Bean的名称格式为:factoryMethodName + RocketConsumer
                beanDefinitionRegistry.registerBeanDefinition(buildRocketConsumerBeanName(entry.getKey().getName()), rocketConsumer);
                LOGGER.info("注册[{}]标识的[{}]方法为RocketMQ Push消费者", RocketListener.class.getSimpleName(), entry.getKey().toString());
            }
        }
    }

    /**
     * 内省{@link RocketListener}标识的方法
     *
     * @param beanClass
     * @return
     */
    private Map introspectorRocketAnnotatedMethod(Class beanClass) {
        return MethodIntrospector.selectMethods(beanClass,
            (MetadataLookup)(method) -> method.getAnnotation(RocketListener.class));
    }

    /**
     * 解析Bean Class对象
     *
     * @param beanDefinition
     * @param beanDefinitionRegistry
     * @return
     * @throws ClassNotFoundException
     */
    private Class resolveBeanClass(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) throws ClassNotFoundException {
        String beanClassName = beanDefinition.getBeanClassName();
        Class beanClass = null;
        if (beanClassName != null) {
            beanClass = resolveClass(beanClassName);
        } else if (beanDefinition.getFactoryMethodName() != null) {
            String factoryMethodName = beanDefinition.getFactoryMethodName();
            String factoryBeanClassName = beanDefinitionRegistry.getBeanDefinition(beanDefinition.getFactoryBeanName()).getBeanClassName();
            Class factoryBeanClass = resolveClass(factoryBeanClassName);
            Set candidateMethods = new HashSet<>();
            ReflectionUtils.doWithMethods(factoryBeanClass,
                candidateMethods::add,
                (method) -> method.getName().equals(factoryMethodName) && method.isAnnotationPresent(Bean.class)
            );
            Assert.state(candidateMethods.size() == 1, String.format("[%s]类中标识@Bean的方法[%s]不止一个", factoryBeanClass.getName(), factoryMethodName));
            beanClass = candidateMethods.iterator().next().getReturnType();
        }
        Assert.notNull(beanClass, String.format("解析[%s]BeanDefinition出现异常,BeanClass解析失败", beanDefinition.toString()));
        return beanClass;

    }

    /**
     * @param className
     * @return
     */
    private Class resolveClass(String className) throws ClassNotFoundException {
        Class beanClass;
        try {
            beanClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            LOGGER.error("不存在[{}]相应的Class", className);
            throw e;
        }
        return beanClass;
    }

    /**
     * 构建{@link RocketListener}定义形式的Consumer的Bean的名称
     *
     * @param factoryMethodName
     * @return
     */
    private String buildRocketConsumerBeanName(String factoryMethodName) {
        return factoryMethodName + ROCKETMQ_CONSUMER_BEAN_NAME_SUFFIX;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

}

RocketListenerAnnotationBeanPostProcessor:提取之前放入BeanDefinition中的@RocketListener的注解信息,填充如DefaultMQPushConsumer的Bean中。

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

import com.bob.intergrate.rocket.ann.RocketListener;
import com.bob.intergrate.rocket.listener.RocketMessageListener;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.core.MethodIntrospector;
import org.springframework.util.StringUtils;

import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.CONSUMER_GROUP;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.CONSUME_BEAN_NAME;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.CONSUME_FROM_WHERE;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.CONSUME_METHOD;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.NAMESRV_ADDR;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.TAG;
import static com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant.TOPIC;

/**
 * @author wb-jjb318191
 * @create 2018-03-20 14:01
 */
public class RocketListenerAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(RocketListenerAnnotationBeanPostProcessor.class);

    @Autowired
    private ConfigurableBeanFactory beanFactory;

    /**
     * 初始化消费者属性
     *
     * @see RocketListener
     */
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        if (bean instanceof DefaultMQPushConsumer) {
            DefaultMQPushConsumer consumer = (DefaultMQPushConsumer)bean;
            consumer.setConsumerGroup(getStringProperty(pvs, CONSUMER_GROUP));
            String namesrvAddr = getStringProperty(pvs, NAMESRV_ADDR);
            if (StringUtils.hasText(namesrvAddr)) {
                consumer.setNamesrvAddr(namesrvAddr);
            }
            consumer.setConsumeFromWhere(getProperty(pvs, CONSUME_FROM_WHERE, ConsumeFromWhere.class));
            //订阅信息
            String topic = getStringProperty(pvs, TOPIC);
            String tag = getStringProperty(pvs, TAG);
            try {
                consumer.subscribe(topic, tag);
            } catch (MQClientException e) {
                throw new BeanCreationException(String.format("订阅基于Topic:[%s],Tag:[%s]的RocketMQ消费者创建失败", topic, tag));
            }
            Object consumeBean = beanFactory.getBean(getStringProperty(pvs, CONSUME_BEAN_NAME));
            Method consumeMethod = getProperty(pvs, CONSUME_METHOD, Method.class);
            if (AopUtils.isAopProxy(consumeBean)) {
                consumeMethod = MethodIntrospector.selectInvocableMethod(consumeMethod, consumeBean.getClass());
            }
            consumer.registerMessageListener(new RocketMessageListener(consumeBean, consumeMethod));
            LOGGER.info("订阅基于ConsumeGroup:[{}],Topic:[{}],Tag:[{}]的RocketMQ消费者创建成功", consumer.getConsumerGroup(), topic, tag);
            return null;
        }
        return super.postProcessPropertyValues(pvs, pds, bean, beanName);
    }

    /**
     * 解析配置,如果配置以"$"开始,则从Properties文件中查找相应的值
     *
     * @param pvs
     * @param propertyName
     * @return
     */
    private String getStringProperty(PropertyValues pvs, String propertyName) {
        String value = (String)pvs.getPropertyValue(propertyName).getValue();
        if (value.startsWith("$")) {
            value = beanFactory.resolveEmbeddedValue(value);
        }
        return value;
    }

    /**
     * @param pvs
     * @return
     */
    private  T getProperty(PropertyValues pvs, String name, Class targetClass) {
        return (T)pvs.getPropertyValue(name).getValue();
    }
}

RocketMessageListener:这个就是将@RocketListener封装成的消息消费器

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.List;

import com.bob.intergrate.rocket.ann.RocketListener;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

/**
 * 封装{@link RocketListener}标识的方法成一个消息消费器
 *
 * @author wb-jjb318191
 * @create 2018-03-20 10:03
 */
public class RocketMessageListener implements MessageListenerConcurrently {

    private Object consumeBean;
    private Method consumeMethod;
    private static final String ERROR_MSG_PREFIX = "[@RocketListener]标识的方法";

    public RocketMessageListener(Object consumeBean, Method consumeMethod) {
        this.consumeBean = consumeBean;
        this.consumeMethod = consumeMethod;
        checkConsumeMethod();
    }

    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) {
        Class[] types = consumeMethod.getParameterTypes();
        Object[] args = new Object[types.length];
        if (types[0] == List.class) {
            args[0] = msgs;
        } else {
            args[0] = msgs.get(0);
        }
        if (types.length == 2) {
            args[1] = context;
        }
        boolean result = (boolean)ReflectionUtils.invokeMethod(consumeMethod, consumeBean, args);
        return result ? ConsumeConcurrentlyStatus.CONSUME_SUCCESS : ConsumeConcurrentlyStatus.RECONSUME_LATER;
    }

    /**
     * 校验{@link RocketListener}标识的方法正确性
     */
    private void checkConsumeMethod() {
        int paramLength = consumeMethod.getParameterCount();
        Assert.state(paramLength == 1 || paramLength == 2, ERROR_MSG_PREFIX + "参数长度只能在1和2之间");
        Parameter param0 = consumeMethod.getParameters()[0];
        boolean isMsg = param0.getType().isAssignableFrom(MessageExt.class);
        boolean isList = param0.getType().isAssignableFrom(List.class);
        Assert.state(isMsg || isList, ERROR_MSG_PREFIX + "第一个参数只能是MessageExt或List类型");
        if (isList) {
            Class generic = ResolvableType.forMethodParameter(consumeMethod, 0).resolveGeneric(0);
            Assert.isAssignable(MessageExt.class, generic, ERROR_MSG_PREFIX + "第一个参数的泛型只能是MessageExt类型");
        }
        if (paramLength == 2) {
            Assert.state(consumeMethod.getParameters()[1].getType() == ConsumeConcurrentlyContext.class,
                ERROR_MSG_PREFIX + "第2个参数类型只能是[ConsumeConcurrentlyContext]");
        }
        Assert.state(Modifier.isPublic(consumeMethod.getModifiers()), ERROR_MSG_PREFIX + "修饰符必须为[Public]");
        Assert.state(consumeMethod.getReturnType() == boolean.class, ERROR_MSG_PREFIX + "返回值类型必须为[boolean]");
    }

}

RocketConsumerLifecycleProcessor:这个Bean会在Spring容器启动完成后启动消费者,开始消费,同时在Spring关闭时终止消费客户端。

import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.PreDestroy;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.exception.MQClientException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.SmartLifecycle;

/**
 * RocketMQ Consumer生命周期处理器
 *
 * @author wb-jjb318191
 * @create 2018-03-20 9:41
 */
public class RocketConsumerLifecycleProcessor implements SmartLifecycle {

    private static final Logger LOGGER = LoggerFactory.getLogger(RocketConsumerLifecycleProcessor.class);

    @Autowired
    private Map rocketMQConsumers;

    private volatile boolean isRunning = false;

    @Override
    public void start() {
        for (Entry entry : rocketMQConsumers.entrySet()) {
            try {
                entry.getValue().start();
            } catch (MQClientException e) {
                LOGGER.error("启动[{}]消费者失败", entry.getKey(), e);
            }
        }
        isRunning = true;
    }

    @Override
    public boolean isAutoStartup() {
        return true;
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }

    @Override
    public int getPhase() {
        return Integer.MAX_VALUE;
    }

    @PreDestroy
    public void destroy() {
        isRunning = false;
        for (DefaultMQPushConsumer consumer : rocketMQConsumers.values()) {
            consumer.shutdown();
        }
    }

    @Override
    public void stop(Runnable runnable) {

    }

    @Override
    public void stop() {

    }

}

RocketBeanDefinitionConstant:一个接口,存放整合解析时的一些常量定义。

/**
 * RocketMQ定义时的常量
 *
 * @author wb-jjb318191
 * @create 2018-03-20 14:38
 */
public interface RocketBeanDefinitionConstant {

    /**
     * 消费者组
     */
    String CONSUMER_GROUP = "consumerGroup";

    /**
     * 主题
     */
    String TOPIC = "topic";

    /**
     * 小标题
     */
    String TAG = "tag";

    /**
     *
     */
    String NAMESRV_ADDR = "namesrvAddr";

    /**
     * 消费Bean
     */
    String CONSUME_BEAN_NAME = "consumeBeanName";

    /**
     * 消费方法
     */
    String CONSUME_METHOD = "consumeMethod";

    /**
     * 消费偏移模式
     */
    String CONSUME_FROM_WHERE = "consumeFromWhere";

    /**
     * RocketMQ消费者Bean名称后缀
     */
    String ROCKETMQ_CONSUMER_BEAN_NAME_SUFFIX = "RocketConsumer";

}

以上就是整合的所有代码了,总共就8个类,不多,但是基本的功能实现了,仅整合了消费者,生产者没有太多整合的必要。

下面看使用的方法:

rocket-config.properties:RocketMQ环境配置信息,Consumer和Producer的一些配置信息可以放在其中,Spring启动时将此配置文件内的信息注册到System的Property中,这样客户端在实例化时就能够用这些信息作为默认值。同时@RocketListener的配置信息可以使用 “${}” 这种形式来引用配置文件中的信息,我在解析时做了相应的处理。

rocketmq.namesrv.addr = 127.0.0.1:9876
com.rocketmq.sendMessageWithVIPChannel = false
rocketmq.client.name = project@001
service.consumerGroup = rmq-group
service.topic = test-topic

RocketConsumerConfiguration:消费者配置类,定义@RocketListener标识的方法

import java.util.List;

import com.bob.intergrate.rocket.ann.RocketListener;
import com.bob.intergrate.rocket.constant.RocketBeanDefinitionConstant;
import com.google.gson.Gson;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * RocketMQ消费者配置
 *
 * @author wb-jjb318191
 * @create 2018-03-20 16:06
 */
public class RocketConsumerConfiguration {

    private Gson gson = new Gson();

    /**
     * 通过{@link RocketListener}形式定义的Consumer,名称形式以为:
     * consumeMethodName + {@link RocketBeanDefinitionConstant#ROCKETMQ_CONSUMER_BEAN_NAME_SUFFIX}
     * BeanName形式可自定义
     */
    @Autowired
    private DefaultMQPushConsumer serviceRocketConsumer;

    /**
     * 定义RocketMQ消费器
     *
     * @param msg
     * @param context
     * @return true:消费成功;  false:消费失败,发回给Broker,一段时间后重试
     */
    @RocketListener(consumerGroup = "${service.consumerGroup}", topic = "${service.topic}")
    public boolean service(MessageExt msg, ConsumeConcurrentlyContext context) {
        System.out.println(gson.toJson(msg));
        return true;
    }

    //@RocketListener(consumerGroup = "demo", topic = "test-topic")
    public boolean demo(MessageExt msg) {
        System.out.println(gson.toJson(msg));
        return true;
    }

    //@RocketListener(consumerGroup = "project", topic = "test-topic")
    public boolean project(List msgs) {
        for (MessageExt msg : msgs) {
            System.out.println(gson.toJson(msg));
        }
        return true;
    }

}

RocketContextConfig:RocketMQ使用时的总配置类,引入Properties配置文件,解析其信息注册到System的property中,定义Consumer和Producer。标识@EnableRocket,引入Spring整合RocketMQ组件。

import java.util.Properties;

import javax.annotation.PostConstruct;

import com.bob.intergrate.rocket.ann.EnableRocket;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.log.ClientLogger;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.ConfigurableEnvironment;

/**
 * Rocket消息队列配置
 *
 * @author wb-jjb318191
 * @create 2018-02-11 15:09
 */
@EnableRocket
@Configuration
@PropertySource(name = "rocket-config", value = "classpath:rocket-config.properties")
public class RocketContextConfig {

    @Autowired
    private ConfigurableEnvironment environment;

    private static final Logger LOGGER = LoggerFactory.getLogger(RocketContextConfig.class);

    static {
        //设置Rocket的日志
        ClientLogger.setLog(LOGGER);
    }

    /**
     * 将rocket-config.properties内的配置信息配置到System中
     * 这样Consumer,Producer实例化时有些属性就有默认值
     */
    @PostConstruct
    private void initRocketContext() {
        Properties properties = (Properties)environment.getPropertySources().get("rocket-config").getSource();
        for (String key : properties.stringPropertyNames()) {
            System.setProperty(key, properties.getProperty(key));
        }
    }

    @Bean
    public RocketConsumerConfiguration rocketMQConsumerConfiguration() {
        return new RocketConsumerConfiguration();
    }

    @Bean
    public DefaultMQProducer RocketMQProducer() throws MQClientException {
        DefaultMQProducer producer = new DefaultMQProducer("rmq_group");
        producer.setNamesrvAddr("127.0.0.1:9876");
        producer.setInstanceName("192.168.0.1@360");
        // 必须设为false否则连接broker10909端口
        producer.setVipChannelEnabled(false);
        producer.start();
        return producer;
    }

}

最后将RocketContextConfig 作为RocketMQ模块的配置类,在Spring里可以通过@Import的形式引入框架中,这样能最大程度的解耦框架间的整合,引入与否由是否Import这个配置类来决定。

@Configuration
@EnableAsync
@EnableWebMvc
@EnableDataValidate
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan(basePackages = {"com.bob.web.mvc"})
@Import({
    AppUserContextConfig.class,
    MysqlContextConfig.class,
    TransactionContextConfig.class,
    RedisContextConfig.class,
    AopContextConfig.class,
    RocketContextConfig.class
})
public class WebContextConfig extends WebMvcConfigurerAdapter {
    ......
}

你可能感兴趣的:(spring,rocketmq,造轮子)