Freemarker

欢迎光临我的博客[http://poetize.cn],前端使用Vue2,聊天室使用Vue3,后台使用Spring Boot

Spring Boot整合

添加pom依赖

	<!-- springboot整合freemarker -->
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-freemarker</artifactId>
	</dependency>


在application.yml中添加相关配置

	spring:
	  # 配置freemarker
	  freemarker:
	    # 设置模板后缀名
	    suffix: .ftl
	    # 设置文档类型
	    content-type: text/html
	    # 设置页面编码格式
	    charset: UTF-8
	    # 设置页面缓存
	    cache: false
	    # 设置ftl文件路径
	    template-loader-path:
	      - classpath:/templates
	  # 设置静态文件路径,js,css等
	  mvc:
	    static-path-pattern: /static/**


# FREEMARKER (FreeMarkerAutoConfiguration)
spring.freemarker.allow-request-override=false
spring.freemarker.allow-session-override=false
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.enabled=true
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=true
spring.freemarker.prefer-file-system-access=true
spring.freemarker.suffix=.ftl
spring.freemarker.settings.template_update_delay=0
spring.freemarker.settings.default_encoding=UTF-8
spring.freemarker.settings.classic_compatible=true
spring.freemarker.order=1
# spring boot 默认的页面模板存放目录
spring.freemarker.template-loader-path=classpath:/templates/

自定义函数(继承 TemplateMethodModelEx 接口)

1 创建自定义函数类

	public class TemplateMethd implements TemplateMethodModelEx {
	    @Override
	    public Object exec(List list) throws TemplateModelException {
	        SimpleSequence simpleSequence = (SimpleSequence) list.get(0);
	        List<BigDecimal> list1 = simpleSequence.toList();
	        Collections.sort(list1, (a, b) -> { return a.intValue() - b.intValue();});
	        return list1;
	    }
	}

2 将自定义函数对象传到模版中

	modelAndView.setViewName("index");
	modelAndView.addObject("sort_int_list", new TemplateMethd());
	return modelAndView;

3 在模版中使用

	<#assign myList=[1,4,5,2,3,7,5,9,0]/>
	<#list sort_int_list(myList) as item>
	    ${item}
	</#list>

自定义标签(解决公共数据集合的重复代码问题)

在 freemarker 中实现自定义的标签,主要就是靠 TemplateDirectiveModel 类。

如字面意思:模板指令模型,主要就是用来扩展自定义的指令。

public interface TemplateDirectiveModel extends TemplateModel {

   public void execute(Environment env, Map params, TemplateModel[] loopVars, 
            TemplateDirectiveBody body) throws TemplateException, IOException;
}


TemplateDirectiveModel 是一个接口,类中只有一个 execute 方法供使用者实现,而我们要做的就是通过实现 execute 方法,实现自定义标签的功能。
当页面模板中使用自定义标签时,会自动调用该方法。


execute 方法的参数含义:

    env : 
    	表示模板处理期间的运行时环境。
    	该对象会存储模板创建的临时变量集、模板设置的值、对数据模型根的引用等等,通常用它来输出相关内容,如Writer out = env.getOut()。
    
    params : 
    	传递给自定义标签的参数(如果有的话)。其中map的key是自定义标签的参数名,value值是TemplateModel实例。
    
    loopVars : 
    	循环替代变量(未发现有什么用)
    
    body : 
    	表示自定义标签中嵌套的内容。说简单点就是自定义标签内的内容体。
    	如果指令调用没有嵌套内容:
    		例如,就像<@myDirective/>或者<@myDirective></mydirective>
    	那么这个参数就会为空。


一、实现 TemplateDirectiveModel 接口

	@Component
	public class CustomTagDirective implements TemplateDirectiveModel {

	    private static final String METHOD_KEY = "method";

	    @Autowired
	    private BizTagsService bizTagsService;

	    @Override
	    public void execute(Environment environment, Map map, TemplateModel[] templateModels, TemplateDirectiveBody templateDirectiveBody) throws TemplateException, IOException {
	        if (map.containsKey(METHOD_KEY)) {
				DefaultObjectWrapperBuilder builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_25);
	            String method = map.get(METHOD_KEY).toString();
	            switch (method) {
	                case "tagsList":
						// 将数据对象转换成对应的TemplateModel
	                    TemplateModel tm = builder.build().wrap(bizTagsService.listAll())
	                    environment.setVariable("tagsList", tm);
	                    break;
					case other...
	                default:
	                    break;
	            }
	        }
	        templateDirectiveBody.render(environment.getOut());
	    }
	}

二、创建 freemarker 的配置类

	@Configuration
	public class FreeMarkerConfig {

	    @Autowired
	    protected freemarker.template.Configuration configuration;

	    @Autowired
	    protected CustomTagDirective customTagDirective;

	    /**
	     * 添加自定义标签
	     */
	    @PostConstruct
	    public void setSharedVariable() {
			/*
			 * 向freemarker配置中添加共享变量;
			 * 它使用 Configurable.getObjectWrapper() 来包装值,因此在此之前设置对象包装器是很重要的。(即上一步的builder.build().wrap操作)
			 */
	        configuration.setSharedVariable("zhydTag", customTagDirective);
	    }
	}

