大家都知道霹菊,對于springboot的app,我們都是通過maven構(gòu)建出war或jar,然后通過java -jar X.war/X.jar的方式來運行項目旋廷。在這篇文章中鸠按,我們來探討一下,不同的包文件后綴和打包方式饶碘,jsp的文件路徑應(yīng)該如何設(shè)置才能正常顯示目尖。
這里我們討論三種情況
1.正常通過maven build成app.war包,即在pom.xml指定<packaging>war</packaging>
2.基于1中build好的war包扎运,強行改名為faker.jar (之所以討論這種情況瑟曲,是因為這種case真實存在于我們的項目中,只單純作為一個研究的case)
3.正常通過maven build成real.jar包豪治,即在pom.xml指定<packaging>jar</packaging>
首先我們先簡單捋一下jsp頁面的加載流程洞拨。對于一個請求,會mapping到對應(yīng)mvc view的template (比如這里對應(yīng)的是/a)负拟,這個template會根據(jù)已經(jīng)配置好的template base path prefix, (比如這里項目里面配置的是/WEB-INF/jsp), 構(gòu)建出/WEB-INF/jsp/a.jsp的相對路徑烦衣,然后通過這個相對路徑,去web resources里面遍歷匹配是否jsp文件真實存在(具體如何匹配掩浙,下面會詳細介紹)花吟。如果不存在,最終返回給前端的是404 not found exception涣脚。如果存在示辈,會基于這個jsp進行java文件的生成以及class文件的編譯,然后解析返回給前端顯示遣蚀。
所以jsp頁面正常顯示問題的關(guān)鍵在于,如何在上述的三種情況下纱耻,保證jsp都可以在web resource里面找到芭梯。這里我們先針對第一種情況下介紹一下web resource生成的過程以及jsp路徑匹配的過程。
web resource本身是一個List弄喘,用來存放文件的路徑玖喘,包括五種resource,這里我們只需要討論main resource和jar resource蘑志,
org/apache/catalina/webresources/StandardRoot.class
web resource初始化的大致調(diào)用鏈如下
org/springframework/boot/SpringApplication.class----run()
org/springframework/context/support/AbstractApplicationContext.class----refresh()
org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.class----onRefresh()
org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.class----getWebServer()
org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.class----prepareContext()
可以看到這里開始獲取DocumentRoot累奈,具體實現(xiàn)細節(jié)會執(zhí)行三個方法返回具體路徑。因為這里我們的命令行是java -jar app.war, 所以判斷后綴為.war,返回路徑為/Users/hzzou/Downloads/cronus/demo/app.war
然后基于剛才返回的DocumentRoot設(shè)置DocBase急但,為/Users/hzzou/Downloads/cronus/demo/app.war
設(shè)置好了DocBase澎媒,開始啟動tomcat
這里開始涉及到web resource的初始化
(1)設(shè)置main resource
這里需要注意這里的internalPath為“/”,初始化會自動轉(zhuǎn)成“”
main resource初始化成功如下
(2)設(shè)置jar resource
獲取擁有META-INF/resources的jar
jar resource初始化成功如下
剛才提到的jsp的relative path /WEB-INF/jsp/a.jsp波桩,會遍歷web resource戒努,根據(jù)base path和internal path構(gòu)造絕對路徑,粗略構(gòu)造效果等同與base path + internal path + jsp relative path镐躲。我們可以看到储玫,對于情況1侍筛,是可以在main resource知道到對應(yīng)jsp頁面的。所以我們針對情況1撒穷,a.jsp頁面只需要放置在/WEB-INF/jsp目錄下面即可匣椰。
而針對情況2,我們發(fā)現(xiàn)在設(shè)置DocumentRoot的時候出現(xiàn)不同端礼,getWarFileDocumentRoot 和getExplodedWarFileDocumentRoot 和 getCommonDocumentRoot 都是返回null窝爪,導(dǎo)致DocBase為一個temp dir,/var/folders/hn/87t2n4nx7hdbxq1chffy7gpr0000gq/T/tomcat-docbase.8080.11206243289434379479
這也直接導(dǎo)致最后的main resource里面構(gòu)建的是一個dir resource
所以對于情況2齐媒,我們沒有辦法適配蒲每,因為此時的main resource并不能幫助定位到j(luò)sp的具體位置
而針對情況3,對于main resource的構(gòu)建如情況2喻括,但是在構(gòu)建jar resouce的時候出現(xiàn)不同邀杏。因為我們知道,構(gòu)建jar resource的集合是有META-INF/resources的jar唬血,所以本身項目的jar也會被加載進去jar resource里面望蜡,具體效果如下
所以對于情況3,我們可以把WEB-INF/jsp/a.jsp放在META-INF/resources下面拷恨,也是可以讓jsp正常被展示的, 具體如下
結(jié)論:
針對情況1脖律,jsp路徑為webapp/WEB-INF/jsp/a.jsp
針對情況2,無法適配
針對情況3腕侄,jsp路徑為jsp路徑為META-INF/resources/WEB-INF/jsp/a.jsp