struts2中模板路径的构建策略

16 篇文章 0 订阅
10 篇文章 0 订阅
本文探讨了Struts2中模板路径的构建策略,解释了Result实现如dispatcher、freemarker、velocity与UI组件的关系,并详细阐述了模板路径的确定,包括templateDir、theme、template等参数的作用和配置优先级。同时,介绍了模板引擎的选择以及不同范围内的属性设置对模板路径的影响。
摘要由CSDN通过智能技术生成

struts2的视图扩展是对xwork框架扩展的最复杂部分,而其中的模板抽象部分占据了大部分内容,今天我想在这里探讨一下模板最终路径的确定问题.在没有讨论这个问题之前先将struts2的视图结构稍作分析.

struts2的视图基础结构实际还是xwork中进行规范的,这个规范就是Result,在struts2中在视图进行扩充时实际就是给出多种形式的Result实现.例如dispatcher,freemarker,redirect,stream,plainText,json等等,并且这种扩充是无限的,作为一个Result实现它们在结构上是具有非常重要的地位,但并不代表这种实现就很复杂,它们的复杂度与它们的实现紧密相关.有得实现很简单,而有得并不是很简单.但在这些Result实现中有一类比较特别的实现主要以dispatcher,freemarker,velocity为代表它们有一个特点就是可以在所谓的"页面"上使用UI组件对象,关于UI组件的问题可以参考我写的另篇文章http://blog.csdn.net/zhongxiucheng/article/details/6774432,关于UI组件是struts2中比较复杂的部分,

这里有一个很重要的问题是不要将Result的dispatcher,freemarker,velocity实现与组件的dispatcher,freemarker,velocity实现混淆,jsp,freemarker,velocity它们只是三种相似的技术,只是它们可以同时用于实现Result与UI component两种抽象结构,这里两种结构虽然不相同,但是它们的联系是非常紧密的,UI组件是建立在Result实现基础之上的,但不要误认为在Result的dispatcher实现就只能使用jsp技术的UI组件,而Result的freemarker实现就只能使用freemarker技术的UI组件.理论上讲Result的在dispatcher,freemarker,velocity三种技术中的任何一种实现,都可以使用三种技术中任何一种UI组件实现.

谈到UI组件,就涉及模板,以及模板路径的问题.这里的模板路径与Result配置中的转发路径是不同的,转发路径就是普通的相对或绝对路径,而模板的路径主要由三个参量来决定:templateDir(模板目录),theme(主题),template(模板名称)而这三个参量又分别有默认参量:defaultTemplateDir,defaultUITheme,defaultTemplate其中defaultTemplateDir默认值是template;而defaultUITheme的默认值为xhtml;defaultTemplate分别是不同的UI组件中定义的比如TextField组件的defaultTemplate值为text.对于,其中从这六个参量就可以看出模板路径的配置相对宽松,可以进行灵活的配置,下面是关于模板参量相关的优先级的说明:

设置模板路径

一般在struts2中的struts.properties 文件中的struts.ui.templateDir属性配置模板路径,默认为template。

模板加载顺序

首先搜索web路径下面的的/template/主题名称/template.ftl

然后再找classpath下面的/template/主题名称/template.ftl

然后再使用struts2包中的主题

设置主题名称

struts.properties 文件

struts.ui.theme设置主题名称 默认为xhtml.

也可以通过struts.xml文件配置以上两个属性。通过constant属性。

上面两个参数配置的实际就是defaultTemplateDir与defaultUITheme的值,这两个参数最好不覆盖,如果要覆盖最好使用templateDir(模板目录),theme(主题),这两个参数

 

设置模板引擎

通过struts.ui.templateSuffix属性设置。

模板引擎一般为;

ftl:基于FreeMarker的模板引擎

vm:基于velocit的模板引擎

jsp:基于jsp的模板引擎

 

模板路径的指定(按照优先排序的)

UI标签指定模板的templateDir属性路径

page范围指定模板的templateDir属性路径

request范围指定模板的templateDir属性路径

session范围指定模板的templateDir属性路径

application范围指定板的templateDir属性路径

通过struts.xml中的constant来指定struts.ui.templateDir

 

主题的指定(按照优先排序的)

UI标签的theme属性指定

UI标签的外围的form标签的theme属性指定

page范围的名为theme属性指定

request范围的名为theme属性指定

session范围的名为theme属性指定

application范围的名为theme属性指定

通过struts.xml中的constant来指定struts.ui.theme

 

覆盖整个标签的主题只需改变form标签的theme属性

可以基于session的不同设置theme属性

改变整个程序的主题,通过struts.xml 指定UI的theme属性

 

