当前位置: 代码网 > it编程>编程语言>Java > SpringBoot升级到2.7.18后不兼容的地方及解决

SpringBoot升级到2.7.18后不兼容的地方及解决

2024年08月15日 Java 我要评论
背景最近为了给kafka加性能指标采集功能,调研后发现spring-kafka在2.3版本之后就自带了micrometer指标采集功能。但是当前项目的spring-boot版本是2.0.2.relea

背景

最近为了给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的循环依赖

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

***************************
application failed to start
***************************

description:

the dependencies of some of the beans in the application context form a cycle:

   collectioncontroller (field private com.biz.manager.collectionmanager com.web.controller.collectioncontroller.collectionmanager)
┌─────┐
|  collectionmanagerimpl (field private com.biz.manager.funnymanager com.biz.manager.impl.collectionmanagerimpl.funnymanager)
↑     ↓
|  funnymanagerimpl (field private com.biz.manager.collectionmanager com.biz.manager.impl.funnymanagerimpl.collectionmanager)
└─────┘

action:

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。

private boolean allowcircularreferences;

/**
 * sets whether to allow circular references between beans and automatically try to
 * resolve them. defaults to {@code false}.
 * @param allowcircularreferences if circular references are allowed
 * @since 2.6.0
 * @see abstractautowirecapablebeanfactory#setallowcircularreferences(boolean)
 */
public void setallowcircularreferences(boolean allowcircularreferences) {
	this.allowcircularreferences = allowcircularreferences;
}

所以,要保持和2.6版本之前行为一样的话,就把allowcircularreferences属性设置为true。

设置可以添加配置spring.main.allow-circular-references=true,或通过springapplicationspringapplicationbuilder 对象直接设置属性。

2.1版本禁用bean覆盖

当出现同名bean时,会判断是否允许覆盖beandefinition,不允许则抛出beandefinitionoverrideexception异常。

实现逻辑如下:

beandefinition existingdefinition = this.beandefinitionmap.get(beanname);
if (existingdefinition != null) {
	if (!isallowbeandefinitionoverriding()) {
		throw new beandefinitionoverrideexception(beanname, beandefinition, existingdefinition);
	}
	
  // ...
	this.beandefinitionmap.put(beanname, beandefinition);
}

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

private boolean allowbeandefinitionoverriding;

/**
 * sets if bean definition overriding, by registering a definition with the same name
 * as an existing definition, should be allowed. defaults to {@code false}.
 * @param allowbeandefinitionoverriding if overriding is allowed
 * @since 2.1.0
 * @see defaultlistablebeanfactory#setallowbeandefinitionoverriding(boolean)
 */
public void setallowbeandefinitionoverriding(boolean allowbeandefinitionoverriding) {
	this.allowbeandefinitionoverriding = allowbeandefinitionoverriding;
}

所以,要和老版本兼容的话,就把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异常。

/**
	 * validate that when {@link #setallowcredentials allowcredentials} is {@code true},
	 * {@link #setallowedorigins allowedorigins} does not contain the special
	 * value {@code "*"} since in that case the "access-control-allow-origin"
	 * cannot be set to {@code "*"}.
	 * @throws illegalargumentexception if the validation fails
	 * @since 5.3
	 */
	public void validateallowcredentials() {
		if (this.allowcredentials == boolean.true &&
				this.allowedorigins != null && this.allowedorigins.contains(all)) {

			throw new illegalargumentexception(
					"when allowcredentials is true, allowedorigins cannot contain the special value \\"*\\" " +
							"since that cannot be set on the \\"access-control-allow-origin\\" response header. " +
							"to allow credentials to a set of origins, list them explicitly " +
							"or consider using \\"allowedoriginpatterns\\" instead.");
		}
	}

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

@configuration
public class corsconfig implements webmvcconfigurer {

    @override
    public void addcorsmappings(corsregistry registry) {
        registry.addmapping("/**")    // 允许跨域访问的路径
                .allowedoriginpatterns("*")    // 允许跨域访问的源
    }
}

