Struts2中使用Velocity模板时模板资源路径配置问题

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

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

resource.loader =file, classclass.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\templatefile.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(也可以写成: vm/test.vm 在Action中默认是以Web工程的根目录为相对路径, 实际在Struts2的源码中结果的前的“/”会处理掉),访问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.propertiesstruts.velocity.configfile = velocity.properties

### Comma separated list of VelocityContext classnames to chain to the StrutsVelocityContextstruts.velocity.contexts =

### Location of the velocity toolboxstruts.velocity.toolboxlocation=

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

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

  • Struts2MVC的V

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

    下图是Struts的view包:

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>

Struts2中使用Velocity模板时模板资源路径配置问题,布布扣,bubuko.com

时间: 2024-10-29 19:08:47

Struts2中使用Velocity模板时模板资源路径配置问题的相关文章

关于在打包Jar文件时遇到的资源路径问题(二)

在关于<关于在打包Jar文件时遇到的资源路径问题(一)>中,以及描述了当资源与可执行JAr分离时的资源路径代码的编写问题,后来想了想,为什么将<Java核心技术卷一>中的程序10-1即ResourceTest的Jar文件放在任何地方都可以执行呢? 图:将原本一起放入目录中的Jar放到桌面上,点击依然能执行 于是我将这个Jar包进行解压,发现在这个包中依然有资源,这就说明了这个包在脱离了原先目录而执行时,依据的资源路径应该是在自己的Jar包中.(至于这个Jar在原先目录时是根据自己包

用node.js express设置路径后 子路径下的页面访问静态资源路径出问题

在routes/news_mian.js 设置了访问news_main.html 的路径 '/',通知设置一个访问news-page.html的子路径'/newspage'子路径.但是在访问loaclhost:3000/news/newspage时静态资源路径前多了一个/news导致不能找到静态资源 app.js var express=require('express'); var app=express(); var path=require('path'); var http=requir

QML Image获取资源路径的细节

ML是门相当新的UI描述语言,因此在文档上总难免有疏漏之处,如果因此产生了bug,则就需要花费大量的时间去排查. 在最近笔者使用QML的过程中,在Image获取资源路径的细节上就出现了这样的情况. Image 是qml中一个非常普通的元素,通常使用方法如下: Image {  width: 120; height: 120  fillMode: Image.TileHorizontally  smooth: true  source: "qtlogo.png"  } 其中,source

一、数据库表中字段的增删改查,二、路由基础.三、有名无名分组.四、多app共存的路由分配.五、多app共存时模板冲突问题.六、创建app流程.七、路由分发.八、路由别名,九、名称空间.十、反向解析.十一、2.x新特性.十二、自定义转换器

一.数据库表中字段的增删改查 ''' 直接在modules中对字段进行增删改查 然后在tools下点击Run manage.py Task执行makemigrations和migrate 注意在执行字段的删除过程中需不需要对数据进行备份 ''' 二.路由基础 ''' # url中含有四个参数 # url(regex, view, kwargs=None, name=None) # 正则路径 视图函数地址 默认关键字参数(了解) 路由别名 # r'index' 只要请求中含有index都可以匹配成

dedeCMS中单独调用子栏目模板和子栏目的文章时修改源代码给channel和chanenartllist加上limit

在网站文件中找到include-taglib-chanel.lib.php,和chaneartllist.lib.php 下载用php的IDE打开, chanel.php加入limit属性修改如下 <?php /** * 获取栏目列表标签 * * @version $Id: channel.lib.php 1 9:29 2010年7月6日Z tianya $ * @package DedeCMS.Taglib * @copyright Copyright (c) 2007 - 2010, Des

WPF中的ControlTemplate(控件模板)(转)

原文地址 http://www.cnblogs.com/zhouyinhui/archive/2007/03/28/690993.html WPF中的ControlTemplate(控件模板)                                                                                                                        周银辉 WPF包含数据模板和控件模板,其中控件模板又包括Contro

win7,M?i?n?d?m?a?n?a?g?e?r?2?0?1?2使用模板时弹出Runtime error R6025解决方法

Mindjet.MindManager2012.v10.0在应用个别模板时提示"参数错误",然后自动关闭. 解决办法: 如果是win7系统,可以进入C:\Users\(用户名)\AppData\Local\Mindjet\MindManager\10\Library\ENU\Templates, 可以看到如下模板:Management/Meetings and Events/Personal Productivity/Problem Solving/Project Management

ThinkPHP添加模板时,犯的三个错

错误一:低级错误,将n打成看m,如图1 图1 这个找错,花了我将近2小时.扫了好几遍与之相关的代码,上网查了好些. 错误二:这个算是个低能的高级错误了.具体模板显示的效果如图2 图2 只要将相对地址及绝对地址找对即可了,但是我都设置好了,感觉就像常量没生效一样,结果还如上图2.我的常量设置如图3 入口文件定义常量,模板再去引用 常量在模板中的引用 问题所在: 我虚拟地址的配置如图 已经到了go目录但是我在设定常量时多设置路径了,导致了路径的错误.应该如下 错误三:差点忘了写了,其中在模板中引用常

WPF 中动态改变控件模板

在某些项目中,可能需要动态的改变控件的模板,例如软件中可以选择不同的主题,在不同的主题下软件界面.控件的样式都会有所不同,这时即可通过改变控件模板的方式实现期望的功能. 基本方法是当用户点击切换主题按钮是加载新的资源字典,并使用新加载的资源字典替代当前的资源字典这时要用到ResourceManager. 假设现有两个不同的资源字典文件Dictionary1.xaml和Dictionary2.xaml存在于Themes文件夹内: 在MainPage中使用其中一个资源字典作为默认样式文件: <Win