rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

简介:

把JavaScript脚本与服务器上Java代码相结合,从而获得在服务器端和客户端都能够自由使用的JavaScript脚本。另外,经过这一系列的被展现技术,无论是基于Ajax还是非Ajax的客户端,都将允许你维护一个单一的代码,因为大多数的服务器端的代码依然是用Java来编写的,同时你还会发现公开给JavaScript的Java EE(Java企业版)的功能特征。在这一系列的技术中,将学习一下内容

1.怎样在服务器端运行JavaScript脚本;

2.用Ajax远程调用JavaScript的功能;

3.与JSP一起使用Java Scripting API。

典型的Ajax应用,在客户端使用JavaScript脚本语言,并且和服务器端的编程语言不同,例如在服务器端使用Java语言。这样,对于某些程序开发者就必须实现两次,在Web浏览器端使用JavaScript语言,而在服务器端使用另外一种语言。通过使用把JavaScript和服务器端的Java代码相结合,获取Javax.script API对脚本语言的完全支持,这样就可以避免代码的双重发行。另外,JDK6(Java SE Development Kit 6)中已经包含了Mazilla’s Rhino JavaScript引擎,如果使用JDK6的话,就不需要重新安装了。

在这一系列的文章的开始,通过一个简单的脚本运行器,来讲解在Java EE应用程序中怎样执行JavaScript文件。这个脚本会访问在JSP页面中被叫做”implicit objects”的对象,例如application对象、session对象,request对象以及response对象。这个例子大部分都是由可重用的代码组成,这样在你的服务器端的应用程序中就可以很容易的使用这些代码作为起始代码。


使用Javax.script API


rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

这一节概要介绍Javax.script API。将学习一下内容:

1.怎样执行访问Java对象的脚本;

2.怎样用Java代码调用JavaScript的功能;

3.为编译后的脚本实现缓存机制。

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

执行脚本

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

javax.script API很简单,通过使用下列方法,创建一个ScriptEngineManager的实例就可以开始了,这个实例可以获得一个ScriptEngine对象(代码1)。

1.getEngineByName();

2.getEngineByExtension();

3.getEngineByMineType()。

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

代码1:获取一个ScriptEngine实例。

  1. import javax.script.*;
  2. ...
  3. ScriptEngineManager manager = new ScriptEngineManager();
  4. ScriptEngine engine = manager.getEngineByName("JavaScript");
  5. ...
  6. engine.eval(...);

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

还可以使用getEngineFactouies()方法获得一个有效的脚本引擎的列表。当前只有一个JavaScript引擎被绑定在JDK6中,但是ScriptEngineManager实现了一种发现机制,能够主动查找发现支持Java平台的JSR-223脚本规范的第三方引擎,但是必须把第三方脚本引擎的JAR文件放到Java的CLASSPATH路径中。

获得javax.script.ScriptEngine实例之后,就可以调用eval()方法来执行脚本。还可以把Java对象作为脚本变量,通过Bindings实例传递给eva()方法。代码2包含了ScriptDemo.java的列子中,把demoVar和strBuf两个变量传递给要执行的DemoScript.js脚本,然后取得变量中的被编辑的值。

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

代码2:ScriptDemo.js

  1. package jsee.demo;
  2. import javax.script.*;
  3. import java.io.*;
  4. public class ScriptDemo {
  5. public static void main(String args[]) throws Exception {
  6. // Get the JavaScript engine
  7. ScriptEngineManager manager = new ScriptEngineManager();
  8. ScriptEngine engine = manager.getEngineByName("JavaScript");
  9. // Set JavaScript variables
  10. Bindings vars = new SimpleBindings();
  11. vars.put("demoVar", "value set in ScriptDemo.java");
  12. vars.put("strBuf", new StringBuffer("string buffer"));
  13. // Run DemoScript.js
  14. Reader scriptReader = new InputStreamReader(
  15. ScriptDemo.class.getResourceAsStream("DemoScript.js"));
  16. try {
  17. engine.eval(scriptReader, vars);
  18. } finally {
  19. scriptReader.close();
  20. }
  21. // Get JavaScript variables
  22. Object demoVar = vars.get("demoVar");
  23. System.out.println("[Java] demoVar: " + demoVar);
  24. System.out.println("    Java object: " + demoVar.getClass().getName());
  25. System.out.println();
  26. Object strBuf = vars.get("strBuf");
  27. System.out.println("[Java] strBuf: " + strBuf);
  28. System.out.println("    Java object: " + strBuf.getClass().getName());
  29. System.out.println();
  30. Object newVar = vars.get("newVar");
  31. System.out.println("[Java] newVar: " + newVar);
  32. System.out.println("    Java object: " + newVar.getClass().getName());
  33. System.out.println();
  34. }
  35. }

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

