Freemarker与Spring设置自定义函数

以前经常用freemarker,但是从来没有系统的学习过,最近有时间了,找到资料学习一下,官方文档下载地址:打开下载


freemarker会用并不难,本篇博客是分享freemarker和spring如何结合使用,代码全部是参考上家公司的经理的,前人栽树后人乘凉


以下两种方法,都是假设已经与springmvc整合完毕


自定义函数(普通)

1:实现TemplateMethodModelEx
package com.mall.web.template.method;

import com.fengyong.base.rely.ResultPoBean;
import com.product.po.user.base.BaseSysUserPo;
import com.product.service.user.UserService;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;

/**
 * 描述:
 *
 * @author fengyong
 * @date 2017/1/22.
 */
public class GetUserMethod implements TemplateMethodModelEx {
    @Autowired
    private UserService userService;
    @Override
    public Object exec(List arguments) throws TemplateModelException {
        ResultPoBean<BaseSysUserPo> resultPoBean = userService.getSysUserPo(new Long(arguments.get(0).toString()));
        return resultPoBean.getValue();
    }
}


2:配置文件
<!--freemarker view config-->
    <bean id="freeMarkerViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="suffix" value=".ftl"/>
        <property name="contentType" value="text/html; charset=UTF-8" />
        <property name="exposeRequestAttributes" value="true" />
        <property name="exposeSessionAttributes" value="false" />
        <property name="exposeSpringMacroHelpers" value="true" />
    </bean>

    <bean id="freemarkerConfig"
          class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPath" value="/WEB-INF/ftl"/>
        <property name="freemarkerSettings">
            <props>
                <prop key="defaultEncoding">utf-8</prop>
                <prop key="url_escaping_charset">utf-8</prop>
                <prop key="template_update_delay">0</prop>
                <prop key="locale">zh_CN</prop>
                <prop key="number_format">0.######</prop>
                <prop key="boolean_format">true,false</prop>
                <prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
                <prop key="date_format">yyyy-MM-dd</prop>
                <prop key="time_format">HH:mm:ss</prop>
                <prop key="whitespace_stripping">true</prop>
                <prop key="auto_import">/app/auto_import.ftl as p</prop>
            </props>
        </property>
        <property name="freemarkerVariables">
            <map>
                <!--一般地址的url-->
                <entry key="base" value="#{servletContext.contextPath}"/>
                <entry key="getUserMethod" value-ref="getUserMethod"/>
            </map>
        </property>
    </bean>

    <bean id="getUserMethod" class="com.mall.web.template.method.GetUserMethod"/>
3:ftl页面中使用
<#assign getUserMethod= getUserMethod("1")/>
${getUserMethod}

自定义函数(完美整合)

上面的方法是很多网上资料中都有得,但是有一定的缺点,如果有很多歌方法,那配置文件中的freemarkerVariables会无限增大,配置起来很麻烦,接下来,我会一步步解析。

