Thymeleaf中setResolvablePatterns()配置失效问题排查
setResolvablePatterns()替代setCheckExistence(true)时出错 按照Thymeleaf官方文档建议,应为不同模板解析器设置可解析模式。我配置了两个SpringResourceTemplateResolver,分别处理HTML和JavaScript类型的模板,尝试用setResolvablePatterns()替代setCheckExistence(true),但出现以下模板解析错误:
o.t.TemplateEngine : [THYMELEAF] Exception processing template "users/login": Error resolving template [users/login], template might not exist or might not be accessible by any of the configured Template Resolvers org.thymeleaf.exceptions.TemplateInputException: Error resolving template [users/login], template might not exist or might not be accessible by any of the configured Template Resolvers
改用setCheckExistence(true)则可正常工作,请问问题出在哪里?
我的配置代码如下:
public SpringResourceTemplateResolver htmlTemplateResolver() { SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(applicationContext); templateResolver.setPrefix("classpath:templates/web/"); templateResolver.setSuffix(".html"); templateResolver.setTemplateMode(TemplateMode.HTML); templateResolver.setCharacterEncoding("UTF-8"); templateResolver.setOrder(0); templateResolver.setResolvablePatterns(Collections.singleton("/*.html")); // templateResolver.getResolvablePatternSpec().addPattern("/*.html"); // 同样无效 // templateResolver.setCheckExistence(true); return templateResolver; } public SpringResourceTemplateResolver javaScriptTemplateResolver() { SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(applicationContext); templateResolver.setPrefix("classpath:static/build/js/"); templateResolver.setSuffix(".js"); templateResolver.setTemplateMode(TemplateMode.JAVASCRIPT); templateResolver.setCharacterEncoding("UTF-8"); templateResolver.setOrder(1); templateResolver.setResolvablePatterns(Collections.singleton("/*.js")); // templateResolver.setResolvablePatterns(Collections.singleton("*.js")); // 同样无效 // templateResolver.setResolvablePatterns(Collections.singleton("*")); // 同样无效 // templateResolver.setCheckExistence(true); return templateResolver; }
核心问题:setResolvablePatterns()的匹配逻辑理解错误
setResolvablePatterns()是针对调用模板时传入的模板名称(比如这里的users/login)进行匹配,而非拼接prefix、suffix后的完整资源路径。
你的配置存在两个关键错误:
- 模板名称不带后缀:因为已经通过
setSuffix(".html")和setSuffix(".js")自动添加后缀,实际传入的模板名称是users/login(无.html),而非users/login.html,所以/*.html或*.js这种带后缀的模式完全匹配不上。 - 路径匹配范围不足:
/*.html模式只能匹配根路径下的模板(比如login对应login.html),但你的模板名称是users/login这种带子路径的,根路径模式无法覆盖。
而setCheckExistence(true)能工作的原因是:开启该选项后,解析器会尝试加载模板,若不存在才交给下一个解析器;但setResolvablePatterns()是先判断模板名称是否匹配模式,不匹配则直接跳过当前解析器,不会尝试加载。
修正后的配置
根据模板结构调整可解析模式:
HTML解析器
匹配所有带子路径的HTML模板名称,用通配符**即可:
templateResolver.setResolvablePatterns(Collections.singleton("**"));
如果需要更精准匹配(比如只处理users目录下的模板),可以写成:
templateResolver.setResolvablePatterns(Collections.singleton("users/**"));
JavaScript解析器
假设JS模板名称是app、utils/validate这类不带.js后缀的,同样用对应路径的通配符:
templateResolver.setResolvablePatterns(Collections.singleton("**")); // 若JS模板都在固定子目录下,可写成"js/**"
修正后的完整示例:
public SpringResourceTemplateResolver htmlTemplateResolver() { SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(applicationContext); templateResolver.setPrefix("classpath:templates/web/"); templateResolver.setSuffix(".html"); templateResolver.setTemplateMode(TemplateMode.HTML); templateResolver.setCharacterEncoding("UTF-8"); templateResolver.setOrder(0); // 匹配所有带子路径的HTML模板名称 templateResolver.setResolvablePatterns(Collections.singleton("**")); return templateResolver; } public SpringResourceTemplateResolver javaScriptTemplateResolver() { SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(applicationContext); templateResolver.setPrefix("classpath:static/build/js/"); templateResolver.setSuffix(".js"); templateResolver.setTemplateMode(TemplateMode.JAVASCRIPT); templateResolver.setCharacterEncoding("UTF-8"); templateResolver.setOrder(1); // 匹配所有带子路径的JS模板名称 templateResolver.setResolvablePatterns(Collections.singleton("**")); return templateResolver; }
额外说明
如果调用JS模板时传入的名称带.js后缀(不建议,违背setSuffix的配置逻辑),才需要将模式改为**.js,但这种做法不符合Thymeleaf的配置规范,建议保持模板名称不带后缀,由解析器自动补充。
内容的提问来源于stack exchange,提问作者Slevin

