在从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。

当REST控制器的返回值类型为List时,无法将其转换为JSON并导致错误。忘记获取错误日志。最初的实现本来不是用List来返回……

    @GetMapping("/hoge")
    public List<Object> hoge() {
        return hogeService.hoges();
    }

因返回类型相当复杂,所以在控制器中将其转换为JSON格式返回。

    @GetMapping("/hoge")
    public String hoge() throws JsonProcessingException {
        final List<Object> hoges = hogeService.hoges();
        final ObjectMapper mapper = new ObjectMapper();
        return mapper.writeValueAsString(hoges);
    }

经过以上步骤,迁移已经完成,成功进行了迁移。

bannerAds