Thymeleaf項目實踐

爲了更好地解釋Thymeleaf處理模板所涉及的概念,本教程將使用一個演示應用程序,您可以下載本項目的應用程序。本項目是基於:Thymeleaf虛擬雜貨店 的漢化修改。

這個應用程序是一個虛擬的虛擬雜貨店的網站,並提供許多場景展示Thymeleaf的許多功能。

首先,我們需要爲應用程序提供一組簡單的模型實體:銷售給客戶的產品有訂單信息記錄。 也將管理對這些產品的註釋:
Thymeleaf項目實踐

應用程序還將有一個非常簡單的服務層,由Service對象組成,其中包含以下方法:

/*
 * =============================================================================
 * 
 *   Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org)
 * 
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * 
 * =============================================================================
 */
package com.yiibai.business.services;

import java.util.List;

import com.yiibai.business.entities.Product;
import com.yiibai.business.entities.repositories.ProductRepository;

public class ProductService {

    public ProductService() {
        super();
    }

    public List<Product> findAll() {
        return ProductRepository.getInstance().findAll();
    }

    public Product findById(final Integer id) {
        return ProductRepository.getInstance().findById(id);
    }
}

在Web層,應用程序將有一個過濾器,根據請求URL將執行委託給啓用Thymeleaf的命令:

/*
 * =============================================================================
 * 
 *   Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org)
 * 
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * 
 * =============================================================================
 */
package com.yiibai.web.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.thymeleaf.ITemplateEngine;
import com.yiibai.business.entities.User;
import com.yiibai.web.application.GTVGApplication;
import com.yiibai.web.controller.IGTVGController;


public class GTVGFilter implements Filter {

    private ServletContext servletContext;
    private GTVGApplication application;

    public GTVGFilter() {
        super();
    }

    private static void addUserToSession(final HttpServletRequest request) {
        // Simulate a real user session by adding a user object
        request.getSession(true).setAttribute("user", new User("John", "Apricot", "Antarctica", null));
    }

    public void init(final FilterConfig filterConfig) throws ServletException {
        this.servletContext = filterConfig.getServletContext();
        this.application = new GTVGApplication(this.servletContext);
    }

    public void doFilter(final ServletRequest request, final ServletResponse response,
            final FilterChain chain) throws IOException, ServletException {
        addUserToSession((HttpServletRequest)request);
        if (!process((HttpServletRequest)request, (HttpServletResponse)response)) {
            chain.doFilter(request, response);
        }
    }

    public void destroy() {
        // nothing to do
    }

    private boolean process(HttpServletRequest request, HttpServletResponse response)
            throws ServletException {
        try {

            // This prevents triggering engine executions for resource URLs
            if (request.getRequestURI().startsWith("/css") ||
                    request.getRequestURI().startsWith("/images") ||
                    request.getRequestURI().startsWith("/favicon")) {
                return false;
            }
            /*
             * Query controller/URL mapping and obtain the controller
             * that will process the request. If no controller is available,
             * return false and let other filters/servlets process the request.
             */
            IGTVGController controller = this.application.resolveControllerForRequest(request);
            if (controller == null) {
                return false;
            }

            /*
             * Obtain the TemplateEngine instance.
             */
            ITemplateEngine templateEngine = this.application.getTemplateEngine();

            /*
             * Write the response headers
             */
            response.setContentType("text/html;charset=UTF-8");
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", 0);

            /*
             * Execute the controller and process view template,
             * writing the results to the response writer. 
             */
            controller.process(
                    request, response, this.servletContext, templateEngine);

            return true;

        } catch (Exception e) {
            try {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            } catch (final IOException ignored) {
                // Just ignore this
            }
            throw new ServletException(e);
        }
    }

}

下面是IGTVGController接口的代碼:

/*
 * =============================================================================
 * 
 *   Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org)
 * 
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * 
 * =============================================================================
 */
package com.yiibai.web.controller;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.thymeleaf.ITemplateEngine;

public interface IGTVGController {

    public void process(
            HttpServletRequest request, HttpServletResponse response,
            ServletContext servletContext, ITemplateEngine templateEngine)
            throws Exception;

}

我們現在要做的就是創建IGTVGController接口的實現,從服務中檢索數據並使用ITemplateEngine對象處理模板。

