SpringBoot框架 之 Thymeleaf

Thymeleaf

介紹
    SpringBoot並不推薦使用jsp
    Thymeleaf 是一個跟 Velocity、FreeMarker 相似的模板引擎,它能夠徹底替代 JSP 
    
特色
    動靜結合
        Thymeleaf 在有網絡和無網絡的環境下皆可運行
        它可讓美工在瀏覽器查看頁面的靜態效果,也可讓程序員在服務器查看帶數據的動態頁面效果
        這是因爲它支持 html 原型,而後在 html 標籤裏增長額外的屬性來達到模板+數據的展現方式
        瀏覽器解釋 html 時會忽略未定義的標籤屬性,因此 thymeleaf 的模板能夠靜態地運行
        當有數據返回到頁面時,Thymeleaf 標籤會動態地替換掉靜態內容,使頁面動態顯示
        
    開箱即用
        它提供標準和spring標準兩種方言,能夠直接套用模板實現JSTL、 OGNL表達式效果,避免天天套模板、該jstl、改標籤的困擾。
        同時開發人員也能夠擴展和建立自定義的方言。
        
    多方言支持
        Thymeleaf 提供spring標準方言和一個與 SpringMVC 完美集成的可選模塊,能夠快速的實現表單綁定、屬性編輯器、國際化等功能。
        
    與SpringBoot完美整合
        與SpringBoot完美整合,SpringBoot提供了Thymeleaf的默認配置
        而且爲Thymeleaf設置了視圖解析器,咱們能夠像之前操做jsp同樣來操做Thymeleaf。

添加啓動器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

建立模板文件夾

  • SpringBoot會自動爲Thymeleaf註冊一個視圖解析器ThymeleafViewResolvercss

  • 還配置了模板文件(html)的位置,與jsp相似的前綴+ 視圖名 + 後綴風格:html

  • 與解析JSP的InternalViewResolver相似,Thymeleaf也會根據前綴和後綴來肯定模板文件的位置:java

    @ConfigurationProperties(prefix="spring.thymeleaf")
    public class ThymeleafProperties{
      private static final Charset DEFAULT_ENCODING=StandardCharsets.UTF_8;
      public static final String DEFAULT_PREFIX="classpath:/templates/";
      public static final String DERAULT_SUEEIX=". html";
      ......
    }

  • 在配置文件中配置緩存,編碼程序員

spring:
  thymeleaf:
    cache: false
    mode: HTML5
    encoding: UTF-8

基本使用

引入名稱空間
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    
表達式
    ${}:變量表達式
    *{} :選擇變量表達式,取出hero對象中的屬性
        <div th:object="${hero}">
            <p th:text="*{username}"></p>
            <p th:text="*{phone}"></p>
            <p th:text="*{id}"></p>
            <p th:text="${hero.username}"></p>
        </div>
    #{...} : Message 表達式
    
