SpringBoot Thymeleaf 从数据库加载模板文件

Use SpringBoot Thymeleaf Loading Template from database

Posted by Perrone on November 19, 2017

问题描述

Thymeleaf 是SpringBoot默认得模板,平时我们使用都是从项目目录下加载,例如:

  • pom.xml 关于Thymeleaf的配置
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--权限标签-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
        </dependency>
  • SpringBoot默认的模板文件路径, 所有的模板文件放在下面的路径就好了
{项目名称}/src/main/resources/templates
  • Controller里直接返回模板的路径,调用 {项目名称}/src/main/resources/templates/nj/error.html 这个模板
    @RequestMapping("/nj/presell/update")
    Object njPresellUpdate() {
        // do something

        return "nj/error";
    }

  • 好了,上面是常规用法,如果我想从数据库读取模板文件(这样可以动态调整模板)而不是文件系统呢?

查看源码

SpringBoot Thymeleaf 提供了几种形式的TemplateResolver

UrlTemplateResolver

从远程地址读取模板文件, 详见如下配置


    @Configuration
    public class ThymeleafConfig{
        @Autowired
        private SpringTemplateEngine springTemplateEngine;

        String resourcesUrl = "http://127.0.0.1";

        public void init(){
        }

        @PostConstruct
        public void templateResolver() {
            UrlTemplateResolver resolver = new UrlTemplateResolver();
            resolver.setTemplateMode("HTML5");
            resolver.setPrefix(resourcesUrl + "/resources/");
            resolver.setSuffix(".html");
            resolver.setCacheable(thymeleafCache);
            resolver.setOrder(1);
            resolver.setCharacterEncoding("UTF-8");
            springTemplateEngine.setTemplateResolver(resolver);
        }
    }

  • application.properties配置
    spring.thymeleaf.cache=false
    spring.thymeleaf.encoding=UTF-8
    spring.thymeleaf.mode=HTML5
    spring.thymeleaf.prefix=http://127.0.0.1/resources/
    spring.thymeleaf.enabled=true
  • nginx配置
    server {
        listen       80;
        server_name   www.xxx.com;
        charset utf-8;
        location /resources/ {
            root D:\workspaces;
        }
    }
  • 如果使用了缓存,刷新缓存的方法

    @Autowired
    private TemplateEngine springTemplateEngine;

    @RequestMapping(value = "/thymeleaf/cache/clear", method RequestMethod.GET)
    @ResponseBody
    public String initModifyCell(@RequestParam("name") String tmpName, Model model) throws Exception {
        log.debug("handling clear tmp cache, tmpName={}", tmpName);
        if ("ALL".equals(tmpName)) {
            springTemplateEngine.clearTemplateCache();
        } else {
            springTemplateEngine.clearTemplateCacheFor(tmpName);
        }
        return "Thymeleaf template cache cleared successfully!";
    }

FileTemplateResolver

顾名思义 从文件系统中读取,这里不再赘述,可以看一下源码

org.thymeleaf.templateresolver.FileTemplateResolver

ClassLoaderTemplateResolver

顾名思义 从类路径中读取,也是SpringBoot默认的

org.thymeleaf.templateresolver.ClassLoaderTemplateResolver

实践起来

言归正传,从DB获取模板文件怎么做呢? 当然是继承 TemplateResolver 自己实现咯

  • DbTemplateResolver

import org.thymeleaf.exceptions.ConfigurationException;
import org.thymeleaf.resourceresolver.IResourceResolver;
import org.thymeleaf.templateresolver.TemplateResolver;

/**
 * Created by xiepeilong on 2017/11/17.
 */
public class DbTemplateResolver extends TemplateResolver {

    public DbTemplateResolver(DbThymeleafTemplateStrategy dbThymeleafTemplateStrategy) {
        super();
        DbResourceResolver dbResourceResolver = new DbResourceResolver();
        dbResourceResolver.setDbThymeleafTemplateStrategy(dbThymeleafTemplateStrategy);
        super.setResourceResolver(dbResourceResolver);
    }


