欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Boot 2.0 升级指南

程序员文章站 2022-11-27 15:58:47
Spring Boot 2.0 升级指南 前言 Spring Boot已经发布2.0有5个月多,多了很多新特性,一些坑也慢慢被填上,最近有空,就把项目中Spring Boot 版本做了升级,顺便整理下升级的时候遇到的一些坑,做个记录。后续的教程就以最新的2.03版本为主。参考官方文档翻译 在你开始之 ......

spring boot 2.0 升级指南

前言

spring boot已经发布2.0有5个月多,多了很多新特性,一些坑也慢慢被填上,最近有空,就把项目中spring boot 版本做了升级,顺便整理下升级的时候遇到的一些坑,做个记录。后续的教程就以最新的2.03版本为主。参考官方文档翻译

在你开始之前

  • 2.x 至少需要 jdk 8 的支持,2.x 里面的许多方法应用了 jdk 8 的许多高级新特性,所以你要升级到 2.0 版本,先确认你的应用必须兼容 jdk 8。
    另外,2.x 开始了对 jdk 9 的支持。

  • 2.x 对第三方类库升级了所有能升级的稳定版本,一些值得关注的类库升级我给列出来了。

    1.spring framework 5+
    2.tomcat 8.5+
    3.flyway 5+
    4.hibernate 5.2+
    5.thymeleaf 3+

在 spring boot 2.0 中,许多配置属性被重新命名/删除,开发人员需要更新application.properties/ application.yml相应的配置。为了帮助你解决这一问题,spring boot 发布了一个新spring-boot-properties-migrator模块。一旦作为该模块作为依赖被添加到你的项目中,它不仅会分析应用程序的环境,而且还会在启动时打印诊断信息,而且还会在运行时为您暂时迁移属性。在您的应用程序迁移期间,这个模块是必备的:

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-properties-migrator</artifactid>
</dependency>

注意:迁移完成之后,请确保从项目的依赖关系中移除该模块。

构建您的 spring boot 应用程序

spring boot maven 插件
为了保持了一致性,并且避免与其他插件发生冲突,现在暴露的插件配置属性都以一个spring-boot前缀开始。

例如,以下命令prod使用命令行启用配置文件

mvn spring-boot:run -dspring-boot.run.profiles=prod

spring boot 新特性,默认动态代理策略

spring boot现在默认使用cglib动态代理(基于类的动态代理), 包括aop. 如果需要基于接口的动态代理(jdk基于接口的动态代理) , 需要设置spring.aop.proxy-target-class属性为false.

开发 web 应用程序嵌入式容器包装结构

为了支持响应式用例,嵌入式容器包结构已经被大幅度的重构。 embeddedservletcontainer已被重新命名为,webserver并且该org.springframework.boot.context.embedded包已被重新定位到org.springframework.boot.web.embedded。例如,如果您使用tomcatembeddedservletcontainerfactory回调接口定制嵌入式 tomcat 容器,则应该使用tomcatservletwebserverfactory。

特定于 servlet 的服务器属性
许多server.* 属性 ( servlet 特有的) 已经转移到server.servlet:

旧的属性 新的属性
server.context-parameters.* server.servlet.context-parameters.*
server.context-path server.servlet.context-path
server.jsp.class-name server.servlet.jsp.class-name
server.jsp.init-parameters.* server.servlet.jsp.init-parameters.*
server.jsp.registered server.servlet.jsp.registered
server.servlet-path server.servlet.path

模板引擎

mustache模板默认的文件扩展名

mustache模板的文件扩展名曾经是.html。现在的扩展名为.mustache,与官方规格和大多数的ide插件保持一致。你可以通过更改spring.mustache.suffix配置文件的configuration key来重写这个新的默认值。

jackson/json支持

在2.0版本,我们已经设置jackson配置文件的默认值,将iso-8601 strings写作了jsr-310。如果你希望返回之前的设置,可以添加spring.jackson.serialization.write-dates-as-timestamps=true 到你的配置文件中。

一个新的spring-boot-starter-json starter收集了必要的位去读写json。它不仅提供了jackson-databind,而且提供了和java8一起运作的时候相当有用的组件:jackson-datatype-jdk8, jackson-datatype-jsr310 和jackson-module-parameter-names。如果你曾经手动地依赖这些组件,现在可以依赖这个新的starter取代。