DempScript.js文件中(代码3)包含了一个叫做printType()的函数,使用这个函数输出每个脚本变量的类型。上例中还调用了strBuf对象的append()方法,编辑了demoVar对象的值,并且设定了一个新的叫做newVar的脚本变量。

如果传递给PrintType()方法的对象有getClass()方法,那么就一定是可以用obj.getClass().name方法取得类名的Java对象,下面的JavaScript表达式调用了对象的java.lang.Class实例的getName()方法;如果对象没有getClass()方法,printType()方法就调用所有的JavaScript对象都有的toSource()方法。

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml"> 代码 3 : DemoScript.js

  1. println("Start script /r/n");
  2. // Output the type of an object
  3. function printType(obj) {
  4. if (obj.getClass)
  5. println("    Java object: " + obj.getClass().name);
  6. else
  7. println("    JS object: " + obj.toSource());
  8. println("");
  9. }
  10. // Print variable
  11. println("[JS] demoVar: " + demoVar);
  12. printType(demoVar);
  13. // Call method of Java object
  14. strBuf.append(" used in DemoScript.js");
  15. println("[JS] strBuf: " + strBuf);
  16. printType(strBuf);
  17. // Modify variable
  18. demoVar = "value set in DemoScript.js";
  19. println("[JS] demoVar: " + demoVar);
  20. printType(demoVar);
  21. // Set a new variable
  22. var newVar = { x: 1, y: { u: 2, v: 3 } }
  23. println("[JS] newVar: " + newVar);
  24. printType(newVar);
  25. println("End script /r/n");

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

下面列出了ScriptDemo.java例子的输出结果,首先注意的是demoVar变量是作为JavaScript的String类型被报告的,而strBuf变量依然是java.lang.StringBuffer类型。对于原始变量和Java string 类型的变量类型是作为JavaScript的本地对象来报告的,而对于其他的任意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相反的类型转换,把JavaScript的原始类型和strings类型转换成Java对象,其他的JavaScript对象会使用引擎内部的特殊的API,把它们封装到Java对象中,例如:sun.org.mozilla.javascript.internal.NativeObject.

因为你可能想使用唯一的标准 APIs ,所以所有的 Java 代码和被执行的脚本之间的数据交换都应该通过原始类型变量、 strings 类型变量以及在 JavaScript 代码中能够非常容易的访问到属性和方法的 Java 对象(如 java beans )。简单的说,在 Java 代码中不要试图访问 JavaScript 对象,而是使用 Java 对象来替代 JavaScript 代码。

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

调用功能函数

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

在前面的例子中,我们已经看到在JavaScript中调用Java的方法是可能的。现在我们开始学习在Java代码中怎样调用JavaScript的功能函数。首先必须执行包含我们想要调用的函数的脚本,然后把ScriptEngine实例对象转化成javax.script.Invocable类型,这类型中提供了invokeFunction()和invokeMethod()方法。如果脚本中实现了所有的Java接口中的方法,那么还可以使用getInterface()方法来获得用脚本语言中编写的Java对象的方法。

InvDemo.java例子(代码5)中执行了一个叫做InvScript.js的脚本,脚本中包含了demoFunction()方法的实现。把ScriptEngine实例转换成Invocable类型的对象后,把函数的名字和参数传递给引擎的invokeFunction()方法,返回值是被demoFunction()方法返回的。

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

代码5:

  1. package jsee.demo;
  2. import javax.script.*;
  3. import java.io.*;
  4. public class InvDemo {
  5. public static void main(String args[]) throws Exception {
  6. // Get the JavaScript engine
  7. ScriptEngineManager manager = new ScriptEngineManager();
  8. ScriptEngine engine = manager.getEngineByName("JavaScript");
  9. // Run InvScript.js
  10. Reader scriptReader = new InputStreamReader(
  11. InvDemo.class.getResourceAsStream("InvScript.js"));
  12. try {
  13. engine.eval(scriptReader);
  14. } finally {
  15. scriptReader.close();
  16. }
  17. // Invoke a JavaScript function
  18. if (engine instanceof Invocable) {
  19. Invocable invEngine = (Invocable) engine;
  20. Object result = invEngine.invokeFunction("demoFunction", 1, 2.3);
  21. System.out.println("[Java] result: " + result);
  22. System.out.println("    Java object: "
  23. + result.getClass().getName());
  24. System.out.println();
  25. } else
  26. System.out.println("NOT Invocable");
  27. }
  28. }

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

