velocity原理探究(下)

三、webx中veloctiy的使用

3.1 webx集成veloctiy配置入口

在webx的webx-component.xml里面有一个配置如下:

<services:template

        xmlns="http://www.alibaba.com/schema/services/template/engines"

        searchExtensions="true">

        <velocity-engine templateEncoding="UTF-8"

            strictReference="false" path="/${component}/templates">

            <plugins>

                <vm-plugins:escape-support defaultEscape="html">

                    <vm-plugins:noescape>

                        <vm-plugins:if-matches pattern="^control\." />

                        <vm-plugins:if-matches pattern="^screen_placeholder" />

                        <vm-plugins:if-matches pattern="^stringEscapeUtil\.escape" />

                        <vm-plugins:if-matches pattern="^csrfToken\.(get)?(\w*)hiddenField" />

                    </vm-plugins:noescape>

                </vm-plugins:escape-support>

            </plugins>

        </velocity-engine>

        <freemarker-engine templateEncoding="UTF-8"

            path="/${component}/templates" />

        <jsp-engine path="/${component}/templates" />

    </services:template>

而解析该配置的解析器为:TemplateServiceDefinitionParser

首先说下,该配置目的是创建一个beanname=templateService的TemplateServiceImpl类,并实例化三种渲染引擎。下面看下解析代码:

public class TemplateServiceDefinitionParser extends AbstractNamedBeanDefinitionParser<TemplateServiceImpl> implements

                                                                                                            ContributionAware {    private ConfigurationPoint templateEnginesConfigurationPoint;    //(1) 解析插件等配置

    public void setContribution(Contribution contrib) {        this.templateEnginesConfigurationPoint = getSiblingConfigurationPoint("services/template/engines", contrib);

    }    @Override

    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {

        parseBeanDefinitionAttributes(element, parserContext, builder);

        Map<Object, Object> engines = createManagedMap(element, parserContext);

        Map<Object, Object> mappings = createManagedMap(element, parserContext);

        ElementSelector engineSelector = ns(templateEnginesConfigurationPoint.getNamespaceUri());

        ElementSelector mappingSelector = and(sameNs(element), name("template-mapping"));        for (Element subElement : subElements(element)) {            //(2) engine,解析并创建三个渲染引擎的bean定义

            if (engineSelector.accept(subElement)) {

                BeanDefinitionHolder engine = parseConfigurationPointBean(subElement,

                                                                          templateEnginesConfigurationPoint, parserContext, builder);

                engines.put(engine.getBeanName(), engine);

            }            // mapping

            else if (mappingSelector.accept(subElement)) {

                String ext = normalizeExtension(subElement.getAttribute("extension"));

                String engineName = assertNotNull(trimToNull(subElement.getAttribute("engine")), "engine");

                assertNotNull(ext, "extension");

                assertTrue(!mappings.containsKey(ext), "duplicated extension: %s", ext);

                mappings.put(ext, engineName);

            }

        }        //设置TemplateServiceImpl的engines和engineNameMappings属性

        builder.addPropertyValue("engines", engines);

        builder.addPropertyValue("engineNameMappings", mappings);

        attributesToProperties(element, builder, "defaultExtension", "searchExtensions", "searchLocalizedTemplates",                               "cacheEnabled");

    }    @Override

    protected String getDefaultName() {        return "templateService";

    }

}

其中(2)创建了velocity的渲染引擎实例VelocityEngineImpl,解析器为VelocityEngineDefinitionParser为:

protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {

    attributesToProperties(element, "configuration.", builder, "path", "cacheEnabled", "modificationCheckInterval",                           "strictReference", "templateEncoding");

    ElementSelector globalMacros = and(sameNs(element), name("global-macros"));

    ElementSelector plugins = and(sameNs(element), name("plugins"));

    ElementSelector advancedProperties = and(sameNs(element), name("advanced-properties"));    for (Element subElement : subElements(element)) {        if (globalMacros.accept(subElement)) {            //设置VelocityEngineImpl的成员变量configuration的属性值macros

            parseGlobalMacros(subElement, parserContext, builder);

        } else if (plugins.accept(subElement)) {            //设置VelocityEngineImpl的成员变量configuration的属性值properties

            parsePlugins(subElement, parserContext, builder);

        } else if (advancedProperties.accept(subElement)) {

            parseAdvancedProperties(subElement, parserContext, builder);

        }

    }

}

3.2 velocity初始化

上节讲了集成velocity需要的一些bean的定义,下面看下如何实例并初始化:

0?wx_fmt=png

SpringIOC首先创建TemplateServiceImpl对象,但是要首先创建成员变量engines里面的VelocityEngineImpl,所以首先创建了它,然后调用init初始化,初始化过程首先获取webx-compoment.xml里面配置的插件等用户自定义属性,然后初始化默认的velocity.properties,然后用户属性覆盖默认属性,这也说明webx框架还是留有口子,让用户自定义veloctiy行为。然后回到

TemplateServiceImpl的创建,调用init方法设置映射,也就是后缀名与模板引擎的映射如下:


    ftl=com.alibaba.citrus.service.freemarker.impl.FreeMarkerEngineImpl#684bd25b:FreeMarkerEngine, jsp=JspEngine[/common/templates/], 

jspx=JspEngine[/common/templates/], vm=com.alibaba.citrus.service.velocity.impl.VelocityEngineImpl#5c6c2c1c:VelocityEngine

}

