近期接手一个陈旧的项目基于SSM,SpringMVCSpringMybatis实现的管理项目,没使用依赖管理。
改造目标:SpringBoot + Maven,jar形式打包,非war形式,并且配置文件及webapp目录中的资源要外部化,通过maven-jar-plugin打包的jar为thin jar,jar包内不包含lib依赖及webapp。
改造后通过assembly打包目录结构如下:
改造后最大的问题就是webapp外部化jsp资源访问404问题:java -cp 形式启动,通过开启trace日志发现:
2020-08-19 10:43:18 [http-nio-8080-exec-3] DEBUG org.apache.jasper.servlet.JspServlet - JspEngine --> /WEB-INF/views/index.jsp
2020-08-19 10:43:18 [http-nio-8080-exec-3] DEBUG org.apache.jasper.servlet.JspServlet - ServletPath: /WEB-INF/views/index.jsp
2020-08-19 10:43:18 [http-nio-8080-exec-3] DEBUG org.apache.jasper.servlet.JspServlet - PathInfo: null
2020-08-19 10:43:18 [http-nio-8080-exec-3] DEBUG org.apache.jasper.servlet.JspServlet - RealPath: /tmp/tomcat-docbase.11133512333430917306.8080/WEB-INF/views/index.jsp
2020-08-19 10:43:18 [http-nio-8080-exec-3] DEBUG org.apache.jasper.servlet.JspServlet - RequestURI: /WEB-INF/views/index.jsp
2020-08-19 10:43:18 [http-nio-8080-exec-3] DEBUG org.apache.jasper.servlet.JspServlet - QueryString: null
JspServlet 是tomcat-embed-jasper 中的jsp处理类,获取jsp文件是从tomcat的docBase目录,该目录通过debug发现,idea上是使用的本地src/main/webapp下的目录,服务器上是创建的临时目录,而临时目录并不存在webapp目录下的文件,所以访问后导致出现404问题。
debug主要涉及类及方法:
// TomcatEmbeddedServletContainerFactory prepareContext() getValidDocumentRoot() getDocumentRoot()
// AbstractConfigurableEmbeddedServletContainer documentRoot()
// 此方法获取的路径最终被用于docBase实际值
protected final File 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;
}
通过spring文档,发现只有server.tomcat.basedir, 并没有地方设置docBase的具体配置,那么只有通过自定义TomcatEmbeddedServletContainerFactory来实现自定义的docBase地址,默认该类在EmbeddedServletContainerAutoConfiguration中实例化,只需要自定义该类覆盖默认配置bean即可。
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
log.info("Tomcat Current DocumentRoot => {}", factory.getDocumentRoot());
factory.addContextCustomizers(context -> {
log.info("Tomcat Customizer DocBase => {}", context.getDocBase());
//temp dir if docbase = tomcat-docbase
// 如果是临时目录,则获取系统变量,设置docBase,系统变量在启动脚本启动时设置
if (context.getDocBase() != null && context.getDocBase().contains("tomcat-docbase")) {
String k = "external.webapp";
String docBase = System.getProperty(k);
if (docBase != null && docBase.trim().length() > 0) {
context.setDocBase(docBase);
log.info("Tomcat DocBase Reset => {}", docBase);
}
}
});
return factory;
}
修改后再次部署:可以正常找到自定义目录下的jsp文件了
2020-08-19 13:44:40 [http-nio-8080-exec-1] DEBUG org.apache.jasper.servlet.JspServlet - JspEngine --> /WEB-INF/views/index.jsp
2020-08-19 13:44:40 [http-nio-8080-exec-1] DEBUG org.apache.jasper.servlet.JspServlet - ServletPath: /index.jsp
2020-08-19 13:44:40 [http-nio-8080-exec-1] DEBUG org.apache.jasper.servlet.JspServlet - PathInfo: null
2020-08-19 13:44:40 [http-nio-8080-exec-1] DEBUG org.apache.jasper.servlet.JspServlet - RealPath: /home/tomz/demo/webapp/WEB-INF/views/index.jsp
2020-08-19 13:44:40 [http-nio-8080-exec-1] DEBUG org.apache.jasper.servlet.JspServlet - RequestURI: /WEB-INF/views/index.jsp
2020-08-19 13:44:40 [http-nio-8080-exec-1] DEBUG org.apache.jasper.servlet.JspServlet - QueryString: null