Spring Boot是否可以使用JAR包装来提供JSP

我熟悉Spring Boot JSP示例应用程序

但是,该示例使用WAR包装。 是否可以对JAR做同样的事情?

我的目标是将JSP放在src/main/resources/jsp以简化我的应用程序的结构。

正如@Andy Wilkinson所说,存在与JSP相关的限制。 请将您的应用程序打包为war并执行jar 。 这是在春季网站上记录的。

使用Tomcat它应该可以使用war包装,即可执行的战争将起作用(…)。 由于Tomcat中的硬编码文件模式,可执行jar将无法工作

  • 27.3.5 JSP限制
  • jsp样本

不推荐的,旧答案

是的,这可以通过Spring Boot实现。

看一下这个例子: https : //github.com/mariuszs/spring-boot-web-jsp-example 。

为此,请使用spring-boot-maven-plugin或gradle等效项。

使用此插件jar是可执行的,可以提供JSP文件。

 $ mvn package $ java -jar target/mymodule-0.0.1-SNAPSHOT.jar 

要不就

 $ mvn spring-boot:run 

使用Tomcat ,还可以将WEB-INF/jsp的内容放到/src/main/resources/META-INF/resources (以Maven为例)。 META-INF / resources基本上被视为传统的webapps文件夹。

详情请见: http : //hillert.blogspot.com/2016/03/spring-boot-with-jsp-in-executable-jar.html

示例代码: https : //github.com/ghillert/spring-boot-jsp-demo

您最好的选择是将包装类型更改为战争,它应该无需进一步更改即可运行。

如上述评论中所述,存在一些限制:

http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-jsp-limitations

如果你的springboot正在构建你的项目并在本地服务器上正确运行它,那么YES 。 您需要做的是使用mvn -U clean package构建项目。 然后在目标文件夹中,你有你的runnable xxxx.jar。 现在你需要做的是将你的xxxx.jar文件放在服务器中或你想要的位置以及同一层次结构中的src/main/webapp/WEB-INF/jsp/ * .jsp文件。 然后尝试java -jar xxxx.jar您的项目将运行没有问题。

 ` . ├── src │  └── main │  └── webapp │  └── WEB-INF │  ├── jsp │  │  ├── default.jsp │  │  ├── help.jsp │  │  ├── index.jsp │  │  ├── insert.jsp │  │  ├── login.jsp │  │  ├── modify.jsp │  │  ├── search.jsp │  │  └── show.jsp │  └── web.xml ├── xxx.jar └── xxx.jar.original` 

