文档章节

Struts2中使用Velocity的resource.loader配置问题详解

secondriver
 secondriver
发布于 2015/09/17 09:24
字数 2113
阅读 18
收藏 0


    在Struts2中使用Velocity模板时,如何以相对与Web工程的路径来配置模板资源文件路径这个问题网上千篇一律的来自Velocity官方文档。官方文档中指出如果是Web工程的话,模板的相对路径是工程根路径,今天在使用的时候有如下配置:


Velocity.properties(默认在WEB-INF下):

resource.loader =file, class
class.resource.loader.description = Velocity Classpath Resource Loader 
class.resource.loader.class = org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
file.resource.loader.description = Velocity File Resource Loader 
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader 
file.resource.loader.path = WEB-INF\template
file.resource.loader.cache = false 
file.resource.loader.modificationCheckInterval = 20


   使用了两种资源加载方式:file 和 class.

  1. 测试1(class方式):

      在Web工程的src目录下有vm.test.vm模板,注意test.vm放置在了类路径上,然后在Struts的配置文件中配置Action结果视图:

     

<action name="vm" class="com.action.VmPathAction">
            <result name="success" type="velocity">/vm/test.vm</result>
        </action>


  这里的结果视图路径为:/vm/test.vm(注意“/”不能省略),访问Action,模板信息正常显示。

    

  2.测试2(file方式)

      在Web工程下的WEB-INF下创建了template/hello.vm,在Struts的配置文件中配置Action结果视图:

    

<action name="hello" class="com.action.HelloAction">
            <result name="success" type="velocity">hello.vm</result>
        </action>


    通过配置Velocity的资源加载器FileResourceLoader和loader.path,始终找不到hello.vm; 接着loader.path使用绝对路径:<tomcat_webapp>/webproject/WEB-INF/template,还是没有找到hello.vm。

  

  3.问题

    类加载的方式是没有问题的,文件加载方式通过如上配置是不可行了,并且参考Velocity手册中指出的Web工程配置方式也是不可行,由于是在Struts2中使用的Velocity,于是在Struts2的手册中一探究竟。


  •   Struts2中有一个velocity.properties:

 

# Velocity Macro libraries.
velocimacro.library = action-default.vm, tigris-macros.vm, myapp.vm


      从内容上看,velocity中配置了Struts2自定义的宏,当然我们也可以配置自己的宏

  •   Struts2的struts.properites文件

       

### Location of velocity.properties file.  defaults to velocity.properties
struts.velocity.configfile = velocity.properties
### Comma separated list of VelocityContext classnames to chain to the StrutsVelocityContext
struts.velocity.contexts =
### Location of the velocity toolbox
struts.velocity.toolboxlocation=


     从内容上看,struts2中可以重新定义velocity文件的路径,名称信息;多Context类的分隔符,toolbox的位置。

     然后,从这两个配置文件中并没能找到问题的入口,回到问题的开始,在引入了Struts2MVC框架的Web工程,配置了Velocity的FileResourceLoader之后,Velocity不再使用默认配置,Struts2的Action结果中找不到视图,由此可以可以推断,视图层出来问题。


  •     Struts2MVC的V

    Struts2提供了多种视图展示方式比如:jsp标签, Freemarker模板,Velocity模板等,这里主要一探Velocity模板究竟是如何加载的。


    下图是Struts的view包:

    wKiom1OWsYHh1MHeAAEiHA0dXpA110.jpg

        

        Struts2视图层的标签库提供了TagLibraary接口,然后在DefaultTagLibrary提供了FreeMarker和Velocity的实现。

        velocity包下的VelocityManager类是用来管理Velocity结果类型的环境。通过阅读该类的代码发现该类的loadConfiguration(ServletContext context)正是Struts2框架下Web工程中加载velocity.properties文件的流程。

       基本流程是:

  1.        判断Context(不为null,说明Web容器已经启动)

  2.        创建velocity配置,Struts2横切的方式对velocity配置进行了处理

  3.        判断Struts2的属性配置中的struts.velocity.configfile配置类确定velocity配置,如果存在采用配置文件,不存在采用默认velocity.properties文件

  4.        按照三种方式顺序搜索(1.相对context path,  2.相对WEB-INF, 3.classpath).

  5.        搜索到velocity.properties文件之后,读取信息到velocity配置中去(合理的进行用户自定义覆盖程序默认配置)

      

       问题出现的场景:未有配置struts2的属性;velocity.properties放置在WEB-INF下;请求有响应tomcat容器正常启动;看来问题出在ii上。

       列出Struts对velocity.properties的配置进行重写的环节代码:

      

