可用等式为:html+java=jsp表示jsp[8]._JavaScript EE,第 3 部分: 结合使用 Java Scripting API 和 JSP...

能够在服务器和客户端上运行同一段 JavaScript 代码有明显的优势;它使您能够对 Ajax 和非 Ajax 客户端使用同一个代码库,并且还能提供更多的灵活性。例如,如果您开发了一些不想让其他人查看的 JavaScript 代码,那么可以在服务器上运行它,这样可以保护您的知识产权并最小化安全风险。如果以后您不再注重代码保护,则可以将 JavaScript 代码移动到客户端,以提高应用程序性能。

页面上下文和脚本上下文

Java Scripting API 和 JavaServer Pages 是两个相互独立并且可以轻松集成的 Java 技术。它们都可以在定义良好的上下文中执行代码。使用 JSP 技术,您可以访问一组 JSP 隐式对象:pageContext、page、request、response、out、session、config 和 application。在 第 1 部分 中,您已经了解了如何将这些对象导入 servlet 可以执行的 JavaScript 文件。在本文中,您将了解如何对 JSP 页面可以执行的代码应用这些操作。

JavaScript 引擎使用不同类型的上下文维护应用程序代码中定义的脚本变量和函数。如果您运行设置变量或包含函数的脚本,以及在同一个上下文中执行的后续脚本,那么可以使用前一个脚本的变量和函数。因此,在处理 HTTP 请求时,应该使用单个脚本上下文,如下一节中所示。

使用 JavaScript 语言编写的脚本可以访问公共字段并调用任何 Java 对象。此外,您可以使用 object.property 语法(而不是使用 get 和 set 方法)获取和修改 JavaBean 属性的值。因为在 JavaScript 代码中使用 Java 对象很容易,唯一缺失的是一组在 JSP 页面上下文和 JavaScript 上下文之间交换对象的定制标记。从本文中可以看到,只需几行代码就可以实现它们。

在 Web 页面中使用服务器端 JavaScript 代码

本节展示如何在整个 Ajax/HTTP 请求中管理 JavaScript 上下文,以及如何在 JSP 页面上下文和 JavaScript 上下文之间交换变量。

在 JavaScript 中使用 JSP 对象

本系列的第 1 部分展示了基于 Java Scripting API 的 Java servlet,Java Scripting API 可以用来在服务器上执行 JavaScript 文件。本节描述一个名为 JSUtil 的类,该类在执行 JSP 页面时使用相同的 Java Scripting API 运行 JavaScript 代码片段。首先,您需要创建一个 ScriptEngineManager 对象,然后获取 ScriptEngine 实例,如清单 1 中所示。

清单 1. JSUtil 的 getScriptEngine() 方法

package jsee.util;

import javax.script.*;

import javax.servlet.*;

import javax.servlet.http.*;

import javax.servlet.jsp.*;

import java.io.*;

public class JSUtil {

private static ScriptEngine engine;

public static synchronized ScriptEngine getScriptEngine() {

if (engine == null) {

ScriptEngineManager manager = new ScriptEngineManager();

engine = manager.getEngineByName("JavaScript");

}

return engine;

}

...

}

清单 2 包含 createScriptContext() 方法,该方法初始化 ScriptContext 实例,从页面上下文获取 JSP 隐式对象并作为脚本上下文的变量设置这些对象。该操作允许通过在脚本上下文中执行的 JavaScript 代码访问隐式对象:

pageContext 对象是用于页面范围变量的存储库,它包含获取所有其他隐式对象的方法。

page 对象是处理当前请求的 servlet 类实例。

request 对象允许您获取 HTTP 请求参数和请求头。

response 对象允许您设置 HTTP 响应头并提供一个写入程序,该程序使用 JSP 代码中的 out 标识。

out 对象用于 JSP 页面的输出。

session 对象维护请求之间与用户相关的状态。

config 对象表示使用 JSP 的 servlet 的配置。

application 对象用于存储所有用户共享的 bean 实例,并获取 Web.xml 文件中指定的初始化参数。

清单 2. JSUtil 的 createScriptContext() 方法

