ROO 中的 tiles 和 viewResolver

问题描述

在ROO Shell里新建了一个controller,roo会自动生成一系列的jspx和view.xml文件,这是怎么一回事呢?jspx和xml文件之间的关系是什么?自定义jsp时应遵循什么规则?

线索

随意打开WEB-INF/views/,会看到每一个子文件夹(roo自动建立的)下面都有若干个**.jspx文件和一个view.xml文件。打开后者,可以看到下面类似代码:

<definition extends="default" name="sensors/create">
    <put-attribute name="body" value="/WEB-INF/views/sensors/create.jspx"/>
</definition>

可知道这是一个xml文件,留意到其被<tiles-definitions>``</tiles-definitions>标签包围,取关键词“tiles”google一下,定位到这里

Apache Tiles™ is a templating framework built to simplify the development of web application user interfaces.

Tiles allows authors to define page fragments which can be assembled into a complete page at runtime. These fragments, or tiles, can be used as simple includes in order to reduce the duplication of common page elements or embedded within other tiles to develop a series of reusable templates. These templates streamline the development of a consistent look and feel across an entire application.

可得治,Tiles是一个显示层的模板框架,其定义的模板组件(tiles)可以在运行时渲染成一个整体的页面来显示。

再次进行搜寻,定位到IBM的一个教程,尽管年代久远,但是是一篇好文章。

tiles

说到tiles,要先了解三个概念:

tile定义

打开WEB-INF/layouts/layouts.xml文件,可看到类似代码:

<definition name="default" template="/WEB-INF/layouts/default.jspx">
  <put-attribute name="header" value="/WEB-INF/views/header.jspx" />
  <put-attribute name="menu" value="/WEB-INF/views/menu.jspx" />
  <put-attribute name="footer" value="/WEB-INF/views/footer.jspx" />
</definition>

<definition name="public" template="/WEB-INF/layouts/default.jspx">
  <put-attribute name="header" value="/WEB-INF/views/header.jspx" />
  <put-attribute name="footer" value="/WEB-INF/views/footer.jspx" />
</definition>

代码中定义了两个布局模板(default.jspx),和一些arrtibute(header、menu、footer等)。可以看做是在tiles的作用范围内定义了若干个全局变量。其中name属性定义了变量名,value属性为变量的值。

模板

打开WEB-INF/layouts/default.jspx文件,可以看到:

<div id="wrapper">
    <tiles:insertAttribute name="header" ignore="true" />
    <tiles:insertAttribute name="menu" ignore="true" />   
    <div id="main">
        <tiles:insertAttribute name="body"/> 
        <tiles:insertAttribute name="footer" ignore="true"/>
    </div>
</div>

该模板通过标签在页面的合适范围内插入了该tile组件所代表的内容,即在上述tiles定义文件中的attribute的属性值。

当访问default.jspx时,Tiles框架会根据模板中的tiles标签的定义对页面进行组装渲染,例如可以在项目中找到footer.jspx

, header.jspx, menu.jspx这些文件,它们运行时组合成我们看到的roo的默认界面。

但是,注意到模板文件中的<tiles:insertAttribute name="body"/>一句插入了一个name为“body”的定义,但是缺找不到body.jspx,这是为什么呢?

在项目中按ctrl+H进行全局查找,以关键词“layouts.xml”查找到在WEB-INF/spring/webmvc-config.xml文件中有如下两个bean定义:

    <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
    </bean>
    <bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" id="tilesConfigurer">
    <property name="definitions">
        <list>
            <value>/WEB-INF/layouts/layouts.xml</value>
            <!-- Scan views directory for Tiles configurations -->
            <value>/WEB-INF/views/**/views.xml</value>
        </list>
    </property>
    </bean>

可以看到注释<!-- Scan views directory for Tiles configurations -->的意思是TilesConfigurer会自动扫描满足/WEB-INF/views/**/views.xml路径下的tiles定义文件,随意打开一个views.xml,可以看到如下定义:

<definition extends="default" name="sensors/list">
    <put-attribute name="body" value="/WEB-INF/views/sensors/list.jspx"/>
</definition>

这里出现了“body”属性。分析该标签的属性,可以得知该标签继承于(extends)default组件,并且name为sensors/list。我们访问sensors/list路径时,正是显示了以/WEB-INF/views/sensors/list.jspx为的页面。因此猜测:存在一种机制,实现了从URL到tiles的访问。tiles通过URL来决定页面的渲染方式(组件的装配方式)。留意到上述代码中的<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">一句,发现了“UrlBasedViewResolver”**这条线索,由此继续google。

viewResolver

我们在浏览器里输入一条URL,例如http://localhost:8080/demo, 页面显示出网站主页。其中究竟发生了什么?从url到jsp,spring mvc遵循着怎么样的一种mapping机制?

首先关于spring mvc的基础知识在此可以了解到,其核心架构图如下所示:

spring mvc 核心架构图

留意一下从1到7的整个过程,dispatcher负责控制流调度,controller负责业务流调度,而调度的反馈结果通过ModelAndView交给ViewResolver来解析和渲染。不同的ViewResolver有不同的解析方法。

    <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
    </bean>

可以看到,tlies框架实现了UrlBasedViewResolver类,名为TilesView。

这里提到:

A ViewResolver is a strategy and factory object used to map logical view names to actual view resources. The interface for ViewResolver is shown below. The single method resolveViewName(..) maps a logical view name together with the request locale to a View instance.

ViewResolver是spring定义的一个接口,如下所示:

public interface ViewResolver {
  View resolveViewName(String name, Locale loc);
}

默认的ViewResolver实现是InternalResourceViewResolver,其声明如下:

<beans ... >
...
  <bean class="org.springframework.web.servlet.view.
               InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
  </bean>

</beans>

如上,若访问http://localhost/abc,则路径/WEB-INF/views/abc.jsp文件会被解析、渲染并返回。

综上,可以猜测TilesView实现了resolveViewName方法,根据url匹配<definition>标签中的name属性值,然后对根据模板进行装配,最后用Model进行页面渲染,最后将结果返回到浏览器。

另外,在这里可以找到详细的ViewResolver实现和继承关系的树状图

总结

在默认配置的ROO项目里,访问一个url,到浏览器显示出目标页面,大概遵循下面一个可见的流程:

用户在浏览器输入url->某个controller->tiles根据definition解析某个view->用model渲染view->返回结果给用户

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据