您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關如何進行Spring-boot JSP頁面無法訪問的問題排查,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
Spring-boot JSP頁面無法訪問問題排查
前段時間在公司項目框架更換時碰到了一個問題,公司項目原來用的是springMVC,沒有使用spring-boot,替換為spring-boot的時候遇到了一些問題,spring-boot可以將項目打包為一個可執行jar/war包,對于web項目可以使用嵌入式tomcat代替tomcat,從而也可以達到直接執行而不用部署到外部tomcat容器中的效果,而正是這個出現了問題。
問題
由于之前我做的項目都是前后端分離的,而這次項目需要用到JSP,所以出現了該問題,那就是JSP頁面無法訪問。
初步排查
先是從網上找資料,最多的就是添加以下配置
spring.mvc.view.suffix=.jsp spring.mvc.view.prefix=/WEB-INF/jsp/
但是項目中已經有該配置了,而且同事那兒打包完是可以運行的,而我是直接在IDE中以main方法運行的(我用的IDE是IDEA),會不會跟這個有關系?我在本地將項目打包,打包完畢后運行發現JSP頁面是可以訪問的,而在IDE中運行的時候雖然JSP頁面訪問不了但是接口是可以正常訪問的,看來問題出在項目在IDEA中運行上。
源碼分析
源碼初步定位
經過初步排查,問題已經定位,正是出在運行方式上,在IDEA中運行的時候JSP頁面會報404,而該錯誤說明是程序沒有找到JSP頁面,那么為什么找不到JSP頁面呢?而想知道該問題的答案,就得知道spring-boot中嵌入式tomcat是如何找到,那么spring-boot中嵌入式容器是如何得知JSP頁面所在的位置呢?通過查看API,在org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer接口中發現了void setDocumentRoot(File documentRoot)方法,而從該方法說明上可以看出靜態資源的根目錄就是通過該方法設置的,只要設置了該方法就可以讓容器找到靜態資源了,包括JSP,但是問題又來了,為什么打包的時候JSP頁面可以找到,而不打包直接在IDEA中運行的時候卻又找不到了呢?看來還需要深入看下沒有設置documentRoot時程序是如何定位documentRoot的。
源碼深入查看
由上邊分析可以得出結論:用戶可以自己設置documentRoot,而如果用戶沒有設置程序則可以使用默認值,但是程序默認值是怎么得來的呢?一點點兒看源碼肯定是很難找到的,但是既然用戶可以通過setDocumentRoot方法自己設置,那么程序確定documentRoot的時候必然要先判斷該值是否設置過,現在問題就很簡單了,我們只需要找到設置的documentRoot都在哪兒被調用了就能找到當該值為空時程序是如何生成默認值的,通過查看源碼方法調用關系,很快就發現只有一個地方使用了該值,那就是
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時會調用getWarFileDocumentRoot方法判斷當前是否是以war包的形式直接運行,而這也是為什么打包成war包后運行JSP頁面可以找到,而如果還不是則會判斷當前是否是把war包解壓后運行的,而該方法會獲取該類所在的位置,在IDEA中運行時該類存在于spring-boot的jar包中,而該jar包則是在maven本地緩存中,maven本地緩存與項目的工作空間并不同,所以得到的路徑自然會是一個錯誤的路徑,這也是為什么之前打包后可以找到JSP頁面而直接在IDEA中以main方法運行的時候沒辦法找到JSP頁面。
解決問題
解決方案
既然問題已經找到了,那么解決就很簡單了,只需要判斷當前是不是在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中運行系統時該方法會生效 * * @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,然后調用setDocumentRoot方法將上述方法獲取的documentRoot設置進去就行,這樣當程序在IDE中直接以main方法運行的時候documentRoot可以正確找到,而當打包后運行由于getIDEDocumentRoot方法會返回null,此時雖然調用了setDocumentRoot方法,但是由于設置的是null,后續程序仍然會尋找默認的documentRoot,這樣JSP同樣可以找到。
細節說明
上述getIDEDocumentRoot方法中可以看到首先是查找了LOCAL_DOC_ROOT,找不到的時候才是查找DEFAULT_DOC_ROOT,這是由于靜態資源并不需要編譯(JSP也可以運行時動態編譯加載),而在IDE中直接以main方法運行的情況下大多都是正在調試程序,而靜態資源例如JSP的調試很麻煩,因為默認的DEFAULT_DOC_ROOT在這時是指向當前classpath的,在IDE中運行如果不重新啟動的話即使源文件修改classpath中的東西一般來說也不會立即修改,這樣在調試JSP的時候就會造成稍微修改一點兒東西就要重啟,而如果項目很大或者主機配置不夠的話重啟就要等很久,這樣也不利于調試,所以默認這些靜態資源就優先讓他們去源碼路徑查找,如果找不到(此處的路徑是使用的maven項目結構,默認認為用戶使用maven并使用這種結構,如果沒有使用這種目錄結構則會找不到)則在使用DEFAULT_DOC_ROOT。
JSP動態加載替換的參數如下(對性能有很大影響,生產環境不建議使用):
server.jsp-servlet.init-parameters.development=true
上述就是小編為大家分享的如何進行Spring-boot JSP頁面無法訪問的問題排查了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。