public class JSUtil {

...

public static ScriptContext createScriptContext(PageContext pageContext) {

ScriptContext scriptContext = new SimpleScriptContext();

int scope = ScriptContext.ENGINE_SCOPE;

scriptContext.setAttribute("pageContext", pageContext, scope);

scriptContext.setAttribute("page", pageContext.getPage(), scope);

scriptContext.setAttribute("request", pageContext.getRequest(), scope);

scriptContext.setAttribute("response", pageContext.getResponse(), scope);

scriptContext.setAttribute("out", pageContext.getOut(), scope);

scriptContext.setAttribute("session", pageContext.getSession(), scope);

scriptContext.setAttribute("config", pageContext.getServletConfig(), scope);

scriptContext.setAttribute("application",

pageContext.getServletContext(), scope);

scriptContext.setWriter(pageContext.getOut());

return scriptContext;

}

...

}

在处理 HTTP 请求的过程中,您需要使用一个脚本上下文,以便 JavaScript 片段能够使用上次执行的脚本中定义的变量和函数。满足该请求的简单方式是将脚本上下文存储为 request 对象的属性。getScriptContext() 方法(见清单 3)使用 jsee.ScriptContext 作为属性名称。

清单 3. JSUtil 的 getScriptContext() 方法

public class JSUtil {

...

public static ScriptContext getScriptContext(PageContext pageContext)

throws IOException {

ServletRequest request = pageContext.getRequest();

synchronized (request) {

ScriptContext scriptContext

= (ScriptContext) request.getAttribute("jsee.ScriptContext");

if (scriptContext == null) {

scriptContext = createScriptContext(pageContext);

request.setAttribute("jsee.ScriptContext", scriptContext);

}

return scriptContext;

}

}

...

}

runScript() 方法(见清单 4)在当前 HTTP 请求的上下文中执行给定的 JavaScript 片段,它使用脚本引擎中的 eval() 方法。如果抛出脚本异常,runScript() 将输出源代码,其后是堆栈跟踪,然后抛出 ServletException 终止执行 JSP 页面。

清单 4. JSUtil 的 runScript() 方法

