温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何进行Spring-boot JSP页面无法访问的问题排查

发布时间:2021-12-13 18:41:10 来源:亿速云 阅读:217 作者:柒染 栏目:大数据

这期内容当中小编将会给大家带来有关如何进行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页面无法访问的问题排查了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI