在从Spring Boot 1.5系迁移到2.0系的迁移过程中,我们所做的工作是什么
首先
我对这个已经进行了Kotlin兼容的项目进行了SpringBoot 2.0.2更新操作。在这里,我将写下那时发生的工作等内容。
マイグレーション前
1.5.8.RELEASE
マイグレーション後
2.0.2.RELEASE
从Spring Boot2.0开始,需要使用Java 8或更高版本。
另外,由于该项目作为REST API服务器运行,因此可以避免进行Thymeleaf的迁移。
另外,由于正在进行Spring MVC项目的迁移,因此将在其他地方进行说明。
我主要参考了以下网站和文章。
-
- Spring-Boot-2.0-Migration-Guide
- Spring Boot 1.5.10 → Spring Boot 2.0.0 にしたときの覚書
首先,进行Spring Boot版本升级。
我将修改pom.xml文件,提升Spring Boot的版本。
提取并总结变更部分
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
- <version>1.5.8.RELEASE</version>
+ <version>2.0.2.RELEASE</version>
<relativePath />
</parent>
・・・
<dependency>
<!-- https://docs.spring.io/spring-session/docs/current/reference/html5/guides/java-redis.html -->
<groupId>org.springframework.session</groupId>
- <artifactId>spring-session</artifactId>
+ <artifactId>spring-session-data-redis</artifactId>
</dependency>
・・・
<!-- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#jackson--json-support -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-json</artifactId>
+ </dependency>
・・・
<!- 元々HikariPCを使用していたので依存関係を削除 -->
<!-- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#configuring-a-datasource -->
- <dependency>
- <groupId>com.zaxxer</groupId>
- <artifactId>HikariCP</artifactId>
- </dependency>
・・・
<!-- ここはテストが終わったら削除 ->
<!-- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#before-you-start -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-properties-migrator</artifactId>
+ <scope>runtime</scope>
+ </dependency>
不使用Application类的WebMvcConfigurerAdapter。
- public class XxxApplication extends WebMvcConfigurerAdapter {
+ public class XxxApplication implements WebMvcConfigurer{
构建 -> 修正 -> 重新构建
完成pom.xml的设置后,进行构建并进行反复修正。
在JPA存储库的findOne方法中出现了编译错误。
首先,发生了如下错误。
Error:(41, 41) Kotlin: Type inference failed: fun <S : #{テーブル名}!> findOne(p0: Example<S!>!): Optional<S!>!
cannot be applied to
(Long)
Error:(41, 49) Kotlin: Type mismatch: inferred type is Long but Example<(???..???)>! was expected
这是因为Spring Data的CRUD方法已经发生了变化。
将第一行的错误更改为从”findOne”改为”findById”。
将第二行的错误更改为根据”findById”的返回值为Java的Optional类型,需要从Optional中调用”get”方法。
同样,由于save和delete的参数已经从传递列表变为传递实体对象,因此需要改为调用saveAll和deleteAll方法。
当然,如果只影响单个记录,可以使用save和delete方法,根据迁移所需的时间来决定。
无法解决ConfigureRedisAction。
Error:(13, 53) java: パッケージorg.springframework.session.data.redis.configは存在しません
顺序可能会倒转,但在以下定义的设置中,ConfigureRedisAction无法解析,这是因为它已经被拆分到名为spring-session-data-redis的包中。
@Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
只需一个选项:请参考pom.xml中spring-session部分的修订内容。
另外,原本没有添加lettuce的依赖关系是因为spring-boot-starter-data-redis已经包含了这个依赖关系。
org.hibernate.validator.constraints.NotBlank等的废弃
根据此进行调整,替换为javax.validation.constraints.NotEmpty等。
使用JDBC的自动配置出现错误。
Error:(7, 51) java: シンボルを見つけられません
シンボル: クラス DataSourceBuilder
場所: パッケージ org.springframework.boot.autoconfigure.jdbc
这似乎是因为处理了多个数据来源。
hoge.datasource.driver-class-name = com.mysql.jdbc.Driver
hoge.datasource.url = jdbc:mysql://127.0.0.1:33306/hoge
hoge.datasource.username = hoge
hoge.datasource.password = fuga
# その他は省略
# 上記x複数データソース分
最初的设定如下所示。
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "hogeEntityManagerFactory", transactionManagerRef = "hogeTransactionManager", basePackages = {
"jp.xxx.data.hoge.repository" })
public class HogeDatabaseConfig extends AbstractDatabaseConfig {
@Bean(name = "hogeDataSource")
@ConfigurationProperties(prefix = "hoge.datasource")
public DataSource hogeDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "hogeEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean hogeEntityManagerFactory(EntityManagerFactoryBuilder builder,
return builder.dataSource(hogeDataSource)
.packages("jp.xxx.data.hoge.entity").persistenceUnit("hoge")
.properties(buildProperties()).build();
}
@Bean(name = "hogeTransactionManager")
public PlatformTransactionManager hogeTransactionManager(
@Qualifier("hogeEntityManagerFactory") EntityManagerFactory hogeEntityManagerFactory) {
return new JpaTransactionManager(hogeEntityManagerFactory);
}
}
然而,我对这部分有一种更巧妙的方式的想法。
@Configuration
@ConfigurationProperties(prefix = "hoge.datasource")
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "hogeEntityManagerFactory", transactionManagerRef = "hogeTransactionManager", basePackages = {
"jp.xxx.data.hoge.repository" })
public class HogeDatabaseConfig extends AbstractDatabaseConfig {
@Autowired
private Environment env;
@Bean(name = "hogeDataSource")
public DataSource hogeDataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("hoge.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("hoge.datasource.url"));
dataSource.setUsername(env.getProperty("hoge.datasource.username"));
dataSource.setPassword(env.getProperty("hoge.datasource.password"));
return dataSource;
}
@Bean(name = "hogeEntityManagerFactory")
public EntityManagerFactory hogeEntityManagerFactory(
@Qualifier("hogeDataSource") DataSource hogeDataSource) {
final LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(hogeDataSource);
factoryBean.setPersistenceUnitName("hoge");
factoryBean.setPackagesToScan("jp.xxx.data.hoge.entity");
factoryBean.setJpaPropertyMap(buildProperties());
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factoryBean.afterPropertiesSet();
return factoryBean.getNativeEntityManagerFactory();
}
@Bean(name = "hogeTransactionManager")
public PlatformTransactionManager hogeTransactionManager(
@Qualifier("hogeEntityManagerFactory") EntityManagerFactory hogeEntityManagerFactory) {
return new JpaTransactionManager(hogeEntityManagerFactory);
}
}
在这里启动Tomcat。
尽管Tomcat已经启动,但以下错误日志正在显示。
java.lang.ClassCastException: jp.xxx.service.HogeService$Fuga cannot be cast to org.springframework.core.io.support.ResourceRegion
at org.springframework.http.converter.ResourceRegionHttpMessageConverter.writeResourceRegionCollection(ResourceRegionHttpMessageConverter.java:182)
at org.springframework.http.converter.ResourceRegionHttpMessageConverter.writeInternal(ResourceRegionHttpMessageConverter.java:139)
at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:102)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:272)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:180)
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:119)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
由于我们的数据源中包含PostgreSQL,所以似乎出现了这个问题。
通过创建并配置hibernate.properties文件来解决了这个问题。
hibernate.jdbc.lob.non_contextual_creation = true
到目前为止,启动过程中已经不再发生错误了。
突如其来的ClassCastException
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method jp.xxx.controller.HogeController.createFuga, parameter form
at jp.xxx.controller.HogeController.createCampaign(HogeController.kt)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
我已经将HogeForm类的参数类型转换为HogeForm类的可空类型,并进行了相应的处理。
日期类型转换错误
Failed to convert value of type 'java.lang.String[]' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2018-06-11T00:00:00'; nested exception is java.lang.IllegalArgumentException
这个地方有点困扰,原因并不太清楚,但是通过将接收数据的对象从 Kotlin 的 Data 类改为普通的 Kotlin 类来解决了问题。
使用PUT方法时,表单的值无法传递。
因为没有时间去调查原因,所以我将PUT的部分改写为POST。