1:首先看freemarkerVariables中定义的函数去了哪里
public void setFreemarkerVariables(Map<String, Object> variables) {
    this.freemarkerVariables = variables;
}
2:查看freemarkerVariables去了哪里
public class FreeMarkerConfigurationFactory {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private Resource configLocation;
    private Properties freemarkerSettings;
    private Map<String, Object> freemarkerVariables;
3:在整个FreeMarkerConfigurationFactory里面,只有一处用到了freemarkerVariables。看下面代码

public Configuration createConfiguration() throws IOException, TemplateException {
    Configuration config = this.newConfiguration();
    Properties props = new Properties();
    if(this.configLocation != null) {
        if(this.logger.isInfoEnabled()) {
            this.logger.info("Loading FreeMarker configuration from " + this.configLocation);
        }

        PropertiesLoaderUtils.fillProperties(props, this.configLocation);
    }

    if(this.freemarkerSettings != null) {
        props.putAll(this.freemarkerSettings);
    }

    if(!props.isEmpty()) {
        config.setSettings(props);
    }

    if(!CollectionUtils.isEmpty(this.freemarkerVariables)) {
        config.setAllSharedVariables(new SimpleHash(this.freemarkerVariables, config.getObjectWrapper()));
    }
4:这时我们知道了自定义函数被放进了config,继续看这个方法

public void setAllSharedVariables(TemplateHashModelEx hash) throws TemplateModelException {
    TemplateModelIterator keys = hash.keys().iterator();
    TemplateModelIterator values = hash.values().iterator();
    while(keys.hasNext())
    {
        setSharedVariable(((TemplateScalarModel)keys.next()).getAsString(), values.next());
    }
}

扫描二维码关注公众号,回复: 1109315 查看本文章
public void setSharedVariable(String name, TemplateModel tm) {
    Object replaced = sharedVariables.put(name, tm);
    if (replaced != null && rewrappableSharedVariables != null) {
        rewrappableSharedVariables.remove(name);
    }
}
ok,现在看到了自定义函数都被放到了sharedVariables里面,那么我们只要获取config,调用setSharedVariable把自定义函数放进去,我们就可以不用无限配置了。

5:继续看config,从FreeMarkerConfigurationFactory的子FreeMarkerConfigurer(配置文件中出现过)可以看到config的去处

private Configuration configuration;
private TaglibFactory taglibFactory;

public FreeMarkerConfigurer() {
}

public void setConfiguration(Configuration configuration) {
    this.configuration = configuration;
}

public void setServletContext(ServletContext servletContext) {
    this.taglibFactory = new TaglibFactory(servletContext);
}

public void afterPropertiesSet() throws IOException, TemplateException {
    if(this.configuration == null) {
        this.configuration = this.createConfiguration();
    }

}

6:现在知道了config,也知道了如何往sharedVariables里面设置自定义函数,就可以开工了。
1:创建注解,用于标注自定义方法的类
package com.mall.web.template.method;

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;

/**
 * Created by fengyong on 2017/1/22.
 */
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface FreemarkerComponent {
    String value() default "";
}
2:创建MdFreeMarkerConfigurer继承FreeMarkerConfigurer,并且ApplicationContextAware,实现ApplicationContextAware是为了获取ApplicationContext,并通过ApplicationContext获取注解和所注解的类
package com.mall.web.template.method;

import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

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

/**
 * Created by fengyong on 2017/1/22.
 */
public class MdFreeMarkerConfigurer extends FreeMarkerConfigurer implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    public void afterPropertiesSet() throws IOException, TemplateException {
        super.afterPropertiesSet();
        Map<String, Object> map = applicationContext.getBeansWithAnnotation(FreemarkerComponent.class);
        Configuration configuration = this.getConfiguration();
        for (String key : map.keySet()) {
            configuration.setSharedVariable(key, map.get(key));
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
解释一下afterPropertiesSet,这个方法是在实例化bean的时候调用。
3:写一个自定义方法
package com.mall.web.template.method;

import com.fengyong.base.rely.ResultPoBean;
import com.product.po.user.base.BaseSysUserPo;
import com.product.service.user.UserService;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;

/**
 * 描述:
 *
 * @author fengyong
 * @date 2017/1/22.
 */
@FreemarkerComponent("getUserMethod")
public class GetUserMethod implements TemplateMethodModelEx {
    @Autowired
    private UserService userService;
    @Override
    public Object exec(List arguments) throws TemplateModelException {
        ResultPoBean<BaseSysUserPo> resultPoBean = userService.getSysUserPo(new Long(arguments.get(0).toString()));
        return resultPoBean.getValue();
    }
}
4:接下来需要修改<context:component-scan base-package="com.*" />,让spring可以扫描到所有的自定义方法类,这样ApplicationContext才可以获得这个注解和所注解的类,之后每次写自定义方法,都只需要加一个@FreemarkerComponent("****")就可以了,不需要再配置了。

猜你喜欢

转载自blog.csdn.net/fengyong7723131/article/details/54668905