关于上面两个参量templateDir与theme当Result实现不是jsp时page范围是不可用的;另外request范围不要与request参数混淆,也就是说在请求中前台向后台传递了参数theme并不代表request范围中存在theme变量,至于前台前后台传递了参数,而在request范围中可以获取其值的原因,可以参考最后的关于StrutsRequestWrapper分析,关于上面优先级的设定可以参考UIBean中的代码,下面是其中的关键部分:

    public boolean end(Writer writer, String body) {
        evaluateParams();
        try {
            super.end(writer, body, false);
            mergeTemplate(writer, buildTemplateName(template, getDefaultTemplate()));
        } catch (Exception e) {
            throw new StrutsException(e);
        }
        finally {
            popComponentStack();
        }

        return false;
    }
    protected Template buildTemplateName(String myTemplate, String myDefaultTemplate) {
        String template = myDefaultTemplate;

        if (myTemplate != null) {
            template = findString(myTemplate);
        }

        String templateDir = getTemplateDir();
        String theme = getTheme();

        return new Template(templateDir, theme, template);

    }
    public String getTemplateDir() {
        String templateDir = null;

        if (this.templateDir != null) {
            templateDir = findString(this.templateDir);//UI标签指定模板的templateDir属性路径
        }

        // If templateDir is not explicitly given,
        // try to find attribute which states the dir set to use
        if ((templateDir == null) || (templateDir.equals(""))) {
            templateDir = stack.findString("#attr.templateDir");//四个web范围
        }

        // Default template set
        if ((templateDir == null) || (templateDir.equals(""))) {
            templateDir = defaultTemplateDir;//默认的模板路径
        }

        // Defaults to 'template'
        if ((templateDir == null) || (templateDir.equals(""))) {
            templateDir = "template";
        }

        return templateDir;
    }

    public String getTheme() {
        String theme = null;

        if (this.theme != null) {
            theme = findString(this.theme);
        }

        if ( theme == null || theme.equals("") ) {//对于theme变量这里多了一个级别
            Form form = (Form) findAncestor(Form.class);
            if (form != null) {
                theme = form.getTheme();
            }
        }

        // If theme set is not explicitly given,
        // try to find attribute which states the theme set to use
        if ((theme == null) || (theme.equals(""))) {
            theme = stack.findString("#attr.theme");
        }

        // Default theme set
        if ((theme == null) || (theme.equals(""))) {
            theme = defaultUITheme;
        }

        return theme;
    }

上面的代码应该是比较清淅的,不好理解的地方主要在于#attr.theme与#attr.templateDir,关于这一点主要需要搞清楚AttributeMap这个类,因为#attr主AttributeMap的对象它的get方法如下:

    public Object get(Object key) {
        PageContext pc = getPageContext();

        if (pc == null) {
            Map request = (Map) context.get("request");
            Map session = (Map) context.get("session");
            Map application = (Map) context.get("application");

            if ((request != null) && (request.get(key) != null)) {
                return request.get(key);
            } else if ((session != null) && (session.get(key) != null)) {
                return session.get(key);
            } else if ((application != null) && (application.get(key) != null)) {
                return application.get(key);
            }
        } else {
            try{
                return pc.findAttribute(key.toString());
            }catch (NullPointerException npe){
                return null;
            }
        }

        return null;
    }

上面的PageContext #findAttribute是按四级范围page,request,session,application依次查找变量的,这就是上面优先级的来历,关于上面的优先级还有一点需要说明,如果在保存变量的时候使用了set标签,并且没有指定范围,在上面的get方法中是否可以获取相应的值呢?这是可以的,分两种情况:如果存在PageContext那当然没得说变量会存在于PageContext中,获取的时候也会从PageContext中获取;第二种情况是不存在PageContext时,set标签在没有指定scope的情况下会将变量放入值栈中,进行再通过#attr.变量名     来访问变量会从requestMap中获取此值,这其中的原因是这样的,一个RequestMap对象封装了一个StrutsRequestWrapper,而StrutsRequestWrapper改变了HttpServletRequest#getAttribute行为,使其在某种程度上可以支持jstl表达式,request除了调用自己的getAttribute方法,还会某些情况下去查询值栈,为了避免死循环需要对表达式有一定的限,其方法源代码如下:

    public Object getAttribute(String s) {
        if (s != null && s.startsWith("javax.servlet")) {
            // don't bother with the standard javax.servlet attributes, we can short-circuit this
            // see WW-953 and the forums post linked in that issue for more info
            return super.getAttribute(s);
        }

        ActionContext ctx = ActionContext.getContext();
        Object attribute = super.getAttribute(s);
        if (ctx != null) {
            if (attribute == null) {
                boolean alreadyIn = false;
                Boolean b = (Boolean) ctx.get("__requestWrapper.getAttribute");
                if (b != null) {
                    alreadyIn = b.booleanValue();
                }
    
                // note: we don't let # come through or else a request for
                // #attr.foo or #request.foo could cause an endless loop
                if (!alreadyIn && s.indexOf("#") == -1) {
                    try {
                        // If not found, then try the ValueStack
                        ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);
                        ValueStack stack = ctx.getValueStack();
                        if (stack != null) {
                            attribute = stack.findValue(s);
                        }
                    } finally {
                        ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);
                    }
                }
            }
        }
        return attribute;
    }

从上面的分析可以看出,RequestMap这个对象在包装一个StrutsRequestWrapper后有了更深一层的含义;另外在值栈中的#attr变量是一个AttributeMap也有其特别之处,它存在变量是将它放在PageContext中,获取值的时候却是按page,request,session,application顺序进行取值,关于#attr与#request两个变量的猫腻通常都会在这两个个特别的地方找到

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值