由于历史原因,项目使用的是SpringBoot1.x
版本,而且由于种种原因,不能升级。在项目开发迭代过程中,决定使用RocketMQ
作为消息中间件,因为是SpringBoot
项目,理所应当的引入了rocketmq-spring-boot-starter
依赖。但在使用@RocketMQMessageListener
注解时,项目就启动不起来了,错误信息如下:
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
对应报错位置:
对应代码为:
分析:SpringBoot1.x使用的spring-context
为4.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
版本。
地址:https://github.com/apache/rocketmq-spring/tags
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 修改对应报错调用代码
BeanDefinition beanDefinition = buildBeanDefinition(containerBeanName, bean, annotation);
genericApplicationContext.registerBeanDefinition(containerBeanName, beanDefinition);
修改源码后,重新打包,项目使用修改后的依赖发现可以正常使用,问题解决。
说明:以上方式虽然能解决问题,但是,个人推荐还是升级SpringBoot版本,毕竟1.x版本确实有点老了。