最後,它會看起來得到類似下面的頁面:
Thymeleaf項目實踐

但首先來看看這個模板引擎是如何初始化的。

創建和配置模板引擎

過濾器中的process(…)方法包含這一行:

ITemplateEngine templateEngine = this.application.getTemplateEngine();

GTVGApplication類負責創建和配置Thymeleaf應用程序中最重要的對象之一:TemplateEngine實例(實現ITemplateEngine接口)。
org.thymeleaf.TemplateEngine對象是這樣初始化的:

public class GTVGApplication {


    ...
    private final TemplateEngine templateEngine;
    ...


    public GTVGApplication(final ServletContext servletContext) {

        super();

        ServletContextTemplateResolver templateResolver = 
                new ServletContextTemplateResolver(servletContext);

        // HTML is the default mode, but we set it anyway for better understanding of code
        templateResolver.setTemplateMode(TemplateMode.HTML);
        // This will convert "home" to "/WEB-INF/templates/home.html"
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        // Template cache TTL=1h. If not set, entries would be cached until expelled by LRU
        templateResolver.setCacheTTLMs(Long.valueOf(3600000L));

        // Cache is set to true by default. Set to false if you want templates to
        // be automatically updated when modified.
        templateResolver.setCacheable(true);

        this.templateEngine = new TemplateEngine();
        this.templateEngine.setTemplateResolver(templateResolver);

        ...

    }

}

配置TemplateEngine對象的方法有很多種,但現在這幾行代碼將足以教會需要的步驟。

模板解析器

從模板解析器開始:

ServletContextTemplateResolver templateResolver = 
        new ServletContextTemplateResolver(servletContext);

模板解析器是實現來自Thymeleaf API(名爲org.thymeleaf.templateresolver.ITemplateResolver)的接口的對象:

public interface ITemplateResolver {

    ...

    /*
     * Templates are resolved by their name (or content) and also (optionally) their 
     * owner template in case we are trying to resolve a fragment for another template.
     * Will return null if template cannot be handled by this template resolver.
     */
    public TemplateResolution resolveTemplate(
            final IEngineConfiguration configuration,
            final String ownerTemplate, final String template,
            final Map<String, Object> templateResolutionAttributes);
}

這些對象負責確定模板將如何被訪問,並且在這個GTVG應用程序中,org.thymeleaf.templateresolver.ServletContextTemplateResolver意味着我們要從Servlet上下文中檢索我們的模板文件作爲資源:一個應用程序範圍的javax .servlet.ServletContext對象,它存在於每個Java Web應用程序中,並解析來自Web應用程序根目錄的資源。

但這並不是我們說的模板解析器,因爲可以在其上設置一些配置參數。 首先,模板模式:

templateResolver.setTemplateMode(TemplateMode.HTML);

HTML是ServletContextTemplateResolver的默認模板模式,但最好還是建立它,以便可以清楚知道代碼執行了什麼。

templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");

前綴和後綴修改了將傳遞給引擎以獲取要使用的真實資源名稱的模板名稱。

使用此配置,模板名稱「product/list」將對應於:

servletContext.getResourceAsStream("/WEB-INF/templates/product/list.html")

可選地,解析模板可以存儲在緩存中的時間量是通過cacheTTLMs屬性在模板解析器中配置:

templateResolver.setCacheTTLMs(3600000L);

如果達到最大高速緩存大小並且它是當前高速緩存的最舊條目,則在達到TTL之前,模板仍可從高速緩存中排除。

模板引擎

模板引擎對象是org.thymeleaf.ITemplateEngine接口的實現。 其中一個實現由Thymeleaf核心提供:org.thymeleaf.TemplateEngine,在這裏創建一個實例:

templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);

很簡單,不是嗎? 需要的只是創建一個實例併爲其設置模板解析器。

模板解析器是TemplateEngine需要的唯一必需的參數,不過稍後還會介紹其他許多參數(消息解析器,緩存大小等)。

我們的模板引擎已準備就緒,就可以使用Thymeleaf開始創建頁面了。有關項目的代碼,可以從: https://pan.baidu.com/s/1nwBa9Gd 下載。

0 條評論,你可以發表評論,我們會進行改進
Comment author placeholder