• freemarker使用自定义的模板加载器通过redis加载模板


    其实更实用的是使用数据库中的数据,不过redis相对简单一些。结合了一些网上的资料:

    CSDN上的通过数据库获取模板,这里了解了如何自定义TemplateLoader地址

    通过FreeMarkerConfigurer 配置freemarker,这里主要是了解如何在SpringBoot中注册自定义的TemplateLoader地址

    分四个部分:

    • 用redis提供模板的存储功能:RedisTemplateStorage
    • 自定义的RedisTempleLoader用redis存储适配freemarkerTemplateLoader
    • 配置freemarker使用自定义RedisTempleLoader
    • 使用模板

    存储部分

    这里的方法被后面的RedisTempleLoader使用来存储和查询模板信息
    StringRedisTemplate是利用的Jedis

    @Component
    public class RedisTemplateStorage {
    
        @Autowired
        StringRedisTemplate stringRedisTemplate;
    
        private final String FTL_REDIS_PREFIX = "ftl-redis-ftl:";
        private final String FTL_REDIS_KEY_PREFIX = FTL_REDIS_PREFIX + "key:";
        private final String FTL_REDIS_TS_PREFIX = FTL_REDIS_PREFIX + "ts:";
    
        /*
         * 新增/修改模板
         */
        public String saveTemplate(String name, String content) {
            stringRedisTemplate.opsForValue().set(getRedisKey(name), content);
            updateTimestamp(name);
            return content;
        }
    
        /*
         * 获取模板的原始内容
         */
        public String getTemplate(String name) {
            return stringRedisTemplate.opsForValue().get(getRedisKey(name));
        }
    
        /*
         * 查询所有已经添加的模板名称
         */
        public String getAllTemplateNames() {
            Set<String> keys = stringRedisTemplate.keys(FTL_REDIS_KEY_PREFIX + "*");
            Set<String> trimmedKeys = new HashSet<String>();
            for (String k : keys) {
                trimmedKeys.add(k.substring(FTL_REDIS_KEY_PREFIX.length()));
            }
            return JSON.toJSONString(trimmedKeys);
        }
    
        /*
         * 删除一个模板
         */
        public Boolean deleteTemplate(String name) {
            stringRedisTemplate.delete(getRedisKey(name));
            stringRedisTemplate.delete(getTimestampRedisKey(name));
            return true;
        }
    
        /*
         * 获取模板最后修改的时间
         */
        public Long getTemplateTimestamp(String name) {
            String ts = stringRedisTemplate.opsForValue().get(getTimestampRedisKey(name));
            return Long.parseLong(ts);
        }
    
        /*
         * 更新模板最后修改时间
         */
        private void updateTimestamp(String name) {
            stringRedisTemplate.opsForValue().set(getTimestampRedisKey(name), new Date().getTime() + "");
        }
    
        /*
         * 删除模板最后更新时间
         */
        private void deleteTimestamp(String name) {
            stringRedisTemplate.delete(getTimestampRedisKey(name));
        }
    
        /*
         * 根据模板名称生成一个redis的key
         */
        private String getRedisKey(String name) {
            return FTL_REDIS_KEY_PREFIX + name;
        }
    
        /*
         * 根据模板名称生成一个模板时间戳的key
         */
        private String getTimestampRedisKey(String name) {
            return FTL_REDIS_TS_PREFIX + name;
        }
    }
    

    适配TemplateLoader

    有几个关键:

    1. findTemplateSource每次被调用来查询模板,返回的Object后续会被传入getLastModifiedgetReader判断时间戳和获取实际模板信息
    2. freemarker每次获取模板时,都会先调用findTemplateSource,然后调用getLastModified来判断是否要重新编译模板,所以最好实现getLastModified方法
    3. 覆写父类的toString,可以让freemarker报错时信息更友好
    @Component("redisTemplateLoader")
    @Slf4j
    public class RedisTemplateLoader implements TemplateLoader {
    
        @Autowired
        private RedisTemplateStorage storage;
    
        @Override
        public Object findTemplateSource(String name) {
            try {
                String tpl = storage.getTemplate(name);
                log.info("Template name:[{}], content:[{}]", name, tpl);
                if (StringUtils.isEmpty(tpl)) {
                    return null;
                }
                Long ts = storage.getTemplateTimestamp(name);
                log.info("Template name:[{}], content:[{}], ts:[{}]", name, tpl, ts);
                return new StringTemplateSource(name, tpl, ts);
            } catch (Exception e) {
                log.error("Failed to get template [{}]", name, e);
                return null;
            }
        }
    
        @Override
        public long getLastModified(Object templateSource) {
            return ((StringTemplateSource) templateSource).lastModified;
        }
    
        @Override
        public Reader getReader(Object templateSource, String encoding) {
            return new StringReader(((StringTemplateSource) templateSource).source);
        }
    
        @Override
        public void closeTemplateSource(Object templateSource) {
            // do nothing
        }
    
        private static class StringTemplateSource {
            private final String name;
            private final String source;
            private final long lastModified;
    
            StringTemplateSource(String name, String source, long lastModified) {
                if (name == null) {
                    throw new IllegalArgumentException("name == null");
                }
                if (source == null) {
                    throw new IllegalArgumentException("source == null");
                }
                if (lastModified < -1L) {
                    throw new IllegalArgumentException("lastModified < -1L");
                }
                this.name = name;
                this.source = source;
                this.lastModified = lastModified;
            }
    
            public boolean equals(Object obj) {
                if (obj instanceof StringTemplateSource) {
                    return name.equals(((StringTemplateSource) obj).name);
                }
                return false;
            }
    
            public int hashCode() {
                return name.hashCode();
            }
        }
    
        @Override
        public String toString() {
            return "RedisTemplateLoader";
        }
    
    }
    

    配置freemarker

    这里需要使用FreeMarkerConfigurer类来配置,我这里调用的setPreTemplateLoaders来设置我自定义的模板加载器实现。

    @Configuration
    @Slf4j
    public class FreemarkerConfig {
    
        @Autowired
        RedisTemplateLoader loader;
    
        @Bean
        public FreeMarkerConfigurer freeMarkerConfigurer() {
            log.info("Custom Freemarker configurer pre.");
            FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
            freeMarkerConfigurer.setPreTemplateLoaders(loader);
            freeMarkerConfigurer.setDefaultEncoding("UTF-8"); // Default encoding of the template files
            return freeMarkerConfigurer;
        }
    }
    
    

    用来输出格式化后信息的工具类

    @Component
    @Slf4j
    public class TemplateUtil {
    
        @Autowired
        freemarker.template.Configuration cfg;
    
        public String format(String templateName, Map<String, Object> param)
                throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException,
                TemplateException {
            Template tpl = cfg.getTemplate(templateName);
            Writer out = new StringWriter();
            tpl.process(param, out);
    
            return out.toString();
        }
    }
    
  • 相关阅读:
    Centeos7搭建selenium+Chrome浏览器
    数据结构学习篇之栈和队列
    数据结构学习篇之线性表
    Tornado基础学习篇
    Python控制函数运行时间
    python线程实现异步任务
    Python实现几种简单的排序算法
    python爬虫遇到会话存储sessionStorage
    Python 有哪些优雅的代码实现让自己的代码更pythonic?
    Ubuntu查看端口使用情况,使用netstat命令:
  • 原文地址:https://www.cnblogs.com/mosakashaka/p/12609222.html
Copyright © 2020-2023  润新知