rocketmq-spring-boot-starter支持SpringBoot 1.x(spring-context 4.x)版本

1 问题说明

由于历史原因,项目使用的是SpringBoot1.x版本,而且由于种种原因,不能升级。在项目开发迭代过程中,决定使用RocketMQ作为消息中间件,因为是SpringBoot项目,理所应当的引入了rocketmq-spring-boot-starter依赖。但在使用@RocketMQMessageListener注解时,项目就启动不起来了,错误信息如下:

rocketmq-spring-boot-starter支持SpringBoot 1.x(spring-context 4.x)版本_第1张图片

2023-06-27 22:39:35.675ERROR [service,[TID: N/A],,,] 52424 --- [           main] o.s.b.SpringApplication                  : Application startup failed
java.lang.NoClassDefFoundError: org/springframework/beans/factory/config/BeanDefinitionCustomizer
	at org.apache.rocketmq.spring.autoconfigure.ListenerContainerConfiguration.registerContainer(ListenerContainerConfiguration.java:114)
	at java.util.HashMap.forEach(HashMap.java:1288)
	at org.apache.rocketmq.spring.autoconfigure.ListenerContainerConfiguration.afterSingletonsInstantiated(ListenerContainerConfiguration.java:79)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:781)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
	at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
	at xxx.Application.main(ServiceApplication.java:25)
Caused by: java.lang.ClassNotFoundException: org.springframework.beans.factory.config.BeanDefinitionCustomizer
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 13 more

对应报错位置:

rocketmq-spring-boot-starter支持SpringBoot 1.x(spring-context 4.x)版本_第2张图片

对应代码为:

rocketmq-spring-boot-starter支持SpringBoot 1.x(spring-context 4.x)版本_第3张图片

2 问题分析

分析:SpringBoot1.x使用的spring-context4.x版本,GenericApplicationContext.registerBean方法是5.0版本才出现,那该如何解决以下问题?

第一个想法,当然是升级SpringBoot版本到2.x,但是,由于种种限制,SpringBoot版本并没有那么好升级。

于是就产生了第二个想法,单独升级spring-context版本,经过测试后,发现也不行。

于是翻阅github的issue发现,有人提交过补丁代码,但是被拒绝了,issue链接 对应的修改代码链接:

研究了一下他的思路,主要是通过使用GenericApplicationContext.registerBeanDefinition来支持SpringBoot 1.x版本,因为该方法在spring-context 4.x版本中已经拥有,所以就可以兼容SpringBoot 1.x版本。

3 具体修复方式

3.1 下载spring-boot-starter源码

地址:https://github.com/apache/rocketmq-spring/tags

rocketmq-spring-boot-starter支持SpringBoot 1.x(spring-context 4.x)版本_第4张图片

3.2 解压后,用编辑器打开

3.3 修改代码

3.3.1 在ListenerContainerConfiguration类中增加方法:

private BeanDefinition buildBeanDefinition(String name, Object bean,
                                           RocketMQMessageListener annotation) {
    String nameServer = environment.resolvePlaceholders(annotation.nameServer());
    nameServer = StringUtils.isEmpty(nameServer) ? rocketMQProperties.getNameServer() : nameServer;
    String accessChannel = environment.resolvePlaceholders(annotation.accessChannel());
    String tags = environment.resolvePlaceholders(annotation.selectorExpression());
    BeanDefinitionBuilder builder = BeanDefinitionBuilder
        .genericBeanDefinition(DefaultRocketMQListenerContainer.class)
        .addPropertyValue("rocketMQMessageListener", annotation)
        .addPropertyValue("nameServer", nameServer)
        .addPropertyValue("topic", environment.resolvePlaceholders(annotation.topic()))
        .addPropertyValue("consumerGroup", environment.resolvePlaceholders(annotation.consumerGroup()))
        .addPropertyValue("tlsEnable", environment.resolvePlaceholders(annotation.tlsEnable()))
        .addPropertyValue("messageConverter", rocketMQMessageConverter.getMessageConverter())
        .addPropertyValue("name", name);

    if (!StringUtils.isEmpty(accessChannel)) {
        builder.addPropertyValue("accessChannel", AccessChannel.valueOf(accessChannel));
    }

    if (!StringUtils.isEmpty(tags)) {
        builder.addPropertyValue("selectorExpression", tags);
    }
    if (RocketMQListener.class.isAssignableFrom(bean.getClass())) {
        builder.addPropertyValue("rocketMQListener", bean);
    } else if (RocketMQReplyListener.class.isAssignableFrom(bean.getClass())) {
        builder.addPropertyValue("rocketMQReplyListener", bean);
    }
    return builder.getBeanDefinition();
}

3.3.2 修改对应报错调用代码

rocketmq-spring-boot-starter支持SpringBoot 1.x(spring-context 4.x)版本_第5张图片

BeanDefinition beanDefinition = buildBeanDefinition(containerBeanName, bean, annotation);
genericApplicationContext.registerBeanDefinition(containerBeanName, beanDefinition);

修改源码后,重新打包,项目使用修改后的依赖发现可以正常使用,问题解决。

说明:以上方式虽然能解决问题,但是,个人推荐还是升级SpringBoot版本,毕竟1.x版本确实有点老了。

你可能感兴趣的:(Spring,Boot,Spring,spring,java-rocketmq,rocketmq)