URL
    絕對網址
        絕對URL用於建立到其餘服務器的連接,它們須要指定一個協議名稱(http://或https://)開頭
        <a th:href="@{https://www.itlike.com/}">
        
    上下文相關URL
        與Web應用程序根相關聯URL
        <a th:href="@{/hello}">跳轉</a>
        
    與服務器相關URL
        服務器相關的URL與上下文相關的URL很是類似
        <a th:href="@{~/hello}">跳轉</a>
        
    攜帶參數
        <a th:href="@{/hero/detail(id=3,action='show_all')}">aa</a>

字面值
    有的時候,咱們須要在指令中填寫基本類型如:字符串、數值、布爾等,並不但願被Thymeleaf解析爲變量,這個時候稱爲字面值。(也就是原樣輸出)
    字符串字面值
        <p>
            內容:<span th:text=」'thymeleaf'+1">template</span>
        </p>
        
    數字字面值
        <p>
            內容:<span th:text=」2+1">template</span>
        </p>
    
    布爾字面值
        布爾類型的字面值是true或false
        
拼接
    普通字符串與表達式拼接的狀況
        <span th:text=」'歡迎您:'+${hero.username}+'!'"></span>
    
    字符串字面值須要用'',拼接起來很是麻煩,Thymeleaf對此進行了簡化,使用一對|便可
        <span th:text="|歡迎您:${hero.username}|"></span>
    
運算符
    算術操做符
        + - * / %
    比較運算
        > , <, >= and <=
        可是>和<不能直接使用,由於xml會解析爲標籤
        >  gt
        <  lt
        >= ge
        <= le
        <!-- 返回值爲true -->
        <h1 th:text=" 1 lt 2"></h1>
        <!-- 返回值爲假 -->
        <h1 th:text="1 gt 2 ? '真' : '假'"></h1>
        
    三元運算
        condition ? then : else
            <span th:text="${false}?'男':'女'"></span〉
        簡寫
            <!--
    默認表達式
    變量值爲空  就顯示默認值
    不然就顯示變量自己
    -->
    <h1 th:text="${hero.username}?:'我是空值'"></h1>
    <h1 th:text="${hero.username} != null ? ${hero.username} : '我是空值'"></h1>

內聯寫法
    <!--原樣輸出-->
    <p>pppp-------—-[(${hero.username})]</p>
    <!--解析內部標籤-->
    <p>pppp-------—-[[${hero.username}]]</p>
        

局部變量
    <div th:with="heroN = ${allHero[0]}">
        <h1 th:text="${heroN.username}"></h1>
        <h1 th:text="${heroN.phone}"></h1>
    </div>
    
判斷
    th:if
        <!--當條件知足時, 纔會顯示標籤-->
        <h1 th:if="${1 lt 2}">內容1</h1>

    th:unless
        <!--
        unless是與if相反  
        不知足條件時,纔會顯示標籤
        -->
        <h1 th:unless="${1 gt 2}">內容2</h1>

    th:switch
        <div th:switch="${hero.username}">
            <p th:case="'itlike'">itlike</p>
            <p th:case="'gxq'">gxq</p>
            <p th:case="*">*爲默認的值,至關於default</p>
        </div>
        
迭代
    <div>
        <p th:each="heroItem:${allHero}">
            <span th:text="${heroItem.username}"></span>
            <span th:text="${heroItem.phone}"></span>
            <span th:text="${#dates.format(heroItem.onlinetime,'yyyy-MM-dd')}"></span>
        </p>
    </div>

    <hr>
    <div>
        <p th:each="heroItem,stat:${allHero}">
            <span th:text="${heroItem.username}"></span>
            <span th:text="${heroItem.phone}"></span>
            <span th:text="${stat.index}"></span>
        </p>
    </div>
        

    stat對象包含如下屬性
        index,從0開始的角標
        count,當前遍歷到第幾個元素,從1開始
        size,總元素個數
        current,當前遍歷到的元素信息
        odd/even,返回是否爲奇偶,boolean值
        first/last,返回是否爲第一或最後,boolean值
內置對象
    環境相關對象
        ${#ctx} 上下文對象,可用於獲取其它內置對象。
        ${#vars}:    上下文變量。
        ${#locale}:上下文區域設置。
        ${#request}: HttpServletRequest對象。
        ${#response}: HttpServletResponse對象。
        ${#session}: HttpSession對象。
        ${#servletContext}:  ServletContext對象。
        
    全局對象功能
        #strings:字符串工具類
        #lists:List 工具類
        #arrays:數組工具類
        #sets:Set 工具類
        #maps:經常使用Map方法。
        #objects:通常對象類,一般用來判斷非空
        #bools:經常使用的布爾方法。
        #execInfo:獲取頁面模板的處理信息。
        #messages:在變量表達式中獲取外部消息的方法,與使用#{...}語法獲取的方法相同。
        #uris:轉義部分URL / URI的方法。
        #conversions:用於執行已配置的轉換服務的方法。
        #dates:時間操做和時間格式化等。
        #calendars:用於更復雜時間的格式化。
        #numbers:格式化數字對象的方法。
        #aggregates:在數組或集合上建立聚合的方法。
        #ids:處理可能重複的id屬性的方法。
        
    示例
        ${#strings.abbreviate(str,10)}  str截取0-10位,後面的所有用…這個點代替,注意,最小是3位
        ${#strings.toUpperCase(name)} 
        判斷是否是爲空:null: 
        <span th:if="${name} != null">不爲空</span> 
        <span th:if="${name1} == null">爲空</span> 

        判斷是否是爲空字符串: 「」 
        <span th:if="${#strings.isEmpty(name1)}">空的</span>
         
        判斷是否相同: 
        <span th:if="${name} eq 'jack'">相同於jack,</span> 
        <span th:if="${name} eq 'ywj'">相同於ywj,</span> 
        <span th:if="${name} ne 'jack'">不相同於jack,</span>

         
        不存在設置默認值: 
        <span th:text="${name2} ?: '默認值'"></span> 

        是否包含(分大小寫): 
        <span th:if="${#strings.contains(name,'ez')}">包ez</span> 
        <span th:if="${#strings.contains(name,'y')}">包j</span> 

        是否包含(不分大小寫) 
        <spanth:if="${#strings.containsIgnoreCase(name,'y')}">包</span> 
        ${#strings.startsWith(name,'o')} 
        ${#strings.endsWith(name, 'o')} 
        ${#strings.indexOf(name,frag)}// 下標 
        ${#strings.substring(name,3,5)}// 截取 
        ${#strings.substringAfter(name,prefix)}// 從 prefix以後的一位開始截取到最後,好比 (ywj,y) = wj, 若是是(abccdefg,c) = cdefg//裏面有2個c,取的是第一個c 
        ${#strings.substringBefore(name,suffix)}// 同上,不過是往前截取 
        ${#strings.replace(name,'las','ler')}// 替換 
        ${#strings.prepend(str,prefix)}// 拼字字符串在str前面 
        ${#strings.append(str,suffix)}// 和上面相反,接在後面 
        ${#strings.toUpperCase(name)} 
        ${#strings.toLowerCase(name)} 
        ${#strings.trim(str)} 
        ${#strings.length(str)} 
        ${#strings.abbreviate(str,10)}//  str截取0-10位,後面的所有用…這個點代替,注意,最小是3位

佈局
    方式1
        hello.html
        <nav th:fragment="header">
            <h1>頭部</h1>
        </nav>
        base.html
        <div th:include="{common/base::header}"></div>

    方式2
        hello.html
        <footer id="footer">
            <h1  th:class="${active == 'footer' ? 'red' : ''}" >尾部---</h1>
            <p>aaaaaa</p>
        </footer>
        base.html
        <div th:insert="{common/base::#footer}"></div>

    引入方式
        th:insert
            將公共的標籤及內容插入到指定標籤當中
        th:replace
            將公共的標籤替換指定的標籤
        th:include
            將公共標籤的內容包含到指定標籤當中
            
    傳值
        hello.html
        <div th:include="~{base::#footer(active='footers')}"></div>
        
        base.html
        <footer id="footer">
            <h1 th:class="${active == 'footer' ? 'red' : ''}" >尾部---</h1>
            <p>aaaaaa</p>
        </footer>   

js模板
    模板引擎不只能夠渲染html,也能夠對JS中的進行預處理。並且爲了在純靜態環境下能夠運行
    在script標籤中經過th:inline="javascript"來聲明這是要特殊處理的js腳本
    取值要採用註釋的形式
    <script th:inline="javascript">
        var username = /*[[${name}]]*/ "myxq";
        console.log(username);
    </script>

綜合使用

導入資源

引入bootstrap
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>bootstrap</artifactId>
        <version>4.0.0</version>
    </dependency>
    
在頁面中引入資源文件
    <link rel="stylesheet" href="js/bootstrap/dist/css/bootstrap.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}">
    <link rel="stylesheet" href="js/font-awesome/css/font-awesome.css" th:href="@{/css/font-awesome/css/font-awesome.css}">
    <link rel="stylesheet" href="css/index.css" th:href="@{/css/index.css}">
    <script  th:href="@{/webjars/bootstrap/4.0.0/js/bootstrap.js}"></script>

設置默認主頁

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addVievController("/")).setViewName("login");
        registry.addVievController("/index.html")).setViewName("login");
    }
}

國際化

1.編寫國際化配置文件
    在資源文件夾resources下,建立一個國際化文件夾i18n
    在i18n文件夾中建立一個login.properties文件
    建立中文/英文國際化文件
        在i18n文件夾中建立一個login_en_US.properties文件
        在i18n文件夾中建立一個login_zh_CN.properties文件
        點擊Resource Bundle,新建property key name爲login.username
        在login.properties窗口下輸入用戶名
        login_en_US.properties窗口中輸入username
    login_zh_CN.properties窗口中輸入用戶
            

    注意
        要更改文件編碼
        idea中,設置->編譯器->文本編碼屬性文件(*.properties)
        設置屬性文件的默認編碼爲UTF-8
        
2.在springboot中有一個messageSourceAutoConfiguration
    會自動管理國際化資源文件
    在全局配置文件中設置基礎名
        spring:
                      messages:
                            basename: i18n.login
          
3.在頁面中獲取國際化的值
    <!-- 登陸 -->
    <div class="login">
        <div class="login-wrap">
            <div class="avatar">
                <img src="./images/logo.jpg" class="img-circle" alt="">
            </div>
            <form action="" class="col-md-offset-1 col-md-10">
                <div class="input-group input-group-lg">
                        <span class="input-group-addon">
                            <i class="fa fa-id-card-o"></i>
                        </span>
                    <input type="text" class="form-control" name="username" th:placeholder="#{login.username}">
                </div>
                <div class="input-group input-group-lg">
                        <span class="input-group-addon">
                            <i class="fa fa-key"></i>
                        </span>
                    <input type="password" class="form-control" name="password" th:placeholder="#{login.password}">
                </div>

                <button type="submit" class="btn btn-lg btn-danger btn-block" th:text="#{login.btn}">登 錄</button>
                <a class="language" th:href="@{/login(lan='zh_CN')}">中文</a>|<a class="language" th:href="@{/login(lan='en_US')}">English</a>
            </form>
        </div>
    </div>

4.切換中英文
    默認
    自定義
    public class MyLocaleResolver implements LocaleResolver {
        @Override
        public Locale resolveLocale(HttpServletRequest request){
            String lan=request.getParameter("lan");
            Locale locale=Locale.getDefault();
            if(!StringUtils.isEmpty(lan)){
                String[] split=lan.split("_");
                locale=new Locale(split[0],split[1]);
            }
            return locale;
        }

        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale){
        }
    }
    
        @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
        @Bean
        public LocaleResolver localeResolver() {
            return new MyLocaleResolver();
        }
    }

MessageSourceAutoConfiguration(自動管理國際化資源文件)
web

默認切換中英文spring

登陸

界面處理
<!-- 登陸 -->
<div class="login">
    <div class="login-wrap">
        <div class="avatar">
            <img src="./images/logo.jpg" class="img-circle" alt="">
        </div>
        <form action="" class="col-md-offset-1 col-md-10">
            <div class="input-group input-group-lg">
                    <span class="input-group-addon">
                        <i class="fa fa-id-card-o"></i>
                    </span>
                <input type="text" class="form-control" name="username" th:placeholder="#{login.username}">
            </div>
            <div class="input-group input-group-lg">
                    <span class="input-group-addon">
                        <i class="fa fa-key"></i>
                    </span>
                <input type="password" class="form-control" name="password" th:placeholder="#{login.password}">
            </div>

            <button type="submit" class="btn btn-lg btn-danger btn-block" th:text="#{login.btn}">登 錄</button>
            <a class="language" th:href="@{/login(lan='zh_CN')}">中文</a>|<a class="language" th:href="@{/login(lan='en_US')}">English</a>
        </form>
    </div>
</div>

接收請求

@PostMapping("/login")
public String login(@RequestParam("username") String username,@RequestParam("password")String password,Model model,HttpSession session){
    if(!StringUtils.isEmpty(username)&& "123456".equals(password)){
        //登錄成功,防止表單重複提交,能夠重定向到主頁
        session.setAttribute("loginUser",username);
        return "redirect:/main.html";
    }else{
        //登錄失敗
        model.addAttribute("msg","用戶名密碼錯誤");
        return "login";
    }
}

登陸錯誤處理
<!--判斷-->
<p style="color:red;margin-left:60px;font-size:18px" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

登陸狀態處理
public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        Object user=request.getSession().getAttribute("loginUser");
        if(user==null){
            //未登錄,返回登錄頁面
            request.setAttribute("msg","沒有權限請先登錄");
            request.getRequestDispatcher("/login").forward(request,response);
            return false;
        }
        else{
            //已登錄,放行請求
            return true;
        }
    }
}

springBoot2.0以後添加攔截器時,要本身添加靜態資源文件路徑(2.0以前是自動添加靜態資源文件路徑)
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/","/1ogin","/main.html","login","/static/**");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/**");
        WebMvcConfigurer.super.addResourceHandlers(registry);
    }
}

列表

列表數據獲取
public interface HeroMapper extends Mapper<Hero> {
}
@Autowired
private MainService mainService;
@RequestMapping("/main")
public String main(Model mode){
    List<Hero> herolist=mainService.getHeroList();
    model.addAttribute("heroList",heroList);
    return"main";
}
列表展現
<tr th:each="hero:${heroList}">
    <td th:text="${hero.id}"></td>
    <td th:text="${hero.profession}"></td>
    <td th:text="${hero.username}"></td>
    <td th:text="${hero.email}"></td>
    <td th:text="${#dates.format(hero.onlinetime,'yyyy-MM-dd HH:mm')}"></td>
    <td>
        <a th:href="@{/edit}" class="btn btn-danger btn-sm">編輯</a>
        <a href="javascript:;" class="btn btn-warning btn-sm">刪除</a>
    </td>
</tr>

添加

跳轉到添加頁面
<div class="panel-body">
    <a href="@{/addPage}" class="btn btn-primary">添加英雄</a>
</div>

添加按鈕點擊
<form th:action="@{/addHero}" class="form-horizontal" method="post">

    <input type="hidden" name="_method" value="put" th:if="${hero!=null}"/>
    <input type="hidden" name="id" th:if="${hero!=null}" th:value="${hero.id}">

    <div class="form-group">
        <label  class="col-md-2 control-label">名稱</label>
        <div class="col-md-6">
            <input type="text" name="username" th:value="${hero!=null}?${hero.username}" class="form-control">
        </div>
    </div>
    <div class="form-group">
        <label  class="col-md-2 control-label">職業</label>
        <div class="col-md-6">
            <input type="text" name="profession" th:value="${hero!=null}?${hero.profession}" class="form-control">
        </div>
    </div>
    <div class="form-group">
        <label  class="col-md-2 control-label">電話</label>
        <div class="col-md-6">
            <input type="text" name="phone" th:value="${hero!=null}?${hero.phone}"  class="form-control">
        </div>
    </div>
    <div class="form-group">
        <label  class="col-md-2 control-label">郵箱</label>
        <div class="col-md-6">
            <input type="text" name="email" th:value="${hero!=null}?${hero.email}"  class="form-control">
        </div>
    </div>
    <div class="form-group">
        <label  class="col-md-2 control-label">上線日期</label>
        <div class="col-md-6">
            <input type="text" name="onlinetime" th:value="${hero!=null}?${#dates.format(hero.onlinetime,'yyyy-MM-dd')}"  class="form-control">
        </div>
    </div>
    <div class="modal-footer">
        <input type="submit" class="btn btn-danger"   th:value="${hero!=null}?'修改':'添加'">
    </div>
</form>

日期處理(寫在控制器中)
@InitBinder
public void InitBinder(WebDataBinder dataBinder){
        dataBinder.registerCustomEditor(Date.class,new PropertyEditorSupport(){
        public void setAsText(String value){
            try{
                setValue(new SimpleDateFormat("yyyy-MM-dd").parse(value));
            }
            catch(ParseException e){
                setValue(null);
            }
        }

        public String getAsText(){
            return new SimpleDateFormat("yyyy-MM-dd").format((Date)getValue());
        }
    });
}
相關文章
相關標籤/搜索