InvScript.js文件(代码6)中包含了demoFunction()函数和在前面例子的脚本文件中相同的printType()函数。

代码6:

  1. println("Start script /r/n");
  2. function printType(obj) {
  3. if (obj.getClass)
  4. println("    Java object: " + obj.getClass().name);
  5. else
  6. println("    JS object: " + obj.toSource());
  7. println("");
  8. }
  9. function demoFunction(a, b) {
  10. println("[JS] a: " + a);
  11. printType(a);
  12. println("[JS] b: " + b);
  13. printType(b);
  14. var c = a + b;
  15. println("[JS] c: " + c);
  16. printType(c);
  17. return c;
  18. }
  19. println("End script /r/n");

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

如果看一下InvDemo.java的输出结果,就会发现,数字参数被转换成了JavaScript对象,demoFunction()方法的返回值是作为Java对象来获得的。这些转换仅限于原始数据类型和strings类型,任何其他的被传递的对象的类型,在JVM和JavaScript引擎之间都不会被改变,反之亦然。

输出结果:

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 是一个可选接口,有些脚本引擎可能没有实现。 JDK6 的 JavaScript 引擎支持这个接口。


rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

编译脚本

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

脚本每次被执行时都需要从新解释,这样会浪费CPU资源。如果相同脚本要多次运行,就可以使用另一个叫做javax.script.Compilable的可选接口来编译脚本,这样就可以明显的降低解释脚本的次数。这个可选接口在JDK6的脚本引擎中被支持。

CachedScript类(代码8)接收一个脚本文件,并且只有在脚本源代码被再次编辑时才从新编译。getCompiledScript()方法调用了脚本引擎的compile()方法,这方法返回了执行脚本的javax.script.CompiledScript对象的eval()方法。

代码8:

  1. package jsee.cache;
  2. import javax.script.*;
  3. import java.io.*;
  4. import java.util.*;
  5. public class CachedScript {
  6. private Compilable scriptEngine;
  7. private File scriptFile;
  8. private CompiledScript compiledScript;
  9. private Date compiledDate;
  10. public CachedScript(Compilable scriptEngine, File scriptFile) {
  11. this.scriptEngine = scriptEngine;
  12. this.scriptFile = scriptFile;
  13. }
  14. public CompiledScript getCompiledScript()
  15. throws ScriptException, IOException {
  16. Date scriptDate = new Date(scriptFile.lastModified());
  17. if (compiledDate == null || scriptDate.after(compiledDate)) {
  18. Reader reader = new FileReader(scriptFile);
  19. try {
  20. compiledScript = scriptEngine.compile(reader);
  21. compiledDate = scriptDate;
  22. } finally {
  23. reader.close();
  24. }
  25. }
  26. return compiledScript;
  27. }
  28. }

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyc%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C03%5Cclip_filelist.xml">

ScriptCache类中(代码9)还使用Java.util.LinkedHashMap对象为被编译的脚本实现了一个脚本仓库。这个map的初始容量被设定为缓存脚本的最大数,并且装载系数是1,这两个参数保证了cacheMap不必重新进行哈希计算。

默认条件下,LinkedHashMap类为其内部实体采用插入顺,因此比需把LinkedHashMap()构造器的第三个参数设定为true,这样map中的实体对象就可以用访问顺来代替默认顺。

到达缓存的最大容量以后,removeEldestEntry()方法开始返回true,使得每次有新的被编译的脚本添加到缓存中时,一个脚本实体能够自动的从cacheMap中删除。

通过LinkedHashMap的自动删除机制和访问顺相结合,在有新的脚本被添加时,ScriptCache确保最近使用的脚本从整个缓存中被删除。

代码9:

  1. package jsee.cache;
  2. import javax.script.*;
  3. import java.io.*;
  4. import java.util.*;
  5. public abstract class ScriptCache {
  6. public static final String ENGINE_NAME = "JavaScript";
  7. private Compilable scriptEngine;
  8. private LinkedHashMap<String, CachedScript> cacheMap;
  9. public ScriptCache(final int maxCachedScripts) {
  10. ScriptEngineManager manager = new ScriptEngineManager();
  11. scriptEngine = (Compilable) manager.getEngineByName(ENGINE_NAME);
  12. cacheMap = new LinkedHashMap<String, CachedScript>(
  13. maxCachedScripts, 1, true) {
  14. protected boolean removeEldestEntry(Map.Entry eldest) {
  15. return size() > maxCachedScripts;
  16. }
  17. };
  18. }
  19. public abstract File getScriptFile(String key);
  20. public synchronized CompiledScript getScript(String key)
  21. throws ScriptException, IOException {
  22. CachedScript script = cacheMap.get(key);
  23. if (script == null) {
  24. script = new CachedScript(scriptEngine, getScriptFile(key));
  25. cacheMap.put(key, script);
  26. }
  27. return script.getCompiledScript();
  28. }
  29. public ScriptEngine getEngine() {
  30. return (ScriptEngine) scriptEngine;
  31. }
  32. }

