20. Spring JavaScript 快速参考
The spring-js-resources 模块是一个遗留模块,不再推荐使用,但为了向后兼容,仍然作为可选模块提供。 其最初的目标是为逐步增强网页的行为和 Ajax 远程处理提供一种客户端编程模型。
Spring JS API 的使用在 示例仓库 中进行了演示。
20.1. 提供 JavaScript 资源
Spring框架提供了一种服务静态资源的机制。 参见Spring框架文档。 通过新的<mvc:resources>元素,资源请求(如.js、.css等)由DispatcherSevlet处理。 以下示例展示了如何在XML中配置该模块(也支持Java配置):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<mvc:annotation-driven/>
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/web-resources/" />
...
</beans>
这会将传入的请求 /resources 映射到类路径下 /META-INF/web-resources 中找到的资源。 Spring JavaScript 资源就是捆绑在该位置的。 但是,您可以修改前面配置中的 location 属性,以从相对于类路径或 Web 应用程序的任何位置提供资源。
请注意,完整的资源URL取决于您的DispatcherServlet是如何映射的。 在mvc-booking示例中,我们选择使用默认的servlet映射(/)进行映射,如下所示:
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
这意味着加载Spring.js的完整URL是/myapp/resources/spring/Spring.js。 如果您的DispatcherServlet映射到/main/*,则完整URL为/myapp/main/resources/spring/Spring.js。
当您使用默认的Servlet映射时,还应该在您的Spring MVC配置中添加以下配置,以确保任何未被Spring MVC映射处理的资源请求都将委托回Servlet容器:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
...
<mvc:default-servlet-handler />
</beans>
20.2. 在页面中包含 Spring Javascript
Spring JS 的设计使得可以为其 API 构建任何流行的 JavaScript 工具包的实现。 Spring.js 的初始实现基于 Dojo 工具包构建。
在页面中使用 Spring Javascript 需要像平常一样包含底层工具包、Spring.js 基础接口文件,以及该底层工具包的 Spring-(library implementation).js 文件。 例如,以下包含内容通过使用 ResourceServlet 来获取 Spring.js 的 Dojo 实现:
<script type="text/javascript" src="<c:url value="/resources/dojo/dojo.js" />"> </script>
<script type="text/javascript" src="<c:url value="/resources/spring/Spring.js" />"> </script>
<script type="text/javascript" src="<c:url value="/resources/spring/Spring-Dojo.js" />"> </script>
当使用底层库的小部件系统时,通常还必须包含一些CSS资源,以获得所需的外观和感觉。 对于booking-mvc参考应用程序,包含了Dojo的tundra.css:
<link type="text/css" rel="stylesheet" href="<c:url value="/resources/dijit/themes/tundra/tundra.css" />" />
20.3. Spring Javascript 装饰
Spring Javascript 中的一个核心概念是将装饰应用到现有的 DOM 节点。这种技术用于逐步增强网页,使得该页面在功能较弱的浏览器中仍然可用。使用 addDecoration 方法来应用装饰。
以下示例通过丰富的建议行为增强了 Spring MVC 的 <form:input> 标签:
<form:input id="searchString" path="searchString"/>
<script type="text/javascript">
Spring.addDecoration(new Spring.ElementDecoration({
elementId: "searchString",
widgetType: "dijit.form.ValidationTextBox",
widgetAttrs: { promptMessage : "Search hotels by name, address, city, or zip." }}));
</script>
ElementDecoration 调用用于为现有的 DOM 节点应用丰富的组件行为。 这种装饰类型并不旨在完全隐藏底层工具包,因此直接使用工具包的原生组件类型和属性。 这种方法允许您使用通用的装饰模型,以一致的方式集成来自底层工具包的任何组件。 更多关于应用装饰的示例,从建议到客户端验证,请参见 booking-mvc 参考应用程序。
当使用 ElementDecoration 来应用具有丰富验证行为的小部件时,一个常见的需求是防止表单在验证通过之前提交到服务器。 这可以使用 ValidateAllDecoration 来实现,如下所示:
<input type="submit" id="proceed" name="_eventId_proceed" value="Proceed" />
<script type="text/javascript">
Spring.addDecoration(new Spring.ValidateAllDecoration({ elementId:'proceed', event:'onclick' }));
</script>
这为“继续”按钮装饰了一个特殊的onclick事件处理器,该处理器会触发客户端验证器,并且在它们成功通过之前不允许表单提交。
AjaxEventDecoration 应用了一个客户端事件监听器,该监听器向服务器发送一个远程 Ajax 请求。 它还自动注册了一个回调函数以链接响应。 以下示例使用了 AjaxEventDecoration:
<a id="prevLink" href="search?searchString=${criteria.searchString}&page=${criteria.page - 1}">Previous</a>
<script type="text/javascript">
Spring.addDecoration(new Spring.AjaxEventDecoration({
elementId: "prevLink",
event: "onclick",
params: { fragments: "body" }
}));
</script>
前面的列表使用 Ajax 调用装饰了“上一个结果”链接的 onclick 事件,传递了一个特殊参数,用于指定要在响应中重新渲染的片段。 请注意,如果客户端中不可用 JavaScript,此链接是完全可用的。 (有关服务器如何处理此请求的详细信息,请参见 处理 Ajax 请求。)
你还可以对一个元素应用多个装饰器。 以下示例显示了一个按钮被 Ajax 和 validate-all 提交抑制功能所装饰:
<input type="submit" id="proceed" name="_eventId_proceed" value="Proceed" />
<script type="text/javascript">
Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', event:'onclick'}));
Spring.addDecoration(new Spring.AjaxEventDecoration({elementId:'proceed', event:'onclick',formId:'booking', params:{fragments:'messages'}}));
</script>
也可以通过使用 Dojo 的查询 API 在单个语句中对多个元素应用装饰。 以下示例将一组复选框元素装饰为 Dojo 复选框小部件:
<div id="amenities">
<form:checkbox path="amenities" value="OCEAN_VIEW" label="Ocean View" /></li>
<form:checkbox path="amenities" value="LATE_CHECKOUT" label="Late Checkout" /></li>
<form:checkbox path="amenities" value="MINIBAR" label="Minibar" /></li>
<script type="text/javascript">
dojo.query("#amenities input[type='checkbox']").forEach(function(element) {
Spring.addDecoration(new Spring.ElementDecoration({
elementId: element.id,
widgetType : "dijit.form.CheckBox",
widgetAttrs : { checked : element.checked }
}));
});
</script>
</div>
20.4. 处理 Ajax 请求
Spring Javascript的客户端Ajax响应处理基于从服务器接收“片段”的概念。 这些片段是标准的HTML,旨在替换现有页面的部分内容。 服务器端所需的关键部分是一种确定完整响应中哪些部分需要被提取以进行局部渲染的方法。
为了能够渲染完整响应的部分片段,必须使用一种支持组合的模板技术来构建完整响应,并且组合的成员部分可以被单独引用和渲染。 Spring Javascript 提供了一些简单的 Spring MVC 扩展,这些扩展利用 tiles 来实现这一功能。 理论上,同样的技术可以应用于任何支持组合的模板系统。
Spring Javascript的Ajax远程功能基于这样的概念:即处理Ajax请求的核心代码不应与标准浏览器请求有所不同。 因此,代码中不需要直接了解Ajax请求的特殊知识,并且可以对两种请求样式使用相同的处理器。
20.4.1. 提供特定于库的 AjaxHandler
将各种 Ajax 库与 Web Flow 的 Ajax 感知行为(例如不进行部分页面更新时的重定向)集成的关键接口是 org.springframework.js.AjaxHandler。 默认情况下,配置了 SpringJavascriptAjaxHandler。它可以检测通过 Spring JS 客户端 API 提交的 Ajax 请求,并在需要重定向时作出适当响应。 要集成其他 Ajax 库(无论是纯 JavaScript 库还是更高级的抽象库,例如支持 Ajax 的 JSF 组件库),您可以将自定义的 AjaxHandler 注入到 FlowHandlerAdapter 或 FlowController 中。
20.4.2. 使用 Spring MVC 控制器处理 Ajax 请求
为了使用Spring MVC控制器处理Ajax请求,您需要在Spring应用程序上下文中配置提供的Spring MVC扩展,以渲染部分响应(请注意,这些扩展需要使用tiles进行模板化),如下所示:
<bean id="tilesViewResolver" class="org.springframework.webflow.mvc.view.AjaxUrlBasedViewResolver">
<property name="viewClass" value="org.springframework.webflow.mvc.view.FlowAjaxTiles3View"/>
</bean>
这配置了AjaxUrlBasedViewResolver,它反过来解释Ajax请求并创建FlowAjaxTilesView对象来处理适当片段的渲染。 注意,FlowAjaxTilesView能够处理Web Flow和纯Spring MVC请求的渲染。 这些片段对应于tiles视图定义中的各个属性。 例如,考虑以下tiles视图定义:
<definition name="hotels/index" extends="standardLayout">
<put-attribute name="body" value="index.body" />
</definition>
<definition name="index.body" template="/WEB-INF/hotels/index.jsp">
<put-attribute name="hotelSearchForm" value="/WEB-INF/hotels/hotelSearchForm.jsp" />
<put-attribute name="bookingsTable" value="/WEB-INF/hotels/bookingsTable.jsp" />
</definition>
Ajax 请求可以指定 body、hotelSearchForm 或 bookingsTable 作为请求中的片段进行渲染。
20.4.3. 使用 Spring MVC 和 Spring Web Flow 处理 Ajax 请求
Spring Web Flow通过使用render元素直接在流定义语言中处理片段的可选渲染。 这种方法的优点是片段的选择完全与客户端代码解耦,因此不需要像目前纯Spring MVC控制器方法那样在请求中传递特殊参数。 例如,如果希望将上一个示例中的hotelSearchForm片段从tiles视图渲染到一个丰富的Javascript弹出窗口中,可以定义以下view-state:
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
<on-entry>
<render fragments="hotelSearchForm" />
</on-entry>
<transition on="search" to="reviewHotels">
<evaluate expression="searchCriteria.resetPage()"/>
</transition>
</view-state>