三、ftl模板中使用自定义标签

	<@zhydTag method="tagsList">
		<#if tagsList?? && (tagsList?size > 0)>
			<#list tagsList as item>
				<li>
					<a href="${config.siteUrl}/tag/${item.id}" title="${item.name!}">
						${item.name!}
					</a>
				</li>
			</#list>
		</#if>
	</@zhydTag>


自定义标签的使用方法跟自定义宏(macro)用法一样,直接使用<@标签名>${值}</@标签名>即可。

注:ftl中通过@调用自定义标签时,后面可以跟任意参数,所有的参数都可以在execute方法的第二个参数(map)中获取,
由此可以根据一个特定的属性开发一套特定的自定义标签,比如上面代码中通过method参数判断调用不同的处理方式。


@Component
public class MenuTagDirective implements TemplateDirectiveModel {

    private static final String METHOD_KEY = "method";

    private final MenuService menuService;

    public MenuTagDirective(Configuration configuration, MenuService menuService) {
        this.menuService = menuService;
        configuration.setSharedVariable("menuTag", this);
    }

    @Override
    public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
        final DefaultObjectWrapperBuilder builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_25);

        if (params.containsKey(HaloConst.METHOD_KEY)) {
            String method = params.get(HaloConst.METHOD_KEY).toString();
            switch (method) {
                case "list":
                    env.setVariable("menus", builder.build().wrap(menuService.listAll()));
                    break;
                case "tree":
                    env.setVariable("menus", builder.build().wrap(menuService.listAsTree(Sort.by(DESC, "priority"))));
                    break;
                case "listTeams":
                    env.setVariable("teams", builder.build().wrap(menuService.listTeamVos(Sort.by(DESC, "priority"))));
                    break;
                case "listByTeam":
                    String team = params.get("team").toString();
                    env.setVariable("menus", builder.build().wrap(menuService.listByTeam(team, Sort.by(DESC, "priority"))));
                    break;
                case "count":
                    env.setVariable("count", builder.build().wrap(menuService.count()));
                    break;
                default:
                    break;
            }
        }
        body.render(env.getOut());
    }
}

TemplateModel

TemplateModel 是一个接口类型,代表 FreeMarker 模板语言(FTL)数据类型的接口的公共超接口,
即所有的数据类型都会被 freemarker 转成对应的 TemplateModel。

通常我们都使用TemplateScalarModel接口来替代它获取一个String 值,如:TemplateScalarModel.getAsString()。
TemplateNumberModel 获取 number。
类型 FreeMarker接口 FreeMarker实现
字符串 TemplateScalarModel SimpleScalar
数值 TemplateNumberModel SimpleNumber
日期 TemplateDateModel SimpleDate
布尔 TemplateBooleanModel TemplateBooleanModel.TRUE
哈希 TemplateHashModel SimpleHash
序列 TemplateSequenceModel SimpleSequence
集合 TemplateCollectionModel SimpleCollection
节点 TemplateNodeModel NodeModel

扩展 FreeMarkerConfig

上面提到的自定义标签,都是通过<@tagName>xxx</@tagName>方式调用的。

针对我们系统中一些类环境变量的数据(全局的配置类属性等):

	@Configuration
	public class FreeMarkerConfig {

	    @Autowired
	    protected freemarker.template.Configuration configuration;

	    @Autowired
	    private SysConfigService configService;

	    /**
	     * 添加自定义标签
	     */
	    @PostConstruct
	    public void setSharedVariable() {
	        try {
	            configuration.setSharedVariable("config", configService.get());
	        } catch (TemplateModelException e) {
	            e.printStackTrace();
	        }
	    }
	}

如此而已,在使用的时候我们可以直接在页面上通过 ${config.siteName} 调用 config 的参数即可。
posted @ 2020-02-02 13:59  LittleDonkey  阅读(621)  评论(0编辑  收藏  举报