您好,登錄后才能下訂單哦!
這期內(nèi)容當中小編將會給大家?guī)碛嘘P如何進行Spring-boot JSP頁面無法訪問的問題排查,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
Spring-boot JSP頁面無法訪問問題排查
前段時間在公司項目框架更換時碰到了一個問題,公司項目原來用的是springMVC,沒有使用spring-boot,替換為spring-boot的時候遇到了一些問題,spring-boot可以將項目打包為一個可執(zhí)行jar/war包,對于web項目可以使用嵌入式tomcat代替tomcat,從而也可以達到直接執(zhí)行而不用部署到外部tomcat容器中的效果,而正是這個出現(xiàn)了問題。
問題
由于之前我做的項目都是前后端分離的,而這次項目需要用到JSP,所以出現(xiàn)了該問題,那就是JSP頁面無法訪問。
初步排查
先是從網(wǎng)上找資料,最多的就是添加以下配置
spring.mvc.view.suffix=.jsp spring.mvc.view.prefix=/WEB-INF/jsp/
但是項目中已經(jīng)有該配置了,而且同事那兒打包完是可以運行的,而我是直接在IDE中以main方法運行的(我用的IDE是IDEA),會不會跟這個有關系?我在本地將項目打包,打包完畢后運行發(fā)現(xiàn)JSP頁面是可以訪問的,而在IDE中運行的時候雖然JSP頁面訪問不了但是接口是可以正常訪問的,看來問題出在項目在IDEA中運行上。
源碼分析
源碼初步定位
經(jīng)過初步排查,問題已經(jīng)定位,正是出在運行方式上,在IDEA中運行的時候JSP頁面會報404,而該錯誤說明是程序沒有找到JSP頁面,那么為什么找不到JSP頁面呢?而想知道該問題的答案,就得知道spring-boot中嵌入式tomcat是如何找到,那么spring-boot中嵌入式容器是如何得知JSP頁面所在的位置呢?通過查看API,在org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer接口中發(fā)現(xiàn)了void setDocumentRoot(File documentRoot)方法,而從該方法說明上可以看出靜態(tài)資源的根目錄就是通過該方法設置的,只要設置了該方法就可以讓容器找到靜態(tài)資源了,包括JSP,但是問題又來了,為什么打包的時候JSP頁面可以找到,而不打包直接在IDEA中運行的時候卻又找不到了呢?看來還需要深入看下沒有設置documentRoot時程序是如何定位documentRoot的。
源碼深入查看
由上邊分析可以得出結(jié)論:用戶可以自己設置documentRoot,而如果用戶沒有設置程序則可以使用默認值,但是程序默認值是怎么得來的呢?一點點兒看源碼肯定是很難找到的,但是既然用戶可以通過setDocumentRoot方法自己設置,那么程序確定documentRoot的時候必然要先判斷該值是否設置過,現(xiàn)在問題就很簡單了,我們只需要找到設置的documentRoot都在哪兒被調(diào)用了就能找到當該值為空時程序是如何生成默認值的,通過查看源碼方法調(diào)用關系,很快就發(fā)現(xiàn)只有一個地方使用了該值,那就是
org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory的getValidDocumentRoot方法,該方法如下: File file = getDocumentRoot(); // If document root not explicitly set see if we are running from a war archive file = file != null ? file : getWarFileDocumentRoot(); // If not a war archive maybe it is an exploded war file = file != null ? file : getExplodedWarFileDocumentRoot(); // Or maybe there is a document root in a well-known location file = file != null ? file : getCommonDocumentRoot(); if (file == null && this.logger.isDebugEnabled()) { this.logger .debug("None of the document roots " + Arrays.asList(COMMON_DOC_ROOTS) + " point to a directory and will be ignored."); } else if (this.logger.isDebugEnabled()) { this.logger.debug("Document root: " + file); } return file;
第一行就是獲取用戶設置的documentRoot,而當該值為null時會調(diào)用getWarFileDocumentRoot方法判斷當前是否是以war包的形式直接運行,而這也是為什么打包成war包后運行JSP頁面可以找到,而如果還不是則會判斷當前是否是把war包解壓后運行的,而該方法會獲取該類所在的位置,在IDEA中運行時該類存在于spring-boot的jar包中,而該jar包則是在maven本地緩存中,maven本地緩存與項目的工作空間并不同,所以得到的路徑自然會是一個錯誤的路徑,這也是為什么之前打包后可以找到JSP頁面而直接在IDEA中以main方法運行的時候沒辦法找到JSP頁面。
解決問題
解決方案
既然問題已經(jīng)找到了,那么解決就很簡單了,只需要判斷當前是不是在IDE中直接以main方法運行的即可,方法如下:
/** * classpath下的doc-root */ private static final String DEFAULT_DOC_ROOT = Thread.currentThread().getContextClassLoader().getResource("") .getFile(); /** * 本地工作空間的doc-root */ private static final String LOCAL_DOC_ROOT = DEFAULT_DOC_ROOT.replace("target/classes", "src/main/webapp"); /** * 當用戶在IDE中運行系統(tǒng)時該方法會生效 * * @return doc-root */ public static File getIDEDocumentRoot() { File docRoot = new File(LOCAL_DOC_ROOT); if (docRoot.exists()) { log.debug("當前在IDE中運行,并且找到了工作空間"); return docRoot; } else if ((docRoot = new File(DEFAULT_DOC_ROOT)).exists()) { log.debug("當前在IDE中運行,沒有找到了工作空間,但是找到了classpath下的doc-root"); return docRoot; } else { log.debug("當前沒有在IDE中運行"); return null; } }
上邊的方法當在IDE中運行時會返回documentRoot,而打包后并不會返回,有了該方法,只需要在spring容器初始化的時候獲取當前上下文中的ConfigurableEmbeddedServletContainer,然后調(diào)用setDocumentRoot方法將上述方法獲取的documentRoot設置進去就行,這樣當程序在IDE中直接以main方法運行的時候documentRoot可以正確找到,而當打包后運行由于getIDEDocumentRoot方法會返回null,此時雖然調(diào)用了setDocumentRoot方法,但是由于設置的是null,后續(xù)程序仍然會尋找默認的documentRoot,這樣JSP同樣可以找到。
細節(jié)說明
上述getIDEDocumentRoot方法中可以看到首先是查找了LOCAL_DOC_ROOT,找不到的時候才是查找DEFAULT_DOC_ROOT,這是由于靜態(tài)資源并不需要編譯(JSP也可以運行時動態(tài)編譯加載),而在IDE中直接以main方法運行的情況下大多都是正在調(diào)試程序,而靜態(tài)資源例如JSP的調(diào)試很麻煩,因為默認的DEFAULT_DOC_ROOT在這時是指向當前classpath的,在IDE中運行如果不重新啟動的話即使源文件修改classpath中的東西一般來說也不會立即修改,這樣在調(diào)試JSP的時候就會造成稍微修改一點兒東西就要重啟,而如果項目很大或者主機配置不夠的話重啟就要等很久,這樣也不利于調(diào)試,所以默認這些靜態(tài)資源就優(yōu)先讓他們?nèi)ピ创a路徑查找,如果找不到(此處的路徑是使用的maven項目結(jié)構(gòu),默認認為用戶使用maven并使用這種結(jié)構(gòu),如果沒有使用這種目錄結(jié)構(gòu)則會找不到)則在使用DEFAULT_DOC_ROOT。
JSP動態(tài)加載替換的參數(shù)如下(對性能有很大影響,生產(chǎn)環(huán)境不建議使用):
server.jsp-servlet.init-parameters.development=true
上述就是小編為大家分享的如何進行Spring-boot JSP頁面無法訪問的問題排查了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。