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

简介:

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


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

1
2
3
4
5
6
7
8
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结果视图:

     

1
2
3
<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结果视图:

    

1
2
3
<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:

 

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


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

  •   Struts2的struts.properites文件

       

1
2
3
4
5
6
### 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的配置进行重写的环节代码:

      

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
      * 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方法传人的参数。

  

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

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

1
p.getProperty(Velocity.RESOURCE_LOADER) ==null


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

2.判断context.getRealPath

1
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加载之后,执行了:

  

1
2
3
4
5
6
7
// 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下面还有一段代码:

  

1
2
3
4
5
6
7
8
// 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:

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


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

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

   

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


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

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

     

1
2
3
4
5
6
7
8
  
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 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1424635,如需转载请自行联系原作者

相关文章
|
6月前
|
前端开发 JavaScript Java
Springboot 使用thymeleaf 服务器无法加载resources中的静态资源异常处理
Springboot 使用thymeleaf 服务器无法加载resources中的静态资源异常处理
516 0
|
JSON 前端开发 JavaScript
浅谈一下 webpack 以及 loader 和 plugin
浅谈一下 webpack 以及 loader 和 plugin
180 0
|
4天前
|
前端开发 JavaScript
webpack 中 loader 和 plugin 的区别
在 webpack 中,loader 用于转换模块的源代码,如将 TypeScript 转为 JavaScript;而 plugin 则扩展了 webpack 的功能,可以执行更复杂的任务,如优化打包文件、注入环境变量等。两者共同作用于构建流程的不同阶段。
|
1月前
|
前端开发 JavaScript
Webpack 常用 Loader 和 Plugin
【10月更文挑战第12天】Webpack 是一个强大的模块打包工具,能够将各种资源模块进行打包和处理。Loader 用于转换模块的源代码,如 `babel-loader` 将 ES6+ 代码转换为 ES5,`css-loader` 处理 CSS 文件等。Plugin 扩展 Webpack 功能,如 `HtmlWebpackPlugin` 自动生成 HTML 文件,`UglifyJsPlugin` 压缩 JavaScript 代码。通过合理配置和使用 Loader 和 Plugin,可以构建高效、优化的项目。
23 2
|
6月前
|
前端开发 JavaScript
Webpack中的Loader和Plugin:理解与使用
Webpack中的Loader和Plugin:理解与使用
|
缓存 前端开发 JavaScript
Webpack入门:常用loader和plugin配置
Webpack入门:常用loader和plugin配置
118 0
|
Java Maven
使用maven构建项目报错Cannot change version of project facet Dynamic Web Module to 3.0解决方案
使用maven构建项目报错Cannot change version of project facet Dynamic Web Module to 3.0解决方案
使用maven构建项目报错Cannot change version of project facet Dynamic Web Module to 3.0解决方案
|
JSON JavaScript 中间件
webpack原理分析(loader, plugin)(上)
webpack原理分析(loader, plugin)
|
JavaScript 前端开发
webpack原理分析(loader, plugin)(下)
webpack原理分析(loader, plugin)