天天看點

将 Spring Boot 應用遷移到 Java 9 — 子產品

配置子產品中繼資料

Java 9 中的子產品資訊通過 

module-info.java

 檔案實作。第一步是在源目錄的根目錄下建立一個這樣的檔案,子產品名稱為:

module org.springframework.samples.petclinic {
}
           

剩下的旅程可能是天堂或地獄。不過我很幸運受益于 IntelliJ IDEA,這個 IDE 向導會準确地說出它不能讀取的類,并允許您将其放入子產品檔案中。最後,看起來是這樣的:

module org.springframework.samples.petclinic {
    requires java.xml.bind;
    requires javax.transaction.api;
    requires validation.api;
    requires hibernate.jpa;
    requires hibernate.validator;
    requires spring.beans;
    requires spring.core;
    requires spring.context;
    requires spring.tx;
    requires spring.web;
    requires spring.webmvc;
    requires spring.data.commons;
    requires spring.data.jpa;
    requires spring.boot;
    requires spring.boot.autoconfigure;
    requires cache.api;
}
           

請注意, 

maven-compiler-plugin

 和 

maven-surefire-plugin

 中的子產品配置可以被删除。

不使用 IDE 進行配置

如果你手上恰巧沒有理想的 IDE 環境,配置過程如下:

  1. 運作 

    mvn clean test

  2. 分析錯誤日志來擷取缺少的包名
  3. 定位到上面提到的包
  4. 如果這是一個子產品 Jar,那麼把子產品名稱加到依賴子產品名稱清單裡面
  5. 否則,猜測一下自動子產品名稱(譯注:自動子產品名稱(automatic module name)是為了在java 9 項目中使用那些沒有進行子產品化處理的包(即沒有 module-info )而出現的概念,它們的名字一般就是去掉了版本号的jar封包件名),再加入到依賴子產品名稱清單裡面

例如:

[ERROR] ~/spring-petclinic/src/main/java/org/springframework/samples/petclinic/system/CacheConfig.java:[21,16]
  package javax.cache is not visible
[ERROR]   (package javax.cache is declared in the unnamed module, but module javax.cache does not read it)      

javax.cache

 位于 

cache-api-1.0.0.jar

中,由于這不是一個子產品,是以在 JAR 包中沒有 module-info 檔案。但可知它的自動子產品名稱為 cache.api 。将其寫入到子產品依賴中。重複上述步驟。

ASM 故障

從這篇文章的第一部分開始,我已經意識到,Spring Boot 1.5 将不會與 Java 9 相容。我們開始吧。

将 Spring Boot 更新到 2.0.0.M5,需要對子產品依賴性進行一些更改:

  • hibernate.validator

     變為 

    org.hibernate.validator

  • validation.api

     變為 

    java.validation

當你認為它可以工作的時候:

Caused by: java.lang.RuntimeException
	at org.springframework.asm.ClassVisitor.visitModule(ClassVisitor.java:148)      

這個問題已經記錄在案。此時,顯式聲明主類解決了這個問題。

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <jvmArguments>--add-modules java.xml.bind</jvmArguments>
        <mainClass>org.springframework.samples.petclinic.PetClinicApplication</mainClass>
    </configuration>
    ...
</plugin>      

Javassist 失敗

現在 app 可以使用以下指令做測試了: 

mvn clean spring-boot:run

 。不幸的是,一個新的異常擋住了我們前行的路:

2017-10-16 17:20:22.552  INFO 45661 --- [           main] utoConfigurationReportLoggingInitializer :

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2017-10-16 17:20:22.561 ERROR 45661 --- [           main] o.s.boot.SpringApplication               :
 Application startup failed

org.springframework.beans.factory.BeanCreationException:
  Error creating bean with name 'entityManagerFactory' defined in class path resource
   [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]:
    Invocation of init method failed; nested exception is org.hibernate.boot.archive.spi.ArchiveException:
     Could not build ClassFile      

快速搜尋下定位到 Java 9 和 Javassit 的一個不相容性問題。Javassist 是這裡的罪魁禍首。它的依賴性需要 Spring Data JPA,通過 Hibernate 傳遞。要解決它,需要移除依賴關系,并添加最新版本:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>javassist</artifactId>
            <groupId>org.javassist</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.22.0-GA</version>
    <scope>runtime</scope>
</dependency>      

幸運的是,此版本是相容的 —— 最起碼對我們的用法是。

它運作了!

我們做到了! 如果你到了這一步,你應該拍拍肩膀,喝杯啤酒,或者你認為你應該得到的任何東西。

錦上添花的是,開發工具的依賴可以被重新添加。

結論

不管你喜歡還是不喜歡,遷移到 Java 9 需要使用 Jigsaw。至少,這意味着一趟痛苦的旅程,以及遇到錯誤就要搜尋下一個更新檔的過程,并且還要删除建構過程中的重要步驟。對于庫/架構開發人員來說,為他們的内部 api 添加額外的通路控制層是很有趣的,對于應用程式來說這樣做比較少。在這個階段,不值得遷移到 Java 9。

我希望在幾個月後再進行這個實驗,到時候再留意情況的改善。

繼續閱讀