待续.....

在服务器端运行JavaScript文件(一)相关推荐

  1. 在服务器端运行JavaScript文件(二)

    在前回的译文中介绍了在Java中怎样调用和执行JavaScript脚本,以及怎样实现JavaScript脚本的缓存机制,在接下的译文中,我们是用将使用前面提到的ScriptCache类,实现它的abs ...

  2. js shell 运行javascript文件

    第一,打开https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/下载适合你电脑的js shell版本: 第二,将 ...

  3. sublime无法运行html文件,sublime如何运行javascript?

    sublime可以通过node.js运行Js文件.安装node.js后,在sublime中点击Tools-Build System-new Build System,写入配置代码保存,然后选择js文件 ...

  4. 高性能javascript 文件加载阻塞

    高性能javascript javascript脚本执行过程中会中断页面加载,直到脚本执行完毕,此操作阻塞了页面加载,造成性能问题.   脚本位置和加载顺序: 如果将脚本放在head内,那么再脚本执行 ...

  5. php运行js代码,如何在PHP中运行JavaScript代码?(代码示例)

    JavaScript是客户端脚本语言,PHP是用于与数据库交互的服务器端脚本语言.那么如何如何在PHP中运行JavaScript?本篇文章就来给大家介绍几种在PHP中运行JavaScript的方法,希 ...

  6. 我可以直接从GitHub运行HTML文件,而不仅仅是查看它们的来源吗?

    本文翻译自:Can I run HTML files directly from GitHub, instead of just viewing their source? If I have a . ...

  7. 如何在另一个JavaScript文件中包含一个JavaScript文件?

    JavaScript中是否有类似于CSS中@import的内容,可让您在另一个JavaScript文件中包含一个JavaScript文件? #1楼 而不是在运行时添加,而是使用脚本在上传之前进行串联. ...

  8. 在WinForm中通过HTTP协议向服务器端上传文件(转)

    相信用ASP.NET写一个上传文件的网页,大家都会写,但是有没有人想过通过在WinForm中通过HTTP协议上传文件呢? 有些人说要向服务器端上传文件,用FTP协议不是很简单吗?效率又高,为什么还要使 ...

  9. 在JavaScript文件中读取properties文件的方法

    假设有JavaScript文件叫做:readproperties.js,这个文件需要读取config.properties这个配置文件,步骤如下: 1.  下载插件jquery.i18n.proper ...

最新文章

  1. IP 管理,几多欣喜几多忧
  2. 如何获取微信openId
  3. LtRecyclerView:自带上拉下拉,能增加头条目和尾条目的RecyclerView
  4. 输入URL到浏览器显示页面的过程,搜集各方面资料总结一下
  5. 音视频技术开发周刊 55期
  6. 为什么鞋带总是松开?罪魁祸首其实是…
  7. 关系网络实战|设备关联信息定位团伙欺诈
  8. asp.net网站后台退出后,点后退按钮仍能进,如何安全退出
  9. Windows读取NXP MiFare Ultralight C类型NFC卡片的信息
  10. php排版word文档试卷,出试卷用word怎么排版
  11. lwj_C#_周总结1
  12. 计算机信息中心管理制度,信息技术中心内部管理制度(试行)
  13. 我说CMMI2.0之:详细剖析(PQA)过程质量保证
  14. CloudPhone真的快要来了
  15. 北京python培训班价格
  16. Win32API大全
  17. -XX:+UseParallelGC与 -XX:+UseParNewGC 区别
  18. linux挂nas盘步骤,linux下需要将nas盘挂
  19. PaddleSpeech:windows下用python快速安装和使用
  20. NOIP 前的垂死挣扎

热门文章

  1. 计算机组成原理实验报告6.2,计算机组成原理实验报告.doc
  2. 小白编译AnyQ-dockerlinux[CentOs]——AnyQ系列之一
  3. 地图可视化不只是pyecharts.map
  4. 哈尔滨工程大学考研经验分享(中):初试
  5. 使用虹软SDK实现离线人脸注册,人脸登录(H5-JS前端,java后台)
  6. 转发至新浪微博、开心网、腾讯微博、人人网代码分享!
  7. <Rasa实战> 内容摘要(四)
  8. Lit:介绍、项目搭建
  9. pyspark.sql.functions.lit(col)
  10. CES Asian 2018 见闻