Springboot 集成Freemarker 自定义标签解决方案

JSON 2019-06-21 10:04:46 37214

  Freemarker  不是  Springboot  主推的,但是对于我们用习惯了  Freemarker  的同学,还是很喜欢  Freemarker  ,  Freemarker  的Macro,自定义标签都是非常好用的。

Springboot + Freemarker 配置

spring:
  mvc:
    view:
      prefix: /templates/
      suffix: .ftl
    #错误直接抛出异常
    #    throw-exception-if-no-handler-found: true
    favicon:
      enabled: false
  freemarker:
    cache: false
    request-context-attribute: request

Springboot 集成 Freemarker Maven配置

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

定义Freemarker 标签注解

我们采用优雅的方式,用注解去实现。要实现一个标签只需要增加 @FreemarkerTag("tag name") 注解即可。tag name为自定义内容,根据您自己业务而定。

import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface FreemarkerTag {

    String value() default "";
}

定义Freemarker config

这个配置是把所有 @FreemarkerTag("tag name") 注解的对象加载到 Freemarker Configuration 中去,方便前端使用。

import freemarker.template.Configuration;
import freemarker.template.TemplateModelException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;

@Component
public class FreeMarkerConfig implements ApplicationContextAware {


    ApplicationContext applicationContext ;
    @Autowired
    Configuration configuration;

    
    /** 启动加载 **/
    @PostConstruct 
    public void setSharedVariable() throws TemplateModelException {
        

        /**
         * 根据注解获取Freemarker Tag对象
         */
        Map<String, Object> map = applicationContext.getBeansWithAnnotation(FreemarkerTag.class);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            configuration.setSharedVariable(entry.getKey(), entry.getValue());
        }

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}


定义Freemarker标签父类

1.此类采用 interface 实现,也可以采用 abstract 实现,为了用模版设计模式,定义业务骨架,委派变化业务给子类实现。

2.里面提供了 getLong、 getString、 getInt 等方法,您也可以自己实现Boolean等,按需求实现。这里是没办法把 SimpleScalar 直接转换为基础包装类。


import freemarker.core.Environment;
import freemarker.ext.beans.BeansWrapperBuilder;
import freemarker.template.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;



/**
 * Freemarker 标签父类
 *
 * 让子类实现后,利用模版设计模式,委派给子类
 *
 *
 */
@Component
public interface FreemarkerSupperTag extends TemplateDirectiveModel {

    // 此变量不可和其他变量冲突
    String DEFAULT_KEY_SO = "so";

    @Override
    default void execute(Environment environment,
                         Map params,
                            TemplateModel[] model,
                                TemplateDirectiveBody body)
                                    throws TemplateException, IOException {

        /**
         * 模版方法设计模式
         *
         * 此处这么写是把业务委派下去让子类实现,子类加工业务数据直接返回,这里包装返回到前端
         *
         * 有疑惑请断点此处和子类
         *
         *
         */
        Object paramWrap = getParms(params);


        /**
         *
         * 把得到的值 wrap 输出前端
         *
         *
         * freemarker.template.ObjectWrapper.DEFAULT_WRAPPER 已经过期
         * The legacy default object wrapper implementation, focusing on backward compatibility and out-of-the W3C DOM
         * wrapping box extra features. See {@link DefaultObjectWrapper} for more information.
         *
         * @deprecated Use {@link DefaultObjectWrapperBuilder#build()} instead; this instance isn't read-only and thus can't
         *             be trusted.
         */
        TemplateModel templateModel = new  BeansWrapperBuilder(Configuration.VERSION_2_3_28).build().wrap(paramWrap);


        /**
         * 输出,为了防止用户定义的变量 KEY 和 冲突,所以包起来赋值给自定义的变量给 so
         */
        environment.setVariable(DEFAULT_KEY_SO,templateModel);
        body.render(environment.getOut());
    }


    /**
     * 委派下去让子类实现,并且返回加工后的返回值
     * 可返回业务对象,或者集合
     *
     * @param params
     * @return
     */
    Object getParms(Map params);

    /**
     * 获取 long 参数
     * @param params   前端提交来的参数<Key,Value>
     * @param key      key
     * @return
     */
    default Long getLong(Map params,String key){
        return new Long(getString(params,key));
    }
    /**
     * 获取 String 参数
     * @param params   前端提交来的参数<Key,Value>
     * @param key      key
     * @return
     */
    default String getString(Map params,String key){
        Object element = params.get(key);
        String value;
        if(element instanceof SimpleScalar){
            value = ((SimpleScalar) element).getAsString();
        }else{
            value = element.toString();
        }
        return value;
    }
    /**
     * 获取 int 参数
     * @param params   前端提交来的参数<Key,Value>
     * @param key      key
     * @return
     */
    default Integer getInt(Map params,String key){
        return new Integer(getString(params,key));
    }
}

Freemarker Demo 标签

自定义标签,需要实现父类 FreemarkerTag 实现 getParms(Map params) 即可,params 为前端标签传参。

