【问题标题】:Is it possible with Spring Boot to serve up JSPs with a JAR packaging?Spring Boot 是否可以使用 JAR 包装提供 JSP?
【发布时间】:2014-02-10 04:40:27
【问题描述】:

我熟悉 Spring Boot JSP sample application

但是,该示例使用 WAR 包装。 <packaging>JAR</packaging> 可以做同样的事情吗?

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

【问题讨论】:

  • 我们将 JSP 放在 WEB-INF 中的原因是它受到保护。将它们移出意味着您将能够使用浏览器访问 JSP 原始代码。从安全方面来看,这很糟糕。但是,我相信你可以做到这一点。您必须配置应用程序的 ViewResolver(这是一个 Spring 类)以从 /resources/jsp 文件夹而不是 WEB-INF 读取文件。
  • 我遇到了同样的问题,所以将包装更改为 WAR 并正常运行,它工作正常。

标签: java spring spring-boot


【解决方案1】:

正如@Andy Wilkinson 所说,JSP 存在一些限制。请将您的应用程序打包为war 并以jar 执行。这记录在 spring 网站上。

如果您使用 Tomcat 打包,它应该可以工作,即可执行的战争将工作 (...)。 由于 Tomcat 中的硬编码文件模式,可执行 jar 将无法工作


已弃用,旧答案

是的,这可以通过 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.war 

(注意上面命令.war中工件的扩展名) 或者只是

$ mvn spring-boot:run

【讨论】:

  • 当您的应用程序被打包为可执行 JAR 文件时,需要注意 JSP 的一些限制:github.com/spring-projects/spring-boot/tree/master/…
  • 不起作用。这个例子不起作用。所有页面 404
  • 好的,我现在确实有一个可执行的战争文件。如果使用spring boot starter parent,只需将war 类型打包并引用spring boot maven plugin 配置-> 可执行文件设置为true
  • 从命令行运行时,github 示例对我有用。直接从 intellij spring boot 运行配置运行时它不起作用。仅供遇到此问题的其他人参考
【解决方案2】:

如果您的 springboot 正在构建您的项目并在本地服务器中正确运行它,那么 YES。 您需要做的是使用 mvn -U clean package。 然后在 target 文件夹中你有你的可运行 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

<pre>    
$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] s.b.c.e.t.TomcatEmbeddedServletContainer : 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] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2017-09-05 19:31:08.401  INFO 10325 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3267 ms
2017-09-05 19:31:08.615  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2017-09-05 19:31:08.617  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'loginServlet' to [/loginServlet/]
2017-09-05 19:31:08.618  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'uploadController' to [/uploadController/]
2017-09-05 19:31:08.622  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-09-05 19:31:08.622  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-09-05 19:31:08.623  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-09-05 19:31:08.623  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2017-09-05 19:31:09.137  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/user-management]}" onto java.lang.String com.myapp.app.DemoController.user()
2017-09-05 19:31:09.288  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto java.lang.String com.myapp.app.DemoController.reload()
2017-09-05 19:31:09.290  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/show]}" onto java.lang.String com.myapp.app.DemoController.show()
2017-09-05 19:31:09.292  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/modify]}" onto java.lang.String com.myapp.app.DemoController.modify()
2017-09-05 19:31:09.293  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin]}" onto java.lang.String com.myapp.app.DemoController.admin()
2017-09-05 19:31:09.294  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/login]}" onto java.lang.String com.myapp.app.DemoController.login()
2017-09-05 19:31:09.294  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/faq-management]}" onto java.lang.String com.myapp.app.DemoController.faq()
2017-09-05 19:31:09.294  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/subject-area]}" onto java.lang.String com.myapp.app.DemoController.subject()
2017-09-05 19:31:09.295  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/Chat]}" onto java.lang.String com.myapp.app.DemoController.index()
2017-09-05 19:31:09.295  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/delete]}" onto java.lang.String com.myapp.app.DemoController.delete()
2017-09-05 19:31:09.296  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/result]}" onto java.lang.String com.myapp.app.DemoController.result()
2017-09-05 19:31:09.296  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/insert]}" onto java.lang.String com.myapp.app.DemoController.insert()
2017-09-05 19:31:09.300  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/fileUpload]}" onto java.lang.String com.myapp.app.DemoController.file()
2017-09-05 19:31:09.301  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/search]}" onto java.lang.String com.myapp.app.DemoController.search()
2017-09-05 19:31:09.301  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/help]}" onto java.lang.String com.myapp.app.DemoController.help()
2017-09-05 19:31:09.312  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : 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] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-09-05 19:31:09.388  INFO 10325 --- [           main] o.s.w.s.handler.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] o.s.w.s.handler.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] o.s.w.s.handler.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] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-09-05 19:31:09.861  INFO 10325 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : 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)

