当前位置:   article > 正文

SpringBoot升级到2.7.18后不兼容的地方_springboot 2.7.18

springboot 2.7.18

背景

最近为了给kafka加性能指标采集功能,调研后发现spring-kafka在2.3版本之后就自带了Micrometer指标采集功能。但是当前项目的spring-boot版本是2.0.2.RELEASE,对应的spring-kafka版本是2.1.6.RELEASE,所以准备将spring-boot版本升级到2.7.18,这是2.x系列的最高版本,对应的spring-kafka版本是2.8.11。

版本升级

module升级前version升级后version
spring-boot2.0.2.RELEASE2.7.18
spring-webmvc5.0.6.RELEASE5.3.31
spring-kafka2.1.6.RELEASE2.8.11

不兼容的地方

Spring boot

2.6版本开始默认禁用Bean的循环依赖

项目启动会检测是否存在循环依赖,存在就报如下错误。

  1. ***************************
  2. APPLICATION FAILED TO START
  3. ***************************
  4. Description:
  5. The dependencies of some of the beans in the application context form a cycle:
  6. collectionController (field private com.biz.manager.CollectionManager com.web.controller.CollectionController.collectionManager)
  7. ┌─────┐
  8. | collectionManagerImpl (field private com.biz.manager.FunnyManager com.biz.manager.impl.CollectionManagerImpl.funnyManager)
  9. ↑ ↓
  10. | funnyManagerImpl (field private com.biz.manager.CollectionManager com.biz.manager.impl.FunnyManagerImpl.collectionManager)
  11. └─────┘
  12. Action:
  13. Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

2.6版本在org.springframework.boot.SpringApplication类里增加了allowCircularReferences属性来控制循环依赖是否允许,默认值是false。

  1. private boolean allowCircularReferences;
  2. /**
  3. * Sets whether to allow circular references between beans and automatically try to
  4. * resolve them. Defaults to {@code false}.
  5. * @param allowCircularReferences if circular references are allowed
  6. * @since 2.6.0
  7. * @see AbstractAutowireCapableBeanFactory#setAllowCircularReferences(boolean)
  8. */
  9. public void setAllowCircularReferences(boolean allowCircularReferences) {
  10. this.allowCircularReferences = allowCircularReferences;
  11. }

所以,要保持和2.6版本之前行为一样的话,就把allowCircularReferences属性设置为true。设置可以添加配置spring.main.allow-circular-references=true,或通过SpringApplication 或 SpringApplicationBuilder 对象直接设置属性。

2.1版本禁用Bean覆盖

当出现同名bean时,会判断是否允许覆盖beanDefinition,不允许则抛出BeanDefinitionOverrideException异常。实现逻辑如下:

  1. BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
  2. if (existingDefinition != null) {
  3. if (!isAllowBeanDefinitionOverriding()) {
  4. throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
  5. }
  6. // ...
  7. this.beanDefinitionMap.put(beanName, beanDefinition);
  8. }

2.1版本在org.springframework.boot.SpringApplication类里增加了allowBeanDefinitionOverriding属性来控制是否允许bean覆盖,默认值是false。

  1. private boolean allowBeanDefinitionOverriding;
  2. /**
  3. * Sets if bean definition overriding, by registering a definition with the same name
  4. * as an existing definition, should be allowed. Defaults to {@code false}.
  5. * @param allowBeanDefinitionOverriding if overriding is allowed
  6. * @since 2.1.0
  7. * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding(boolean)
  8. */
  9. public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
  10. this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
  11. }

所以,要和老版本兼容的话,就把allowBeanDefinitionOverriding属性设置为true。设置可以添加配置spring.main.allow-bean-definition-overriding=true,或通过SpringApplication 对象直接设置属性。

默认的路径匹配策略改成了PATH_PATTERN_PARSER

2.6版本之前默认策略是ANT_PATH_MATCHER,改成PATH_PATTERN_PARSER会遇到IllegalArgumentException错误。