/**
 * 备案类型处理
 */
@FreemarkerTag(value = "bizType")
public class BizType implements FreemarkerSupperTag {


    /**
     * 业务处理
     * @param params Map<String,SimpleScalar>
     * @return
     */
    @Override
    public Object getParms(Map params) {
        /**
         *
         * 获取参数,因为此处 Value 为 SimpleScalar 类型,不能直接转换为  String  Integer 等类型
         */
        Integer type = getInt(params, "type");
        //返回
        return TYPES_K_V.get(type);
    }
}

页面  Freemarker  使用:

<@bizType type="1">${so!'-'}</@bizType>

这样 type 会转参到标签内的 parms ,获取即可,自定义标签中getParms函数中的return的值会赋值给 so,如果有点模糊,请断点执行多看几次。有问题加群。

输出的列表中单位性质为数值,去后端获取对应的类型。


代码如下:

    <#--输出列表-->
    <table class="layui-table">
        <thead>
        <tr>
            <td colspan="2">
                <h3>网站备案信息(ICP)查询结果
                    <#--<small>注:当前信息仅供参考</small>-->
                    <span class=" fr"><small>更新时间:${inDate?string('yyyy-MM-dd HH:mm:ss')}&nbsp;&nbsp; </small></span>
                </h3>
            </td>
        </tr>
        </thead>
    </table>
    <hr>
    <#--数据输出-->
    <table class="layui-table">
        <thead>
        <tr>
            <td>#</td>
            <td>单位名称</td>
            <td>单位性质</td>
            <td>网站备案/许可证号</td>
            <td>网站名称</td>
            <td>网站首页网址</td>
            <td>审核时间</td>
        </tr>
        </thead>
        <tbody>
        <#list data as it>
            <tr>
                <td>${it_index+1}</td>
                <td>${it.orgName}</td>
                <td>
                    <#if it.orgType??>
                        <@bizType type="${it.orgType}">${so!'-'}</@bizType>
                    </#if>
                </td>
                <td>${it.icpSubNumber}</td>
                <td>${it.name}</td>
                <td>${it.siteIndex}</td>
                <td>${it.checkTime}</td>
            </tr>
        </#list>

        </tbody>
    </table>

有疑问加群和我说。


版权所属:SO JSON在线解析

原文地址:https://www.sojson.com/blog/335.html

转载时必须以链接形式注明原始出处及本声明。

本文主题:

如果本文对你有帮助,那么请你赞助我,让我更有激情的写下去,帮助更多的人。

关于作者
一个低调而闷骚的男人。
相关文章
Freemarker教程,Freemarker 自定义方法 【含源码】
Springboot + Freemarker 集成配置
Springboot JPA 执行原生sql ,自定义SQL占位符增加参数
Shiro教程(八)Shiro Freemarker标签的使用。
N多系统单点登录,实现、解决方案。四种解决方案
阿里云 RDS Specified key was too long; max key length is 767 bytes 解决方案
iTunes 没有“应用”这个选项解决方案
Jsoup 提交参数乱码,解决思路,解决过程及解决方案
Shiro教程,Shiro 配置文件详细解释,Shiro自定义Filter配置
使用zxing解析二维码抛出com.google.zxing.NotFoundException 解决方案
最新文章
文件上传漏洞与防御 4058
前端构建工具选型指南:Webpack、Vite、Rollup、esbuild 深度对比 1444
物联网时代2026年时序数据库选型指南 1151
SaaS行业面临AI挑战:从“无限复用”到“灵活适应” 1269
神经网络:从构造到模型训练全链路解析 1168
一文吃透 Redis 核心存储结构:ziplist、listpack 与哈希表扩容 / 并发查询 1593
Linux sudo提权完整指南:从基础用法到生产级安全配置 691
XSS 和 CSRF 的本质区别及开发防御全解析 772
JVM垃圾回收(GC)全维度解析:从原理到调优实战 813
Linux动静态库与ELF加载全解析:从实操制作到底层原理 912
最热文章
免费天气API,天气JSON API,不限次数获取十五天的天气预报 783114
最新MyEclipse8.5注册码,有效期到2020年 (已经更新) 711464
苹果电脑Mac怎么恢复出厂系统?苹果系统怎么重装系统? 679993
Jackson 时间格式化,时间注解 @JsonFormat 用法、时差问题说明 562673
我为什么要选择RabbitMQ ,RabbitMQ简介,各种MQ选型对比 512621
Elasticsearch教程(四) elasticsearch head 插件安装和使用 484794
Jackson 美化输出JSON,优雅的输出JSON数据,格式化输出JSON数据... ... 302947
Java 信任所有SSL证书,HTTPS请求抛错,忽略证书请求完美解决 247433
Elasticsearch教程(一),全程直播(小白级别) 233097
谈谈斐讯路由器劫持,你用斐讯路由器,你需要知道的事情 228329
支付扫码

所有赞助/开支都讲公开明细,用于网站维护:赞助名单查看

查看我的收藏

正在加载... ...