    @Override
    public void setResourceResolver(final IResourceResolver resourceResolver) {
        throw new ConfigurationException(
                "Cannot set a resource resolver on " + this.getClass().getName() + ". If " +
                        "you want to set your own resource resolver, use " + TemplateResolver.class.getName() +
                        "instead");
    }
}
  • DbResourceResolver

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.TemplateProcessingParameters;
import org.thymeleaf.resourceresolver.IResourceResolver;
import org.thymeleaf.util.Validate;

import java.io.InputStream;

/**
 * Created by xiepeilong on 2017/11/17.
 *  数据库读取模板资源
 */
public class DbResourceResolver implements IResourceResolver {

    private static final Logger logger = LoggerFactory.getLogger(DbResourceResolver.class);

    public static final String NAME = "DB";

    public DbThymeleafTemplateStrategy dbThymeleafTemplateStrategy;

    public DbThymeleafTemplateStrategy getDbThymeleafTemplateStrategy() {
        return dbThymeleafTemplateStrategy;
    }

    public void setDbThymeleafTemplateStrategy(DbThymeleafTemplateStrategy dbThymeleafTemplateStrategy) {
        this.dbThymeleafTemplateStrategy = dbThymeleafTemplateStrategy;
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public InputStream getResourceAsStream(TemplateProcessingParameters templateProcessingParameters, String resourceName) {
        Validate.notNull(resourceName, "Resource name cannot be null");
        Validate.notNull(getDbThymeleafTemplateStrategy(), "dbThymeleafTemplateStrategy name cannot be null");

        try {
            //使用策略模式获取模板
            return getDbThymeleafTemplateStrategy().getTemplate(resourceName);
        } catch (final Exception e) {
            if (logger.isDebugEnabled()) {
                if (logger.isTraceEnabled()) {
                    logger.trace(
                            String.format(
                                    "[THYMELEAF][%s][%s] Resource \"%s\" could not be resolved. This can be normal as " +
                                            "maybe this resource is not intended to be resolved by this resolver. " +
                                            "Exception is provided for tracing purposes: ",
                                    TemplateEngine.threadIndex(), templateProcessingParameters.getTemplateName(),
                                    resourceName),
                            e);
                } else {
                    logger.debug(
                            String.format(
                                    "[THYMELEAF][%s][%s] Resource \"%s\" could not be resolved. This can be normal as " +
                                            "maybe this resource is not intended to be resolved by this resolver. " +
                                            "Exception message is provided: %s: %s",
                                    TemplateEngine.threadIndex(), templateProcessingParameters.getTemplateName(),
                                    resourceName, e.getClass().getName(), e.getMessage()));
                }
            }
            return null;
        }
    }
}
  • DbThymeleafTemplateStrategy

策略类

import java.io.InputStream;

/**
 * Created by xiepeilong on 2017/11/17.
 * 数据库读取模板策略类
 */
public interface DbThymeleafTemplateStrategy {
    InputStream getTemplate(String resourceName);
}
  • 实例化 TemplateEngine
    /**
     * db读取模板文件
     * @return
     */
    @Bean
    public TemplateEngine dbTemplateEngine(){
        final DbTemplateResolver resolver = new DbTemplateResolver(new DbThymeleafTemplateStrategy() {
            @Override
            public InputStream getTemplate(String resourceName) {
                //读取数据库,返回模板文件生成输入流,然后就没有然后了
            }
        });

        resolver.setCharacterEncoding("UTF-8");

        //从系统中读取是否需要进行缓存
        //resolver.setCacheTTLMs(Long.valueOf(3600000L));
        //resolver.setCacheable(true);

        TemplateEngine templateEngine = new TemplateEngine();
        templateEngine.setTemplateResolver(resolver);
        return templateEngine;
    }
  • 在需要调用的地方,如生成静态文件

        NutMap nutMap = Lang.map("list", list);

        Context context = new Context();
        context.setVariables(nutMap);

        StringWriter writer = new StringWriter();

        dbTemplateEngine.process("api/gz", context, writer);

        String s = writer.toString();

        // 这个s就是渲染过的,你想要的

参考