SpringCloud/SpringBoot升级拆坑
在spring官方网站的主页里,赫赫醒目的标题写着“Spring makes Java simple.modern.productive.reactive.cloud-ready。”;(Spring使Java简单易用、紧跟时代、复用性强、快速响应、云就绪)
如spring所宣称的;springcloud/springboot就是使用java语言里实现和搭建微服务体系;最好的Java微服务技术框架、微服务项目开发框架以及微服务化的解决方案; 目前springcloud是依托springboot来实现整个微服务体系;每个springcloud的组件也都是依赖于springboot; springboot和springcloud版本如下
笔者一直使用的2.3.x的版本
今天要给拆的坑,就是笔者升级springboot从2.3.x到2.6.x,springcloud从hoxton升级到2021.0.x的过程中踩到的坑; 这个升级的过程是发生在2022年上半年,笔者升级的时候,springboot还没有出2.7.x的版本,最后的版本是springboot2.6.3; springcloud的版本使用的是2021.0.1; springcloud-alibaba在升级时,还没有出针对springcloud2021.0.1的版本,所以升级中,springcloud alibaba的版本没变,用的是2.2.7.RELEASE(现在springcloud alibaba已经发布了支持springcloud2021.0.1的版本2021.0.1);
升级后目标版本如下图pom.xml所示
项目启动后不能跑, datasource不能配置成功,debug也不能配置成功,推断是配置文件bootstrap没有加载的问题
原因:
SpringBoot本身并不支持,需要和Spring Cloud 的组件结合——只有加上Spring Cloud Context依赖才能生效。
拆坑:
需要加入
org.springframework.cloud
spring-cloud-starter-bootstrap
Spring Boot 2.6.x版本引入依赖 springfox-boot-starter (Swagger 3.0) 后,启动容器会报错: Failed to start bean ‘ documentationPluginsBootstrapper ‘ ; nested exception…
原因:
Springfox 假设 Spring MVC 的路径匹配策略是 ant-path-matcher,而 Spring Boot 2.6.x版本的默认匹配策略是 path-pattern-matcher,这就造成了上面的报错。
拆坑:
方案一(治标)
在 application.properties 配置文件中修改mvc的匹配策略:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
注意:开始的时候我用这个方法的确可以正常启动了,但后来我发现此方法在某些服务启动时会失效!我查了一下才发现这个方法治标不治本,具体如下:
只有在不使用 Spring Boot 的执行器时,此功能才起作用。
无论配置的匹配策略如何,执行器将始终使用基于路径模式的解析 ( 也就是默认策略 ) 。
如果您想在 Spring Boot 2.6及更高版本中将其与执行器一起使用,则需要对 Springfox 进行更改。
方案二(治本)
这个办法是我在 github 上找到的,解决方案是将 Springfox 的某 .java 文件复制到自己项目里进行修改,后来有看到一个更好的解决方案,我觉得非常好,在这里分享一下:
在你的项目里添加这个 bean :(加在配置类里就可)
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private void customizeSpringfoxHandlerMappings(List mappings) {
List copy = mappings.stream()
.filter(mapping -> mapping.getPatternParser() == null)
.collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
field.setAccessible(true);
return (List) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
};
}
解决上面问题后,文档中心的接口列表没有了;
原因:
部分jar不相互的版本冲突;
各种配置,各种搭配试过来,
最终方案:
必须两者同时都修改
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
@Configuration
@EnableKnife4j
@EnableSwagger2
@EnableConfigurationProperties(ApiDocConfigProperties.class)
public static class DocketConfiguration{
原因:
Spring Cloud 2020.0.0版本之前会自动引入Netflix Ribbon依赖,Netflix Ribbon功能跟loadbalancer一样, 因Netflix公司停止维护Ribbon后, 在Spring Cloud 2020.0.0版本之后Spring使用loadbalancer替代了Ribbon, 但是loadbalancer依赖需要手动引入。
拆坑(解决方案):
引入loadbalancer
org.springframework.cloud
spring-cloud-loadbalancer
增加loadbalancer后启动提示:Spring Cloud LoadBalancer is currently working with the default cache. You can switch to using Caffeine cache, by adding it and org.springframework.cache.caffeine.CaffeineCacheManager to the classpath
拆坑(解决方案):
解决方案是引入caffeine,或者关闭cache
com.github.ben-manes.caffeine
caffeine
3.0.5
启动的问题解决了,访问其他服务时提示:LoadBalancerCacheManager not available, returning delegate without caching
拆坑(解决方案):
修改配置:
spring:
cloud:
loadbalancer:
ribbon:
enabled: false
cache:
enabled: true
caffeine:
spec: initialCapacity=500,expireAfterWrite=5s
但是发现配置有可能还会存在提示,没有生效!网络上有说Naocs discovery依赖有冲突导致没生效,exclusion spring-cloud-starter-netflix-ribbon,但是实际查看了下此版本并没有依赖这个包,所以还需确认。
启动出现错误:Loading class ‘com.mysql.jdbc.Driver’. This is deprecated. The new driver class is ‘com.mysql.cj.jdbc.Driver’. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
原因:
项目中使用到了log4jdbc-log4j2-jdbc4.1,升级后出现此错误提示。 因为mysql最新驱动名称为com.mysql.cj.jdbc.Driver,log4jdbc只支持com.mysql.jdbc.Driver,所以需要将自动加载数据库驱动关闭。
拆坑(解决方案):
resources文件下新增log4jdbc.log4j2.properties文件
log4jdbc.auto.load.popular.drivers=false
log4jdbc.drivers=com.mysql.cj.jdbc.Driver
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
测试启动过程中,发现Idea直接退出,没有任何提示输出。
原因:
Idea的设置问题导致
拆坑(解决方案):
在启动的Edit Configuration里设置指定服务的Active profiles,例如:local
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext@f0da945 has not been refreshed yet
原因:
javax/ws/rs/Path 是属于jsr311-api.jar 包下的,引入相关jsr311-api包即可。
拆坑(解决方案):
引入相关包
javax.ws.rs
jsr311-api
1.1.1
原因:
新版本中Spring Boot不自动包含validation包
拆坑(解决方案):
org.springframework.boot
spring-boot-starter-validation
访问是报错java.net.UnknownHostException
原因:
此处还是因为2020版本不再使用netflix,没有了ribbon导致的问题,依旧是引入loadbalance代替rbibon:
拆坑(解决方案):
org.springframework.cloud
spring-cloud-loadbalancer
如果出现错误
An attempt was made to call a method that does not exist. The attempt was made from the following location: org.hibernate.validator.internal.xml.config.ValidationBootstrapParameters.
(ValidationBootstrapParameters.java:65) The following method did not exist: javax.validation.BootstrapConfiguration.getClockProviderClassName()Ljava/lang/String;
拆坑(解决方案):
升级JAVAEE至少8.0
javax
javaee-api
8.0.1
原因:
升级后的Nacos Client会连接9848的端口,来进行健康监控;旧版本Nacos Client不会有这个检查动作
拆坑(解决方案):
修改防火墙策略,打开Nacos Server的9848端口,允许内网访问Nacos Server的9848端口
升级Nacos-Discovery后,所有的feign调用都会报错;如下
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.AbstractMethodError: org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.choose(Ljava/lang/String;Lorg/springframework/cloud/client/loadbalancer/Request;)Lorg/springframework/cloud/client/ServiceInstance;
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1082)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
原因:
包的依赖关系,在最上面我就说过了,我升级的时候SpringCloud Alibaba只是从2.2.6升级到了2.2.7还是不支持springcloud的2021.0.1版本; 所以包依赖出现冲突;
SpringCloud2021.0.1版本已经去除了对netfix-ribbon的依赖, 使用loadbalance代替,但是对于spring-cloud-starter-alibaba-nacos-discovery2.2.7版本并没有对此进行处理,还是使用netfix-ribbon
拆坑(解决方案):
去除掉对netfix-ribbon的依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-netflix-ribbon