java -jar xxx.jar

 
 $java -jar xxx.jar . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.5.4.RELEASE) 2017-09-05 19:31:05.009 INFO 10325 --- [ main] com.myapp.app.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT on dipu-HP with PID 10325 (/home/dipu/Documents/workspace-sts/jspjartest/xxx.jar started by dipu in /home/dipu/Documents/workspace-sts/jspjartest) 2017-09-05 19:31:05.014 INFO 10325 --- [ main] com.myapp.app.DemoApplication : No active profile set, falling back to default profiles: default 2017-09-05 19:31:05.138 INFO 10325 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6e5e91e4: startup date [Tue Sep 05 19:31:05 IST 2017]; root of context hierarchy 2017-09-05 19:31:07.258 INFO 10325 --- [ main] sbcetTomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8090 (http) 2017-09-05 19:31:07.276 INFO 10325 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2017-09-05 19:31:07.278 INFO 10325 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.15 2017-09-05 19:31:08.094 INFO 10325 --- [ost-startStop-1] org.apache.jasper.servlet.TldScanner : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time. 2017-09-05 19:31:08.396 INFO 10325 --- [ost-startStop-1] oaccC[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2017-09-05 19:31:08.401 INFO 10325 --- [ost-startStop-1] osweb.context.ContextLoader : Root WebApplicationContext: initialization completed in 3267 ms 2017-09-05 19:31:08.615 INFO 10325 --- [ost-startStop-1] osbwservlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/] 2017-09-05 19:31:08.617 INFO 10325 --- [ost-startStop-1] osbwservlet.ServletRegistrationBean : Mapping servlet: 'loginServlet' to [/loginServlet/] 2017-09-05 19:31:08.618 INFO 10325 --- [ost-startStop-1] osbwservlet.ServletRegistrationBean : Mapping servlet: 'uploadController' to [/uploadController/] 2017-09-05 19:31:08.622 INFO 10325 --- [ost-startStop-1] osbwservlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*] 2017-09-05 19:31:08.622 INFO 10325 --- [ost-startStop-1] osbwservlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] 2017-09-05 19:31:08.623 INFO 10325 --- [ost-startStop-1] osbwservlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*] 2017-09-05 19:31:08.623 INFO 10325 --- [ost-startStop-1] osbwservlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*] 2017-09-05 19:31:09.137 INFO 10325 --- [ main] swsmmaRequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6e5e91e4: startup date [Tue Sep 05 19:31:05 IST 2017]; root of context hierarchy 2017-09-05 19:31:09.286 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/user-management]}" onto java.lang.String com.myapp.app.DemoController.user() 2017-09-05 19:31:09.288 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/]}" onto java.lang.String com.myapp.app.DemoController.reload() 2017-09-05 19:31:09.290 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/show]}" onto java.lang.String com.myapp.app.DemoController.show() 2017-09-05 19:31:09.292 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/modify]}" onto java.lang.String com.myapp.app.DemoController.modify() 2017-09-05 19:31:09.293 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/admin]}" onto java.lang.String com.myapp.app.DemoController.admin() 2017-09-05 19:31:09.294 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/login]}" onto java.lang.String com.myapp.app.DemoController.login() 2017-09-05 19:31:09.294 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/faq-management]}" onto java.lang.String com.myapp.app.DemoController.faq() 2017-09-05 19:31:09.294 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/subject-area]}" onto java.lang.String com.myapp.app.DemoController.subject() 2017-09-05 19:31:09.295 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/Chat]}" onto java.lang.String com.myapp.app.DemoController.index() 2017-09-05 19:31:09.295 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/delete]}" onto java.lang.String com.myapp.app.DemoController.delete() 2017-09-05 19:31:09.296 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/result]}" onto java.lang.String com.myapp.app.DemoController.result() 2017-09-05 19:31:09.296 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/insert]}" onto java.lang.String com.myapp.app.DemoController.insert() 2017-09-05 19:31:09.300 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/fileUpload]}" onto java.lang.String com.myapp.app.DemoController.file() 2017-09-05 19:31:09.301 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/search]}" onto java.lang.String com.myapp.app.DemoController.search() 2017-09-05 19:31:09.301 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/help]}" onto java.lang.String com.myapp.app.DemoController.help() 2017-09-05 19:31:09.312 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/LoginServlet]}" onto public void com.myapp.app.LoginServlet.LoginServlet.doPost(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException 2017-09-05 19:31:09.313 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/image],methods=[GET],produces=[text/html;charset=UTF-8]}" onto public java.lang.String com.myapp.app.controller.ImageController.image() 2017-09-05 19:31:09.316 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/authenticate],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.authenticate(java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.317 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/searchCompany],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.getCompany(java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.318 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/executeQuery],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.executeQuery(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.318 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/insertData],methods=[POST]}" onto public int com.myapp.app.controller.AjaxController.insertResources(java.lang.String,java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.319 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/showData],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.showResources(java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.319 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/searchData],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.getSearchData(java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.319 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/deleteData],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.deleteData(java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.320 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/modifyData],methods=[POST]}" onto public int com.myapp.app.controller.AjaxController.modifyData(java.lang.String,java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.322 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/suggestWords],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.suggestWords(java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.322 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/authenticateUser],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.authenticateUser(java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.323 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/insertJson],methods=[POST]}" onto public int com.myapp.app.controller.AjaxController.insertJsonResources(java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.323 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/getvalue],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.getResource(java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.324 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/searchQuery],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.getResources(java.lang.String,java.lang.String) throws java.io.IOException 2017-09-05 19:31:09.330 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/upload],methods=[POST]}" onto public void com.myapp.app.controller.UploadController.doPost(org.springframework.web.multipart.MultipartFile,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException 2017-09-05 19:31:09.333 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) 2017-09-05 19:31:09.334 INFO 10325 --- [ main] swsmmaRequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest) 2017-09-05 19:31:09.388 INFO 10325 --- [ main] oswshandler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2017-09-05 19:31:09.388 INFO 10325 --- [ main] oswshandler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2017-09-05 19:31:09.461 INFO 10325 --- [ main] oswshandler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2017-09-05 19:31:09.752 INFO 10325 --- [ main] osjeaAnnotationMBeanExporter : Registering beans for JMX exposure on startup 2017-09-05 19:31:09.861 INFO 10325 --- [ main] sbcetTomcatEmbeddedServletContainer : Tomcat started on port(s): 8090 (http) 2017-09-05 19:31:09.867 INFO 10325 --- [ main] com.myapp.DemoApplication : Started DemoApplication in 5.349 seconds (JVM running for 5.866) 

这里服务器正在运行

$ curl 127.0.0.1:8090/login Welcome to Login page