</pre>

这里服务器正在运行

$ curl 127.0.0.1:8090/login Welcome to Login page

我的 POM.xml

<code>
    
        <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        	<modelVersion>4.0.0</modelVersion>
        <groupId>com.myapp</groupId>
        	<artifactId>app</artifactId>
        	<version>0.0.1-SNAPSHOT</version>
        	<packaging>jar</packaging> 
        	<name>Jsp Springboot</name>
        	<description>Jsp Springboot</description> 
        	<parent>
        		<groupId>org.springframework.boot</groupId>
        		<artifactId>spring-boot-starter-parent</artifactId>
        		<version>1.5.4.RELEASE</version>
        		<relativePath/> 
        	</parent> 
        	<properties>
        		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        		<java.version>1.8</java.version>
        	</properties> 
        	<dependencies>
        		<dependency>
        			<groupId>org.springframework.boot</groupId>
        			<artifactId>spring-boot-starter</artifactId>
        		</dependency> 
        		<dependency>
        			<groupId>org.springframework.boot</groupId>
        			<artifactId>spring-boot-starter-test</artifactId>
        			<scope>test</scope>
        		</dependency>
        		<dependency>
        			<groupId>org.springframework.boot</groupId>
        			<artifactId>spring-boot-starter-web</artifactId>
        		</dependency> 
        		
        		<dependency>
        			<groupId>org.springframework.boot</groupId>
        			<artifactId>spring-boot-starter-tomcat</artifactId>
        			<scope>provided</scope>
        		</dependency> 
        		
        		
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            </dependency> 
        		
        		<dependency>
        			<groupId>org.apache.tomcat.embed</groupId>
        			<artifactId>tomcat-embed-jasper</artifactId>
        			<scope>provided</scope>
        		</dependency> 
        		<!-- Need this to compile JSP,
        			tomcat-embed-jasper version is not working, no idea why -->
        		<dependency>
        			<groupId>org.eclipse.jdt.core.compiler</groupId>
        			<artifactId>ecj</artifactId>
        			<version>4.6.1</version>
        			<scope>provided</scope>
        		</dependency>
        		 
        		
        		<dependency>
        			<groupId>org.webjars</groupId>
        			<artifactId>bootstrap</artifactId>
        			<version>3.3.7</version>
        		</dependency>
        		
        		
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
        		
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>rest</artifactId>
            <version>5.5.1</version>
        </dependency>
        		
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.0</version>
        </dependency>
            
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>3.16</version>
            </dependency>
            
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>3.16</version>
            </dependency>
            
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml-schemas</artifactId>
                <version>3.16</version>
            </dependency>
    
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency> 
        	</dependencies> 
        	<build>
        		<plugins>
        			<plugin>
        				<groupId>org.springframework.boot</groupId>
        				<artifactId>spring-boot-maven-plugin</artifactId>
        			</plugin>
        <plugin>
        				<groupId>org.apache.maven.plugins</groupId>
        				<artifactId>maven-surefire-plugin</artifactId>
        				<configuration>
        					<useSystemClassLoader>false</useSystemClassLoader>
        				</configuration>
        			</plugin> 
        		
        		</plugins>
        	</build>
        </project>
    
</code>

我的应用程序.properties

<code>
    spring.mvc.view.prefix: /WEB-INF/jsp/
    spring.mvc.view.suffix: .jsp
    server.port=8090
