当从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 迁移指南,其中还包含了许多没有在此次迁移中遇到的变更点。