springboot集成rabbitmq,生产与消费封装的坎坷历程

上篇文章介绍了rabbitmq与springboot的整合。这篇文章介绍基于springboot封装rabbitmq,以及其中的头脑风暴。
先放GIT地址: https://github.com/chenguangju/config,基于上篇文章进行封装,所以分为两个工程。
1:生产者很简单,提供一个MessgaeSender类,如果想用默认的格式去发送数据,直接用@Autowired注解,在你想发送数据的类注入MessgaeSender就可以调用send方法发送数据。如果不想用默认的,还提供了一个SenderInterface接口,具体逻辑自己写就可以。
2:Config配置类,这里提供了一个默认配置类DefaultRabbitMQConfig,默认配置一个交换机,一个队列,并且绑定。交换机的类型与名称,队列的名称,绑定的路由键都可以在配置文件制定。还提供了一个RabbitConfig接口,如果需要多个队列,绑定不同的路由,实现接口即可。
3:消费者,提供了一个BaseConsumer类,这是一个抽象类,监听的队列可以配置,如果工程只有一个消费者直接继承此类,只写逻辑代码即可。还提供了一个Consumer接口,如果存在多个消费者,实现此接口,按照BaseConsumer的形式写几个方法即可。
工程中都有例子,按照例子即可以。

但是!!!我觉得封装还是不完美怎么破?虽然都没解决但是说一下思路
1:其实我本意是这样的,无论多少个消费者,只要继承BaseConsumer就可以。但是有个问题,@RabbitListener必须加在父类上,上面的参数是监听的队列,但是子类没法传递过去,注解的参数都是final的啊,根本不可行。反射动态改也是不行。放弃
2:还有一个点,如果多个队列,想要几个我直接build出来多好,不需要配置,但是又不想用spring提供的工具怎么办?我想如果我能在spring启动的时候,加载进去几个BeanDefinition多好,先做做能不能实现,然后就写了个类实现BeanDefinitionRegistryPostProcessor,实现了postProcessBeanDefinitionRegistry方法,这个方法可以允许我在BeanDefinition都加载完后,实例化之前,在向容器中写入bean定义,然后就是坑了:坑1)首先注册个Queue试试?好的,代码如下
@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        
        BeanDefinitionBuilder builder1 = BeanDefinitionBuilder.genericBeanDefinition();
        GenericBeanDefinition definition1 = (GenericBeanDefinition) builder1.getRawBeanDefinition();
        definition1.setBeanClass(Queue.class);
        definition1.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        registry.registerBeanDefinition("myQ", definition1);
	}
注册名为myQ的Bean定义,开心启动然后---报错,没有默认构造方法,这就牛逼了,这货就没有默认构造,但是spring在实例化一个对象时一定要找默认构造---然后不行,我也不能加是不是。这不是逼我么。改造,没有默认的构造,那就自己造吧,如果你看了我前面的shiro的启动分析,里面说了FactoryBean的原理,这里就用它,然后改造代码如下
@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        Class<?> cls = Queue.class;
        Class<?> m=QueueFactoryBean.class;
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(m);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.getPropertyValues().add("queueClass", cls);
        definition.setBeanClass(QueueFactoryBean.class);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        // 注册bean名,一般为类名首字母小写
        registry.registerBeanDefinition("myFactory", definition);
		
	}

对应的factorybean如下
public class QueueFactoryBean implements FactoryBean<Queue>{
	 private  Class<?> queueClass;
	 	
	@Override
	public Queue getObject() throws Exception {
		@SuppressWarnings("unchecked")
		Constructor<Queue> constructor = (Constructor<Queue>) queueClass.getConstructor(String.class,boolean.class);
		return (Queue) constructor.newInstance("myQ",true);
	}

	@Override
	public Class<?> getObjectType() {
		return queueClass;
	}

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


	public Class<?> getQueueClass() {
		return queueClass;
	}

	public void setQueueClass(Class<?> queueClass) {
		this.queueClass = queueClass;
	}
	
}