</code>

我的项目结构

<code>
.
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── myapp
│       │           └── app
│       │               ├── mynewapp
│       │               ├── controller
│       ├── resources
│       │   └── static
│       │       ├── assets
│       │       │   ├── css
│       │       │   │   ├── fonts
│       │       │   │   └── lib
│       │       │   ├── img
│       │       │   └── js
│       │       │       └── lib
│       │       ├── css
│       │       │   └── img
│       │       ├── fonts
│       │       ├── images
│       │       └── js
│       │           └── img
│       └── webapp
│           └── WEB-INF
│               ├── jsp
│               └── lib
</code>

谢谢。

【讨论】:

  • 将 src 文件夹放入 target 并使用 java -jar xxxx.jar 运行 jar。您的项目将提供没有 404 错误的 jsp 页面。
  • 这就是为什么当我运行 java -jar target/my-project.jar 时它在本地工作的原因!但这对于生产部署来说不是很方便。
【解决方案3】:

您最好的选择是将包装类型更改为战争,它应该可以在没有进一步更改的情况下工作。

如上所述,有一些限制:

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

【讨论】:

  • 这不是解决方案,问题与 JAR 而非 WAR 相关
  • @Orby java -jar my-project.war 有效。如果您确实需要将工件名称扩展名设置为 .jar,这只是一个问题。
【解决方案4】:

27.3.5 JSP 限制

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

如果你使用 Tomcat,它应该可以工作,如果你使用战争包装,即 可执行的战争将起作用,并且还可以部署到标准 容器(不限于,但包括 Tomcat)。

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

如果你使用 Jetty 打包,它应该可以工作,即一个可执行文件 战争将起作用,并且还可以部署到任何标准容器。

Undertow 不支持 JSP。

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

source

