web服务器有哪些端script有哪些

酷勤网 C 程序员的那点事!
当前位置: >
浏览次数:次
对于那些需要web服务的初创公司里的大型新项目,我会选择何种后端编程语言呢?在客户端侧有CSS、html和javascript(包括CoffeeScript和TypeScript这类衍生语言)可供选择。而对于存储,我曾经用过mysql和redis,这也基本够用了。但是选择服务器端的语言却没那么容易。我曾经使用了好长时间的php,因为在我刚开始编程时候,它最简单,成本也最低。但是时过境迁,现在的我不用再担心昂贵的服务器托管费用了。尽管我对web项目的经验没那么多,但我还是想分享自己的一些看法。
定义:后端与安全性
事先声明一下:我只讨论后端。所谓的后端,指的是处理服务器请求的数据接入层。它需要肩负着处理很多请求(大于100请求每秒)和快速处理请求(平均处理时间小于300ms)的职责。而那些可以被提前计算,或者是不需要直接展示给客户端的繁重计算的工作,则并不适合用后端进行处理。这些工作可以通过其他的语言通过另外的方式实现。后端也并不直接处理对用户的展现,这是前端应该做的。但是后端开发者,应该对如何给前端提供数据有着清晰的认知。我所见过最清晰的方式就是在前后端之间使用纯Restful API进行交互。
后端语言也应该很容易校验前端发来的数据,方便的连接数据库,以及从文件系统保存或是获取文件。
在接下来的篇幅中,我会描述语言的安全性。但这并不意味着你不能用不安全的语言写出安全的代码,而是不同语言编译器能够保证你的代码不出现某些特定的bug。举例而言,C的编译器并不进行边界检查,因此它是一种非常不安全的语言。而那些可以被自动工具检查出来的错误包括:
内存溢出(C/C++并不检查,但在java中几乎不可能)
未被使用的变量
类型问题:这个问题略微有些模棱两可,因为在每种语言你都能写出强类型的代码。但是在一些语言中,类型更为常见。有些语言同样也使用类型系统来发现错误。举例而言,php从这个意义上来说十分不安全。连123==&123ab&都可以为true。 Python稍微安全一点,但你却可以随意的返回值。Java相比之下安全多了。Haskel从这个层面来说更加安全,因为它的强类型系统会拒绝执行任何无意义的表达式,同时也不会进行任何形式的强制转换。
在程序的运行时,也有一些错误会被发现。而在不同的语言中,对这种运行时错误的处理完全不同。举例来说,C和C++会默默的失败。(就像这个里说的) 这当然是很不好的。相比C/C++对越界错误的忽略,Rust则会发出明显的提示。(之前的heartbleed是一个典型的例子,可以看看)
当然了,所有这些问题都可以通过良好的测试来发现。但是,能够被自动化的过程越多,当你测试不那么充分的时候,可能发生的意外也就越少。
Java是跑在Java虚拟机的面向对象的语言。对于商业网站而言,java是使用最广泛的语言。为什么呢?
java比较成熟:1995年就已出现
很多大学都会教授Java,很多人也因此至少对它一知半解。所以公司寻找Java的开发者也并不困难。尽管那些号称了解Java的人可能都是泛泛之辈。
Java的生态系统非常成熟:
进行持续集成
/或进行自动化构建
,是自动化单元测试的利器
和进行打log
进行负载测试
提供RESTful的web服务
/: 应用服务器、web服务器、servlet容器
/: Web服务器
,进行代码质量把控与静态代码分析
进行ORMapping
像,,,,这类框架。
Java是由Oracle开发的。因此当遇到的问题的时候,可以向他们寻求技术支持。
上述是Java的优点,那么它的缺点呢:
非常笨拙的语法。为了完成一个功能,你可能要打很多字。。当然自动补全是必不可少的,但是要打的东西还是不少。代码越多,维护起来成本越高。
要把一个工具跑起来难度很高
那些被设计用来扩展的super-abstract结构,事实上可能永远不会被用到。
因为没有指针的存在,Java不能访问越界的数组,所以比C/C++安全一些。那些烦恼你的内存溢出也几乎不存在了。但是付出的代价就是,语法更加的复杂,而且你如果一不小心,就会掉入Java中得不安全的陷阱。
速度和内存使用量:Java在速度上并不突出,而在内存使用上则是相当的多。但是这并没给它的大范围使用带来太多的问题。感兴趣的可以看看这篇
对于Java还可以参考以下文章:
答案是否定的,但是Java可能不想听到这个结论:)
: 答案也是否定的,想想C/C++吧。
Javascript: Node.js
自从2009年发布一下,使用十分广泛。Node.js具有异步,事件驱动以及可扩展的特性。Node.js应用是由JavaScript开发的,因此它具有JavaScript的所有优点:
享受到了JavaScript V8引擎的好处
灵活而轻量级的语法
跟Java一样,JavaScript出现在1995年。所以语言本身发展历史很长,相对稳定。
很多开发者都或多或少的了解一些JavaScript
生态系统很成熟
和进行包管理
/进行MVP/MVC
进行单元测试
进行自动化任务管理
进行测试构建
:web应用框架
还有什么要补充的?
Node很快,而且可扩展。(参考)
JavaScript不太安全。即使很小的语法错误也只会在执行的时候暴露出来。因此单元测试十分重要。
Node.js被LinkedIn,Yahoo!, Uber, Paypal使用
有很多人从Node.js转向了Go
是由Google开发的,静态类型的编译语言。它直到2009年才出现,十分短暂。
Go提供了那些你进行web开发需要的基本工具
/: 一个web开发框架
进行模板渲染
有不错的和一些的材料
有一些任务在Go语言中超过预期的复杂,比如排序,详见
Go有一些跟其他语言完全不同的diffuser,比如如果你想声明一个public方法,那这个方法的第一个字母一定要是大写的。还有未被使用的变量会导致编译错误。
是由微软开发的具有静态类型的编译语言。但是它却有很多。 它出现在2000年。而针对web开发的框架直到2002年才发布。
C#的生态系统包括:
: web服务器
我对C#/ASP.NET的理解并不深入,因此无法更详尽的分析它的优略。
是现在在用的最古老的语言之一。它最早出现于1991年。Python是具有动态的类型,面向对象的解释性语言。而且它具有函数编程的特性。
尽管我曾经在很多项目中使用过Python,但并不包括web项目。因此那些重要的工具和框架可能会被遗漏:
生态系统:
和pip: 包托管和包管理
: 自动代码文档生成,比如就是通过Sphinx生成的。这是我所见过的最好的文档
: 基于协程的Python网络框架
: Web服务器
有些使用Python的开发者切换到了Go
Python有不错的教程和很赞的文档:
Flask和Django配合Pypy(), 性能会更高。
那些使用Python的成熟公司:
Prezi,Pinterest,Instagram()
Bitbucket, The Onion
我认为Python最主要的优势在于代码容易撰写,而且可读性很高。我对Python显然是有偏爱的,而且我坚信很多人和我有着同样的观点。
是动态类型的服务器端语言,它在1995年被推出。
是PHP很大的一个问题,可以看看这篇
生态系统是比较完善的:
和进行持续集成
有类似于和这类轻量级框架
进行单元测试
进行包管理,来寻找php包
PHP最大的优势是学习门槛低。它基本可以在任何地方被执行,托管和非常简单。Wikipedia使用的就是PHP,所以很明显使用PHP来创建可用的大型网站是可行的。
是由Facebook在2014年开发的编程语言。它是PHP的变体。它与PHP最主要的不同在于:
函数参数和返回值是指定类型的
Hack并不完全支持PHP的一些特性,比如goto、可变变量、字符串自增,这有时候也挺好的。
Rust是很安全的语言,但是对于它在生产环境的使用,仍有疑虑。
: 我知道它应用广泛,但是我对Ruby的了解并不充分。Ruby的语法与Python类似。
: 很不多不同的后端技术集合
: 不少教程
仔细想来,对我来说以下的语言更适合我做后端:
Go:很快,而且是编译型的
node.js: 很好的可扩展性
Python: 这是我最了解的语言。除此之外,它有非常清晰的语法,很好的社区开发的变法风格,以及易读性。
对我来说不适合的语言:
PHP: 因为语言的不一致,导致建立稳定的后端没那么简单。
C#: 这个语言太以微软为中心了
Java:语法有点拖沓,成本高。
其他语言可能也是很好的选择,只是我知之甚少。我对rust在后端使用的可行性非常好奇。Hack语言非常年轻,让我们对它未来的发展拭目以待。
& 相关主题:下次自动登录
现在的位置:
& 综合 & 正文
在服务器端运行JavaScript文件(一)
把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
这一节概要介绍Javax.script
API。将学习一下内容:
1.怎样执行访问Java对象的脚本;
2.怎样用Java代码调用JavaScript的功能;
3.为编译后的脚本实现缓存机制。
javax.script API很简单,通过使用下列方法,创建一个ScriptEngineManager的实例就可以开始了,这个实例可以获得一个ScriptEngine对象(代码1)。
1.getEngineByName();
2.getEngineByExtension();
3.getEngineByMineType()。
代码1:获取一个ScriptEngine实例。
import javax.script.*;
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.eval(...);
还可以使用getEngineFactouies()方法获得一个有效的脚本引擎的列表。当前只有一个JavaScript引擎被绑定在JDK6中,但是ScriptEngineManager实现了一种发现机制,能够主动查找发现支持Java平台的JSR-223脚本规范的第三方引擎,但是必须把第三方脚本引擎的JAR文件放到Java的CLASSPATH路径中。
获得javax.script.ScriptEngine实例之后,就可以调用eval()方法来执行脚本。还可以把Java对象作为脚本变量,通过Bindings实例传递给eva()方法。代码2包含了ScriptDemo.java的列子中,把demoVar和strBuf两个变量传递给要执行的DemoScript.js脚本,然后取得变量中的被编辑的值。
代码2:ScriptDemo.js
package jsee.
import javax.script.*;
import java.io.*;
public class ScriptDemo {
public static void main(String args[]) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
Bindings vars = new SimpleBindings();
vars.put("demoVar", "value set in ScriptDemo.java");
vars.put("strBuf", new StringBuffer("string buffer"));
Reader scriptReader = new InputStreamReader(
ScriptDemo.class.getResourceAsStream("DemoScript.js"));
engine.eval(scriptReader, vars);
} finally {
scriptReader.close();
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();
DempScript.js文件中(代码3)包含了一个叫做printType()的函数,使用这个函数输出每个脚本变量的类型。上例中还调用了strBuf对象的append()方法,编辑了demoVar对象的值,并且设定了一个新的叫做newVar的脚本变量。
如果传递给PrintType()方法的对象有getClass()方法,那么就一定是可以用obj.getClass().name方法取得类名的Java对象,下面的JavaScript表达式调用了对象的java.lang.Class实例的getName()方法;如果对象没有getClass()方法,printType()方法就调用所有的JavaScript对象都有的toSource()方法。
println("Start script /r/n");
function printType(obj) {
if (obj.getClass)
Java object: " + obj.getClass().name);
JS object: " + obj.toSource());
println("");
println("[JS] demoVar: " + demoVar);
printType(demoVar);
strBuf.append(" used in DemoScript.js");
println("[JS] strBuf: " + strBuf);
printType(strBuf);
demoVar = "value set in DemoScript.js";
println("[JS] demoVar: " + demoVar);
printType(demoVar);
var newVar = { x: 1, y: { u: 2, v: 3 } }
println("[JS] newVar: " + newVar);
printType(newVar);
println("End script /r/n");
下面列出了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]
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.
因为你可能想使用唯一的标准,所以所有的代码和被执行的脚本之间的数据交换都应该通过原始类型变量、类型变量以及在代码中能够非常容易的访问到属性和方法的对象(如)。简单的说,在代码中不要试图访问对象,而是使用对象来替代代码。
调用功能函数
在前面的例子中,我们已经看到在JavaScript中调用Java的方法是可能的。现在我们开始学习在Java代码中怎样调用JavaScript的功能函数。首先必须执行包含我们想要调用的函数的脚本,然后把ScriptEngine实例对象转化成javax.script.Invocable类型,这类型中提供了invokeFunction()和invokeMethod()方法。如果脚本中实现了所有的Java接口中的方法,那么还可以使用getInterface()方法来获得用脚本语言中编写的Java对象的方法。
InvDemo.java例子(代码5)中执行了一个叫做InvScript.js的脚本,脚本中包含了demoFunction()方法的实现。把ScriptEngine实例转换成Invocable类型的对象后,把函数的名字和参数传递给引擎的invokeFunction()方法,返回值是被demoFunction()方法返回的。
&&&&推荐文章:
【上篇】【下篇】【图文】javascript服务器端编程技术_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
javascript服务器端编程技术
上传于||文档简介
&&j​a​v​a​s​c​r​i​p​t​服​务​器​端​编​程​技​术
大小:131.00KB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢服务器端调用javascript函数的几种方法(参见新浪博客:希多希多) - 雨
落 - 博客园
首先就我个人而言,我最常用的第一种方法是:
&&&&&& Response.Write("&script&alert('');&/script&");
&&&&&&& Response.Write("&script& Type='javaScript'&alert();&/script&");
2 Response.Write("&script&alert('账户已注册,请重新注册')&/script&");
这种方法他是先服务端后客户端,当其执行完,我们查看html源文件会发现"&script& Type='javaScript'&alert();&/script&"出现在源文件的最前门。他总是在html界面渲染前执行。所以你会发现当执行它时的页面都是空白的,就因为html页面还没加载。
&& 这种方法的缺点是:它无法调用脚本文件中的自定义函数,只能调用内部函数。如果想要调用自定义的函数,则只能在response.write()上写自定义函数。比如Response.Write("&script& type='text/javascript'&function(){.........}&/script&");
2 ClientScriptRegisterStartupScript(ClientScript.GetType(),"myScript","&script&MyFun();&/script&")
5 ClientScriptRegisterStartupScript(ClientScript.GetType(),"myScript","&script&alert('Please
Drop it!');&/script&");
这种方法则比第一种简单多了。它可以直接调用脚本自定义函数,并且可以在任何地方使用,前面说了Response.Write()是先Server后Client,,而ClientScriptRegisterStartupScript()则是先Client后Server。执行结束后在html源文件会发现它在html的最后加载了其调用的javascript语句。同时你在执行时也发现总是界面先加载它在执行。即html界面先渲染然后再执行其中的语句。
OnClientClick(vs2003不支持,其实我也知道,因为我没用过03,反正是话说不能用,呵呵)
&asp:Button ID ="button1" Runat="Server" text ="button" OnClientClick ="client_click()"
OnClick="Button_Click"/&
  client_Click()就是javascript的一个方法,它是一个前台方法,可以替换成一般的脚本如: return confirm('确定要删除吗?');
&&&&&&&&&&&&&&& Button1.Attributes.Add("onclick", "return Client_Click()"); 通过添加属性的方法来调用。
ClientScript.RegisterStartupScript& 此方法比较灵活
StringBuilder str
= new StringBuilder();
str.Append("&script language='javaScript'&");
str.Append("Button1_Click('"+ServerPath+"')");
str.append("&/script&");
ClientScript.RegisterStartupScript(this.GetType(),"LoadScript&,str.ToString());
阅读(...) 评论()JavaScript EE,第 1 部分: 在服务器端运行 JavaScript 文件
JavaScript EE,第 1 部分: 在服务器端运行 JavaScript 文件
典型的 Ajax 应用程序在客户端一般都使用 JavaScript,而在服务器端常常使用另外一种语言,比如 Java。因此,开发人员必须将其中一些例程实现两次,一次用于在 Web 浏览器使用 JavaScript,另一次用于在服务器使用另外一种语言。这种双重编码问题实际上可以通过将 JavaScript 和服务器端的 Java 代码结合起来加以避免,而对脚本语言的完整支持可以通过 javax.script API 获得。此外,Java SE Development Kit (JDK) 6 已经包含了 Mozilla 的 Rhino JavaScript 引擎,这意味着您将无需进行任何设置。
在本系列的第一篇文章中,将使用一个简单的脚本运行程序来在一个 Jave EE 应用程序内执行 JavaScript 文件。这些脚本将能访问被用在 JSP 页面内的所谓的 “隐式对象”,比如 application、session、request 和 response。本文中的大多数示例均包含可重用代码,这样一来,您可以在自己的应用程序中轻松地将 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 示例导出两个名为 demoVar 和 strBuf 的变量、执行 DemoScript.js 脚本,然后让这些变量输出它们修改后的值。
清单 2. ScriptDemo.java 示例
package jsee.
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"));
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)
Java object: " + obj.getClass().name);
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.
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"));
engine.eval(scriptReader);
} finally {
scriptReader.close();
// Invoke a JavaScript function
if (engine instanceof Invocable) {
Invocable invEngine = (Invocable)
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();
System.out.println("NOT Invocable");
InvScript.js 文件(如清单 6 所示)包含 demoFunction() 例程和之前的脚本示例所用的相同 printType() 函数。
清单 6. InvScript.js 示例
println("Start script \r\n");
function printType(obj) {
if (obj.getClass)
Java object: " + obj.getClass().name);
JS object: " + obj.toSource());
println("");
function demoFunction(a, b) {
println("[JS] a: " + a);
printType(a);
println("[JS] b: " + b);
printType(b);
var c = a +
println("[JS] c: " + c);
printType(c);
println("End script \r\n");
InvDemo.java 的输出如清单 7 所示,注意到其中的数值参数均被转换成了 JavaScript 对象,并且由 demoFunction() 返回的值是作为 Java 对象获得的。这种转换只会针对原始变量和字符串进行。任何其他的对象在 JVM 和 Javascript 引擎之间都是原样传递的,反之亦然。
清单 7. InvDemo.java 的输出
Start script
End script
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 资源。在多次执行相同的脚本时,若能编译脚本,就可以显著减少执行时间,而脚本编译所需要的方法可由另外一个可选接口 pilable 提供,JDK 6 所带的 JavaScript 引擎亦支持该接口。
CachedScript 类(参见清单 8)接受一个脚本文件并只有当源代码有修改时才会进行重编译。getCompiledScript() 方法会调用此脚本引擎的 compile(),进而返回 piledScript 对象,该对象的 eval() 方法会执行脚本。
清单 8. CachedScript 类
package jsee.
import javax.script.*;
import java.io.*;
import java.util.*;
public class CachedScript {
private Compilable scriptE
private File scriptF
private CompiledScript compiledS
private Date compiledD
public CachedScript(Compilable scriptEngine, File scriptFile) {
this.scriptEngine = scriptE
this.scriptFile = scriptF
public CompiledScript getCompiledScript()
throws ScriptException, IOException {
Date scriptDate = new Date(scriptFile.lastModified());
if (compiledDate == null || scriptDate.after(compiledDate)) {
Reader reader = new FileReader(scriptFile);
compiledScript = pile(reader);
compiledDate = scriptD
} finally {
reader.close();
return compiledS
ScriptCache 类(参见清单 9)使用 java.util.LinkedHashMap 对象为所编译的脚本实现存储库。map 的初始容量被设为所缓存脚本的最大数量并且加载系数是 1。这两个参数就确保了该 cacheMap 不需要重新处理。
默认地,LinkedHashMap 类会使用条目的插入顺序。若不想使用默认顺序,LinkedHashMap() 构造函数的第三个参数必须是 true 以便使用条目的访问顺序。
达到缓存的最大容量后,removeEldestEntry() 方法就会开始返回 true,以便当每次向此缓存添加一个新的编译了的脚本时,一个条目都会自动从 cacheMap 删除。
通过联合使用 LinkedHashMap 的自动删除机制和访问顺序,ScriptCache 就确保了当添加了新脚本时,最近最少使用的(Least Recently Used,LRU)的脚本将能够从缓存中删除。
清单 9. ScriptCache 类
package jsee.
import javax.script.*;
import java.io.*;
import java.util.*;
public abstract class ScriptCache {
public static final String ENGINE_NAME = "JavaScript";
private Compilable scriptE
private LinkedHashMap&String, CachedScript& cacheM
public ScriptCache(final int maxCachedScripts) {
ScriptEngineManager manager = new ScriptEngineManager();
scriptEngine = (Compilable) manager.getEngineByName(ENGINE_NAME);
cacheMap = new LinkedHashMap&String, CachedScript&(
maxCachedScripts, 1, true) {
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() & maxCachedS
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) scriptE
下一节将使用 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.
import javax.script.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import jsee.cache.*;
public class JSServlet extends HttpServlet {
private String cacheControlH
private String contentTypeH
private ScriptCache scriptC
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 文件
&web-app ...&
&servlet-name&JSServlet&/servlet-name&
&servlet-class&jsee.servlet.JSServlet&/servlet-class&
&init-param&
&param-name&Cache-Control&/param-name&
&param-value&no-cache&/param-value&
&/init-param&
&init-param&
&param-name&Content-Type&/param-name&
&param-value&text/plain&/param-value&
&/init-param&
&init-param&
&param-name&Max-Cached-Scripts&/param-name&
&param-value&1000&/param-value&
&/init-param&
&load-on-startup&1&/load-on-startup&
&/servlet&
&servlet-mapping&
&servlet-name&JSServlet&/servlet-name&
&url-pattern&*.jss&/url-pattern&
&/servlet-mapping&
&/web-app&
从清单 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 覆盖,这一点在本节稍后的部分还会看到。
使用脚本上下文
脚本引擎的线程模型
JSR-223 Scripting for the Java Platform(参见 )定义了三类脚本引擎:
能执行并发脚本的多线程引擎,可以修改由其他线程看到的变量
线程隔离引擎,也是多线程的,但每个线程都具有其自身的引擎范围来保存变量
无状态引擎,作为线程隔离引擎定义,但引擎范围在任何脚本执行后均保持不变
脚本引擎的类型可通过 engine.getFactory().getParameter("THREADING") 获得,而返回的结果可能会是 "MULTITHREADED"、"THREAD-ISOLATED" 或 "STATELESS"。
每个脚本引擎实例都具有一个默认的上下文,在其中,可以用 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 执行的脚本内的可用变量
servlet 的 javax.servlet.ServletConfig 实例
application
Web 应用程序的 javax.servlet.ServletContext 实例
javax.servlet.http.HttpSession 对象
javax.servlet.http.HttpServletRequest 对象
javax.servlet.http.HttpServletResponse 对象
用于输出响应的 java.io.PrintWriter 对象
脚本引擎的 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 scriptC
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);
runScript("/init.jss", scriptContext);
String uri = request.getRequestURI();
uri = uri.substring(request.getContextPath().length());
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);
JSServlet 的 doGet() 和 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 =
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. 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.firstName 和 param.lastName 可以得到这两个请求参数。
清单 20. ParamDemo.jss 示例
println("Hello " + param.firstName + " " + param.lastName);
访问 JavaBean
Web 应用程序的 application、session 和 request 范围可存储 bean 实例,可以分别使用 ServletContext、 HttpSession 和 HttpServletRequest 的 getAttribute() 和 setAttribute() 来获得或替代这些实例。也可以使用 getBean() 和 setBean() 函数(如清单 21 所示),但这两个函数必须位于 init.jss 文件内以便任何脚本均可调用它们。scope 参数应是如下字符串中的一个:
"application"
清单 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.
public class DemoBean {
public int getCounter() {
public void setCounter(int counter) {
this.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);
如果所要导入的包是以 java、javax、org、edu、com 或 net 开头的,那么无须在 importPackage() 使用 Packages 前缀。此外,还可以使用 importClass() 导入单个类。
返回 JSON 响应
清单 24 所示的示例会生成一个 JSON 响应,该响应会包含有关 JavaScript 引擎和此脚本 URI 的某些信息。此示例使用 JavaScript 语法来创建 json 对象,其源代码则用 toSource() 方法以 String 形式获得。
清单 24. JsonDemo.jss 示例
var json = {
name: String(factory.engineName),
version: String(factory.engineVersion),
threading: String(factory.getParameter("THREADING"))
language: {
name: String(factory.languageName),
version: String(factory.languageVersion)
uri: String(request.requestURI)
println(json.toSource());
在本例中,从 factory 和 request 的属性中检索到的所有 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 函数。
本文的示例应用程序
发表评论:
TA的最新馆藏[转]&[转]&[转]&[转]&}

我要回帖

更多关于 web服务器有哪些 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信