thymeleaf用法
thymeleaf菜鸟教程_SpringMVC应⽤程序中的Thymeleaf模板
布局,⽆扩展
thymeleaf菜鸟教程
在使⽤JSP / JSTL和Apache Tiles⼏年后,我开始为我的Spring MVC应⽤程序发现Thymeleaf。 Thymeleaf是⼀个⾮常出⾊的视图引擎,尽管⽬前缺乏良好的IntelliJ(投票:http: )⽀持,但它简化并加快了开发速度(有)。 在学习如何使⽤Thymeleaf时,我研究了使⽤布局的不同可能性。
除了本机,还⾄少有两个选项可⽤于布局: 以及 。 两者似乎都可以正常⼯作,但是受到有关简单和⾃定义选项的启发,我尝试了⼀下。 在这篇⽂章中,我将展⽰我创建了解决⽅案。
创建⼀个带有Thymeleaf⽀持的Spring MVC项⽬
为了快速⼊门,我在Thymeleaf 2.1⽀持下使⽤了。 我通过简单地调⽤原型创建了⼀个项⽬,然后将其导⼊到IntellJ中。
创建布局⽂件
在WEB-INF / views⽬录中,我创建了⼀个布局⽂件夹,在其中放置了第⼀个名为default.html的布局⽂件:$ {view}变量将包含
@Controller返回的视图名称和$ {view}中的内容⽚段⽂件将放置在这⾥。
创建视图⽂件
我编辑了WEB-INF / views / homeNotSignedIn.html,然后按以下⽅式定义了内容⽚段:因此,唯⼀的更改是定义名为content的⽚段并删除重复的⽚段包含。 ⽆需其他更改。 @Controller返回原始视图名称,就像以前⼀样:
@Controller
class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
String index(Principal principal) {
return principal != null ? "home/homeSignedIn" : "home/homeNotSignedIn";
}
}
我相应地改变了其他观点。
创建并与Spring MVC集成
为了完成“新布局框架”,我创建了⼀个处理程序来完成⼯作:
public class ThymeleafLayoutInterceptor extends HandlerInterceptorAdapter {
private static final String DEFAULT_LAYOUT = "layouts/default";
private static final String DEFAULT_VIEW_ATTRIBUTE_NAME = "view";
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {        if (!modelAndView.hasView()) {
return;
}
String originalViewName = ViewName();
modelAndView.setViewName(DEFAULT_LAYOUT);
modelAndView.addObject(DEFAULT_VIEW_ATTRIBUTE_NAME, originalViewName);
}
}
ThymeleafLayoutInterceptor获取从处理程序的⽅法返回的原始视图名称,并将其替换为布局名称(在WEB-INF / views / layouts / default.html中定义)。 原始视图作为视图变量放置在模型中,因此可以在布局⽂件中使⽤它。 我覆盖了postHandle⽅法,因为它是在呈
现视图之前执⾏的。
添加很容易:
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ThymeleafLayoutInterceptor());
}
}
这就是基本配置。 此后没有⽕箭。 转到localhost:8080后的结果。 这是我所期望的。 奇迹般有效。 因此,我尝试注册⼀个帐户以及提
交表单后看到的内容:
500 returned for /signup with message Error resolving template "redirect:/", template might not exist or might not be accessible by any of the configured Te 重定向:/提交表单后。 我需要像这样修改:
当然,重定向:/
public class ThymeleafLayoutInterceptor extends HandlerInterceptorAdapter {
private static final String DEFAULT_LAYOUT = "layouts/default";
private static final String DEFAULT_VIEW_ATTRIBUTE_NAME = "view";
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {        if (!modelAndView.hasView()) {
return;
}
String originalViewName = ViewName();
if (isRedirectOrForward(originalViewName)) {
return;
}
modelAndView.setViewName(DEFAULT_LAYOUT);
modelAndView.addObject(DEFAULT_VIEW_ATTRIBUTE_NAME, originalViewName);
}
private boolean isRedirectOrForward(String viewName) {
return viewName.startsWith("redirect:") || viewName.startsWith("forward:");
}
}
它按预期⼯作。 但是我意识到我需要定义和附加布局,因为Signup和Signin之前(⽽不是在应⽤上述更改之后)使⽤了此布局。
创建其他布局
我创建了⼀个名为blank.html的新布局,并将其放置到WEB-INF / views / layouts⽂件夹中。 但是如何使⽤选择布局? 可能有很多⽅法可以做到这⼀点。 不过,最简单的⽅法之⼀是,只需添加⼀个名为layout的模型属性,即可从@Controller返回布局名称。 如果未给出布局,则使⽤默认布局,否则使⽤给定布局。 简单。 但是我想要⼀个更强⼤的解决⽅案。 所以我想也许我可以这样使⽤注释:
@Controller
class SigninController {
@Layout(value = "layouts/blank")
@RequestMapping(value = "signin")
String signin() {
return "signin/signin";
}
}
对我来说,这听起来像是⼀个很好的解决⽅案。 所以我实现了它。
选择布局
我创建了⼀个⽅法级别@Layout批注,并将其放置在org.thymeleaf.spring.support包中(与ThymeleafLayoutInterceptor⼀起):
package org.thymeleaf.spring.support;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Layout {
String value() default "";
}
我将更改如下:
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {        if (!modelAndView.hasView()) {
return;
}
String originalViewName = ViewName();
if (isRedirectOrForward(originalViewName)) {
return;
}
String layoutName = getLayoutName(handler);
modelAndView.setViewName(layoutName);
modelAndView.addObject(DEFAULT_VIEW_ATTRIBUTE_NAME, originalViewName);
}
private boolean isRedirectOrForward(String viewName) {
return viewName.startsWith("redirect:") || viewName.startsWith("forward:");
}
private String getLayoutName(Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Layout layout = MethodAnnotation(Layout.class);
if (layout == null) {
return DEFAULT_LAYOUT;
} else {
return layout.value();
}
}
}
现在,当使⽤@Layout注释对处理程序⽅法进⾏注释时,它将获得其value属性。 效果很好。 但是当
我开始更改SignupController时,我意识到我需要注释这两种⽅法。 如果可以通过对@Controller类进⾏注释,将我的注释⼀次⽤于所有⽅法,那就更好了:
@Controller
@Layout(value = "layouts/blank")
class SignupController {
}
所以我做了。
最后的润⾊
⾸先,我更改了注释,以便可以将其定位为类型级别:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Layout {
String value() default "";
}
和:
private String defaultLayout = DEFAULT_LAYOUT;
private String viewAttributeName = DEFAULT_VIEW_ATTRIBUTE_NAME;
public void setDefaultLayout(String defaultLayout) {
Assert.hasLength(defaultLayout);
this.defaultLayout = defaultLayout;
}
public void setViewAttributeName(String viewAttributeName) {
Assert.hasLength(defaultLayout);
this.viewAttributeName = viewAttributeName;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {        if (!modelAndView.hasView()) {
return;
}
String originalViewName = ViewName();
if (isRedirectOrForward(originalViewName)) {
return;
}
String layoutName = getLayoutName(handler);
modelAndView.setViewName(layoutName);
modelAndView.addObject(this.viewAttributeName, originalViewName);
}
private boolean isRedirectOrForward(String viewName) {
return viewName.startsWith("redirect:") || viewName.startsWith("forward:");
}
private String getLayoutName(Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Layout layout = getMethodOrTypeAnnotation(handlerMethod);
if (layout == null) {
return this.defaultLayout;
} else {
return layout.value();
}
}
private Layout getMethodOrTypeAnnotation(HandlerMethod handlerMethod) {
Layout layout = MethodAnnotation(Layout.class);
if (layout == null) {
BeanType().getAnnotation(Layout.class);
}
return layout;
}
}
如您所见,⽅法级别注释⽐类型级别注释更重要,它提供了⼀定的灵活性。 此外,我还添加了配置的可能性。 我认为,设置默认布局名称和视图属性名称可能很有⽤。
概要
提出的解决⽅案可能需要⼀些改进才能在⽣产中使⽤,但是它显⽰了我们如何轻松地构建模板布局⽽⽆需向项⽬中添加额外的库并仅利⽤Thymeleaf的核⼼功能。 请分享您对解决⽅案的评论和意见。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。