当从Spring Boot 1.5迁移到Spring Boot 2.0时,所发生的变更如下:

首先

我将在这里记录一下将Spring Boot 1.5的应用程序(约7KL,SPA的服务器部分)迁移到Spring Boot 2.0时需要进行的修改点。
这涉及到类和包的更改,以及某些功能已被弃用,所以需要进行相当数量的修改。

顺便说一下

最初并没有打算将Spring Boot升级到2.0版本,只是想要访问Elasticsearch 6而已,但为了访问Elasticsearch 6,必须将spring-boot-starter-data-elasticsearch升级到2.0版本以及spring Boot自身也不得不升级到2.0版本,这是无奈之举。

对Spring Boot进行修正的部分

Maven (一个项目管理和构建自动化工具)

这次迁移的应用程序依赖于以下库。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.ldap</groupId>
  <artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.ldap</groupId>
  <artifactId>spring-ldap-core-tiger</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-ldap</artifactId>
</dependency>

最初我們使用的是Spring IO平台,但是它的結束生命周期(EOL)設定在2019年9月,且其中提到了「建議使用spring-boot-starter-parent或者導入spring-boot-dependencies bom」。

根據這個建議,我們決定從Spring IO平台轉換至spring-boot-starter-parent。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.spring.platform</groupId>
      <artifactId>platform-bom</artifactId>
      <version>Brussels-SR3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.2.RELEASE</version>
</parent>

更改application.yml文件的属性

上下文路径已更改。看起来即使保持原样也能正常工作,但最好还是进行修改为妥当。

server:
  context-path: /sample
server:
  servlet:
    context-path: /sample

废除Adapter类

由于Spring Boot 2.0兼容Java 8,所以空实现的Adapter类被弃用,取而代之的是在接口本身添加了默认方法实现。
因此,不再使用Adapter的extends,而是直接实现接口。