这里大体说下,postProcessBeanDefinitionRegistry构造了一个QueueFactoryBean的BeanDefinition,并为其属性queueClass赋值Queue.class,然后在QueueFactoryBean实例化的时候会去调用getObject方法,返回一个Quene注册到spring容器中,艾玛,开心启动,发现MQ中已经有了名为myQ的队列,高兴一分钟。坑2)完成上一步,spring容器中已经有了Queue和Exchange(默认)的实例,那是不是我只要把这两者绑定一下就好了?开心啊,那就实现下,这里用到了BeanPostProcessor,具体代码如下:
@Component
public class PostProcesser implements BeanPostProcessor{
	
	private Object  defaultExchange;
	private Object  queue;
	private boolean flag=true;

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if(null!=defaultExchange && null!=queue){
			if(flag){
				getBinding((AbstractExchange)defaultExchange,"item",(Queue)queue);
				flag=false;
			}
			
		}
		if(beanName.equals("defaultExchange")){
			this.defaultExchange=bean;
			return bean;
		}
		if(beanName.equals("myFactory")){
			this.queue=bean;
			return bean;
		}
		return bean;
	}
	
	public Binding getBinding(AbstractExchange defaultExchange, String Key,  Queue queue) {
		if(defaultExchange instanceof TopicExchange){
			return BindingBuilder.bind(queue).to((TopicExchange)defaultExchange).with(Key);
		}
		if(defaultExchange instanceof FanoutExchange){
			return BindingBuilder.bind(queue).to((FanoutExchange)defaultExchange);
		}
		if(defaultExchange instanceof DirectExchange){
			return BindingBuilder.bind(queue).to((DirectExchange)defaultExchange).with(Key);
		}
		return null;
	}
}

是不是很机智,在bean初始化完成后,去拿到defaultExchange和myFactory然后绑定就好啦,开心,启动,不报错,难道要大功告成,查看MQ发现还是默认的绑定,即使Queue有两个了(一个默认一个myQ),哪里出了问题呢?然后回去看DefaultRabbitMQConfig的代码,发现个犯浑的事,Binding上竟然有@Bean注解,我尼玛啊,眼瞎了。我这是在作死啊,实例化完成怎么可能再去定义Bean定义,不可取。还要改造。坑3)先个半天,晚上都没睡好,看绑定的代码,怎么看怎么需要两个实例,但是你在未实例化对象的时候去要两个实例对象这怎么可能,然后看了Binding的代码
BindingBuilder.bind(queue).to((DirectExchange)defaultExchange).with(Key);

这代码最后只是用到了Queue的name和Exchange的name,呵呵哒,这不就好了,重复1)的步骤代码如下:
@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        Class<?> cls = Binding.class;
        Class<?> m=BindingFactoryBean.class;
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(m);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.getPropertyValues().add("bindingClass", cls);
        definition.setBeanClass(BindingFactoryBean.class);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        // 注册bean名,一般为类名首字母小写
        registry.registerBeanDefinition("myBinding", definition);
        
	}

factorybean代码
public class BindingFactoryBean implements FactoryBean<Binding>{
	 private  Class<?> bindingClass;
	 	
	@Override
	public Binding getObject() throws Exception {
		@SuppressWarnings("unchecked")
		Constructor<Binding> constructor =
		(Constructor<Binding>) bindingClass.getConstructor(String.class,DestinationType.class,
				String.class,String.class,Map.class);
		return (Binding) constructor.newInstance("myQ",DestinationType.QUEUE,"topic","item.*",null);
	}

	@Override
	public Class<?> getObjectType() {
		return bindingClass;
	}

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

	public Class<?> getBindingClass() {
		return bindingClass;
	}

	public void setBindingClass(Class<?> bindingClass) {
		this.bindingClass = bindingClass;
	}
}

逻辑和queue是一样的,写完,启动,---完成,绑定好了。不开心,因为我为了绑定,多写了四个类,比我原来的做法复杂多了,拜拜。还是不多想了。就用原来的封装吧,也许后续会改。上面失败的代码在GIT的消费者,测试代码下
但是!!!你以为一点收获就没有么,虽然失败了,但是对spring流程更清晰了。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackson陈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值