背景
最近为了给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-boot | 2.0.2.release | 2.7.18 |
| spring-webmvc | 5.0.6.release | 5.3.31 |
| spring-kafka | 2.1.6.release | 2.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,或通过springapplication 或 springapplicationbuilder 对象直接设置属性。
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();
}
}总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论