public class JSUtil {

...

public static void runScript(String source, PageContext pageContext)

throws ServletException, IOException {

try {

getScriptEngine().eval(source, getScriptContext(pageContext));

} catch (ScriptException x) {

((HttpServletResponse) pageContext.getResponse()).setStatus(500);

PrintWriter out = new PrintWriter(pageContext.getOut());

out.println("

" + source + "
");

out.println("

");

x.printStackTrace(out);

out.println("

");

out.flush();

throw new ServletException(x);

}

}

}

使用定制标记执行 JavaScript 代码片段

script.tag 文件(见清单 5)允许您在服务器或客户端(具体取决于 runat 属性的值)执行 JavaScript 代码片段。如果 runat 是 client 或 both,那么标记文件将输出一个 HTML

清单 5. script.tag 文件

<%@ attribute name="runat" required="true" rtexprvalue="true" %>

<%@ tag body-content="scriptless" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

jsee.util.JSUtil.runScript(

(String) jspContext.getAttribute("source"),

(PageContext) jspContext);

%>

理解

在服务器端处理 JSP 页面时,println(link(request.requestURL)) 调用向当前页面输出一个链接。request 对象可以通过服务器上的 JavaScript 代码访问,因为上下文初始化由 JSUtil 类执行,而该类的 runScript() 方法可以通过标记文件调用。当 JavaScript 引擎计算 request.requestURL 表达式时,它实际上调用 HttpServletRequest 实例的 getRequestURL() 方法。

在客户端,document.writeln(link(location)) 调用将向 ScriptDemo.jsp 页面输出另一个链接,该页面的 URL 可从 location 属性中获取,该属性仅在 Web 浏览器中可用。

清单 6. ScriptDemo.jsp 示例

function link(url) {

return '' + url + '';

}

println(link(request.requestURL));

document.writeln(link(location));

清单 7 显示了 ScriptDemo.jsp 页面生成的 HTML 输出,该页面包含 link() 函数、服务器端生成的 元素,以及在客户端上生成第二个链接的 JavaScript 代码。

清单 7. ScriptDemo.jsp 生成的输出

function link(url) {

return '' + url + '';

}

http://localhost:8080/jsee/ScriptDemo.jsp

document.writeln(link(location));

获取并设置变量

在 Web 页面中使用服务器端的 JavaScript 代码时,您需要在 JSP 上下文和脚本上下文之间交换变量。get.tag 文件(见清单 8)实现名为 的定制标记,该标记导出一个脚本变量作为 JSP 变量,该变量的名称通过 var 属性提供。

变量名称可以通过 JSP 上下文获取,因为它作为 JSP 属性传递到标记文件。通过脚本上下文的 getAttribute() 方法检索脚本变量的值。然后,通过 jspContext.setAttribute() 在标记文件的页面范围内创建一个名为 varAlias 的 JSP 变量。在标记文件中使用的 指令指示 JSP 容器(例如,Tomcat)向调用标记文件的页面输出 varAlias 变量,并使用 var 属性指定的变量名称。该技术允许调用的标记文件能使用 标记定义 page 范围内中的 JSP 变量。

清单 8. get.tag 文件

variable-class="java.lang.Object" %>

String var = (String) jspContext.getAttribute("var");

Object value = jsee.util.JSUtil.getScriptContext(

(PageContext) jspContext).getAttribute(var);

jspContext.setAttribute("varAlias", value);

%>

标记使用 set.tag 文件(见清单 9)实现,该文件带有两个变量,用于使用脚本上下文的 setAttribute() 方法设置 JavaScript 变量。如果没有提供可选的 value 属性,那么标记文件将使用 执行 JSP 页面中 和 之间的代码。

清单 9. set.tag 文件

type="java.lang.Object"%>

String var = (String) jspContext.getAttribute("var");

Object value = jspContext.getAttribute("value");

jsee.util.JSUtil.getScriptContext((PageContext) jspContext)

.setAttribute(var, value, javax.script.ScriptContext.ENGINE_SCOPE);

%>

清单 10 包含的 VarDemo.jsp 样例使用 标记初始化两个脚本变量,借助

清单 10. VarDemo.jsp 样例

2

x++;

y--;

构建可在浏览器中运行的 Ajax UI

本节展示一个基于 Ajax 和 DHTML 的动态表单。如果在 Web 浏览器中禁用了 JavaScript,只要在服务器端运行脚本化代码,DynamicForm.jsp 页面生成的用户界面仍然可以正常运行。可以使用名为 BackendScript.jss 的服务器端 JavaScript 文件处理提交的表单数据。

为了代码易于理解,这里使用一个很简单的表单。本例的主要目的是演示几种可以在实际的复杂应用程序中使用的 Web 技术。

开发动态表单

图 1 中的屏幕截屏展示了样例表单,该表单包含许多输入字段和三个按钮,按钮标签分别为 Add、Remove 和 Send。Add 按钮将新的输入字段附加到表单,Remove 按钮删除上一个字段,Send 向服务器提交表单数据。

图 1. 简单的动态表单

40053975_1.jpg

当在服务器上执行 DynamicForm.jsp 时,名为 outputForm() 的 JavaScript 函数生成初始输入字段。如清单 11 所示,这些字段都放入

元素,元素内容可以使用 updateForm() 函数进行修改,当用户单击 Add 或 Remove 时,将在 Web 浏览器中调用该函数。用户单击 Send 时,用户输入将使用 submitForm() 函数(该函数使用 XMLHttpRequest)提交到服务器。

如果禁用 JavaScript,那么用户按钮按钮时将不调用任何函数,在这种情况下,Web 浏览器将数据发送到服务器。启用 JavaScript 时不会发生这种情况,因为 updateForm() 和 submitForm() 函数就返回 false。

清单 11. DynamicForm.jsp 表单

Dynamic Form

...

Dynamic Form

outputForm();

οnclick="return updateForm('add')">

οnclick="return updateForm('remove')">

οnclick="return submitForm()">

DynamicForm.jsp 页面包含几个可以在 Web 服务器或 Web 浏览器中运行的函数。htmlEncode() 和 buildInput() 例程(见清单 12)用于生成一个 元素。

清单 12. DynamicForm.jsp 的 htmlEncode() 和 buildInput() 函数

...

function htmlEncode(value) {

return value ? value.replace(/&/g, "&").replace(/"/g, """)

.replace(//g, ">") : "";

}

function buildInput(name, value) {

var content = '

if (name)

content += ' name="' + htmlEncode(name) + '"';

if (value)

content += ' value="' + htmlEncode(value) + '"';

content += ' size="30">';

return content;

}

...

buildForm() 函数(见清单 13)带有两个参数:一列值和一个命令。当给定命令是 remove 时,将忽略上一个值。如果 cmd 参数为 add,则在返回的 HTML 字符串的尾部添加一个空字段。

清单 13. DynamicForm.jsp 的 buildForm() 函数

...

function buildForm(values, cmd) {

var n = values.length;

if (cmd == 'remove' && n > 1)

n--;

var content = '';

for (var i = 0; i < n; i++)

content += buildInput("inputField", String(values[i])) + '
';

if (cmd == 'add')

content += buildInput("inputField") + '
';

return content;

}

实现服务器端代码

DynamicForm.jsp 页面还包含两个只能在 Web 服务器执行的 JavaScript 函数。清单 14 展示了 getInputValues() 例程,如果 HTTP 方法为 POST,则该例程返回 inputField 参数的值。

清单 14. DynamicForm.jsp 的 getInputValues() 函数

function getInputValues() {

if (request.getMethod().toUpperCase().equals("POST"))

return request.getParameterValues("inputField");

else

return null;

}

...

另一个服务器端函数是 outputForm()(见清单 15)。如上文所述,如果用户单击按钮时禁用了 JavaScript,那么 Web 浏览器则将表单数据提交到服务器。在这种情况下,outputForm() 检测单击的是哪个按钮并设置 cmd 变量。

如果命令是 send,则 DynamicForm.jsp 页面包含名为 BackendScript.jss 的脚本的输出,该脚本由 JSServlet 类执行,我们已经在本系列的第一部分介绍过该类。该脚本返回一列使用 JSON 格式编码的值。

BackendScript.jss 的输出将成为服务器端执行的 JavaScript 代码片段的一部分,因为 放在 之间。

最后,outputForm() 函数生成一列带有 buildForm() 的 HTML 输入字段,并打印生成的字符串。

清单 15. DynamicForm.jsp 的 outputForm() 函数

...

function outputForm() {

var cmd = "";

if (request.getParameter("add") != null)

cmd = "add";

else if (request.getParameter("remove") != null)

cmd = "remove";

else if (request.getParameter("send") != null)

cmd = "send";

var values = null;

if (cmd == "send") {

values = ;

} else {

values = getInputValues();

if (!values)

values = ["", "", ""]; // default input values

}

println(buildForm(values, cmd));

}

清单 16 展示了 BackendScript.jss,该文件使用 paramValues.inputField 获取输入值并存储数组,然后使用 toSource() 将其转换为 JSON。正如本系列第 1 部分解释的那样,使用由 JSServlet 执行的 init.jss 脚本的请求参数值初始化 paramValues 变量。

清单 16. BackendScript.jss 文件

var values = paramValues.inputField;

if (values)

println(values.sort().toSource());

else

println("[]");

在客户端使用 JavaScript

DynamicForm.jsp 页面包含几个只在 Web 浏览器中运行的例程。getInputValues() 函数(见清单 17)使用 document.forms.dynamicForm.inputField 表达式获取表示输入字段的 DOM 对象数组。如果只有一个输入字段,那么该表达式可以返回一个 DOM 对象。因此,getInputValues() 验证 fields 是否是一个数组 —— 通过测试 length 属性的可用性来实现。输入字段的值作为字符串数组返回。

清单 17. DynamicForm.jsp 的 getInputValues() 函数

function getInputValues() {

var values = new Array();

var fields = document.forms.dynamicForm.inputField;

if (typeof fields.length == "number")

for (var i = 0; i < fields.length; i++)

values[i] = fields[i].value;

else if (fields)

values[0] = fields.value;

return values;

}

...

setInputValues() 函数(见清单 18)设置表单输入字段的值。调用该函数之后,用户将在 Web 浏览器中看到新值。

清单 18. DynamicForm.jsp 的 setInputValues() 函数

...

function setInputValues(values) {

var fields = document.forms.dynamicForm.inputField;

if (typeof fields.length == "number")

for (var i = 0; i < fields.length; i++)

fields[i].value = values[i];

else if (fields)

fields.value = values[0];

}

...

用户单击 Add 或 Remove 时,Web 浏览器将调用 updateForm() 函数(见清单 19),该函数使用这些按钮的 onclick 属性。使用 buildForm() 函数生成输入字段列表,该函数保留字段的当前值,新的 HTML 内容通过 inputDiv 元素的 innerHTML 属性插入到 Web 页面中。

清单 19. DynamicForm.jsp 的 updateForm() 函数

...

function updateForm(cmd) {

document.getElementById('inputDiv').innerHTML

= buildForm(getInputValues(), cmd);

return false;

}

...

如果启用 JavaScript,那么当用户单击 onclick 属性包含 return submitForm() 的 Send 按钮时,Web 浏览器将调用 submitForm() 函数(见清单 20)。该 JavaScript 函数使用本系列第二部分使用的小型 Ajax 库。您可以在 xhr.js 文件中找到这个库的源代码。

在 DynamicForm.jsp 页面,创建了一个 XHR 对象并存储在 xhr 变量中。XHR() 构造器带有三个参数:

HTTP 方法,本例中为 POST

HTTP 请求的 URL,为 BackendScript.jss

指示请求是否同步的布尔值参数。本例中是同步的,这意味着函数在返回控件之前等待 HTTP 响应。

清单 20. DynamicForm.jsp 的 submitForm() 函数

...

...

var xhr = new XHR("POST", "BackendScript.jss", false);

function submitForm() {

var request = xhr.newRequest();

if (!request)

return true;

var values = getInputValues();

for (var i = 0; i < values.length; i++)

xhr.addParam("inputField", values[i]);

function processResponse() {

if (xhr.isCompleted()) {

var newValues = eval(request.responseText);

setInputValues(newValues);

}

}

xhr.sendRequest(processResponse);

return false;

}

submitForm() 函数使用 xhr.newRequest() 创建新的 XMLHttpRequest 实例。如果无法创建该对象(即 Web 浏览器不支持 Ajax),那么 submitForm() 将返回 true,因此 Send 按钮的 onclick 表达式可以返回 true,这意味着 Web 浏览器将把表单数据提交到服务器。

如果成功创建了新的 XMLHttpRequest 对象,则下一步是获取输入字段的值,在 xhr.addParam() 的帮助下,该字段值将作为请求参数添加。

processResponse() 内部函数可以用作一个回调。如果 HTTP 请求完成且状态代码为 200,则计算文本响应,并且将获得的数组传递到 setInputValues() 函数,该函数将更新输入字段的值。

xhr.sendRequest() 调用将调用 XMLHttpRequest 实例的 send() 方法。然后,submitForm() 返回 false,因此 Send 按钮的 onclick 表达式返回 false,指示 Web 浏览器不要将表单数据提交到服务器。该行为是正确的,因为在启用 JavaScript 时使用 XMLHttpRequest 发送输入值,浏览器不应该重复提交相同的数据。

结束语

在本文中,您学习了如何运行嵌入到 JSP 页面的服务器端 JavaScript 代码,以及如何在 JSP 页面上下文和 JavaScript 上下文之间交换变量。您还学习了如何构建一个在 Web 浏览器禁用 JavaScrip 时仍然能够运行的 Ajax 用户界面。当应用程序必须支持除 Ajax Web 浏览器之外的非 Ajax 客户端设备时,同样可以使用本文介绍的技术。

下载

描述

名字

大小

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值