zoukankan      html  css  js  c++  java
  • 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();
        }
    }
    
  • 相关阅读:
    Atitit.ati orm的设计and架构总结 适用于java c# php版
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.js 与c# java交互html5化的原理与总结.doc
  • 原文地址:https://www.cnblogs.com/mosakashaka/p/12609222.html
Copyright © 2011-2022 走看看