Spring Boot 2.5.6 发布,缺陷修复,依赖升级

101 篇文章 14 订阅

网上查了查2.5.6 发布资料,这里简单终结一下

1、先说说Spring Boot 2.5 新特性一览

1.1、支持 Java 16

        我只能说 Spring Boot 厉害了,Java 16 都开始支持了(同时兼容 Java 8),毕竟 Java 16 才发布两个多月。。

        新版本任它发,我们都用 8,所以,虽然现在我们还远远用不上 Java 16,但 Spring Boot 能支持上就最好不过,这样对于那些想基于 Java 16 进行学习、开发的 Spring Boot 的同鞋就太好了(反正不是我)。

参考:JDK 16 正式发布,一次性发布 17 个新特性

1.2、支持 Gradle 7

Spring Boot Gradle 插件支持 Gradle 7.0.x,并已针对其进行了测试。

1.3、支持 Jetty 10

Spring Boot 2.5 现在支持 Jetty 10 内嵌了,因为 Jetty 10 需要依赖 Java 11,所以现在默认还是保持在 Jetty 9。

1.4、支持 HTTP/2 over TCP (h2c)

Spring Boot 2.5 所有内置的容器都可以支持 HTTP/2 over TCP (h2c),而无需进行任何手动定制。

1.5、新的数据源初始化机制

1)数据源调整

Spring Boot 2.5 中的某些数据源初始化的方法被重新设计了,下面的数据源参数配置也被废除了:

spring.datasource.*

新的数据源参数配置如下:

spring.sql.init.*

这个改动还挺大的,没事乱改啥参数,个人觉得之前的没毛病!

2)Flyway 和 Liquibase 调整

       这两个是主流的数据库版本管理工具,使用 spring.flyway.url 和 spring.liquibase.url 定义的时候需要加上 username/ password 进行认证。

       早期的版本中,这些参数都是从 spring.datasource 中派生的,用户在自定义数据源的时候会存在问题。

       另外,如果使用了自定义数据源和 Liquibase 一起使用,现在会使用 SimpleDriverDataSource 数据源类进行配置,之前的版本使用的是池化数据源,会导致数据源初始化效率低下。

1.6、环境变量前缀

       Spring Boot 2.5 可以为系统环境变量指定前缀了,这样可以方便我们在同一环境中运行多个不同的 Spring Boot 应用,而不用受重名环境变量冲突。

使用方法如下:

SpringApplication application = new SpringApplication(Javastack.class);
application.setEnvironmentPrefix("javastack");
application.run(args);

这样设置一下的话,所有 javastack-* 开头的系统环境变量才会被绑定。

比如:

javastack-id
javastack-name
javastack-age

1.7、增强 Docker 映像构建

Spring Boot Maven/ Gradle 插件现在都开始支持:

1)都支持自定义构建包;

2)都支持 Docker 构建包时传递卷数据(volume)绑定;

3)都支持打包可执行的 war 文件到 Docker 映像中;

1.8、Layered WARs

      Spring Boot Maven 和 Gradle 插件现在可以创建分层的 WAR 了,分层 WAR 与 Spring Boot 2.3 版本中提供的分层 JAR 工作方式类似,为了可以更好的与 Docker 映像一起使用,提升构建速度。

       这个我后续再更新一篇吧,想学习接收最新推送,请持续关注微信公众号:Java技术栈,公众号第一时间推送。

1.9、相关端点和指标变更

1)现在 /info actuator 端点不再默认开放了,如果使用了 Spring Security 的话,这个端点还需要身份认证才能访问,加强 info 端点的安全性;

2)新增了 /quartz 端点,这个端点提供了 Quartz 作业和触发器相关的的详细数据;

3)为 /startup 端点支持 get 请求,与 post 请求不同的是,get 请求的端点不会释放事件缓冲区,而且事件将继续保留在内存中;

4)为 /actuator/prometheus 端点提供标准 Prometheus 以及 OpenMetrics 响应;

5)为 Spring Data repositories 生成 Micrometer 指标;

6)自动生成 MongoDB 连接池、客户端命令相关指标;

