SpringBoot 目标是简化开发和部署,web方面则使用嵌入式web容器来替代依赖外部容器的部署方式。
在JavaWeb 领域,现在有了多种页面渲染技术可选,如velocity,freemarker等。并且还有绝对的前后端分离思想的影响,多年过去了,JSP技术的应用虽然有所下降,但仍然广泛。许多遗留系统还是JSP开发的。JSP的支持还是tomcat做的比较好,其他如jetty啥的要么不支持jsp要么Springboot没支持他们,SpringBoot对内嵌web容器的支持默认也是用tomcat。但tomcat对web资源的处理上写死了要使用文件目录,对于打包成jar包的SpringBoot应用来说,显然不行,也有的人打包成war,然后还是部署到tomcat上,这样我认为违反了SpringBoot的初衷,这样一来,等于否定了嵌入式容器,而且程序员还要处理嵌入式环境和外部tomcat环境的不同带来的问题。
本文目地是为使用jar包部署带有jsp的SpringBoot应用提供一个解决方案。
思路也很简单,就是把资源打包进jar,然后main方法里面运行应用之前把资源拷贝到外部文件。
1) 把web资源目录打包进jar
其实就是放在classpath目录, 如果是普通java应用,可以放在src目录。如果是maven项目可以配置资源处理:
<!-- 打包时将jsp文件拷贝到META-INF目录下 -->
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/**</include>
</includes>
</resource>
2) main()方法里面拷贝资源到外部文件
public static void main(String[] args) {
// 为了让SpringBoot支持jsp,在jar包环境下需要把jsp解压到src/main/webapp/目录下
boolean override = "true".equals(System.getProperty("cleanOnstart", "true"));
Class<?> bootClass = MyApplication.class;// main()方法所在的类
copyResourceIfInJar(bootClass,"META-INF/resources","src/main/webapp",override);
SpringApplication.run(bootClass, args);
}
拷贝的方法,利用 spring 的 resource 神器
private static void copyResourceIfInJar(Class<?> clazz, String resPreffix, String targetDir, boolean override) {
String cls = clazz.getSimpleName() + ".class";
URL url = clazz.getResource(cls);
if (url == null) {
System.err.println("不可思议的可能.");
return;
}
if ("file".equals(url.getProtocol())) {
// 本地开发环境,忽略
System.out.println("ignore when local dev.");
return;
}
File webRootFile = new File(targetDir);
if (webRootFile.exists()) {
File oldFolder;
if (!webRootFile.isDirectory()) {
webRootFile.delete();
webRootFile.mkdir();
} else if (override && webRootFile.renameTo(oldFolder = new File(targetDir + "-" + UUID.randomUUID()))) {
webRootFile.mkdirs();
oldFolder.deleteOnExit();
} else if (!override) {
System.out.println("lazy return when not override.");
return;
}
} else {
webRootFile.mkdirs();
}
// 目录准备完毕,开始解压和复制
PathMatchingResourcePatternResolver searcher = new PathMatchingResourcePatternResolver(clazz.getClassLoader());
try {
Resource[] webResources = searcher.getResources(resPreffix + (resPreffix.endsWith("/") ? "**" : "/**"));
if (webResources != null && webResources.length > 0) {
for (Resource resource : webResources) {
String desc = resource.getDescription();
String uri = desc.substring(desc.indexOf(resPreffix) + resPreffix.length(), desc.lastIndexOf(']'));
File tar = new File(targetDir, uri);
if (uri.endsWith("/")) {
tar.mkdirs();
continue;
}
if (!tar.getParentFile().exists()) {
tar.getParentFile().mkdirs();
}
FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream(tar));
}
} else {
System.err.println("Resource Not Found in path:" + resPreffix);
}
} catch (IOException e) {
e.printStackTrace();
}
}