Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇

Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇

01 前言

有段时间不做项目了,但是由于工作需要,要做一个小项目,就想直接用Spring Boot+Maven+JSP+Spring Data JPA+Mysql。之前简单接触过spring Boot,比较方便的一点是能直接打包运行,不需另外配Tomcat,赶紧上车练练手。比较郁闷的是,噼里啪啦敲完V1.0,好了,在打包独立运行的过程中碰到不少坑,查了一通资料,也没真正解决问题。历经几番折腾,总算能用了。在此做一下记录,也给其他碰到类似问题的小伙伴一个参考。

提个醒
此文主要解决以下两个问题(如果没有符合各位实际的问题的话,也没必要继续往后读了):
1、未使用Maven打包前:Spring Boot 整合JSP后,前端访问(通过Controller访问JSP)时变成下载文件。
2、使用Maven打包后:打包jar或war后独立运行,前端访问不到JSP页面(但是Controller返回其他数据正常,JSP就是不行);或者是访问到了JSP页面,但是JSP引用的静态资源无法访问。

02 正文

1、准备

环境:

JDK 1.8+IDEA+MySQL 5.5+Maven 3.5 + Spring Boot 2.1.2
*连接池使用 alibaba 的druid

具体的搭建过程这里就不想细说了,使用向导一步步操作下来即可。主要在解决上文提到的两个问题。
但是已经截了图,还是把图贴上来示意一下。
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第1张图片
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第2张图片
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第3张图片
新建Project完成后的结构:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第4张图片
大体完成V1.0时(此时还未做JSP相关配置,只写了bookinfo对应的entity、dao、service,在application.properties中配置了数据源相关、JPA相关、日志相关、json相关的项,同时写了一个简单的controller),Project的结构是这样的:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第5张图片
此时controller是这样的:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第6张图片
此时,直接启动工具栏的Run BookmisApplication,前端访问http://127.0.0.1:8080/getDate,毫无疑问是可以的:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第7张图片
但是要访问http://127.0.0.1:8080/getbook/b0001却还不行:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第8张图片

提示:entity、dao、service的各层需加上对应的注解,同时主类BookmisApplication上需加@AutoConfigurationPackage来自动扫描配置


下面说一下配置JSP
(1)配置pom.xml

  • 设置项目打包为war:war
  • 添加tomcat支持和JSTL标签库:

<dependency>
	<groupId>org.apache.tomcat.embedgroupId>
	<artifactId>tomcat-embed-jasperartifactId>
	<scope>providedscope>
dependency>

<dependency>
	<groupId>javax.servletgroupId>
	<artifactId>jstlartifactId>
dependency>

(2)添加Web目录:在src/main下建立webapp/WEB-INF/pages目录结构,webappjava目录同一级。如果此时想在pages下新建一个jsp文件,右键,发现没有jsp的选项,这是因为此目录还未关联Web,如图:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第9张图片

(3)关联Web目录:
①设置:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第10张图片
②设置Facets,添加web,关联到前面创建的webapp目录:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第11张图片
其中,将web.xml放到webapp/WEB-INF下:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第12张图片
同时,将根目录映射为webapp
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第13张图片
此时,如果下方提示未建立对应的artifact,则单击创建:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第14张图片
③设置Artifacts,默认即可:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第15张图片
完成以上三步后,应用并确定。
此时观察项目结构略有变化:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第16张图片
同时,在pages目录右键,可以创建JSP文件:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第17张图片

(4)配置application.properties

  • 设置JSP视图前、后缀:
spring.mvc.view.prefix=/WEB-INF/pages/
spring.mvc.view.suffix=.jsp

以上,已经可以进行jsp访问了,但是启动方式要注意:
不能直接在右上角的工具栏上的Run BookmisApplication,而是要打开Maven Projects窗口,找到spring-boot:run,右键,运行,,如图:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第18张图片
若直接使用工具栏的运行,如果无法访问JSP,后台可能会这么输出:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第19张图片

经调试(断点处参考文尾附表, PathResourceResolver.java类中151行)发现,通过工具类直接启动的这种方式,虽然在application.properties中配置了jsp前后缀,但是在处理时只会查找默认的5个位置(classpath:/META-INF/resources/,classpath:/resources/,classpath:static/,classpath:/public/,classpath:/),并不会去查找WEB-INF。这个地方表示很疑惑,知道的大神可以交流下。可能是通过这种方式运行,不会将webapp目录下的东西拷贝到运行时tomcat目录下?或者哪里设置不对。

下面试试效果:

  • 已经在pages目录下创建两个jsp(由index.jsp跳转到book.jsp):index.jspbook.jsp
  • controller也修改了一下:
    Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第20张图片
    那么,现在的项目结构是这样的:
    Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第21张图片
    Maven Projects窗口启动spring-boot:run,前端访问 http://127.0.0.1:8080,完全OK:
    Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第22张图片
    Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第23张图片
    数据库的表:
    Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第24张图片

2、针对问题1

参考上文的JSP配置步骤操作下来,一般此问题不会再有。如有,请检查配置:添加依赖;打war包;添加web模块并关联到webapp目录;配jsp视图前后缀;使用Maven Projects窗口的命令spring-boot:run启动项目。

3、针对问题2

其实这个主要是注意目录对应关系。
(1)配置application.properties

#静态资源默认读取路径(默认有这4个位置,可以根据需要自己添加) 
#spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/