/**
 * alternative to {@link #setallowedorigins} that supports more flexible
 * origins patterns with "*" anywhere in the host name in addition to port
 * lists. examples:
 * <ul>
 * <li>{@literal https://*.domain1.com} -- domains ending with domain1.com
 * <li>{@literal https://*.domain1.com:[8080,8081]} -- domains ending with
 * domain1.com on port 8080 or port 8081
 * <li>{@literal https://*.domain1.com:[*]} -- domains ending with
 * domain1.com on any port, including the default port
 * </ul>
 * <p>in contrast to {@link #setallowedorigins(list) allowedorigins} which
 * only supports "*" and cannot be used with {@code allowcredentials}, when
 * an allowedoriginpattern is matched, the {@code access-control-allow-origin}
 * response header is set to the matched origin and not to {@code "*"} nor
 * to the pattern. therefore allowedoriginpatterns can be used in combination
 * with {@link #setallowcredentials} set to {@code true}.
 * <p>by default this is not set.
 * @since 5.3
 */
public corsconfiguration setallowedoriginpatterns(@nullable list<string> allowedoriginpatterns) {
	if (allowedoriginpatterns == null) {
		this.allowedoriginpatterns = null;
	}
	else {
		this.allowedoriginpatterns = new arraylist<>(allowedoriginpatterns.size());
		for (string patternvalue : allowedoriginpatterns) {
			addallowedoriginpattern(patternvalue);
		}
	}
	return this;
}

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

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。

// 5.3之前
@override
public boolean isreadable() {
	try {
		url url = geturl();
    // file/vfsfile/vfs开头的url
		if (resourceutils.isfileurl(url)) {
			// proceed with file system resolution
			file file = getfile();
			return (file.canread() && !file.isdirectory());
		}
		else {
			return true;
		}
	}
	catch (ioexception ex) {
		return false;
	}
}

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

@override
public boolean isreadable() {
	url url = resolveurl();
	return (url != null && checkreadable(url));
}

boolean checkreadable(url url) {
	try {
    // file/vfsfile/vfs开头的url
		if (resourceutils.isfileurl(url)) {
			// proceed with file system resolution
			file file = getfile();
			return (file.canread() && !file.isdirectory());
		}
		else {
			// try inputstream resolution for jar resources
			urlconnection con = url.openconnection();
			customizeconnection(con);
			if (con instanceof httpurlconnection) {
				httpurlconnection httpcon = (httpurlconnection) con;
				httpcon.setrequestmethod("head");
				int code = httpcon.getresponsecode();
				if (code != httpurlconnection.http_ok) {
					httpcon.disconnect();
					return false;
				}
			}
			long contentlength = con.getcontentlengthlong();
			if (contentlength > 0) {
				return true;
			}
			else if (contentlength == 0) {
				// empty file or directory -> not considered readable...
				return false;
			}
			else {
				// fall back to stream existence: can we open the stream?
				getinputstream().close();
				return true;
			}
		}
	}
	catch (ioexception ex) {
		return false;
	}
}

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

requestmappinginfo#getpatternscondition()返回null

5.3开始新增了pathpatternscondition属性,它和patternscondition是互斥的,所以getpatternscondition()可能会返回null了。

可以通过getactivepatternscondition()方法获取requestcondition对象:

/**
 * returns either {@link #getpathpatternscondition()} or
 * {@link #getpatternscondition()} depending on which is not null.
 * @since 5.3
 */
@suppresswarnings("unchecked")
public <t> requestcondition<t> getactivepatternscondition() {
	if (this.pathpatternscondition != null) {
		return (requestcondition<t>) this.pathpatternscondition;
	}
	else if (this.patternscondition != null) {
		return (requestcondition<t>) this.patternscondition;
	}
	else {
		// already checked in the constructor...
		throw new illegalstateexception();
	}
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com