1.10、官方文档界面和功能更新,还支持黑暗模式

       这个功能太炸了,有了黑暗模式,Java 程序员同鞋们再也不怕晚上熬夜学习 Spring Boot 了,如果你说你的电脑本来就支持,那当然没问题,但是原生支持可能兼容性更好。。

       另外栈长顺便看了下 Spring / Spring Cloud 的文档,目前还不支持黑暗模式,估计后面所有项目文档都会支持上。

2、Spring Boot 2.5 功能废除

1)移除 Spring Data Solr 相碰的自动配置,由于 Spring Data 2021.0.0 已经将其移除了;

2)移除了 org.springframework.boot.actuate.endpoint.http 包下的 ActuatorMediaType 和 ApiVersion 类;

3)jOOQ’s *Provider 的相关回调接口实现和设置已经被弃用,现在应该改用 DefaultConfigurationCustomizer 配置类;

4)org.springframework.boot.autoconfigure.data.jpa 包下的 EntityManagerFactoryDependsOnPostProcessor 类被移到了 org.springframework.boot.autoconfigure.orm.jpa 包下;

3、pom配置

10月21日,Spring官方发布了Spring Boot 2.5.6版本,此版本包括 43 个错误修复、文档改进和依赖项升级。

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.5.6</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

4、缺陷修复

  • 由于 R2DBC 优先于 JDBC,jOOQ 的 DSLContext 不可用时的误导性故障分析#28379
  • 启用延迟初始化,JMX 端点不可用 #28371
  • JarFileWrapper 可能会导致许多 FinalReferences 导致 GC 压力 #28356
  • 默认情况下不清理 VCAP_SERVICES 属性 #28353
  • 计时器不能正确解析 带有 d 字符后缀的 MeterValue #28351
  • CachingOperationInvoker 缓存会消耗大量的堆空间 #28347
  • 当内存中 R2DBC 数据库和 SQL 初始化脚本 devtools 重启失败 #28345
  • ActiveMQ 启动器依赖于 org.apache.geronimo.specs:geronimo-j2ee-management_1.1_spec #28340
  • spring-boot-starter-oauth2-client 不需要依赖 com.sun.mail.jakarta. #28333
  • Layertools 提取不保留上次修改和上次访问时间 #28190
  • spring.rabbitmq.addresses 使用 IPv6 地址配置时出现 NumberFormatException #28134
  • PrometheusScrapeEndpoint 生成的 OpenMetrics 内容无法解析 #28130

5、依赖升级

  • 升级 Dropwizard Metrics 4.1.26 #28280
  • 升级 Ehcache3 3.9.7 #28394
  • 升级 HttpCore5 5.1.2 #28281
  • 升级 Jaybird 4.0.4.java8 #28282
  • 升级 Jetty 9.4.44.v20210927 #28283
  • 升级 Lombok 1.18.22 #28284
  • 升级 Micrometer 1.7.5 #28242
  • 升级 MySQL 8.0.27 #28395
  • 升级 Netty 4.1.69.Final #28360
  • 升级 Netty tcNative 2.0.44.Final #28285
  • 升级 Postgresql 42.2.24 #28286
  • 升级 R2DBC Bom Arabba-SR11 #28287
  • 升级 Reactor 2020.0.12 #28240
  • 升级 SendGrid 4.7.6 #28396
  • 升级 Spring AMQP 2.3.11 #28245
  • 升级 Spring Data 2021.0.6 #28244
  • 升级 Spring Framework 5.3.12 #28241
  • 升级 Spring HATEOAS 1.3.5 #28243
  • 升级 Spring Integration 5.5.5 #28249
  • 升级 Spring Kafka 2.7.8 #28246
  • 升级 Spring Security 5.5.3 #28247
  • 升级 Spring Session 2021.0.3 #28248
  • 升级 Tomcat 9.0.54 #28288, 有个致命(CVE-2021-42340)漏洞
  • 升级 Undertow 2.2.12.Final #28289
  • 升级 XmlUnit2 2.8.3 #28397

官方公告:Spring Boot 2.5.6 is now available