public class WebMvcConfig extends WebMvcConfigurerAdapter {
public class WebMvcConfig implements WebMvcConfigurer {

自动配置的包变更

为了根据配置文件切换安全设置,我们将安全的自动配置SecurityAutoConfiguration排除在外,但是现在SecurityAutoConfiguration的包已经发生了变化。

import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;

@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;

@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)

似乎已经将位于security包下的servlet包进一步移动过去。

PasswordEncoder相关类的结构更改。

我们以前使用ShaPasswordEncoder来进行内部缓存,但是这个类已经被删除了。
PasswordEncoder相关的类结构正在进行重新审查。根据这个情况,使用弱算法的ShaPasswordEncoder类和Md5PasswordEncoder类已经消失了(虽然算法本身还存在,但被标记为不推荐使用)。

从Spring Boot 2.0开始,将使用PasswordEncoderFactories来生成PasswordEncoder。

MessageDigestPasswordEncoder encoder = new ShaPasswordEncoder(256);
return encoder.encodePassword(password);
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
return encoder.encode(password);

方法也已从encodePassword()更改为encode()。

可以将「Repository的方法名称/类型更改」翻译为「仓库的方法名/类型修改」。

这次修正中最困难的部分是这个(因为有很多个……)。CrudRepository的findOne()方法已被重命名为findById(),并且返回值从Entity类型变为Optional类型。

Employee employee = employeeRepository.findOne(employeeId);
Optional<Employee> employee = employeeRepository.findById(employeeId);

修改存储库分页生成方法。

PageRequest的new已经被弃用,取而代之,请使用of()。

employeeRepository.findAll(specifications,
        new PageRequest(page, size, new Sort(
                Sort.Direction.fromString(sortDirection), sortColumn)));
employeeRepository.findAll(specifications,
        PageRequest.of(page, size, new Sort(
                Sort.Direction.fromString(sortDirection), sortColumn)));

如果将PageRequest改为of(),将Sort也改为of()会使样式更统一。

employeeRepository.findAll(specifications,
        PageRequest.of(page, size, Sort.of(
                Sort.Direction.fromString(sortDirection), sortColumn)));

Specifications的弃用

随着Java 8的推出,Specification接口新增了默认实现,因此不再推荐直接使用实现了Specification接口的Specifications类。(虽然Specification接口的默认实现是使用Specifications类的。)

employeeRepository.findAll(
        Specifications.where((root, query, cb) -> cb.equal(property, value)));
employeeRepository.findAll(
        Specification.where((root, query, cb) -> cb.equal(property, value)));

Hibernate的修改部分

随着Spring Boot升级到2.0版本,Hibernate的版本也提升到了5.2.17,这导致了一些变化。

将Hibernate的包进行了更改。

使用Hibernate的NativeQuery时,我们曾经使用ExplicitParameterInfo类来显式指定NULL。然而,该类的包名已从org.hibernate.jpa.criteria.compile.ExplicitParameterInfo更改为org.hibernate.query.criteria.internal.compile.ExplicitParameterInfo。

2018/07/12更新
在PostgreSQL 10中,至少在指定NULL时,无法再使用ExplicitParameterInfo。
使用ExplicitParameterInfo会导致类似于“org.postgresql.util.PSQLException: ERROR: 演算子が存在しません: text = bytea”这样的错误。
因此,如果想指定NULL的类型,需要使用TypedParameterValue代替ExplicitParameterInfo。

import org.hibernate.jpa.criteria.compile.ExplicitParameterInfo;

// 中略

Query query = entityManager.createNativeQuery(query);
query.setParameter(new ExplicitParameterInfo<>("employeeName", null, String.class), null);
List<Employee> employeeList = query.getResultList();
import org.hibernate.jpa.TypedParameterValue;
import org.hibernate.type.StringType;

// 中略

Query query = entityManager.createNativeQuery(query);
query.setParameter("employeeName", new TypedParameterValue(new StringType(), null));
List<Employee> employeeList = query.getResultList();

更改UserType的方法参数。

在这个应用程序中,有一个实现了UserType的实现类,用于将PostgreSQL的JSONB列与Java的String相互转换。

在Spring Boot 2.0(Hibernate 5.2.17)中,nullSafeGet/nullSafeSet的参数SessionImplementor类已经被移除,取而代之的是SharedSessionContractImplementor。

public class PostgresJsonType implements UserType {
    @Override
    public Object nullSafeGet(ResultSet rs, String[] names,
            SessionImplementor session, Object owner)
            throws HibernateException, SQLException {
        // ・・・
    }

    public void nullSafeSet(PreparedStatement st, Object value,
            int index, SessionImplementor session)
            throws HibernateException, SQLException {
        // ・・・
    }

    // ・・・
}
public class PostgresJsonType implements UserType {
    @Override
    public Object nullSafeGet(ResultSet rs, String[] names,
            SharedSessionContractImplementor session, Object owner)
            throws HibernateException, SQLException {
        // ・・・
    }

    public void nullSafeSet(PreparedStatement st, Object value,
            int index, SharedSessionContractImplementor session)
            throws HibernateException, SQLException {
        // ・・・
    }

    // ・・・
}

其他

这是一开始就存在的实现问题,但可以将其保存在application.yml文件中。

app:
  function:
    message-type:
      AAA: xxx
      BBB: yyy

在使用ConfigurationProperties绑定值时,有一个叫做的定义。

@ConfigurationProperties(prefix = "app.function.messageType")

就像使用驼峰命名法引用链式变量并进行绑定一样。

在Spring Boot 2.0中,检查变得更严格,如果处于上述状态,就会发生错误。(尽管这本来是理所当然的错误。。)

请按照定义的要求在链接中写下来。

@ConfigurationProperties(prefix = "app.function.message-type")

总结

您可以参考官方网站上的Spring Boot 2.0 迁移指南,其中还包含了许多没有在此次迁移中遇到的变更点。

广告
将在 10 秒后关闭
bannerAds