/**
     * once we've loaded up the user defined configurations, we will want to apply Struts specification configurations.
     * <ul>
     * <li>if Velocity.RESOURCE_LOADER has not been defined, then we will use the defaults which is a joined file,
     * class loader for unpackaed wars and a straight class loader otherwise</li>
     * <li>we need to define the various Struts custom user directives such as #param, #tag, and #bodytag</li>
     * </ul>
     *
     * @param context
     * @param p
     */
    private void applyDefaultConfiguration(ServletContext context, Properties p) {
        // ensure that caching isn't overly aggressive
        /**
         * Load a default resource loader definition if there isn't one present.
         * Ben Hall (22/08/2003)
         */
        if (p.getProperty(Velocity.RESOURCE_LOADER) == null) {
            p.setProperty(Velocity.RESOURCE_LOADER, "strutsfile, strutsclass");
        }
        /**
         * If there's a "real" path add it for the strutsfile resource loader.
         * If there's no real path and they haven't configured a loader then we change
         * resource loader property to just use the strutsclass loader
         * Ben Hall (22/08/2003)
         */
        if (context.getRealPath("") != null) {
            p.setProperty("strutsfile.resource.loader.description", "Velocity File Resource Loader");
            p.setProperty("strutsfile.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
            p.setProperty("strutsfile.resource.loader.path", context.getRealPath(""));
            p.setProperty("strutsfile.resource.loader.modificationCheckInterval", "2");
            p.setProperty("strutsfile.resource.loader.cache", "true");
        } else {
            // remove strutsfile from resource loader property
            String prop = p.getProperty(Velocity.RESOURCE_LOADER);
            if (prop.indexOf("strutsfile,") != -1) {
                prop = replace(prop, "strutsfile,", "");
            } else if (prop.indexOf(", strutsfile") != -1) {
                prop = replace(prop, ", strutsfile", "");
            } else if (prop.indexOf("strutsfile") != -1) {
                prop = replace(prop, "strutsfile", "");
            }
            p.setProperty(Velocity.RESOURCE_LOADER, prop);
        }
        /**
         * Refactored the Velocity templates for the Struts taglib into the classpath from the web path.  This will
         * enable Struts projects to have access to the templates by simply including the Struts jar file.
         * Unfortunately, there does not appear to be a macro for the class loader keywords
         * Matt Ho - Mon Mar 17 00:21:46 PST 2003
         */
        p.setProperty("strutsclass.resource.loader.description", "Velocity Classpath Resource Loader");
        p.setProperty("strutsclass.resource.loader.class", "org.apache.struts2.views.velocity.StrutsResourceLoader");
        p.setProperty("strutsclass.resource.loader.modificationCheckInterval", "2");
        p.setProperty("strutsclass.resource.loader.cache", "true");
        // components
        StringBuilder sb = new StringBuilder();
        for (TagLibrary tagLibrary : tagLibraries) {
            List<Class> directives = tagLibrary.getVelocityDirectiveClasses();
            for (Class directive : directives) {
                addDirective(sb, directive);
            }
        }
        String directives = sb.toString();
        String userdirective = p.getProperty("userdirective");
        if ((userdirective == null) || userdirective.trim().equals("")) {
            userdirective = directives;
        } else {
            userdirective = userdirective.trim() + "," + directives;
        }
        p.setProperty("userdirective", userdirective);
    }



    阅读完这一个方法之后,可以清楚的看到Struts对velocity的配置文件做了处理。

    下面分析一下这段代码:

  1.参数Properties(p)

     这里要看看velocity.properties加载流程,然后看调用applyDefaultConfiguration方法传人的参数。

  

Properties properties = new Properties();
        // now apply our systemic defaults, then allow user to override
        applyDefaultConfiguration(context, properties);

   由此可以看出创建的Properties对象次数没有值。

p.getProperty(Velocity.RESOURCE_LOADER) ==null


  Struts将为resource.loader添加strutsfile, strutsclass;

2.判断context.getRealPath

context.getRealPath("") != null //成立


   结果是成立的。

   因此struts2为velocity配置resource.loader=sturtsfile的参数;

   至于配置resource.loader=strutsclass的参数,注释上写的很清楚(为了从Web Path下的classpath下获取Struts Taglib中的模板)。

 

   如果是不成立的话,可以看到else块中在对resource.loader=strutsfile的strutsfile进行清理。


 3.解决问题

    很遗憾的是在文中开始出现访问不到FileResourceLoader加载的模板,问题是由于配置了resource.loader,且resource.loader=file, 回到调用applyDefaultConfiguration方法的方法,看看applyDefaultConfiguration处理完Properties对象之后,还做了什么处理?

   按照velocity.properties的加载流程,当velocity.properties加载之后,执行了:

  

// if we've got something, load 'er up
            if (in != null) {
                if (LOG.isInfoEnabled()) {
                    LOG.info("Initializing velocity using " + resourceLocation);
                }
                properties.load(in);
            }


     可以看到properties读取velocity.properties文件后会产生覆盖操作(前提是resource.loader等信息在velocity.properties中配置过),直接导致最开始Struts赋值给properties的resource.loader改变了,因此后面的参数配置自然就无效了。

   另外loadConfiguration下面还有一段代码:

  

// overide with programmatically set properties
        if (this.velocityProperties != null) {
            Iterator keys = this.velocityProperties.keySet().iterator();
            while (keys.hasNext()) {
                String key = (String) keys.next();
                properties.setProperty(key, this.velocityProperties.getProperty(key));
            }
        }


   最后一步:VelocityManager类的velocityProperties属性对最终放回的properites对象进行符合规则地重写。


   写到这里,问题自然迎刃而解。

  •    不配置resource.loader(注:模板路径相对于WEB工程根路径)

  •    新配置resource.loader,由于velocity默认配置了resource.loader=file,因此需要覆盖此配置,并且给resource.loader添加上strutsfile, strutsclass, 然后就可以重新设置loader.path.

  •    在已有的resource.loader配置中添加strutsfile, strutsclass(如何存在class配置,  strutsclass可以不配置 )


     另外需要注意的是:strutsfile, strutsclass这两个resource.loader的名称是不能变了,但是其它命名是可以改变(如:file, class),其实际的运行取决于loader.class:

resource.loader=file
<file>.resource.loader.class = <org.apache.velocity.runtime.resource.loader.FileResourceLoader>


  1.中的Action结果视图路径配置问题:

     本想着既然配置了模板的路径,那么在result中直接写相对模板路径的模板视图文件名就可以了,实际操作中发现是不可以的。在struts的Action结果配置中有如下配置:

   

<action name="hello" class="com.action.HelloAction">
            <result name="success" type="velocity">
                <param name="location">/WEB-INF/template/hello.vm</param>
            </result>
        </action>
<action name="hello" class="com.action.HelloAction">
            <result name="success" type="velocity">
                /WEB-INF/template/hello.vm
            </result>
  </action>


          这两种配置都是有效的。注意:"/"是不可以缺少的。

          下面看一下VelocityResult类下的getTemplate方法,就能说明“/”的重要性。

     

 
protected Template getTemplate(ValueStack stack, VelocityEngine velocity, ActionInvocation invocation, String location, String encoding) throws Exception {
        if (!location.startsWith("/")) {
            location = invocation.getProxy().getNamespace() + "/" + location;
        }
        Template template = velocity.getTemplate(location, encoding);
        return template;
    }


     假设缺少:“/”,  如果namespace为“”可以很侥幸的获取到template;否则template的location变为namespace+/+location(namespace/WEB-INF/template/hello.vm),这样以来找不到模板就很自然了。此外Struts2的其它的StrutsResultSupport的子类或者Result类的实现类都对location的值做了处理。


     PS:框架涉及的面多了,文档在边边角角似乎有点乏力,只能剖析源码解决疑问。

本文出自 “野马红尘” 博客,谢绝转载!

© 著作权归作者所有

secondriver
粉丝 10
博文 229
码字总数 233821
作品 0
广州
程序员
私信 提问
Velocity解析模板出现多余的空白和制表符的解决方法

最近在使用Velocity模板引擎进行项目页面输出时发现代码中为了可读性进行的缩进等都反应到了实际输出的页面中,导致页面的结构不整齐,出现了很多的空白,就像下面的这样: 结果页面是这样的...

山哥
2013/08/31
3.9K
4
求高手帮忙啊,velocity加载不上模板老是说路径有问题悲剧的

Template template = new Template(); template = velocityConfigurers.getVelocityEngine().getTemplate("template/soliloquizeContent.vm"); 我的属性配置是这样的velocimacro.permissions......

卡布达
2015/08/17
588
2
WEB环境下使用Velocity模板的问题

velocity.properties如下: input.encoding = UTF-8 output.encoding = UTF-8 resource.loader = webapp webapp.resource.loader.class = org.apache.velocity.tools.view.WebappResourceLo......

steemit
2013/02/03
1K
0
struts配置velocity-tools不能使用tools.xml中配置的工具类

struts2集成velocity模版,不能使用tools.xml中配置的工具变量。以下是我的详细配置: struts.xml velocity.properties tools.xml web.xml index.vm 如果输出一个日期就说明velocity tools....

money1991
2013/04/18
1K
4
velocity模板使用打成jar包后报路径错误的解决办法

近期用java做了个小工具,用了velocity模板生成的方法,在eclipse上运行正常,但打成jar包后老是报错,如下:Template not found:org.apache.velocity.exception.ResourceNotFoundException:...

尉迟逍遥
2016/09/18
303
0

没有更多内容

加载失败,请刷新页面

加载更多

nginx学习之模块

1、 stub_status模块: 用于展示nginx处理连接时的状态。 配置语法如下: Syntax:stub_status;Default:默认没有配置Context:server、location 可以编辑default.conf,加上如下配置: ...

码农实战
37分钟前
4
0
MySQL,必须掌握的6个知识点

目录 一、索引B+ Tree 原理 MySQL 索引 索引优化 索引的优点 索引的使用条件 二、查询性能优化使用 Explain 进行分析 优化数据访问 重构查询方式 三、存储引擎InnoDB MyISAM 比较 四、数据类...

李红欧巴
41分钟前
4
0
堆”和“栈

C++作为一款C语言的升级版本,具有非常强大的功能。它不但能够支持各种程序设计风格,而且还具有C语言的所有功能。我们在这里为大家介绍的是其中一个比较重要的内容,C++内存区域的基本介绍。...

SibylY
53分钟前
4
0
总结:Https

一、介绍 简单理解,https即在http协议的基础上,增加了SSL协议,保障数据传输的安全性。 它由以前的http—–>tcp,改为http——>SSL—–>tcp;https采用了共享密钥加密+公开密钥加密的方式 ...

浮躁的码农
55分钟前
6
0
数据库表与表之间的一对一、一对多、多对多关系

表1 foreign key 表2 多对一:表 1 的多条记录对应表 2 的一条记录 利用foreign key的原理我们可以制作两张表的多对多,一对一关系 多对多: 表1的多条记录可以对应表2的一条记录 表2的多条记...

Garphy
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部