webx是基于模块的,并且所有模块都import了webx-component.xml ,所以每个模块都有自己的veloctiyengine,例如我的webx例子里面有auth,comm,business,rock模块,所以如下创建了四个引擎对象,对应不同的模板文件路径:

{com.alibaba.citrus.service.velocity.impl.VelocityEngineImpl#3fdeabbc=com.alibaba.citrus.service.velocity.impl.VelocityEngineImpl#3fdeabbc:VelocityEngine, [/rock/templates/]}

{com.alibaba.citrus.service.velocity.impl.VelocityEngineImpl#5783395d=com.alibaba.citrus.service.velocity.impl.VelocityEngineImpl#5783395d:VelocityEngine, [/comm/templates/]}

{com.alibaba.citrus.service.velocity.impl.VelocityEngineImpl#2d4f8a40=com.alibaba.citrus.service.velocity.impl.VelocityEngineImpl#2d4f8a40:VelocityEngine, [/business/templates/]}

{com.alibaba.citrus.service.velocity.impl.VelocityEngineImpl#2ee2af0=com.alibaba.citrus.service.velocity.impl.VelocityEngineImpl#2ee2af0:VelocityEngine, [/auth/templates/]}

3.3一次渲染过程

0?wx_fmt=png

首先通过pipeline里面的PerformScreenValve调用screen对应的Java类设置变量,然后invoke RenderTemplateValve委托给veloctiy引擎VelocityEngineImpl执行模板获取与渲染。

四、SpringMVC中veloctiy的使用

4.1 SpringMVC集成veloctiy配置入口

在DispatcherServlet的配置文件里面添加如下:


    <!--1、对模型视图名称的解析,即在模型视图名称添加前后缀 -->

    <bean id="viewResolver"

        class="org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver">

        <property name="allowSessionOverride" value="true" />

        <property name="exposeSessionAttributes" value="true" />

        <property name="cache" value="true" />

        <property name="prefix" value="" />

        <property name="layoutUrl" value="layout.vm" />

        <property name="suffix" value=".vm" />

        <property name="contentType">

            <value>text/html;charset=UTF-8</value>

        </property>

    </bean>

    <!--2、velocity的一些设置 -->

    <bean id="velocityConfig"

        class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">

        <property name="resourceLoaderPath">

            <value>/WEB-INF/templates/</value>

        </property>

        <property name="velocityProperties">

            <props>

                <prop key="input.encoding">UTF-8</prop>

                <prop key="output.encoding">UTF-8</prop>

                <prop key="contentType">text/html;charset=UTF-8</prop>

            </props>

        </property>

    </bean>

4.2 veloctiy初始化

先上一个时序图:

0?wx_fmt=png

如图DispatcherServlet的init方法中创建了一个spring的context,去解析springDispatcher-servlet.xml里面配置的bean,首先实例并初始化了VelocityLayoutViewResolver,然后实例初始化了VelocityConfigurer,在它的afterPropertiesSet方法创建了veloctiy引擎并初始化。

4.3 一次渲染过程

先看下时序图

0?wx_fmt=png

首先找到对应的controller返回包含参数的mv, 然后通过VelocityLayoutViewResolver去创建view:

public View resolveViewName(String viewName, Locale locale) throws Exception {        //是否开启缓存,没开启则创建

        if (!isCache()) {            return createView(viewName, locale);

        }        else {            //开启缓存,首先看缓存

            Object cacheKey = getCacheKey(viewName, locale);

            View view = this.viewAccessCache.get(cacheKey);            if (view == null) {                synchronized (this.viewCreationCache) {

                    view = this.viewCreationCache.get(cacheKey);                    if (view == null) {                        // Ask the subclass to create the View object.

                        view = createView(viewName, locale);                        if (view == null && this.cacheUnresolved) {

                            view = UNRESOLVED_VIEW;

                        }                        if (view != null) {                            this.viewAccessCache.put(cacheKey, view);                            this.viewCreationCache.put(cacheKey, view);                            if (logger.isTraceEnabled()) {

                                logger.trace("Cached view [" + cacheKey + "]");

                            }

                        }

                    }

                }

            }            return (view != UNRESOLVED_VIEW ? view : null);

        }

    }

由于配置时候cache=true所以打开了cache,这里有两级缓存,定义如下:

/** 快速从Cache中获取view, 不加全局锁 */

    private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<Object, View>(DEFAULT_CACHE_LIMIT);    /** 创建view时候加全局锁 */

    @SuppressWarnings("serial")    private final Map<Object, View> viewCreationCache =            new LinkedHashMap<Object, View>(DEFAULT_CACHE_LIMIT, 0.75f, true) {                @Override

                protected boolean removeEldestEntry(Map.Entry<Object, View> eldest) {                    if (size() > getCacheLimit()) {

                        viewAccessCache.remove(eldest.getKey());                        return true;

                    }                    else {                        return false;

                    }

                }

            };

为了性能,对已经创建的view直接从viewAccessCache中就可以快速获取,这避免了在创建view期间其他线程等待的现象,只有两个线程都要创建view时候才会等待。也就说当一个线程进入同步块创建view时候,其他线程获取已经存在的view不需要等待。

获取velocityview后,会委托给velocityengine获取模板,然后调用velocityview的render方法调用template.merge实际对AST进行渲染

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值