项目背景

一些前端还是JSP的老项目,需要改造为springboot,所以需要springboot能支持JSP。

项目结构

afei-demo
├──src/main
├ ├──java
├ ├ ├──com.afei.test.demo
├ ├ ├ ├──controller
├ ├ ├ ├──service
├ ├ ├ ├──mapper
├ ├ ├ ├──Application.java springboot项目main方法所在的主类
├ ├──resources
├ ├ ├──css 存放css文件的地方
├ ├ ├──images 存放.jpg,.png等图片资源的地方
├ ├ ├──js 存放js文件的地方
├ ├ ├──application.properties springboot配置文件
├ ├──webapp
├ ├ ├──WEB-INF/jsp 这个路径和视图配置中的spring.mvc.view.prefix要对应
├ ├ ├ ├──index.jsp
├ ├ ├ ├──monitor 监控相关jsp页面
├ ├ ├ ├──warning 告警相关jsp页面

依赖组件

springboot支持JSP的主要依赖组件如下:

  • spring-boot-starter-web版本为1.5.9.RELEASE;
  • jstl版本为1.2;
  • tomcat-embed-jasper版本为8.5.23;
  • spring-boot-maven-plugin版本为1.4.2.RELEASE;

核心POM

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot tomcat jsp 支持开启 start-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>8.5.23</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--springboot tomcat jsp 支持开启 end-->
</dependencies>

插件配置

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<!--springboot-maven不能使用1.5.9.RELEASE版本, 否则会导致WEB-INF下的jsp资源无法访问-->
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.4.2.RELEASE</version>
<configuration><!--指定springboot的main方法类,否则会由于某些测试类中也有main方法而导致构建失败-->
<mainClass>com.yyfax.fdfsfront.Application</mainClass>
</configuration>
<executions>
<execution> <goals> <goal>repackage</goal> </goals> </execution>
</executions>
</plugin>

<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>

</plugins>

<resources>
<!-- 打包时将jsp文件拷贝到META-INF目录下-->
<resource>
<!-- 指定resources插件处理哪个目录下的资源文件 -->
<directory>src/main/webapp</directory>
<!--注意此次必须要放在此目录下才能被访问到-->
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/**</include>
</includes>
</resource>
<resource>
<directory>${project.basedir}/lib</directory>
<targetPath>BOOT-INF/lib/</targetPath>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>

</build>

再次强调,spring-boot-maven-plugin的版本是1.4.2.RELEASE,而spring-boot-starter-web的版本是1.5.9.RELEASE。否则会导致即使能成功构建JAR包,也无法访问JSP资源。这里坑浪费了我不少时间!

静态资源

resources标签下的配置非常重要,其作用是将webapp目录下所有的资源(主要是JSP资源,因为css,js,图片等资源放在resoures目录下)拷贝到最终springboot的JAR包中的​​META-INF/resources​​目录下。

  • 为什么是​​/META-INF/resources​​目录?

这个与springboot的配置​​spring.resources.static-locations​​有关,其默认赋值在源码ResourceProperties.java中,由源码可知,​​classpath:/META-INF/resources/​​是其中一个默认的静态资源存放路径:

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware {

private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" };

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };

private static final String[] RESOURCE_LOCATIONS;

static {
RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length
+ SERVLET_RESOURCE_LOCATIONS.length];
System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0,
SERVLET_RESOURCE_LOCATIONS.length);
System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,
SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);
}

/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/] plus context:/ (the root of the servlet context).
*/
private String[] staticLocations = RESOURCE_LOCATIONS;
... ...
}

至于其他的css,图片,js等静态资源文件,放到resources目录下即可,并结合如下核心代码:

@Configuration
@Slf4j
public class FdfsWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {

@Bean
public SecurityInterceptor getSecurityInterceptor() {
return new SecurityInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 除了login登陆访问路径和error页面,其他都要经过SecurityInterceptor拦截器(SecurityInterceptor extends HandlerInterceptorAdapter)
InterceptorRegistration addInterceptor = registry.addInterceptor(getSecurityInterceptor());
// 排除配置
addInterceptor.excludePathPatterns("/error");
addInterceptor.excludePathPatterns("/login**");
// 拦截配置
addInterceptor.addPathPatterns("/**");
}

@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 配置welcome默认首页
registry.addViewController("/").setViewName("forward:/main/index.shtml");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
super.addViewControllers(registry);
}

@Bean
public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
ServletRegistrationBean bean = new ServletRegistrationBean(dispatcherServlet);
// RequestMapping路径都以.shtml结尾,例如:http://localhost/main/login.shtml
bean.addUrlMappings("*.shtml");
// 其他文件该是什么后缀就是什么后缀
bean.addUrlMappings("*.html");
bean.addUrlMappings("*.css");
bean.addUrlMappings("*.js");
bean.addUrlMappings("*.png");
bean.addUrlMappings("*.gif");
bean.addUrlMappings("*.ico");
bean.addUrlMappings("*.jpeg");
bean.addUrlMappings("*.jpg");
return bean;
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 这里的配置很重要,由于我们把css,图片,js等静态资源文件放在resources目录下,所以必须增加这些ResourceHandler,才能访问到这些资源
registry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/favicon.ico");
registry.addResourceHandler("/css/**").addResourceLocations("classpath:/css/");
registry.addResourceHandler("/images/**").addResourceLocations("classpath:/images/");
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js/");
registry.addResourceHandler("/500.html").addResourceLocations("classpath:/500.html");
registry.addResourceHandler("/index.jsp").addResourceLocations("classpath:/index.jsp");
super.addResourceHandlers(registry);
}

}

视图配置

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

这里的配置要非常注意,以笔者这两个配置为例,如果我在controller中​​return new ModelAndView("main/index");​​​,那么它访问的资源路径是/WEB-INF/jsp/main/index.jsp(prefix+ModelAndView+suffix)。这里强烈建议spring.mvc.view.prefix的配置以​​/​​​结尾,即配置​​/WEB-INF/jsp/​​​而不要配置​​/WEB-INF/jsp​​​。否则如果​​return new ModelAndView("main/index");​​就会抛出如下错误:

File [/WEB-INF/jspmain/index.jsp] not found
  • 总结
  1. 配置为​​/WEB-INF/jsp/​​,那么​​return new ModelAndView("/main/index");​​和​​return new ModelAndView("main/index");​​都可以访问。
  2. 配置为​​/WEB-INF/jsp​​,那么只有​​return new ModelAndView("/main/index");​​才可以访问。