Spring Boot集成Thymeleaf,默认情况下,前端的动态html页面放在resources/templates目录下,浏览器如果要访问这个目录下的页面,必须先发一个请求到Controller方法,由该方法返回一个页面的路径加名称前缀实现浏览器上展示动态页面
package com.zhutao.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/indexPage")
public class IndexController {
@GetMapping
public String toIndex() {
return "index";
}
}
如上,浏览器要访问resources/templates/index.html页面,必须先发请求http://ip:port/contextPath/indexPage。
如果templates下面有很多个动态页面,这么干是否觉得麻烦?能否通过http://ip:port/contextPath/index就能访问到templates/index.html界面?
Spring Boot提供了WebMvcConfigurer,该方法有个addViewControllers方法,可以实现这种想法
package com.zhutao.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfigTemp implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index");
}
}
这个效果类似下面的代码
package com.zhutao.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping
public class PageController {
@GetMapping("/index")
public String toIndex() {
return "/index";
}
}
但是如果有很多个页面,都通过registry注册,是否又显得很麻烦,能否更简单一点。
思路:在程序启动的时候,自动的读取templates目录下的html文件,通过registry注册,以下是实现,分两种情况考虑,第一本地开发环境运行时,可以通过文件系统读取所有的html文件;第二,通过运行jar包来启动程序,就不能再通过File读取所有的html文件,只能解析jar包来读取所有的html文件
package com.zhutao.demo.config;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private static Logger logger = LoggerFactory.getLogger(WebMvcConfig.class);
//默认值为classpath:/templates/,和springboot保持一致
@Value("${spring.thymeleaf.prefix:classpath:/templates/}")
private String prefix;
//默认值为.html,和springboot保持一致
@Value("${spring.thymeleaf.prefix:.html}")
private String suffix;
@Resource
private ResourceLoader resourceLoader;
@Override
public void addViewControllers(ViewControllerRegistry registry) {
String prefixName = prefix.substring("classpath:".length());
if(prefixName.startsWith("/")) {
prefixName = prefixName.substring(1);
}
if(prefixName.endsWith("/")) {
prefixName = prefixName.substring(0,prefixName.length()-1);
}
org.springframework.core.io.Resource resource = resourceLoader.getResource(prefix);
JarFile jarFile = null;
try {
String path = resource.getURL().getPath();
//打成jar包后的场景
if(path.indexOf(".jar!/BOOT-INF/classes!/"+prefixName+"/") != -1) {
jarFile = new JarFile(path.substring("file:".length(),path.lastIndexOf("!/BOOT-INF/classes!")));
Enumeration<JarEntry> jarEntries = jarFile.entries();
while(jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String classPrefix = "BOOT-INF/classes/"+prefixName+"/";
if(jarEntry.getName().startsWith(classPrefix) && jarEntry.getName().endsWith(suffix)) {
String url = jarEntry.getName().substring(classPrefix.length()-1,jarEntry.getName().length() - suffix.length());
registry.addViewController(url);
}
}
} else {
//本地开发环境的场景
File prefixFile = resource.getFile();
List<File> allHtmlFiles = htmlFiles(prefixFile);
if(allHtmlFiles == null || allHtmlFiles.isEmpty()) {
return;
}
int prefixFilePathLength = prefixFile.getPath().length();
for(File e : allHtmlFiles) {
String url = e.getPath().substring(prefixFilePathLength,e.getPath().length()-suffix.length());
registry.addViewController(url);
}
}
} catch (IOException e) {
logger.error("自动将模板页面的路径映射为controller的url时异常",e);
} finally {
if(jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
logger.error("关闭jar文件时异常",e);
}
}
}
}
private List<File> htmlFiles(File file) {
File[] subFiles = file.listFiles();
if(subFiles == null || subFiles.length == 0) {
return new ArrayList<File>();
}
List<File> files = new ArrayList<>();
for(File subFile : subFiles) {
if(subFile.isFile()) {
if(subFile.getName().toLowerCase().endsWith(suffix)) {
files.add(subFile);
}
} else {
List<File> fs = htmlFiles(subFile);
if(fs != null && !fs.isEmpty()) {
files.addAll(fs);
}
}
}
return files;
}
}