http/2 支持

为 tomcat,undertow 和 jetty 提供 http / 2 支持。支持取决于所选的 web 服务器和应用程序环境(因为 jdk 8 不支持该协议)。

配置属性的绑定

在 spring boot 2.0 中,用于绑定environment属性的机制@configurationproperties已经完全彻底修改。我们借此机会收紧了松散绑定的规则,并修复了 spring boot 1.x 中的许多不一致之处。

新的binderapi 也可以@configurationproperties直接在你自己的代码之外使用。例如,下面将结合到list的customerproperty对象:

list<customerproperty> people = binder.get(applicationcontext.getenvironment())
    .bind("customer.property", bindable.listof(customerproperty.class))
    .orelsethrow(illegalstateexception::new);

配置源可以像这样在 yaml 中表示:

customer:
  property:
  - first-name: wang
    last-name: lao
  - first-name: li
    last-name: sheng

actuator 改进

在 spring boot 2.0 中 actuator endpoints 有很大的改进。所有 http actuator endpoints 现在都在该/actuator路径下公开,并且生成的 json 有效负载得到了改进。

我们现在也不会在默认情况下暴露很多端点。如果您要升级现有的 spring boot 1.5 应用程序,请务必查看迁移指南并特别注意该management.endpoints.web.exposure.include属性。

hikaricp

spring boot 2.0 中的默认数据库池技术已从 tomcat pool 切换到 hikaricp。我们发现 hakari 提供了卓越的性能,我们的许多用户更喜欢 tomcat pool

初始化

数据库初始化逻辑在 spring boot 2.0 中已经合理化。spring batch,spring integration,spring session 和 quartz的初始化现在仅在使用嵌入式数据库时才会默认发生。该enabled属性已被替换为更具表现力枚举。例如,如果你想一直执行 spring batch 的初始化,您可以设置spring.batch.initialize-schema=always。

如果 flyway 或 liquibase 正在管理您的 datasource 的模式,并且您正在使用嵌入式数据库,spring boot 现在会自动关闭 hibernate 的自动 ddl 功能。

配置文件绑定

1.简单类型
在spring boot 2.0中对配置属性加载的时候会除了像1.x版本时候那样移除特殊字符外,还会将配置均以全小写的方式进行匹配和加载。所以,下面的4种配置方式都是等价的:

  • properties格式:
spring.jpa.databaseplatform=mysql
spring.jpa.database-platform=mysql
spring.jpa.databaseplatform=mysql
spring.jpa.database_platform=mysql
  • yaml格式:
spring:
  jpa:
    databaseplatform: mysql
    database-platform: mysql
    databaseplatform: mysql
    database_platform: mysql

tips:推荐使用全小写配合-分隔符的方式来配置,比如:spring.jpa.database-platform=mysql

2.list类型
在properties文件中使用[]来定位列表类型,比如:

spring.my-example.url[0]=http://example.com
spring.my-example.url[1]=http://spring.io

也支持使用逗号分割的配置方式,上面与下面的配置是等价的:

spring.my-example.url=http://example.com,http://spring.io

而在yaml文件中使用可以使用如下配置:

spring:
  my-example:
    url:
      - http://example.com
      - http://spring.io

也支持逗号分割的方式:

spring:
  my-example:
    url: http://example.com, http://spring.io

注意:在spring boot 2.0中对于list类型的配置必须是连续的,不然会抛出unboundconfigurationpropertiesexception异常,所以如下配置是不允许的:
name[0]=aaa
name[2]=bbb
在spring boot 1.x中上述配置是可以的,name[1]由于没有配置,它的值会是null

3.map类型
map类型在properties和yaml中的标准配置方式如下:

properties格式:

spring.my-example.foo=bar
spring.my-example.hello=world

yaml格式:

spring:
  my-example:
    foo: bar
    hello: world

注意:如果map类型的key包含非字母数字和-的字符,需要用[]括起来,比如:

spring:
  my-example:
    '[foo.baz]': bar

测试
对 spring boot 2.0 中提供的测试支持进行了一些补充和调整:

@webfluxtest已添加新注释以支持 webflux 应用程序的“切片”测试。
converter和genericconverter豆类现在自动扫描@webmvctest和@webfluxtest。
@autoconfigurewebtestclient已经添加了一个注释来提供一个webtestclientbean 供测试使用。注释会自动应用于@webfluxtest测试。
增加了一个新的applicationcontextrunner测试实用程序,可以很容易地测试您的自动配置。我们已将大部分内部测试套件移至此新模型。详细信息请参阅更新的文档。

升级遇到的一些问题总结

1.spring data jpa 2.0.x findone() 无效

在使用 spring data jpa 中,根据 主键获得对象,一般是使用<s extends t> s findone(example<s> example); 方法,或者 t getone(id id);,在 spring-data-jpa 2.0.x版本中,获取单个对象的方法改为 optional<t> findbyid(id id);返回的是一个optional<t>(java8新特性)optional 类是一个可以为null的容器对象。如果值存在则ispresent()方法会返回true,调用get()方法会返回该对象。delete()方法和findone()类似也被去掉了,可以使用deletebyid(long id)来替换,还有一个不同点是deletebyid(long id)默认实现返回值为void。

spring-data-jpa 1.5.2 的crudrepository 接口

@norepositorybean
public interface crudrepository<t, id extends serializable> extends repository<t, id> {

    <s extends t> s save(s entity);

    <s extends t> iterable<s> save(iterable<s> entities);

    t findone(id id);

    boolean exists(id id);

    iterable<t> findall();

    iterable<t> findall(iterable<id> ids);

    long count();

    void delete(id id);

    void delete(t entity);

    void delete(iterable<? extends t> entities);

    void deleteall();
}

spring-data-jpa 2.0.8 的crudrepository 接口

@norepositorybean
public interface crudrepository<t, id> extends repository<t, id> {

    <s extends t> s save(s entity);

    <s extends t> iterable<s> saveall(iterable<s> entities);

    optional<t> findbyid(id id);

    boolean existsbyid(id id);

    iterable<t> findall();

    iterable<t> findallbyid(iterable<id> ids);

    long count();

    void deletebyid(id id);

    void delete(t entity);

    void deleteall(iterable<? extends t> entities);

    void deleteall();
}

2.@generatedvalue(strategy = generationtype.auto)spring boot 2.0 需要指定主键的自增策略,这个和 spring boot 1.0 有所区别,1.0 会使用默认的策略@generatedvalue(strategy = generationtype.identity)

@id
@generatedvalue(strategy= generationtype.identity)
private long id;

3.jpa2.0的方言设置如果需要指定innodb存储引擎可以使用database-platform: org.hibernate.dialect.mysql57innodbdialect默认会使用myisam(不支持事务)在jpa中无法体现换成mybatis可以很好的验证。

4.日志类报错:spring boot 2.0 默认不包含 log4j,建议使用 slf4j 。

import org.apache.log4j.logger;
protected logger logger = logger.getlogger(this.getclass());

改为:

import org.slf4j.logger;
import org.slf4j.loggerfactory;
protected logger logger =  loggerfactory.getlogger(this.getclass());

5.thymeleaf 3.0 默认不包含布局模块。

将 pom 包升级到 2.0之后,访问首页的时候一片空白什么都没有,查看后台也没有任何的报错信息,首先尝试着跟踪了 http 请求,对比了一下也没有发现什么异常,在查询 thymeleaf 3.0 变化时才发现:spring boot 2.0 中spring-boot-starter-thymeleaf 包默认并不包含布局模块,需要使用的时候单独添加,添加布局模块如下:

<dependency>
   <groupid>nz.net.ultraq.thymeleaf</groupid>
   <artifactid>thymeleaf-layout-dialect</artifactid>
</dependency>

6.分页组件pagerequest变化。

在 spring boot 2.0 中 ,方法new pagerequest(page, size, sort) 已经过期不再推荐使用,推荐使用以下方式来构建分页信息:
pageable pageable =pagerequest.of(page, size, sort.by(sort.direction.asc,"id"));
跟踪了一下源码发现pagerequest.of()方法,内部还是使用的new pagerequest(page, size, sort),只是最新的写法更简洁一些。

public static pagerequest of(int page, int size, sort sort) {
    return new pagerequest(page, size, sort);
}