IntelliJ玩轉Spring Boot: ClassNotFoundException javax.servlet.ServletContext

一個簡單的專案需求,專案要可以正常在Eclipse和IntelliJ內執行、執行可以選擇用bootRun或是run application的方式啟動,部屬是用war部屬。這邊簡單的用Spring Boot建立一個Gradle專案,View則是使用JSP為例,透過IDE的精靈跟簡單的幾行程式碼就能建立一個簡單的web應用程式,程式碼如下,也可以參考

執行一下,沒意外地在console正常的看到tomcat正常啟動於8080 port了。但其實在這一切正常執行前,遇到了一個可能比較吹毛求疵的問題。

用IntelliJ執行的時候出現了ServletContext class not found

這邊發現一些很有趣的事情,基本上用bootRun的方式(一般建議方式)執行專案,不管在哪個IDE跑上都沒問題,但如果是用run application的方式,在IntelliJ啟動時就會出現上面圖所顯示的class not found例外,但在Eclipse卻可以正常執行。

大致上爬文,主要原因被歸為IntelliJ的bug,而可以透過下面這行修改即可排解:

providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')

要改成

compile('org.springframework.boot:spring-boot-starter-tomcat')

有沒有provided最大的差別是,被下載下來的dependencies最後會不會被包進archived裡,而compile和runtime的選擇最大差別在於這個dependency是在專案編譯的時候就需要,還是執行的時候才需要,參考。由此可見,當使用war的方式部屬,其實是不希望這個embed tomcat jar檔也一起被包起來。但因IntelliJ bug原因,執行會出錯,雖可以改成用compile方式就可以解決,只是在包war時或commit時都要改來改去,還是會希望有其他解決方式。

其實網路爬文後還有第二招,就是把執行的module classpath從main改成用test package下的去執行。原理一樣,因為intelliJ沒有在classpath注入provided的函示庫關係,所以透過不同的module(例如test)裡面可能擁有這些dependencies了,繞一圈的方式去啟動。自己測試約5個不同專案結構,4個半都可以用這招解決,剩下半個可能是自己還沒領悟吧。

其實後來想想,spring boot主要發布可以為executable jar 或是 war檔,如果今天只是要發jar檔,那其實可以把下列這行移除

apply plugin: 'war'

還有一種解法,spring-boot-starter-web裡面已經含有spring-boot-starter-tomcat,如下把31行移除其實也可以正常執行,參考

compile ("org.springframework.boot:spring-boot-starter-web")
providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')

雖然花了半天在疑難排解上述問題,但其實只要一開始用建議的方式bootRun去執行,就不會有ClassNotFoundException javax.servlet.ServletContext的問題。但無論如何,上面四種方式都可以解決問題,千萬不要因為class not found又跑去找相關的servlet dependencies。

最近的語言框架越來越強大,IDE也越來越方便,現在要建立起一個可執行的專案,真的是彈指之間就可完成。但其實穿梭在這些工具框架裡,要如何建立一個好的專案環境供不同開發者環境使用,還真得花花功夫。

 

Leave a Reply

當第一個留言者!

avatar
wpDiscuz