java.lang.IllegalArgumentException: Expected lookupPath in request attribute "org.springframework.web.util.UrlPathHelper.PATH".

解决方案是将策略回滚到ANT_PATH_MATCHER:

**spring.mvc.pathmatch.matching-strategy=***ANT_PATH_MATCHER*

Spring webmvc

Cors不允许将allowedOrigins设置为*

原来为了方便,会将跨域的请求来源设置为代表允许来自所有host的请求。5.3开始增加了allowedOrigins值的校验,不允许为,否则抛出IllegalArgumentException异常。

  1. /**
  2. * Validate that when {@link #setAllowCredentials allowCredentials} is {@code true},
  3. * {@link #setAllowedOrigins allowedOrigins} does not contain the special
  4. * value {@code "*"} since in that case the "Access-Control-Allow-Origin"
  5. * cannot be set to {@code "*"}.
  6. * @throws IllegalArgumentException if the validation fails
  7. * @since 5.3
  8. */
  9. public void validateAllowCredentials() {
  10. if (this.allowCredentials == Boolean.TRUE &&
  11. this.allowedOrigins != null && this.allowedOrigins.contains(ALL)) {
  12. throw new IllegalArgumentException(
  13. "When allowCredentials is true, allowedOrigins cannot contain the special value \\"*\\" " +
  14. "since that cannot be set on the \\"Access-Control-Allow-Origin\\" response header. " +
  15. "To allow credentials to a set of origins, list them explicitly " +
  16. "or consider using \\"allowedOriginPatterns\\" instead.");
  17. }
  18. }

另外,5.3增加了allowedOriginPatterns属性来代替allowedOrigins的功能。所以,要允许所有host的跨域请求的话,把allowedOriginPatterns设置为*。

  1. @Configuration
  2. public class CorsConfig implements WebMvcConfigurer {
  3. @Override
  4. public void addCorsMappings(CorsRegistry registry) {
  5. registry.addMapping("/**") // 允许跨域访问的路径
  6. .allowedOriginPatterns("*") // 允许跨域访问的源
  7. }
  8. }
  9. /**
  10. * Alternative to {@link #setAllowedOrigins} that supports more flexible
  11. * origins patterns with "*" anywhere in the host name in addition to port
  12. * lists. Examples:
  13. * <ul>
  14. * <li>{@literal https://*.domain1.com} -- domains ending with domain1.com
  15. * <li>{@literal https://*.domain1.com:[8080,8081]} -- domains ending with
  16. * domain1.com on port 8080 or port 8081
  17. * <li>{@literal https://*.domain1.com:[*]} -- domains ending with
  18. * domain1.com on any port, including the default port
  19. * </ul>
  20. * <p>In contrast to {@link #setAllowedOrigins(List) allowedOrigins} which
  21. * only supports "*" and cannot be used with {@code allowCredentials}, when
  22. * an allowedOriginPattern is matched, the {@code Access-Control-Allow-Origin}
  23. * response header is set to the matched origin and not to {@code "*"} nor
  24. * to the pattern. Therefore allowedOriginPatterns can be used in combination
  25. * with {@link #setAllowCredentials} set to {@code true}.
  26. * <p>By default this is not set.
  27. * @since 5.3
  28. */
  29. public CorsConfiguration setAllowedOriginPatterns(@Nullable List<String> allowedOriginPatterns) {
  30. if (allowedOriginPatterns == null) {
  31. this.allowedOriginPatterns = null;
  32. }
  33. else {
  34. this.allowedOriginPatterns = new ArrayList<>(allowedOriginPatterns.size());
  35. for (String patternValue : allowedOriginPatterns) {
  36. addAllowedOriginPattern(patternValue);
  37. }
  38. }
  39. return this;
  40. }

静态文件是否存在的判断方式变了

5.3版本开始,ClassPathResource类型的资源文件,判断是否可读的isReadable()方法的逻辑改成了文件存在且内容不为空。当我们访问一个内容为空的资源文件时,spring返回404。

