`
FengShen_Xia
  • 浏览: 279345 次
  • 性别: Icon_minigender_1
  • 来自: 东方水城
社区版块
存档分类
最新评论

JavaScript EE:在服务器端运行JavaScript文件

阅读更多

       本系列文章围绕的主旨是了解如何在 Ajax 和 Java EE 应用程序内使用 javax.script API。如何将 JavaScript 与服务器上的 Java™ 代码结合起来,从而能够在服务器和客户机上使用相同的 JavaScript 例程。此外,本系列所展示的这些技术将让您能为 Ajax 客户机和非 Ajax 客户机维护同一个代码库。由于服务器端的大部分代码依然用 Java 语言编写,所以有必要对 JavaScript 公开这些 Java Platform, Enterprise Edition (Java EE) 特性。在本系列中,您将了解如何在服务器端运行 JavaScript 文件、如何用 Ajax 调用远程 JavaScript 函数以及如何借助 JavaServer Pages (JSP) 技术使用这个 Java Scripting API。

 

      典型的 Ajax 应用程序在客户端一般都使用 JavaScript,而在服务器端常常使用另外一种语言,比如 Java。因此,开发人员必须将其中一些例程实现两次,一次用于在 Web 浏览器使用 JavaScript,另一次用于在服务器使用另外一种语言。这种双重编码问题实际上可以通过将 JavaScript 和服务器端的 Java 代码结合起来加以避免,而对脚本语言的完整支持可以通过 javax.script API 获得。此外,Java SE Development Kit (JDK) 6 已经包含了 Mozilla 的 Rhino JavaScript 引擎,这意味着您将无需进行任何设置。

 

      在本系列的第一篇文章中,将使用一个简单的脚本运行程序来在一个 Jave EE 应用程序内执行 JavaScript 文件。这些脚本将能访问被用在 JSP 页面内的所谓的 “隐式对象”,比如 applicationsessionrequestresponse。本文中的大多数示例均包含可重用代码,这样一来,您可以在自己的应用程序中轻松地将 JavaScript 应用于服务器上。

 

使用 javax.script API

      本节给出了 javax.script API 的概览,展示了如何执行脚本来访问 Java 对象、如何从 Java 代码调用 JavaScript 函数,以及如何为所编译的脚本实现缓存机制。

 

执行脚本

   javax.script API 十分简单。可以先创建一个 ScriptEngineManager 实例,有了这个实例就能用下列方法中的任一个来获得 ScriptEngine 对象(参见清单 1):

  • getEngineByName()
  • getEngineByExtension()
  • getEngineByMimeType()

 清单 1. 获得一个 ScriptEngine 实例

import javax.script.*;
...
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
...
engine.eval(...);

 

      此外,还可以通过 getEngineFactories() 获得可用脚本引擎的列表。目前,只有 JavaScript 引擎是与 JDK 6 捆绑的,不过 ScriptEngineManager 实现了一种发现机制,能发现支持 JSR-223 Scripting for the Java Platform 的第三方引擎(参见 参考资料)。

只需将脚本引擎的 JAR 文件放入 CLASSPATH 即可。

 

      获得了 javax.script.ScriptEngine 实例后,就可以调用 eval() 来执行脚本了。也可以将 Java 对象作为脚本变量导出,其间要将 Bindings 实例传递给 eval() 方法。

清单 2 所示的 ScriptDemo.java 示例导出两个名为 demoVarstrBuf 的变量、执行 DemoScript.js 脚本,然后让这些变量输出它们修改后的值。

 

清单 2. ScriptDemo.java 示例

package jsee.demo;

import javax.script.*;
import java.io.*;

public class ScriptDemo {

    public static void main(String args[]) throws Exception {
        // Get the JavaScript engine
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");

        // Set JavaScript variables
        Bindings vars = new SimpleBindings();
        vars.put("demoVar", "value set in ScriptDemo.java");
        vars.put("strBuf", new StringBuffer("string buffer"));
        
        // Run DemoScript.js
        Reader scriptReader = new InputStreamReader(
            ScriptDemo.class.getResourceAsStream("DemoScript.js"));
        try {
            engine.eval(scriptReader, vars);
        } finally {
            scriptReader.close();
        }
        
        // Get JavaScript variables
        Object demoVar = vars.get("demoVar");
        System.out.println("[Java] demoVar: " + demoVar);
        System.out.println("    Java object: " + demoVar.getClass().getName());
        System.out.println();
        Object strBuf = vars.get("strBuf");
        System.out.println("[Java] strBuf: " + strBuf);
        System.out.println("    Java object: " + strBuf.getClass().getName());
        System.out.println();
        Object newVar = vars.get("newVar");
        System.out.println("[Java] newVar: " + newVar);
        System.out.println("    Java object: " + newVar.getClass().getName());
        System.out.println();
    }
    
}

 

      DemoScript.js 文件(如清单 3 所示)包含一个 printType() 函数,该函数可用来输出每个脚本变量的类型。这个示例会调用 strBuf 对象的 append() 方法、修改 demoVar 的值并设置一个名为 newVar 的新变量脚本。

 

     如果传递给 printType() 的对象具有 getClass() 方法,那么它一定是个 Java 对象,该对象的类名由obj.getClass().name 获得。这个 JavaScript 表达式调用此对象的 java.lang.Class 实例的 getName() 方法。如果此对象不具备 getClass,那么 printType() 就会调用 toSource() 方法,而该方法是所有 JavaScript 对象都有的。

 

清单 3. DemoScript.js 示例

println("Start script \r\n");

// Output the type of an object
function printType(obj) {
    if (obj.getClass)
        println("    Java object: " + obj.getClass().name);
    else
        println("    JS object: " + obj.toSource());
    println("");
}

// Print variable
println("[JS] demoVar: " + demoVar);
printType(demoVar);

// Call method of Java object
strBuf.append(" used in DemoScript.js");
println("[JS] strBuf: " + strBuf);
printType(strBuf);

// Modify variable
demoVar = "value set in DemoScript.js";
println("[JS] demoVar: " + demoVar);
printType(demoVar);

// Set a new variable
var newVar = { x: 1, y: { u: 2, v: 3 } }
println("[JS] newVar: " + newVar);
printType(newVar);

println("End script \r\n");

 

      清单 4 是 ScriptDemo.java 示例的输出。值得注意的是 demoVar 作为 JavaScript String 导出,而 strBuf 的类型仍然是 java.lang.StringBuffer。原始变量和 Java 字符串均作为本地 JavaScript 对象导出。任何其他的 Java 对象(包括数组)均原样导出。

 

清单 4. ScriptDemo.java 的输出

Start script

[JS] demoVar: value set in ScriptDemo.java
    JS object: (new String("value set in ScriptDemo.java"))

[JS] strBuf: string buffer used in DemoScript.js
    Java object: java.lang.StringBuffer

[JS] demoVar: value set in DemoScript.js
    JS object: (new String("value set in DemoScript.js"))

[JS] newVar: [object Object]
    JS object: ({x:1, y:{u:2, v:3}})

End script

[Java] demoVar: value set in DemoScript.js
    Java object: java.lang.String

[Java] strBuf: string buffer used in DemoScript.js
    Java object: java.lang.StringBuffer

[Java] newVar: [object Object]
    Java object: sun.org.mozilla.javascript.internal.NativeObject

 

      运行该脚本后,此引擎就会接受所有变量(包括新变量)并执行反转变换,将 JavaScript 原始变量和字符串转变成 Java 对象。其他的 JavaScript 对象则被包装成 Java 对象,这些对象能使用某种特定于引擎的内部 API,比如 sun.org.mozilla.javascript.internal.NativeObject

 

      有时,可能会只想使用那些标准的 API,因此 Java 代码和所执行脚本间的全部数据转换都应通过原始变量、字符串和 Java 对象(比如 bean)完成,这是因为在 JavaScript 代码内可以很容易地访问到它们的属性和方法。简言之,就是不要试图在 Java 代码内访问本地 JavaScript 对象,相反,应该在 JavaScript 代码内使用 Java 对象。

 

调用函数

      在之前的例子中,您已经看到了从 JavaScript 调用 Java 方法是可行的。现在您将会了解如何从 Java 代码调用 JavaScript 函数。首先,必须先执行包含想要调用的那个函数的脚本。然后,再将 ScriptEngine 实例强制转换为 javax.script.Invocable,后者还提供了 invokeFunction()invokeMethod()

如果脚本实现了 Java 接口的全部方法,那么也可以使用 getInterface() 获得一个 Java 对象,该对象的方法用此脚本语言编码。

 

      InvDemo.java 示例(如清单 5 所示)执行一个名为 InvScript.js 的脚本,它包含 demoFunction() 例程。

      在进行强制类型转换以将 ScriptEngine 实例转换为 Invocable 之后,这个 Java 示例才能将函数名和参数传递给引擎的 invokeFunction() 方法,而此方法会返回由 demoFunction() 返回的值。

 

清单 5. InvDemo.java 示例

package jsee.demo;

import javax.script.*;
import java.io.*;

public class InvDemo {

    public static void main(String args[]) throws Exception {
        // Get the JavaScript engine
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        
        // Run InvScript.js
        Reader scriptReader = new InputStreamReader(
            InvDemo.class.getResourceAsStream("InvScript.js"));
        try {
            engine.eval(scriptReader);
        } finally {
            scriptReader.close();
        }
        
        // Invoke a JavaScript function
        if (engine instanceof Invocable) {
            Invocable invEngine = (Invocable) engine;
            Object result = invEngine.invokeFunction("demoFunction", 1, 2.3);
            System.out.println("[Java] result: " + result);
            System.out.println("    Java object: "
                    + result.getClass().getName());
            System.out.println();
        } else
            System.out.println("NOT Invocable");
    }
    
}

 InvScript.js 文件(如清单 6 所示)包含 demoFunction() 例程和之前的脚本示例所用的相同 printType() 函数。

 

清单 6. InvScript.js 示例

println("Start script \r\n");

function printType(obj) {
    if (obj.getClass)
        println("    Java object: " + obj.getClass().name);
    else
        println("    JS object: " + obj.toSource());
    println("");
}

function demoFunction(a, b) {
    println("[JS] a: " + a);
    printType(a);
    println("[JS] b: " + b);
    printType(b);
    var c = a + b;
    println("[JS] c: " + c);
    printType(c);
    return c;
}

println("End script \r\n");

      InvDemo.java 的输出如清单 7 所示,注意到其中的数值参数均被转换成了 JavaScript 对象,并且由 demoFunction() 返回的值是作为 Java 对象获得的。这种转换只会针对原始变量和字符串进行。任何其他的对象在 JVM 和 Javascript 引擎之间都是原样传递的,反之亦然。

 

清单 7. InvDemo.java 的输出

Start script

End script

[JS] a: 1
    JS object: (new Number(1))

[JS] b: 2.3
    JS object: (new Number(2.3))

[JS] c: 3.3
    JS object: (new Number(3.3))

[Java] result: 3.3
    Java object: java.lang.Double

 

      请注意 javax.script.Invocable 是一个可选接口,有些脚本引擎可能不会实现该接口。不过,JDK 6 所带的JavaScript 引擎提供对该接口的支持。

 

编译脚本

      脚本在每次执行时都进行解析会浪费 CPU 资源。在多次执行相同的脚本时,若能编译脚本,就可以显著减少执行时间,而脚本编译所需要的方法可由另外一个可选接口 javax.script.Compilable 提供,JDK 6 所带的 JavaScript 引擎亦支持该接口。

 

    CachedScript 类(参见清单 8)接受一个脚本文件并只有当源代码有修改时才会进行重编译。getCompiledScript() 方法会调用此脚本引擎的 compile(),进而返回 javax.script.CompiledScript 对象,该对象的 eval() 方法会执行脚本。


清单 8. CachedScript 类

package jsee.cache;

import javax.script.*;
import java.io.*;
import java.util.*;

public class CachedScript {
    private Compilable scriptEngine;
    private File scriptFile;
    private CompiledScript compiledScript;
    private Date compiledDate;

    public CachedScript(Compilable scriptEngine, File scriptFile) {
        this.scriptEngine = scriptEngine;
        this.scriptFile = scriptFile;
    }
    
    public CompiledScript getCompiledScript()
            throws ScriptException, IOException {
        Date scriptDate = new Date(scriptFile.lastModified());
        if (compiledDate == null || scriptDate.after(compiledDate)) {
            Reader reader = new FileReader(scriptFile);
            try {
                compiledScript = scriptEngine.compile(reader);
                compiledDate = scriptDate;
            } finally {
                reader.close();
            }
        }
        return compiledScript;
    }

}

 

ScriptCache 类(参见清单 9)使用 java.util.LinkedHashMap 对象为所编译的脚本实现存储库。map 的初始容量被设为所缓存脚本的最大数量并且加载系数是 1。这两个参数就确保了该 cacheMap 不需要重新处理。

 

默认地,LinkedHashMap 类会使用条目的插入顺序。若不想使用默认顺序,LinkedHashMap() 构造函数的第三个参数必须是 true 以便使用条目的访问顺序。

 

达到缓存的最大容量后,removeEldestEntry() 方法就会开始返回 true,以便当每次向此缓存添加一个新的编译了的脚本时,一个条目都会自动从 cacheMap 删除。

 

通过联合使用 LinkedHashMap 的自动删除机制和访问顺序,ScriptCache 就确保了当添加了新脚本时,最近最少使用的(Least Recently Used,LRU)的脚本将能够从缓存中删除。


清单 9. ScriptCache 类

package jsee.cache;

import javax.script.*;

import java.io.*;
import java.util.*;

public abstract class ScriptCache {
    public static final String ENGINE_NAME = "JavaScript";
    private Compilable scriptEngine;
    private LinkedHashMap cacheMap;

    public ScriptCache(final int maxCachedScripts) {
        ScriptEngineManager manager = new ScriptEngineManager();
        scriptEngine = (Compilable) manager.getEngineByName(ENGINE_NAME);
        cacheMap = new LinkedHashMap(
                maxCachedScripts, 1, true) {
            protected boolean removeEldestEntry(Map.Entry eldest) {
                return size() > maxCachedScripts;
            }
        };
    }

    public abstract File getScriptFile(String key);

    public synchronized CompiledScript getScript(String key)
            throws ScriptException, IOException {
        CachedScript script = cacheMap.get(key);
        if (script == null) {
            script = new CachedScript(scriptEngine, getScriptFile(key));
            cacheMap.put(key, script);
        }
        return script.getCompiledScript();
    }
    
    public ScriptEngine getEngine() {
        return (ScriptEngine) scriptEngine;
    }
    
}

 

下一节将使用 ScriptCache 类、实现抽象的 getScriptFile() 方法并使用 getScript() 从缓存检索所编译的脚本。

 

构建一个脚本运行程序

在本节中,您将了解如何创建一个简单的 Java servlet 来实现 URL-脚本的映射以便能够从 Web 浏览器调用服务器端脚本。此外,servlet 还将会把几个 Java EE 对象公开为可在 JavaScript 代码内使用的变量。您还将了解如何使用脚本上下文来用单一一个 JavaScript 引擎运行多个并发的脚本。

 

初始化 servlet

servlet 类的名称是 JSServlet。其 init() 方法(参见清单 10)会获得几个配置参数并创建一个 ScriptCache 对象。servlet 的脚本缓存使用 getRealPath() 获得与给定 URI 相映射的脚本文件的路径。


清单 10. JSServlet 的 init() 方法

package jsee.servlet;

import javax.script.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import jsee.cache.*;

public class JSServlet extends HttpServlet {
    private String cacheControlHeader;
    private String contentTypeHeader;
    private ScriptCache scriptCache;
    
    public void init() throws ServletException {
        ServletConfig config = getServletConfig();
        cacheControlHeader = config.getInitParameter("Cache-Control");
        contentTypeHeader = config.getInitParameter("Content-Type");
        int maxCachedScripts = Integer.parseInt(
                config.getInitParameter("Max-Cached-Scripts"));
        scriptCache = new ScriptCache(maxCachedScripts) {
            public File getScriptFile(String uri) {
                return new File(getServletContext().getRealPath(uri));
            }
        };
    }
    ...
}

 

清单 11 中包含一些 servlet 的参数,这些参数在 web.xml 文件内指定。Cache-Control 头与脚本缓存毫无关系。两个头都是由 servlet 返回的此 HTTP 响应的一部分。no-cache 值告知浏览器不要缓存此 servlet 的响应,该响应应被作为 text/plain 对待。


清单 11. web.xml 文件

    
        JSServlet
        jsee.servlet.JSServlet
        
            Cache-Control
            no-cache
        
        
            Content-Type
            text/plain
        
        
            Max-Cached-Scripts
            1000
        
        1
    

    
        JSServlet
        *.jss
    

 

      从清单 11 可以看出,*.jss 模式被映射给此 servlet。这意味着 JSServlet 将会处理 URL 以 .jss 扩展名结束的所有请求。当用户在 Web 浏览器内输入这样的一个 URL 或是单击了一个 .jss 链接时,浏览器就会发送此 HTTP 请求给 Web 服务器(例如, Apache),而此服务器应该被配置成将该请求分派给 servlet 容器(比如,Tomcat)。如果 servlet 容器也充当 Web 服务器,就无需额外的配置。

 

      当 servlet 容器获得了 URL 以 .jss 结束的这个请求时,就会调用 service() 方法,该方法是由 JSServlet 继承自 javax.servlet.http.HttpServlet 的。此方法再进而调用 doGet()doPost(),最终调用哪一个取决于此请求的 HTTP 方法。

两个方法都可由 JSServlet 覆盖,这一点在本节稍后的部分还会看到。

 

 

使用脚本上下文

      每个脚本引擎实例都具有一个默认的上下文,在其中,可以用 put() 方法存储变量,而所执行的脚本的输出则被默认定向到 System.out。在服务器环境内,常希望运行具有各自上下文的并发脚本。

javax.script API 能满足这个需求,它能提供 ScriptContext 接口和 SimpleScriptContext 实现。

 

      Mozilla 的 Rhino JavaScript 引擎是个多线程引擎(参见侧栏),允许执行共享相同上下文的并发线程。不过,在本例中,我们想要隔离这些引擎范围以及运行在不同线程内的那些脚本的输出,这意味着必须要针对每个 HTTP 请求创建一个新的 ScriptContext 实例。

 

      清单 12 给出了 JSServlet 类的 createScriptContext() 方法。此方法设置了上下文的 writer 属性以便在脚本执行时将脚本的输出发送给 response 对象的编写者。这意味着传递给脚本内的 print()println() 的任何东西都将会包含在此 servlet 的响应内。

 

      此外,createScriptContext() 还通过脚本上下文的 setAttribute() 方法定义了如下的脚本变量:

 

表 1. 由 JSServlet 执行的脚本内的可用变量

 

脚本变量 描述
config servlet 的 javax.servlet.ServletConfig 实例
application Web 应用程序的 javax.servlet.ServletContext 实例
session javax.servlet.http.HttpSession 对象
request javax.servlet.http.HttpServletRequest 对象
response javax.servlet.http.HttpServletResponse 对象
out 用于输出响应的 java.io.PrintWriter 对象
factory 脚本引擎的 javax.script.ScriptEngineFactory

 

 

    factory 变量可用来获得有关 JavaScript 引擎的信息,比如语言版本或引擎版本。其余的变量的作用与它们在 JSP 页面上的无异。

 

清单 12. JSServlet 的 createScriptContext() 方法

public class JSServlet extends HttpServlet {
    ...
    protected ScriptContext createScriptContext(
            HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        ScriptContext scriptContext = new SimpleScriptContext();
        scriptContext.setWriter(response.getWriter());
        int scope = ScriptContext.ENGINE_SCOPE;
        scriptContext.setAttribute("config", getServletConfig(), scope);
        scriptContext.setAttribute("application", getServletContext(), scope);
        scriptContext.setAttribute("session", request.getSession(), scope);
        scriptContext.setAttribute("request", request, scope);
        scriptContext.setAttribute("response", response, scope);
        scriptContext.setAttribute("out", response.getWriter(), scope);
        scriptContext.setAttribute("factory",
                scriptCache.getEngine().getFactory(), scope);
        return scriptContext;
    }
    ...
}

 

runScript() 方法(参见清单 13)从缓存获得一个编译后的脚本并调用 eval() 方法,将给定的脚本上下文作为参数传递。


清单 13. JSServlet 的 runScript() 方法

public class JSServlet extends HttpServlet {
    ...
    protected void runScript(String uri, ScriptContext scriptContext)
            throws ScriptException, IOException {
        scriptCache.getScript(uri).eval(scriptContext);
    }
    ...
}

 

处理请求

可以通过调用上述的 runScript() 方法来执行 与此 HTTP 请求的 URL 相映射的那个脚本。不过,在实际的应用程序中,可能需要在运行脚本之前进行初始化并在脚本执行完后进行最后的清理工作。

 

在同一个上下文中,可以连续运行多个脚本。例如,一个脚本可以定义一组变量和函数,而另一个脚本则可以使用之前在相同上下文内执行的脚本的变量和函数进行一些处理。

 

servlet 的 handleRequest() 方法(如清单 14 所示)可设置这些 HTTP 报头、运行 init.jss 脚本、从请求的 URI 中删除此上下文路径、执行具有所获得的 URI 的脚本,然后运行另一个名为 finalize.jss 的脚本。


清单 14. JSServlet 的 handleRequest() 方法

public class JSServlet extends HttpServlet {
    ...
    protected void handleRequest(
            HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        if (cacheControlHeader != null)
            response.setHeader("Cache-Control", cacheControlHeader);
        if (contentTypeHeader != null)
            response.setContentType(contentTypeHeader);
        ScriptContext scriptContext = createScriptContext(request, response);
        try {
            runScript("/init.jss", scriptContext);
            String uri = request.getRequestURI();
            uri = uri.substring(request.getContextPath().length());
            try {
                runScript(uri, scriptContext);
            } catch (FileNotFoundException x) {
                response.sendError(404, request.getRequestURI());
            }
            runScript("/finalize.jss", scriptContext);
        } catch (ScriptException x) {
            x.printStackTrace(response.getWriter());
            throw new ServletException(x);
        }
    }
    ...
}

 

JSServletdoGet()doPost() 方法(参见清单 15)用来调用 handleRequest()


清单 15. JSServletdo 的 Get() 和 doPost() 方法

public class JSServlet extends HttpServlet {
    ...
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        handleRequest(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        handleRequest(request, response);
    }

}

 

开发服务器端脚本

本节包含了服务器端脚本的几个例子,向您展示了如何获得请求参数、访问 JavaBeans 的属性并生成 JSON 响应。

 

预处理和后处理

在前一节中,曾提及在执行所请求的脚本之前,JSServlet 会调用 init.jss(如清单 16 所示)。若想估量执行脚本所需时间,可以将开始时间存储到一个变量内,如下所示。


清单 16. init.jss 脚本

var debug = true;
var debugStartTime = java.lang.System.nanoTime();

 

之后,可以在 finalize.jss 内(参见清单 17)计算执行时间。该时间作为 JavaScript 注释打印以便 JSServlet 能够生成有效的 JSON 响应。


清单 17. finalize.jss 脚本

var debugEndTime = java.lang.System.nanoTime();
if (debug)
    println("// Time: " + (debugEndTime - debugStartTime) + " ns");

 

本系列后面的文章将向 init.jss 和 finalize.jss 添加更多的代码。

 

获得请求参数

借助 JSServlet 调用的脚本可以通过 request.getParameter()request.getParameterValues() 访问请求参数,这二者会返回 Java 对象。若想使语法更简短或处理 JavaScript 对象而非 Java 字符串和数组,也不难,只需将下面这些代码行加入到 init.jss(参见清单 18)。


清单 18. 在 init.jss 内获得请求参数。

var param = new Object();
var paramValues = new Object();

function initParams() {
    var paramNames = request.getParameterNames();
    while (paramNames.hasMoreElements()) {
        var name = paramNames.nextElement();
        param[name] = String(request.getParameter(name));
        paramValues[name] = new Array();
        var values = request.getParameterValues(name);
        for (var i = 0; i < values.length; i++)
            paramValues[name][i] = String(values[i]);
    }
}

initParams();

 

假设您使用清单 19 所示的 URL 请求一个名为 ParamDemo.jss 的脚本。


清单 19. 请求一个脚本的 URL 示例

http://localhost:8080/jsee/ParamDemo.jss?firstName=John&lastName=Smith

 

在 ParamDemo.jss(参见清单 20)内,用 param.firstNameparam.lastName 可以得到这两个请求参数。


清单 20. ParamDemo.jss 示例

println("Hello " + param.firstName + " " + param.lastName);

 

访问 JavaBean

Web 应用程序的 applicationsessionrequest 范围可存储 bean 实例,可以分别使用 ServletContextHttpSessionHttpServletRequestgetAttribute()setAttribute() 来获得或替代这些实例。

也可以使用 getBean()setBean() 函数(如清单 21 所示),但这两个函数必须位于 init.jss 文件内以便任何脚本均可调用它们。scope 参数应是如下字符串中的一个:

  • "application"
  • "session"
  • "request"

清单 21. init.jss 的 getBean() 和 setBean() 函数

function getBean(scope, id) {
    return eval(scope).getAttribute(id);
}

function setBean(scope, id, bean) {
    if (!bean)
        bean = eval(id);
    return eval(scope).setAttribute(id, bean);
}

现在,假设您想要在 session 范围内保存 DemoBean(参见清单 22)的一个实例。


清单 22. DemoBean.java 示例

package jsee.demo;

public class DemoBean {
    private int counter;

    public int getCounter() {
        return counter;
    }

    public void setCounter(int counter) {
        this.counter = counter;
    }

}

 

BeanDemo.jss 脚本(如清单 23 所示)用 importPackage(Packages.jsee.demo) 导入了包含此 JavaBean 的那个包。之后,脚本试图用 getBean()session 范围获得这个 bean 实例。

如果这个 bean 没有找到,那么 BeanDemo.jss 就会创建一个对象并利用 setBean() 将其放入 session 范围。

最终,此脚本会进行增量处理并输出这个 bean 的 counter 属性。


清单 23. BeanDemo.jss 示例

importPackage(Packages.jsee.demo);
var bean = getBean("session", "demo");
if (!bean) {
    bean = new DemoBean();
    setBean("session", "demo", bean);
}
bean.counter++;
println(bean.counter);

 

如果所要导入的包是以 javajavaxorgeducomnet 开头的,那么无须在 importPackage() 使用 Packages 前缀。此外,还可以使用 importClass() 导入单个类。

 

返回 JSON 响应

清单 24 所示的示例会生成一个 JSON 响应,该响应会包含有关 JavaScript 引擎和此脚本 URI 的某些信息。此示例使用 JavaScript 语法来创建 json 对象,其源代码则用 toSource() 方法以 String 形式获得。


清单 24. JsonDemo.jss 示例

var json = {
    engine: { 
        name: String(factory.engineName),
        version: String(factory.engineVersion),
        threading: String(factory.getParameter("THREADING"))
    }, 
    language: { 
        name: String(factory.languageName),
        version: String(factory.languageVersion)
    },
    script: {
        uri: String(request.requestURI)
    }
};

println(json.toSource());

 

在本例中,从 factoryrequest 的属性中检索到的所有 Java 对象都必须转变为 JavaScript 对象,以便 toSource() 能够正常工作。清单 25 包含了此脚本的输出:


清单 25. JsonDemo.jss 的输出

({language:{name:"ECMAScript", version:"1.6"}, 
engine:{name:"Mozilla Rhino", threading:"MULTITHREADED", 
version:"1.6 release 2"}, script:{uri:"/jsee/JsonDemo.jss"}})

 

 

结束语

在本文中,您了解了如何使用 javax.script API 编译和执行 JavaScript 文件。您还了解了如何基于java.util.LinkedHashMap 实现 LRU 缓存、如何将 Java 对象作为脚本变量导出、如何实现 URL-脚本映射以及如何构建在服务器端执行 的 JavaScript 文件。请您继续关注本系列的下一篇文章,在该文章中,您将了解如何用 Ajax 调用远程 JavaScript 函数。

 

 

 

 

 

原载:http://www.ibm.com/developerworks/cn/web/wa-aj-javaee/?S_TACT=105AGX52&S_CMP=tec-csdn

 

作者:

Andrei Cioroianu 是 Devsphere 公司的创始人,该公司专门提供 Java EE 开发和 Web 2.0/Ajax 顾问服务。他自 1997 年就开始使用 Java 和 Web 技术,具有 10 多年解决复杂技术问题和管理商业产品的整个生命周期以及定制应用程序和开源框架的专业经验。您可以通过 www.devsphere.com 上的联系列表与 Andrei 联系。

 

本文的示例应用程序:

分享到:
评论
2 楼 FengShen_Xia 2009-01-16  
<p>我昨天看到这篇文章时候,提到:</p>
<p> </p>
<p><span style="color: #0000ff;">开发人员必须将其中一些例程实现两次,一次用于在 Web 浏览器使用 JavaScript,另一次用于在服务器使用另外一种语言。这种双重编码问题实际上可以通过将 JavaScript 和服务器端的 Java 代码结合起来加以避免。</span> </p>
<p> </p>
<p>感觉很不错,所以贴出来跟大家分享一下</p>
1 楼 ninecat 2009-01-15  
收藏了,回家看~

相关推荐

    Pure JS (1): 在 jetty 上运行 JavaScript

    标题中的“Pure JS (1): 在 jetty 上运行 JavaScript”表明了本文主要关注的是如何在Jetty...学习这个主题将有助于提升对服务器端JavaScript开发的理解,特别是对于那些希望在Java平台上实现JavaScript功能的开发者。

    服务器端返回json数据

    1. **数据准备**:在服务器端,如Java EE应用中,开发者会使用Java对象来存储业务数据。这些对象可能包括自定义的实体类或者Map结构。然后,通过JSON库(如Gson、Jackson或org.json)将Java对象转换成JSON字符串。 ...

    Java和JavaScript.doc

    Java以其面向对象的特性、强大的功能和跨平台能力在服务器端和企业级应用中占据主导地位,而JavaScript则以其轻量级、灵活和易于学习的特性在前端开发中不可或缺。了解它们的差异有助于选择合适的工具来解决问题。

    jsp+css+javascript

    在JSP中,开发者可以插入Java表达式、脚本片段和声明来处理服务器端逻辑。JSP文件被Web服务器解释为Servlet,编译成Java类,然后执行。JSP生命周期包括加载、初始化、服务和销毁四个阶段。 **Cascading Style ...

    todosapp:探索Java中的服务器端无阻塞Web应用程序开发

    托多萨普todosapp是一个管理todosapp...http://localhost:8080每个子项目中实际上打包了三个“应用程序”: 在服务器端呈现的Web应用程序: RESTful Web服务,提供待办事项的CRUD操作: 单页应用程序:在浏览器端呈现的

    struts2实现服务器端校验示例

    在这个"struts2实现服务器端校验示例"中,我们将探讨如何在Struts2中实现高效的服务器端数据校验,并结合Dojo实现类似AJAX的实时验证。 首先,Struts2的校验框架是基于Action类的,每个Action类都可以关联一个或多...

    Server文件

    4. **有序图片文件夹**:在服务器端,有序图片文件夹可能指的是存储有特定顺序或分类的图像资源,这在诸如产品展示、用户上传内容或教学资源的网站中很常见。服务器需要能高效地管理和检索这些图片,比如通过数据库...

    JAVA(jsp+servlet+javascript)超市商品管理系统

    这个系统集成了多种核心技术,包括动态网页技术JSP(JavaServer Pages)、服务器端编程技术Servlet以及前端交互脚本JavaScript。下面将详细解释这些技术及其在系统中的应用。 1. **JSP(JavaServer Pages)**: ...

    使用Web页面实现英文词典(Javascript 调用Java方法)

    首先,我们需要理解的是JavaScript是客户端的脚本语言,它主要用于处理用户界面交互,而Java则通常在服务器端运行,提供更强大的计算和数据处理能力。 标题中的“使用Web页面实现英文词典”意味着我们将构建一个...

    Ajax-Data-Handling.pdf

    - **构建服务器端的XML数据**:在服务器端,可以使用JAXB(Java Architecture for XML Binding)或其他类似的库来构建XML数据。这些库提供了将Java对象转换为XML文档的方法,以及从XML文档中反序列化Java对象的能力...

    JSTL EL JAVASCRIPT实例

    通过将JavaScript与JSTL和EL结合,可以在服务器端和客户端之间实现数据的交互和处理,提升用户体验。 在提供的文件列表中,我们可以看到: 1. **JavaScript.CHM** - 这可能是一个帮助文件,包含了JavaScript语言的...

    JAVA(JSP+servlet+javascript)课程设计:教务管理系统

    - Servlet是Java EE规范中的一部分,是服务器端的Java应用程序,用于扩展服务器的功能,处理HTTP请求。 - 在JSP中,Servlet通常作为后台处理逻辑,处理由JSP页面提交的请求,并将结果返回给JSP进行展示。 - ...

    Ajax演示代码, 注释详细, 简明易懂

    3. **Servlet** 或 **JSP**:在服务器端,Servlet或JSP通常用于处理Ajax请求,处理业务逻辑,生成响应数据。在本示例中,Tomcat 6.0是一个流行的Java应用服务器,它将运行这些服务器端组件。 4. **Tomcat**:是一个...

    ssh+javaScript

    - `文件上传.txt`:在Java EE中,文件上传通常通过Servlet API或者Struts2的File Upload拦截器来处理。开发者需要处理文件的临时存储、大小限制、类型检查等问题,确保上传过程的安全性和稳定性。 4. **日期控件的...

    tort_ee:带有cms和crm机会的面包店

    7. 服务器端脚本:可能使用Node.js或其他JavaScript运行时环境,处理后端逻辑。 8. 测试文件:确保代码的正确性和性能。 综上所述,“tort_ee”是一个结合了内容管理和客户关系管理功能的面包店解决方案,利用...

    JAVA(JSP+servlet+javascript)课程设计:教务管理系统Java源码

    在教务管理系统中,Java主要用于编写服务器端逻辑,处理来自客户端的请求,执行数据操作和业务逻辑。 2. **JSP(JavaServer Pages)**: - JSP是Java EE的一部分,它允许开发人员将Java代码嵌入到HTML页面中,实现...

    ejb&&javascript-pdf

    EJB提供了一种组件模型,使得开发者可以创建服务器端的业务逻辑组件,这些组件可以在容器的管理下运行,提供事务处理、安全性、状态管理和并发控制等服务。EJB主要包含三种类型的组件:会话bean(Session Beans)...

    java实现拖拽式文件上传

    6. **Java.IO流**:在服务器端,Java.IO库提供了处理输入流和输出流的方法,用于读取上传的文件数据并将其保存到服务器的文件系统。 7. **临时文件存储**:为了处理大文件上传,通常会先将文件保存到临时目录,然后...

    jsp.war.rar_JSP.w_javascript_jsp war_jsp.war_jsp登陆

    2. **JavaScript验证**:在用户提交表单之前,JavaScript可能会检查输入是否为空,或者密码是否符合特定的复杂性要求。 3. **JSP后端处理**:当表单提交后,JSP页面接收请求,处理数据,并可能与数据库进行交互,...

Global site tag (gtag.js) - Google Analytics