上篇文章介绍了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{
private Class> queueClass;
@Override
public Queue getObject() throws Exception {
@SuppressWarnings("unchecked")
Constructor constructor = (Constructor) 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{
private Class> bindingClass;
@Override
public Binding getObject() throws Exception {
@SuppressWarnings("unchecked")
Constructor constructor =
(Constructor) 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流程更清晰了。