一、SpringBoot 整合 JSP
1、引入 jsp 依赖
SpringBoot 默认是不支持 JSP 的,若使用需要引入以下依赖
<!-- 添加servlet依赖模块 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!-- 添加jstl标签库依赖模块-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- 使用jsp引擎,springboot内置tomcat没有此依赖加上即可 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
2、添加 webapp 目录
SpringBoot 默认网站资源放置到 webapp 目录下,如果没有需要手动创建该目录,注意目录层级是在 java 下面
测试选中 webapp 右键是发现没有创建 JSP 的选项,如下图示:
此时可以按 command+;
进入以下这个界面,如下图示:
设置完之后,然后再点选 webapp 文件目录,右键就可以看到可以创建 JSP 文件,然后再 webapp 文件目录下在创建一个 WEB-INF 目录(安全目录),如下图示:
从上面可以发现文件夹样式发生变化,这表示可以创建 JSP 文件。
3、创建 JSP 文件
新建一个 abc2.jsp 文件在 webapp 目录下,abc3.jsp 在 WEB-INF 文件夹下面,代码如下:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h1>name===>${name}我是 abc2.jsp 测试页面hello jsp dispatcher.forward(path) Spring 只是告诉<br>
Tomcat 它一个 path 路径,Tomcat 需要自己去动态编译 jsp 存储到 work 目录</h1>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h1>name===>${name}我是 abc3.jsp 测试页面hello jsp dispatcher.forward(path) Spring 只是告诉<br>
Tomcat 它一个 path 路径,Tomcat 需要自己去动态编译 jsp 存储到 work 目录</h1>
4、直接访问 JSP 文件
文件创建好了,那么现在就看看能不能直接访问到,访问地址:http://localhost:9292/abc2.jsp 效果如下:
在访问 http://localhost:9292/abc3.jsp 效果如下:
访问不到 abc3.jsp ,是因为在 Tomcat 中有约束 WEB-INF 下资源是安全资源,只能通过 Servlet 或者 Controller 才能访问,所以定义个 ToJspController 访问。
这里需要注意下,如果以上设置都设置好了,访问页面还是 404,比如你是多模块的项目,可以做一下设置:
如果你还想把 Tomcat 编译过后的 JSP 源码文件显示保存,可以通过一下配置:
server:
port: 9292
tomcat:
basedir: /Users/gongweiming/IdeaProjects/springcloud2022/cloud-redis-service9292
baseDir 就是 Tomcat 的 work 工作目录,也就是 JSP 页面编译后的 Java 源码文件和编译文件都在这个目录保存着。
5、通过 Controller 访问 JSP 页面
一般 JSP 页面访问都是需要携带数据,所以定义 Controller 去访问页面是比较好的,而且 WEB-INF 下面资源是不能直接访问,需要通过 Controller 跳转访问, 代码如下
@Controller
public class ToJspController {
@RequestMapping("/toJsp")
public ModelAndView toJsp(String cx) {
ModelAndView view = new ModelAndView();
System.out.println(">>>>>>toJsp...");
System.out.println("hnhds");
view.addObject("name", "gwm");
view.setViewName(cx);
return view;
}
}
然后通过 Controller 访问 abc2.jsp 路径为:http://localhost:9292/toJsp?cx=abc2,效果如下:
是因为没 SpringBoot 默认 InternalResourceViewResolver 解析器没有读取到配置 prefix、suffix
这个设置只需要在 application.yml 文件中加上即可,配置如下:
server:
port: 9292
spring:
mvc:
view:
suffix: .jsp
prefix: /
然后再去访问就可以访问到 abc2.jsp 页面,如果需要访问 abc3.jsp 则需要修改 yml 文件,如下所示:
server:
port: 9292
spring:
mvc:
view:
suffix: .jsp
prefix: /WEB-INF/
然后访问地址 http://localhost:9292/toJsp?cx=abc3,效果如下:
6、SpringBoot 访问静态资源
SpringBoot 默认在 static 目录中存放静态页面,而 templates 中放动态页面。静态资源如下图示:
也可以通过 yml 位置修改位置,如下a.html 页面就在不在 static 文件夹中,而是在 html 文件夹中,配置如下:
spring:
mvc:
view:
suffix: .jsp
prefix: /WEB-INF/
# 访问静态资源时需要加上前缀 /boot
static-path-pattern: /boot/**
resources:
# 访问的静态资源在以下文件夹仲可以找到
static-locations: classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/, classpath:/html/
/boot/** 表示访问静态资源时需要带上这个 /boot 前缀。
如访问 static 下面的 abc2.html,访问路径名为 http://localhost:9292/boot/abc2.html
如访问 static 下面的 text.txt,访问路径名为 http://localhost:9292/boot/text.txt
如访问 html 下面的 a.html,访问路径名为 http://localhost:9292/boot/a.html
/
表示 classpath 类路径,表示编译后的路径存放位置,classes 等于 classpath 根路径,所以要访问 html 下面的静态资源,就可以配置 /html/ 访问到,而不是配置 /resources/html/ ,编译后路径如下图示:
所以加上配置 classpath:/html/ 可以访问到 html 文件夹下静态资源 a.html,访问路劲为http://localhost:9292/boot/a.html
7、手动开启 @EnableWebMvc 注解(看自己需要)
如果你要在 SpringBoot 框架中开启 @EnableWebMvc 注解,表示你要完全接管 SpringMVC 功能,SpringBoot 不会去管理 SpringMVC,源码如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({
Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({
DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}
可以从 SpringBoot 装载 WebMvcAutoConfiguration 自动类时看出,是有条件约束的,其中有个条件是 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 表示 Spring 容器中必须不存在 WebMvcConfigurationSupport 类才会装载这个配置类。
回过头看到手动开启的 @EnableWebMvc 注解,源码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
}
发现恰好在这里通过 @Import 注解导入了 WebMvcConfigurationSupport 类,这样就会让 Spring 容器中存在一个 WebMvcConfigurationSupport 实例 bean,导致 WebMvcAutoConfiguration 不自动装配。所以在 SpringBoot 中开启这个注解要慎重。
@EnableWebMvc
注解有一些组件在 spring-webmvc
模块中有默认配置的,比如视图解析器 InternalResourceViewResolver,源码如下:
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
@Bean
public ViewResolver mvcViewResolver(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
ViewResolverRegistry registry =
new ViewResolverRegistry(contentNegotiationManager, this.applicationContext);
configureViewResolvers(registry);
if (registry.getViewResolvers().isEmpty() && this.applicationContext != null) {
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.applicationContext, ViewResolver.class, true, false);
if (names.length == 1) {
registry.getViewResolvers().add(new InternalResourceViewResolver());
}
}
return composite;
}
}
从上述源码可以看出,只有 Spring 容器中存在一个 ViewResolver 接口实现时才会默认装载 InternalResourceViewResolver 视图解析器,如果存在两个就不装载。在 SpringBoot 中刚好有个处理错误页面的视图解析器存在,所以这里就会导致 InternalResourceViewResolver 不会被装载。这里可以关闭这个错误页处理器,通过 yml 配置即可,配置如下:
server:
error:
whitelabel:
enabled: false
但是这里只是装载的视图解析器,并没有指定 prefix、suffix 前后缀。所以在访问的时候,你自己需要带上前后缀格式。
比如要访问上面说的 abc2.jsp 页面,访问路径为:http://localhost:9292/toJsp?cx=abc2.jsp 前后缀分别是 /
.jsp
,访问 abc3.jsp 页面,访问路径为:http://localhost:9292/toJsp?cx=/WEB-INF/abc3.jsp 前后缀分别是 /WEB-INF/
.jsp
。一般是不会这样访问,那就需要自定义配置,自己指定视图解析器 prefix、suffix 前后缀。
7.1、实现 WebMvcConfigurer 接口添加配置实现自定义配置(看自己需要)
更早版本是实现 WebMvcConfigurerAdapter 类,但是现在已经过期,推荐使用 WebMvcConfigurer 接口,缺少那部分功能就添加对应功能即可。
MvcConfig
配置类如下:
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp().suffix(".jsp").prefix("/");
}
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/","classpath:/html/" };
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
}
}
在这里自定义两个组件,一个是视图解析器组件,一个是静态资源解析组件。@EnableWebMvc
注解默认是没有打开静态资源解析,需手动开启,通过 addResourceHandlers() 方法添加需解析的静态资源。
其中/**
表示访问静态资源时,不限制访问层级目录。一般会像下面配置,通过访问路径隔开资源文件,如下:
Spring boot 默认对 /** 的访问 是可以直接访问类路径下的四个静态资源目录下的文件:
- classpath:/public/
- classpath:/resources/
- classpath:/static/
- classpath:/META-INFO/resouces/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/dist/**").addResourceLocations("classpath:/static/dist/");
registry.addResourceHandler("/theme/**").addResourceLocations("classpath:/static/theme/");
registry.addResourceHandler("/boot/*").addResourceLocations("classpath:/static/");
// registry.addResourceHandler("/boot/**").addResourceLocations("classpath:/static/");
// registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
二、SpringBoot 整合 Thymeleaf
轻量级的模板引擎(负责逻辑业务的不推荐,解析DOM或者XML 会占用多的内存)可以直接在浏览器中打开且正确显示模板页面,直接是 html 结尾。
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2、配置 yml 文件
spring:
freemarker:
suffix: .ftlh
enabled: true
prefix: /
3、templates 下新建 html 页面
在 templates 文件夹下面新建 html 页面,声明域名解析是 http://www.thymeleaf.org ,这样在页面中使用 Thymeleaf 标签才能够生效,最常用是 th:
标签
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"> </meta>
<title>Thymeleaf project</title>
</head>
<body>
<h1>thymeleaf 页面</h1><h1 th:text="${name}"></h1>
</body>
</html>
4、通过 Controller 访问
@Controller
public class ToJspController {
@RequestMapping("/toJsp")
public ModelAndView toJsp(String cx) {
ModelAndView view = new ModelAndView();
System.out.println(">>>>>>toJsp...");
System.out.println("hnhds");
view.addObject("name", "gwm");
view.setViewName(cx);
return view;
}
}
访问路径为:http://localhost:9292/toJsp?cx=abc3 页面效果如下:
可以发现 Controller 封装的 Model 的 name 值。这个就是 Thymeleaf 提供的 th:
标签可以做到。
然后再试试以静态资源的形式去访问,路径为:http://localhost:9292/boot/abc3.html 效果如下:
可以发现 abc3.html
直接就被 write()
在浏览器上了,就是被当做事一个静态资源打印出。
三、SpringBoot 整合 Freemarker
FreeMarker Template Language 文件一般保存为 xxx.ftlh 或者 xxx.ftl 严格依赖 MVC模式,不依赖 Servlet 容器(不占用 JVM 内存)内建函数。
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
2、编写模板文件
一般 thymeleaf 默认.html
, freemarker 默认 .ftl
放在 resources 下面的 templates 目录下(默认访问路径)
3、编写 Controller 层代码
public class Users {
private String username;
private String usersex;
private String userage;
public Users(String username, String usersex, String userage) {
this.username = username;
this.usersex = usersex;
this.userage = userage;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsersex() {
return usersex;
}
public void setUsersex(String usersex) {
this.usersex = usersex;
}
public String getUserage() {
return userage;
}
public void setUserage(String userage) {
this.userage = userage;
}
}
@Controller
public class TestController {
@RequestMapping("/myfreemarker")
public String showUsers(Model model) {
List<Users> list = new ArrayList<>();
list.add(new Users("aa", "F", "21"));
list.add(new Users("bb", "M", "20"));
list.add(new Users("cc", "F", "22"));
model.addAttribute("list", list);
return "myfreemarker";
}
}
4、效果展示
访问地址: http://localhost:9292/myfreemarker
四、总结
1、以上 JSP、Thymeleaf、Freemarker 全部共存时,SpringBoot 默认做了排序,优先是静态文件资源,最后是 JSP 资源。因为 JSP 在 Spring 中是不会真正去判断存不存在该文件,而是交给 Tomcat 去自己处理判断。而 Thymeleaf、Freemarker 两种后端模版引擎直接会先去判断文件收存在。从源码可以看出他们的优先级,如下图示:
2、JSP 现在很少用,现在就看 Thymeleaf 和 Freemarker 谁更好用?
如果你选了 Thymeleaf,非常不幸截至到现在 Thymeleaf 已经两年没更新了,不知道会不会Velocity 停止更新7年之久很多年前也有人问 Velocity 和 Freemarker 选哪个?Velocity 可是Apache 背书。但时间证明freemarker自始至终都是一种明智得选择过去的20年,Freemarker 经历过4次重构和长期更新迭代,尽可能保持了向后兼容,它能良好的支持 JSP 标签、JSON 格式数据、XML 格式数据,能方便的让你实现 java 扩展,拥有完善的字符串、数字、序列、哈希表等数据处理方法,对 html,url,js,json 等安全转义等,它可以用于任何模板引擎可以应用的场景,自带国际化解决方案,清晰的异常日志(包含错误原因、错误位置、错误提示、模板堆栈、java 堆栈)能让你第一时间找到模板中错误的问题所在,强大到完全可以创造一门新的动态语言,却又严守模板引擎边界,其强大、完整、安全性可以超越任何一款不仅限于 java 语言的模板引擎。