我的POM.xml

  
   4.0.0 com.myapp app 0.0.1-SNAPSHOT jar Jsp Springboot Jsp Springboot  org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE    UTF-8 UTF-8 1.8    org.springframework.boot spring-boot-starter   org.springframework.boot spring-boot-starter-test test   org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-tomcat provided   javax.servlet jstl   org.apache.tomcat.embed tomcat-embed-jasper provided    org.eclipse.jdt.core.compiler ecj 4.6.1 provided   org.webjars bootstrap 3.3.7   org.apache.httpcomponents httpclient 4.5.2   org.elasticsearch.client rest 5.5.1   com.google.code.gson gson 2.8.0   org.apache.poi poi 3.16   org.apache.poi poi-ooxml 3.16   org.apache.poi poi-ooxml-schemas 3.16   commons-fileupload commons-fileupload 1.3.3      org.springframework.boot spring-boot-maven-plugin   org.apache.maven.plugins maven-surefire-plugin  false      

27.3.5 JSP限制

运行使用嵌入式servlet容器的Spring Boot应用程序(并打包为可执行存档)时,JSP支持存在一些限制。

使用Tomcat它应该可以使用war包装,即可执行的war会起作用,并且也可以部署到标准容器(不限于但包括Tomcat)。

由于Tomcat中的硬编码文件模式,可执行jar将无法工作。

使用Jetty它应该可以使用war包装,即可执行的战争将起作用,并且也可以部署到任何标准容器。

Undertow不支持JSP。

创建自定义error.jsp页面不会覆盖error handling的默认视图,而应使用自定义错误页面。

我在使用jar包装的 Spring Boot Application上成功使用JS P,将我的JSP文件放在src / main / webapp / WEB-INF / JSP目录中,并将我的应用程序配置为如下

 @Bean public InternalResourceViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class); viewResolver.setPrefix("/WEB-INF/JSP/"); viewResolver.setSuffix(".jsp"); return viewResolver; } 

我的控制器是:

 @Controller public class HelloController { @RequestMapping(value = { "/", "/hello**" }, method = RequestMethod.GET) public ModelAndView welcomePage() { ModelAndView model = new ModelAndView(); model.addObject("title", "Spring Security Example"); model.addObject("message", "This is Hello World!"); model.setViewName("hello"); System.out.println("° ° ° ° welcomePage running. model = " + model); return model; } . . . 

我的hello.jsp是

 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@page session="true"%>   

Title : ${title}

${message}

Options: " >Home | " >Administration | " >Super user | " >Logout

我的pom.xml文件是

 br.com.joao-parana my-spring-boot-app jar 1.0 my-spring-boot-app Maven Webapp http://maven.apache.org  org.springframework.boot spring-boot-starter-parent 1.3.4.RELEASE   1.8   . . .  org.springframework.boot spring-boot-starter-test test   org.springframework spring-test     org.springframework.boot spring-boot-starter-web   jstl jstl 1.2   org.apache.tomcat.embed tomcat-embed-jasper provided   org.springframework.boot spring-boot-starter-actuator    my-spring-boot-app   org.springframework.boot spring-boot-maven-plugin    

我希望这能有所帮助!

Spring Boot与JSP相当不错,而且只需要在配置下面就可以了

1 – tomcat-embad-jasper依赖

  org.apache.tomcat.embed tomcat-embed-jasper provided  

2 – 添加以下配置是application.properties

 spring.mvc.view.prefix: / spring.mvc.view.suffix: .jsp 

这仍然有一些疑问,然后在链接下面查看它

Spring Boot和JSP集成

嗨,我明确了如何将jar从jar复制到嵌入式tomcat文件夹

 package com.demosoft.stlb.loadbalancer; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import org.springframework.web.context.ServletContextAware; import javax.annotation.PostConstruct; import javax.servlet.ServletContext; import java.io.*; import java.net.MalformedURLException; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; @Component public class JarFileResourcesExtractor implements ServletContextAware { private String resourcePathPattern = "WEB-INF/**"; private String resourcePathPrefix = "WEB-INF/"; private String destination = "/WEB-INF/"; private ServletContext servletContext; private AntPathMatcher pathMatcher = new AntPathMatcher(); /** * Extracts the resource files found in the specified jar file into the destination path * * @throws IOException If an IO error occurs when reading the jar file * @throws FileNotFoundException If the jar file cannot be found */ @PostConstruct public void extractFiles() throws IOException { try { JarFile jarFile = (JarFile) getClass().getProtectionDomain().getCodeSource().getLocation().getContent(); Enumeration entries = jarFile.entries(); System.out.println("Tomcat destination : " + servletContext.getRealPath(destination)); while (entries.hasMoreElements()) { processJarEntry(jarFile, entries); } } catch (MalformedURLException e) { throw new FileNotFoundException("Cannot find jar file in libs: "); } catch (IOException e) { throw new IOException("IOException while moving resources.", e); } } private void processJarEntry(JarFile jarFile, Enumeration entries) throws IOException { JarEntry entry = entries.nextElement(); if (pathMatcher.match(resourcePathPattern, entry.getName())) { String fileName = getFileName(entry); if (checkFileName(fileName)) return; String sourcePath = getSourcePath(entry, fileName); if (checkSourcePath(sourcePath)) return; copyAndClose(jarFile.getInputStream(entry), getOutputStream(fileName, sourcePath)); } } private FileOutputStream getOutputStream(String fileName, String sourcePath) throws IOException { File destinationFolder = new File(servletContext.getRealPath(destination)+sourcePath); destinationFolder.mkdirs(); File materializedFile = new File(destinationFolder, fileName); materializedFile.createNewFile(); return new FileOutputStream(materializedFile); } private boolean checkSourcePath(String sourcePath) { return sourcePath.startsWith(".idea"); } private String getSourcePath(JarEntry entry, String fileName) { String sourcePath = entry.getName().replaceFirst(fileName, ""); sourcePath = sourcePath.replaceFirst(resourcePathPrefix, "").trim(); return sourcePath; } private boolean checkFileName(String fileName) { return fileName.trim().equals(""); } private String getFileName(JarEntry entry) { return entry.getName().replaceFirst(".*\\/", ""); } @Override public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } public static int IO_BUFFER_SIZE = 8192; private static void copyAndClose(InputStream in, OutputStream out) throws IOException { try { byte[] b = new byte[IO_BUFFER_SIZE]; int read; while ((read = in.read(b)) != -1) { out.write(b, 0, read); } } finally { in.close(); out.close(); } } } 