6、升级建议

       如果当前版本不影响使用,不建议频繁升级,当然,如果你现在还没升级到 Spring Boot 2.5.x 或者 2.4.x,那么可以直接升级到最新版本。

       但是,我个人不建议直接在生产环境上升级 Spring Boot 2.5.x,从这修复 bug 的周期和 bug 数量都能看出来,版本太不稳定了,修复 bug 的版本周期越来越短了,谁能跟得上。。

      切记不要轻易升级到这个版本,如需升级,最好是详细看清楚变更内容,做好充分测试,以免年终奖没了。。

       后续栈长有时间再深入研究下更多的细节,给大家带来更详细的技术文章。

参考:

https://segmentfault.com/a/1190000040049702

Spring Boot 2.5.6 发布 | 程序猿DD

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
rar包内含有spring2.5.6源码,解压即可使用 源代码分析,是一件既痛苦又快乐的事情,看别人写的代码是通过的,但当你能够看明白的时候,相信快乐也会随之而来,为了减少痛苦,更快的带来快乐,在这里希望通过这篇文章对觉得困难的朋友有一个帮助。 本文以spring框架的XmlBeanFactory为入手点进行分析,希望能够以尽量简洁明了的方式给予有需要的朋友一定的帮助。 首先来打开该类的代码,我们将看到如下代码: Java代码 public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } } public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } } 这个类的代码很简单,一个成员对象加两个构造函数,从这里我们可以看出,最重要的地方在于最后一个构造函数: Java代码 super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); 第一句就是将父亲工厂交给父类的构造函数,实际上最后也就是把父工厂保存到类的parentBeanFactory成员对象中,这个对象是在AbstractBeanFactory抽象类中定义的,而这个父工厂也会一直传递到该抽象类进行保存。第二句就是整个类中最重要的地方了,顾名思义,它的目的是通过XmlBeanDefinitionReader这个XML的Reader从资源resource中(也就是你的配置文件)读取bean的定义。接下来我们打开XmlBeanDefinitionReader的loadBeanDefinitions方法,我们可看到在这个方法里代码就一行,调用了一个同名不同参的方法,而参数是EncodedResource的一个实例,这个类实际上是Resource的一个包装类,用来保存资源的Encode的,那接下来我们再看被调用的loadBeanDefinitions方法,这个方法里最主要的部分就是: Java代码 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 这里的目的是将资源包装成一个InputSource,连同Resource作为参数传递到doLoadBeanDefinitions方法 Java代码 DocumentBuilderFactory factory = createDocumentBuilderFactory(); if (logger.isDebugEnabled()) { logger.debug("Using JAXP implementation [" + factory + "]"); } DocumentBuilder builder = createDocumentBuilder(factory); Document doc = builder.parse(inputSource); return registerBeanDefinitions(doc, resource); DocumentBuilderFactory factory = createDocumentBuilderFactory(); if (logger.isDebugEnabled()) { logger.debug("Using JAXP implementation [" + factory + "]"); } DocumentBuilder builder = createDocumentBuilder(factory); Document doc = builder.parse(inputSource); return registerBeanDefinitions(doc, resource); 这个方法的目的一目了然,就是为了将资源解释成为Document对象,然后调用registerBeanDefinitions方法,这里不做详细解释,不了解的话请去看看关于JAXP的介绍。接下来我们打开registerBeanDefinitions方法: Java代码 public int registerBeanDefinitions(Document doc, Resource resource) throws BeansException { XmlBeanDefinitionParser parser = (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass); return parser.registerBeanDefinitions(this, doc, resource); } public int registerBeanDefinitions(Document doc, Resource resource) throws BeansException { XmlBeanDefinitionParser parser = (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass); return parser.registerBeanDefinitions(this, doc, resource); } 这里创建了一个XmlBeanDefinitionParser接口的实现,这个接口的具体类是DefaultXmlBeanDefinitionParser,这个接口很简单,只有registerBeanDefinitions一个方法,这个方法的作用也很明了,就是用来注册Bean的定义的,所以说类和方法的名字一定要起得有意义,这样可以让人一看就大概了解其作用,减少了很多阅读代码的痛苦。废话不多说,我们打开DefaultXmlBeanDefinitionParser的registerBeanDefinitions方法,这个类就是解释XML配置文件的核心类了,打开registerBeanDefinitions方法后我们看到如下代码: Java代码 public int registerBeanDefinitions(BeanDefinitionReader reader, Document doc, Resource resource) throws BeanDefinitionStoreException { this.beanDefinitionReader = reader; this.resource = resource; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); //初始化根元素 initDefaults(root); if (logger.isDebugEnabled()) { logger.debug("Default lazy init '" + getDefaultLazyInit() + "'"); logger.debug("Default autowire '" + getDefaultAutowire() + "'"); logger.debug("Default dependency check '" + getDefaultDependencyCheck() + "'"); } preProcessXml(root);//一个空方法用于扩展 int beanDefinitionCount = parseBeanDefinitions(root);//解释配置的主要方法 if (logger.isDebugEnabled()) { logger.debug("Found " + beanDefinitionCount + " elements in " + resource); } postProcessXml(root); //一个空方法用于扩展 return beanDefinitionCount; } public int registerBeanDefinitions(BeanDefinitionReader reader, Document doc, Resource resource) throws BeanDefinitionStoreException { this.beanDefinitionReader = reader; this.resource = resource; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); //初始化根元素 initDefaults(root); if (logger.isDebugEnabled()) { logger.debug("Default lazy init '" + getDefaultLazyInit() + "'"); logger.debug("Default autowire '" + getDefaultAutowire() + "'"); logger.debug("Default dependency check '" + getDefaultDependencyCheck() + "'"); } preProcessXml(root);//一个空方法用于扩展 int beanDefinitionCount = parseBeanDefinitions(root);//解释配置的主要方法 if (logger.isDebugEnabled()) { logger.debug("Found " + beanDefinitionCount + " elements in " + resource); } postProcessXml(root); //一个空方法用于扩展 return beanDefinitionCount; } 在这个方法当中,主要用于解释定义的有两个方法,一个是initDefaults,一个是parseBeanDefinitions,第一个方法是用来解释根元素的属性的,例如lazy-init, autowire等,而parseBeanDefinitions就是用来解释具体的bean定义了,方法代码如下: Java代码 protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException { NodeList nl = root.getChildNodes(); int beanDefinitionCount = 0; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (IMPORT_ELEMENT.equals(node.getNodeName())) { importBeanDefinitionResource(ele); } else if (ALIAS_ELEMENT.equals(node.getNodeName())) { String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); this.beanDefinitionReader.getBeanFactory().registerAlias(name, alias); } else if (BEAN_ELEMENT.equals(node.getNodeName())) { beanDefinitionCount++; BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory()); } } } return beanDefinitionCount; } protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException { NodeList nl = root.getChildNodes(); int beanDefinitionCount = 0; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (IMPORT_ELEMENT.equals(node.getNodeName())) { importBeanDefinitionResource(ele); } else if (ALIAS_ELEMENT.equals(node.getNodeName())) { String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); this.beanDefinitionReader.getBeanFactory().registerAlias(name, alias); } else if (BEAN_ELEMENT.equals(node.getNodeName())) { beanDefinitionCount++; BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory()); } } } return beanDefinitionCount; } 其他标签具体如何被解释这里就不多说,相信大家也能看得懂,这里主要讲一下解释bean的的处理,我们注意以下代码: Java代码 else if (BEAN_ELEMENT.equals(node.getNodeName())) { beanDefinitionCount++; BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory()); } else if (BEAN_ELEMENT.equals(node.getNodeName())) { beanDefinitionCount++; BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory()); } 这里是当碰到一个bean标签的时候所进行的处理,也既是对bean的定义进行解释,可以看到parseBeanDefinitionElement方法的第一个参数就是bean则个元素,第二个参数表示该bean是否为内置的bean,从这里进行解释的bean都不可能是内置的,所以这里直接以false为参数,打开parseBeanDefinitionElement方法,就可以看到这个方法里就是对bean的内部的解释,也很简单,也不多讲了,呵呵(下班时间已经到了,所以就写这么多了,基本的流程也就这样,没什么特别难的地方。),对了,最后还有一点就是解释完后,bean的定义将会被保存到beanFactory中,这个beanFactory的实现就是XmlBeanFactory了,该beanFactory是在new的时候被传递到reader中的,就是该类中以下这行代码: Java代码 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); 好了,就这么多了,本文只作为参考,只讲解了如何加载bean定义这块,只作为一个参考,希望对其他朋友能有所帮助吧,因为时间匆忙,有错漏的地方请指正。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值