【讨论】:

    【解决方案5】:

    如果由于某种原因,您无法处理战争包装,那就是 hack。完全归功于 this guy 为旧版本的 spring-boot 所做的工作。

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

    现在我们需要想办法把fat jar的BOOT-INF/classes目录添加到ResourceSet中。我们可以通过tomcat的LifecycleListener接口,在Lifecycle.CONFIGURE_START_EVENT事件中,获取BOOT-INF/classes URL,然后将此URL添加到WebResourceSet中。一个完整的例子是here,但你可以这样做:

    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<WebServerFactory> staticResourceCustomizer() {
            return new WebServerFactoryCustomizer<WebServerFactory>() {
    
            @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");
    
            }
        }
    }
    

    【讨论】:

      【解决方案6】:

      Spring Boot 非常适合 JSP,它也很简单,你只需要下面的配置

      1 - tomcat-embad-jasper 依赖

      <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
      </dependency>
      

      2 - 添加下面的配置是 application.properties

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

      那还是有疑问的,看看下面的链接吧

      Spring Boot and JSP integration

      【讨论】:

        【解决方案7】:

        我在 Spring Boot 应用程序上成功使用 JSP,并带有 jar 包装,将我的 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"%>
        <html>
        <body>
            <h1>Title : ${title}</h1>   
            <h1>${message}</h1>
            <h2>Options: 
                <a href="<c:url value="/hello" />" >Home</a> | 
                <a href="<c:url value="/admin" />" >Administration</a> | 
                <a href="<c:url value="/super" />" >Super user</a>
                <c:if test="${pageContext.request.userPrincipal.name != null}"> |
                    <a href="<c:url value="/logout" />" >Logout</a> 
                </c:if>
            </h2>   
        </body>
        </html>
        

        我的 pom.xml 文件是

        <groupId>br.com.joao-parana</groupId>
        <artifactId>my-spring-boot-app</artifactId>
        <packaging>jar</packaging>
        <version>1.0</version>
        <name>my-spring-boot-app Maven Webapp</name>
        <url>http://maven.apache.org</url>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.3.4.RELEASE</version>
        </parent>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            . . . 
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-test</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>jstl</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-jasper</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
        </dependencies>
        <build>
            <finalName>my-spring-boot-app</finalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
        

        我希望这对某人有所帮助!

        【讨论】:

        • 您的应用运行状况如何?你能用java -jar your_app.jar成功运行它吗?可执行 jar 文件中可能缺少 WEB-INF 目录?您仍然可以使用mvn spring:boot 或直接运行主应用程序成功运行它。但是我很感兴趣你是否可以使用上面的可执行jar文件运行。
        • 如果是 jar 包,这将不起作用(这是这里讨论的主要问题)
        【解决方案8】:

        这是我的解决方案:

        <build>
            <resources>
                <resource>
                    <directory>src/main/webapp</directory>
                    <targetPath>META-INF/resources</targetPath>
                    <includes>
                        <include>**/**</include>
                    </includes>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                    <filtering>true</filtering>
                    <includes>
                        <include>**/**</include>
                    </includes>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <!-- must use 1.4.2 version -->
                    <version>1.4.2.RELEASE</version>
                </plugin>
            </plugins>
        </build>
        

        【讨论】:

          【解决方案9】:

          嗨,我想知道如何将 jsp 从 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<JarEntry> 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<JarEntry> 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();
              }
          }
          }
          

          【讨论】:

          • 欢迎来到 Stack Overflow!虽然您可能已经解决了这个用户的问题,但纯代码的答案对于将来遇到这个问题的用户来说并不是很有帮助。请编辑您的答案以解释为什么您的代码解决了原始问题。
          【解决方案10】:

          我尝试了所有可能的解决方案,最后这对我有所帮助。
          在 pom.xml 中添加如下依赖

          <!-- Need this to compile JSP -->
          <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
          </dependency>
          <!-- Need this to compile JSP -->
          <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.6.1</version>
          </dependency>
          

          将视图解析器添加到 application.properties 文件。

          spring.mvc.view.prefix: /WEB-INF/jsp/
          spring.mvc.view.suffix: .jsp
          

          一旦完成上述代码,我们将使用 maven 命令构建项目,然后使用标准 java 的 jar 执行命令作为独立 jar 执行。

          cd <TO_YOUR_PROJECT_DIR>
          mvn clean package spring-boot:repackage
          java -jar target/<YOUR_PROJECT_CREATED_JAR>
          

          【讨论】:

          • 尝试将该 jar 文件复制到目标文件夹以外的其他位置,然后使用 java -jar 运行应用程序。看看会发生什么。
          【解决方案11】:

          如果由于某种原因有人在 SB2+ 中仍然需要这个,我们必须稍微调整生命周期事件方法

          @Override
          public void lifecycleEvent(LifecycleEvent event) {
              if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
                  URL location = this.getClass().getProtectionDomain().getCodeSource().getLocation();
                  if (new LocalDevelDetection().isDocker()){
                      try {
                          //This is necessary to make JSP work in docker --> not sure which of those mounts actually does the trick but I think /YOUR_SB_APP_NAME one works for docker-compose and the other one for "vanilla docker" (non-compose)
                          context.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.RESOURCE_JAR, "/<<YOUR_SB_APP_NAME>>", new URL("jar:file:/<<PATH_TO_YOUR_SB_APP>>.jar!/"), "/META-INF/resources");
                          context.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.RESOURCE_JAR, "/", new URL("jar:file:/<<PATH_TO_YOUR_SB_APP>>.jar!/"), "/META-INF/resources");
                      } catch (MalformedURLException e) {
                          throw new RuntimeException(e);
                      }
                  }
                  else {
                      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(WebResourceRoot.ResourceSetType.RESOURCE_JAR, "/", location, "/META-INF/resources");
                  }
              }
          }
          

          哦,还有我们的 LocalDevelDetection.isDocker() 方法:

          public boolean isDocker(){
              try (Stream< String > stream =
                      Files.lines(Paths.get("/proc/1/cgroup"))) {
                  return stream.anyMatch(line -> line.contains("/docker"));
              } catch (IOException e) {
                  return false;
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-10-26
            • 1970-01-01
            • 2018-04-01
            • 2011-04-21
            • 2018-07-21
            • 2017-07-24
            • 2023-03-31
            • 2019-11-21
            相关资源
            最近更新 更多