Spring MVC应用程序中的Thymeleaf模板布局,无扩展

在使用JSP / JSTL和Apache Tiles几年之后,我开始为我的Spring MVC应用程序发现Thymeleaf。 Thymeleaf是一个非常出色的视图引擎,尽管目前缺乏良好的IntelliJ(投票:http: //youtrack.jetbrains.com/issue/IDEABKL-6713 )支持,但它简化并加快了开发速度(有Eclipse)插件 )。 在学习如何使用Thymeleaf的同时,我研究了使用布局的不同可能性。

除了本机片段包含机制之外 ,还至少有两个选项可用于布局: Thymeleaf与Apache Tile的集成以及Thymeleaf Layout Dialect 。 两者似乎都可以正常工作,但是受关于简单和自定义选项的评论的启发,我尝试了一下。 在这篇文章中,我将展示我创建了解决方案。

创建具有Thymeleaf支持的Spring MVC项目

为了快速入门,我在Thymeleaf 2.1支持下使用了Spring MVC原型 。 我通过简单地调用原型创建了一个项目,然后将其导入到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";@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {if (!modelAndView.hasView()) {return;}String originalViewName = modelAndView.getViewName();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 {@Overrideprotected 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 Template Resolvers

当然,
重定向:/提交表单后。 我需要像这样修改拦截器:

public class ThymeleafLayoutInterceptor extends HandlerInterceptorAdapter {private static final String DEFAULT_LAYOUT = "layouts/default";private static final String DEFAULT_VIEW_ATTRIBUTE_NAME = "view";@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {if (!modelAndView.hasView()) {return;}String originalViewName = modelAndView.getViewName();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 "";
}

我将拦截器更改如下:

public class ThymeleafLayoutInterceptor extends HandlerInterceptorAdapter {private static final String DEFAULT_LAYOUT = "layouts/default";private static final String DEFAULT_VIEW_ATTRIBUTE_NAME = "view";@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {if (!modelAndView.hasView()) {return;}String originalViewName = modelAndView.getViewName();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 = handlerMethod.getMethodAnnotation(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 "";
}

和拦截器:

public class ThymeleafLayoutInterceptor extends HandlerInterceptorAdapter {private static final String DEFAULT_LAYOUT = "layouts/default";private static final String DEFAULT_VIEW_ATTRIBUTE_NAME = "view";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;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {if (!modelAndView.hasView()) {return;}String originalViewName = modelAndView.getViewName();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 = handlerMethod.getMethodAnnotation(Layout.class);if (layout == null) {return handlerMethod.getBeanType().getAnnotation(Layout.class);}return layout;}
}

如您所见,方法级别注释比类型级别注释更重要,这提供了一定的灵活性。 此外,我还添加了配置拦截器的可能性。 我认为,设置默认布局名称和视图属性名称可能会很有用。

摘要

提出的解决方案可能需要一些改进才能在生产中使用,但是它显示了我们可以简单地构建模板布局而无需在项目中添加额外的库并仅利用Thymeleaf的核心功能。 请分享您对解决方案的评论和意见。

  • 请在GitHub上找到源代码: https : //github.com/kolorobot/thymeleaf-custom-layout

参考: Spring MVC应用程序中的Thymeleaf模板布局,在Codeleak.pl博客上没有 JCG合作伙伴 Rafal Borowiec的扩展 。

翻译自: https://www.javacodegeeks.com/2013/11/thymeleaf-template-layouts-in-spring-mvc-application-with-no-extensions.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366553.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

注意安全!XSS 和 XSRF

[Tips] 本文是从 jianshu 平台重新修改编辑后移植来的,比上一版本做了些修订。 最近在看一些关于网络安全的问题,当然许多是跟前端相关的,包括且不局限于xss和xsrf 了,那么小编就结合最近的学习实践谈一些粗浅的认识。&#xff08…

go分析和kegg分析_干货预警:3分钟搞定GO/KEGG功能富集分析(2)

在 3分钟了解GO/KEGG功能富集分析 一文中给大家讲解了GO和KEGG的基本概念和内涵,并且给大家介绍了DAVID这一神奇网站。今天我们就把GO/KEGG功能富集分析的详细教程按部就班地呈现给大家,有请小猎豹。 多图预警,轻点图片,查看高清大图 1 Step1: 打开DAVID官网:https://dav…

区块链即将面临的问题有哪些?

很多人只知道区块链给我们带来的好处,但在这个技术和行业高速发展的阶段,区块链也出现了一些问题,那么区块链即将面临的问题有哪些?有哪些问题会对区块链行业的发展产生非常不利的影响呢?下面我来给大家说说区块链发展…

如何在Java中读取CSV文件-Iterator和Decorator的案例研究

在本文中,我将讨论如何使用Apache Common CSV读取CSV(逗号分隔值)文件。 从这个案例研究中,我们将学习如何在设计模式的上下文中使用Iterator和Decorator来提高不同情况下的可重用性。 但是在开始之前,我想我必须先回答…

如何在本地开发环境调试微信 JS-SDK

以下篇幅将会描述不同前提下对应的调试策略,当然也有可能不是最优解,望斧正 →_→ 前言 何谓「安全域名限制」? 以微信 JS-SDK 的使用为例,每个公众号被限制最多可设置三个安全域名,且必须能被腾讯服务器所验证&#…

PHP中常用加解密方式

PHP中使用OpenSSL生成RSA公钥私钥及进行加密解密示例 加密基础 加密算法一般分为两种:对称加密算法和非对称加密算法。 对称加密 对称加密算法是消息发送者和接收者使用同一个密匙,发送者使用密匙加密了文件,接收者使用同样的密匙解密&#x…

云南省农村信用社计算机岗位待遇如何,云南农村信用社薪资待遇如何?

在云南如果去存钱,相信大多数人都会把自己的小钱钱存在农村信用社而不是XX银行。在这一块风景秀丽,人美山美水美的地方,就金融行业来说云南农村信用社要是说自己差,那基本没有谁敢说自己做的好。所以在云南农信社这家企业里做一名…

小票上为啥指甲能划出印_指甲上出现竖纹,除遗传问题,或是身体在向你拉警报了,别忽视...

生活中常见女生给指甲抹上各种不同的颜色来让它变得美美的,指甲起着修饰人的形象的作用。而指甲的状况也能折射出身体的健康状态如何。每个人的指甲形态不一,有的润滑饱满,光滑平整,有月牙;有的坑坑洼洼,凸…

require.context

带表达式的 require 语句 如果你的 require参数含有表达式(expressions),会创建一个上下文(context),因为在编译时(compile time)并不清楚具体是哪一个模块被导入 require("./template/" name ".ejs");webpack 解析 require() 的…

使用JSF 2.2功能来开发可滚动,可延迟加载的Ajax数据表

这次,我想与您分享我最近从JSF 2.2功能中学到的知识。 为此,我决定创建一个简单的ajax,可滚动的延迟加载数据表。 请注意, 绝不这是相当大的库如Primefaces , RichFaces的或ICEFaces的 。 这只是为了告诉您我学到了什…

如何监视ps/查询的性能和使用

可以使用“查询管理”页面监视查询性能和使用情况。您可以获得的一些统计信息包括平均运行时、运行次数和上次运行日期。使用预定义的搜索,还可以选择要检查和报告的查询。查询管理还允许您取消当前在查询管理器和查询查看器中运行的查询,以及启用和禁用…

金融计算机怎么调成链式,FRM金融计算器使用方法

2020FRM考试计算器:想一想FRM一级考试基本上按计算器停不下来,我们就一定要买一个简单易操作的计算器,但是GARP对于FRM考生所使用的计算器是有规定的:所有参加FRM考试的考生必须使用GARP指定的计算器,如果考生在考试期…

spring 基础

作者:Spring太难链接:https://zhuanlan.zhihu.com/p/38131490来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。目录 Spring 概述 依赖注入 Spring beans Spring注解 Spring数据访问 Spring面向切面…

参数调优为什么要采样_程序员精进之路:性能调优利器--火焰图

本文主要分享火焰图使用技巧,介绍 systemtap 的原理机制,如何使用火焰图快速定位性能问题原因,同时加深对 systemtap 的理解。让我们回想一下,曾经作为编程新手的我们是如何调优程序的?通常是在没有数据的情况下依靠主…

我是这么理解Vue中的响应式系统的

遇到知识,尤其是复杂的概念,我不能类比的话,我很难接收(所以学习很差...)。在看了大神染陌同学的Vue源码解析后,我想分享一下我所类比的Vue响应式系统,您得先看他的文章(至少看他写的…

图的顺序存储(邻接矩阵存储)【摘录自严长生老师的网站】

图是表达多对多关系的一种数据结构,组成要素为顶点和连接顶点的边。 根据边有无方向可分为有向图和无向图 当边有权重时,升级为有向网和无向网 图在存储时,可采用邻接矩阵,比如下面的无向图(A)和&#xff0…

Neo4j:使隐式关系成为显式和双向关系

最近,我阅读了Michal Bachman关于 Neo4j中双向关系的文章 ,他建议对于某些关系类型,我们对关系的方向不那么感兴趣,因此可以在查询时忽略它。 他使用以下示例显示了Neo Technology和GraphAware之间的合作关系: 两家公…

复旦大学计算机学院博士生王斌,复旦大学计算机科学技术学院博士生刘鹏飞荣获...

计算机科学技术学院发布时间:2017-01-04 小字体 中字体 大字体2016年12月22日,历时七个月选拔的2016年度百度奖学金获奖者揭晓,复旦大学计算机科学技术学院2014级博士生刘鹏飞以其在“自然语言处理与深度学习”上取得的突出成绩,从国内外近百所著名高校数百位竞争者中脱颖而出,…

BZOJ3878: [Ahoi2014Jsoi2014]奇怪的计算器

BZOJ3878: [Ahoi2014&Jsoi2014]奇怪的计算器 Description 【故事背景】 JYY有个奇怪的计算器,有一天这个计算器坏了,JYY希望你能帮助他写一个程序来模拟这个计算器的运算。【问题描述】JYY的计算器可以执行N条预设好的指令。每次JYY向计算器输入一个…

Deno 兼容浏览器具体指的是什么?

Deno 里面有一句描述:"Aims to be browser compatible",可以看到 Deno 的目标是兼容浏览器。那么这里的兼容浏览器到底如何是什么意思呢? 我简单谈谈我的理解吧。 首先这里的兼容性肯定不是 Deno 直接在浏览器端运行。因为 Deno …