如果出于某种原因,你无法处理战争包装,那就是黑客攻击。 这个家伙完全赞同为旧版本的spring-boot做这件事。

一种方法是个性化tomcat并将BOOT-INF/classes添加到tomcat的ResourceSet。 在tomcat中,所有扫描的资源都被放入称为ResourceSet的东西中。 例如,servlet 3.0规范中的应用程序jar包的META-INF / resources被扫描并放入ResourceSet中。

现在我们需要找到一种方法将胖jar的BOOT-INF / classes目录添加到ResourceSet中。 我们可以通过tomcat LifecycleListener接口在Lifecycle.CONFIGURE_START_EVENT事件中执行此操作,获取BOOT-INF / classes URL,然后将此URL添加到WebResourceSet。 这里有一个完整的例子,但你可以这样做:

 import org.apache.catalina.Context; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnProperty(name = "tomcat.staticResourceCustomizer.enabled", matchIfMissing = true) public class TomcatConfiguration { @Bean public WebServerFactoryCustomizer staticResourceCustomizer() { return new WebServerFactoryCustomizer() { @Override public void customize(WebServerFactory factory) { if (factory instanceof TomcatServletWebServerFactory) { ((TomcatServletWebServerFactory) factory) .addContextCustomizers(new org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer() { @Override public void customize(Context context) { context.addLifecycleListener(new StaticResourceConfigurer(context)); } }); } } }; } } 

然后使用LifecycleListener,如下所示:

 import java.io.File; import java.net.MalformedURLException; import java.net.URL; import org.apache.catalina.Context; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleListener; import org.apache.catalina.WebResourceRoot.ResourceSetType; import org.springframework.util.ResourceUtils; public class StaticResourceConfigurer implements LifecycleListener { private final Context context; StaticResourceConfigurer(Context context) { this.context = context; } @Override public void lifecycleEvent(LifecycleEvent event) { if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { URL location = this.getClass().getProtectionDomain().getCodeSource().getLocation(); if (ResourceUtils.isFileURL(location)) { // when run as exploded directory String rootFile = location.getFile(); if (rootFile.endsWith("/BOOT-INF/classes/")) { rootFile = rootFile.substring(0, rootFile.length() - "/BOOT-INF/classes/".length() + 1); } if (!new File(rootFile, "META-INF" + File.separator + "resources").isDirectory()) { return; } try { location = new File(rootFile).toURI().toURL(); } catch (MalformedURLException e) { throw new IllegalStateException("Can not add tomcat resources", e); } } String locationStr = location.toString(); if (locationStr.endsWith("/BOOT-INF/classes!/")) { // when run as fat jar locationStr = locationStr.substring(0, locationStr.length() - "/BOOT-INF/classes!/".length() + 1); try { location = new URL(locationStr); } catch (MalformedURLException e) { throw new IllegalStateException("Can not add tomcat resources", e); } } this.context.getResources().createWebResourceSet(ResourceSetType.RESOURCE_JAR, "/", location, "/META-INF/resources"); } } }