例如,访问http://localhost:8080/hello.html,spring会在/META-INF/resource、resources、static、public这几个目录下查找hello.html。如果文件放在static文件夹下,实际查找的是/static/hello.html文件。如果是jar包里,则完整的路径是这样jar:file:/opt/apps/demo.jar!/BOOT-INF/classes!/static/hello.html。

然后我们看看5.3前后版本代码,对这个文件是否可读判断的差异。

5.3版本之前,jar开头的文件直接返回true。

  1. // 5.3之前
  2. @Override
  3. public boolean isReadable() {
  4. try {
  5. URL url = getURL();
  6. // file/vfsfile/vfs开头的url
  7. if (ResourceUtils.isFileURL(url)) {
  8. // Proceed with file system resolution
  9. File file = getFile();
  10. return (file.canRead() && !file.isDirectory());
  11. }
  12. else {
  13. return true;
  14. }
  15. }
  16. catch (IOException ex) {
  17. return false;
  18. }
  19. }

5.3版本开始,jar开头的文件会通过con.getContentLengthLong()获取文件长度,如果是0的话就返回false。

  1. @Override
  2. public boolean isReadable() {
  3. URL url = resolveURL();
  4. return (url != null && checkReadable(url));
  5. }
  6. boolean checkReadable(URL url) {
  7. try {
  8. // file/vfsfile/vfs开头的url
  9. if (ResourceUtils.isFileURL(url)) {
  10. // Proceed with file system resolution
  11. File file = getFile();
  12. return (file.canRead() && !file.isDirectory());
  13. }
  14. else {
  15. // Try InputStream resolution for jar resources
  16. URLConnection con = url.openConnection();
  17. customizeConnection(con);
  18. if (con instanceof HttpURLConnection) {
  19. HttpURLConnection httpCon = (HttpURLConnection) con;
  20. httpCon.setRequestMethod("HEAD");
  21. int code = httpCon.getResponseCode();
  22. if (code != HttpURLConnection.HTTP_OK) {
  23. httpCon.disconnect();
  24. return false;
  25. }
  26. }
  27. long contentLength = con.getContentLengthLong();
  28. if (contentLength > 0) {
  29. return true;
  30. }
  31. else if (contentLength == 0) {
  32. // Empty file or directory -> not considered readable...
  33. return false;
  34. }
  35. else {
  36. // Fall back to stream existence: can we open the stream?
  37. getInputStream().close();
  38. return true;
  39. }
  40. }
  41. }
  42. catch (IOException ex) {
  43. return false;
  44. }
  45. }

所以,5.3开始,静态文件不能是空文件,否则会返回404。

RequestMappingInfo#getPatternsCondition()返回null

5.3开始新增了pathPatternsCondition属性,它和patternsCondition是互斥的,所以getPatternsCondition()可能会返回null了。可以通过getActivePatternsCondition()方法获取RequestCondition对象:

  1. /**
  2. * Returns either {@link #getPathPatternsCondition()} or
  3. * {@link #getPatternsCondition()} depending on which is not null.
  4. * @since 5.3
  5. */
  6. @SuppressWarnings("unchecked")
  7. public <T> RequestCondition<T> getActivePatternsCondition() {
  8. if (this.pathPatternsCondition != null) {
  9. return (RequestCondition<T>) this.pathPatternsCondition;
  10. }
  11. else if (this.patternsCondition != null) {
  12. return (RequestCondition<T>) this.patternsCondition;
  13. }
  14. else {
  15. // Already checked in the constructor...
  16. throw new IllegalStateException();
  17. }
  18. }

引用

Bean循环引用:Spring Boot 2.6 Release Notes · spring-projects/spring-boot Wiki · GitHub

BeanDefinition覆盖:Spring Boot 2.1 Release Notes · spring-projects/spring-boot Wiki · GitHub

Spring-kafka监控文档:Spring for Apache Kafka

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/191643
推荐阅读
相关标签
  

闽ICP备14008679号