#静态资源访问路径规则 
spring.mvc.static-path-pattern=/static/**

(2)举例
①现在把jquery.min.js放到resources/static下,然后在index.jsp中调用(注意js和jsp的相对位置):
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第25张图片

此处jsp中引用位置写成/static/jquery.min.js也是可以的

②在Maven Projects窗口使用Maven打包(打war包,前文已经提到过,jar包万万不行的):
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第26张图片
③打包完成:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第27张图片
④单独运行(war包也可以直接通过java -jar运行:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第28张图片
⑤前端访问http://127.0.0.1:8080,顺利运行:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第29张图片
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第30张图片
跳转也没问题:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第31张图片


如果没理解这波操作也不要紧,先照步骤跑起来,然后用解压工具打开生成的war文件,看看里面的目录结构,相信很快就明白过来了(从后往前推)~
war如图:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第32张图片
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第33张图片
上面只是一种实现,而实际开发中,这样把静态资源放在resources/static目录下,而在另一边目录的JSP中引用,Ctrl+鼠标放上去,IDE不能自动跳转,很不方便。

所以也可以这样:在WEB-INF/下创建目录static,与pages同一级,将静态资源放在此处,然后在application.properties中将spring.resources.static-locations配置成这样:

spring.resources.static-locations=/WEB-INF/static/,classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/

如图:
Spring Boot 2.1.2整合JSP配置及打包WAR独立运行——IDEA篇_第34张图片
总之一句话:要么把静态资源放在默认的4个路径下,要么将自定义路径加入到application.propertiesspring.resources.static-locations中。

03 后记

有时间还是要多看看官网的文档。配置文档参考
项目已经打包上传到GitHub(数据库文件也在里面)。 【点我传送】

附件1:application.properties

server.port=8080

#配置数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/bookmis?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=
#配置druid
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.filters=stat
spring.datasource.druid.max-active=20
spring.datasource.druid.initial-size=1
spring.datasource.druid.max-wait=60000
spring.datasource.druid.min-idle=1
spring.datasource.druid.time-between-eviction-runs-millis=60000
spring.datasource.druid.min-evictable-idle-time-millis=300000
spring.datasource.druid.validation-query=select 'x'
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.pool-prepared-statements=true
spring.datasource.druid.max-open-prepared-statements=20

#静态资源默认读取路径(默认有4个) 
spring.resources.static-locations=/WEB-INF/static/,classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/

#mvc
spring.mvc.view.prefix=/WEB-INF/pages/
spring.mvc.view.suffix=.jsp

#JPA
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.open-in-view=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

#json
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

#关闭thymeleaf模板
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=false

#日志
logging.path=./logs/


#热部署
#spring.devtools.restart.additional-paths=src/main/webapp/WEB-INF/pages

附件2 :pom.xml


<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.0modelVersion>
	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>2.1.2.RELEASEversion>
		<relativePath/> 
	parent>
	<groupId>com.examplegroupId>
	<artifactId>bookmisartifactId>
	<version>0.0.1-SNAPSHOTversion>
	<name>bookmisname>
	<description>图书管理系统description>
	<packaging>warpackaging>

	<properties>
		<java.version>1.8java.version>
		<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
	properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-data-jpaartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-devtoolsartifactId>
			<scope>runtimescope>
		dependency>
		<dependency>
			<groupId>mysqlgroupId>
			<artifactId>mysql-connector-javaartifactId>
			<scope>runtimescope>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
			
		dependency>

		
		<dependency>
			<groupId>org.apache.tomcat.embedgroupId>
			<artifactId>tomcat-embed-jasperartifactId>
			<scope>providedscope>
		dependency>
		
		<dependency>
			<groupId>javax.servletgroupId>
			<artifactId>jstlartifactId>
		dependency>


		
		<dependency>
			<groupId>com.alibabagroupId>
			<artifactId>druid-spring-boot-starterartifactId>
			<version>1.1.10version>
		dependency>

	dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			plugin>
		plugins>
	build>

project>

最后,作为一名帅气又new比的coder,怎能不会充分利用调试功能。所以附上个人的断点处作为参考,顺着这个思路捋一捋应该问题不大(自行酌用)。ε=(´ο`*)))唉,此番折腾下来,很spring boot呢。
如表:

序号 代码 备注
1 DispatcherServlet.java 1044 applyDefaultViewName(processedRequest, mv);
2 DispatcherServlet.java 1116 render(mv, request, response);
3 DispatcherServlet.java 1370 view.render(mv.getModelInternal(), request, response);
4 RequestContext.java 214 WebApplicationContext wac = (WebApplicationContext) request.getAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE);
5 DispatcherServlet.java 1347 view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
6 DispatcherServlet.java 1055 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
7 DispatcherServlet.java 1411 View view = viewResolver.resolveViewName(viewName, locale);
8 InternalResourceView.java 147 String dispatcherPath = prepareForRendering(request, response);
9 InternalResourceView.java 170 rd.forward(request, response);
10 ApplicationDispatcher.class 130 this.processRequest(request, response, state);
11 ApplicationDispatcher.class 76 this.doForward(request, response);
12 ApplicationDispatcher.class 91 ApplicationDispatcher.State state = new ApplicationDispatcher.State(request, response, false);
13 DispatcherServlet.java 1038 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
14 ApplicationDispatcher.class 178 this.invoke(state.outerRequest, response, state);
15 ApplicationDispatcher.class 347 ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, this.wrapper, servlet);
16 FrameworkServlet.java 897 processRequest(request, response);
17 DispatcherServlet.java 942 doDispatch(request, response);
18 ResourceHttpRequestHandler.java 515 path = processPath(path);
19 ResourceHttpRequestHandler.java 451 Resource resource = getResource(request);
20 ResourceHttpRequestHandler.java 526 Resource resource = this.resolverChain.resolveResource(request, path, getLocations());
21 PathResourceResolver.java 151 for (Resource location : locations) {
22 PathResourceResolver.java 154 Resource resource = getResource(pathToUse, location);

新手上路,欢迎交流~
Spring Boot 车队,我来了~

你可能感兴趣的:(JAVA)