迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
KJServlet - 一个基于 Java 的 JS web 框架Nashorn 已死Nashorn 生于Java 8,死于Java 11,由于之后 Nashorn 会被从 JDK 中完全移除,本工程不再进行维护。 简介KJServlet 是一个轻量级的 Javascript web 框架,该框架基于由 Java 8 开始引入的 Nashorn 引擎,这意味着你所写的 javascript 代码最终会运行在 JVM 的环境下,也正因为如此,你可以非常容易的使用各种已经存在的第三方 Java 类库来构建你的应用程序。 这也是一个比较自由风格的框架,支持你使用不同的代码风格来编写你的逻辑。拿编写 controller 为例,你可以将使用流行的面向函数变成的方式使用函数来当作入口,或许你习惯了面向对象的风格,你也可以使用对象和方法来作为入口;在编写 controller 方法的时候,你可以使用回调(callback)的方式来返回页面数据,也可以直接返回数据由框架来确定如何返回,并且,该框架支持了 MVC,你可以使用非常熟悉的 JSP、Freemarker、Velocity 来编写你的显示层代码。 这个文档是本框架的使用文档,绝大部分内容是在描述框架提供的接口和使用方式,偶尔会有一些设计的内容,但是非常少,如果你们对框架的设计本身有兴趣,在读源代码的时候有问题,可以随时联系我。另外由于这个框架非常的新,并且目前只有一个人在维护,一些错误和 BUG 是无可避免的,所以如果出现了问题,除了可以在这里提交 BUG 之外,也可以随时邮件联系我,我的邮箱未 [email protected]。在国内可发送邮件到 [email protected] 关于许可:就目前而言,该框架提供两种许可,默认的情况是 GPL,如果你想将该框架作为商业用途,又不想开放源代码的话,可以随时联系我,我会提供一个 Apache 的许可。 快速开始本框架所有的接口都基于J2EE的标准接口,所以你可以非常容易的将其嵌入到任何的 J2EE 项目当中。你可以按照以下步骤来运行一下例子:
[webapps]/WEB-INF/lib
<dependency> <groupId>me.keijack.kjservlet</groupId> <artifactId>kjservlet</artifactId> <version>0.1.0</version> </dependency>
<servlet> <display-name>k-js-servlet</display-name> <servlet-name>KJServlet</servlet-name> <servlet-class>org.keijack.kjservlet.KJServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>KJServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
function sayHello(req){ var name=req.param["name"]; return "<!DOCTYPE html><html><head><title>Say Hello</title></head><body>Hello, " + name + "</body></html>";}
更多详细的内容,请参考以下的用户手册。 路由就如你在上面例子所看到的,你甚至不需要配置任何的路由信息,为何?因为这个框架是基于目录来进行路由的。 就上述的例子而言,如果你将 事实上,除了路经之外,还是有一些别的配置项会影响路由规则的。 这个框架在每次启动的时候都会从 classpath 目录下尝试去读取一个名为 $webapp = { controller : { fileHome : "classpath:/org/keijack/kjservlet/demo/controller", // 你的 controller 以及其引入的 javascript 文件的根目录, 默认情况下是 "classpath:" fileSuffix : ".js", // 你的 controller 以及其引入的 javascript 文件的后缀,默认是".js" suffix : "", // 你访问服务器的 url 的后缀,例如常用的 .do 或者 .action }, resources : [ "*.html", "/images/*" ], // 一些静态文件的规则。};
以”/“未分割,除了服务器,上下文路经之外,前面的部分是计算的 controller 文件的路经,最后的一段则是 controller 的函数或者是对象和方法。在上面例子中, 还有另外一种方式可以配置路由,这一点我们稍后再述。 本框架的设计未面向函数设计。所以,在你的 controller js 文件中,你完全可以使用全局函数,而不必拘泥于使用对象。事实上,每个请求都会运行在一个新的上下文(context)里,这意味着你甚至可以在 controller 文件中使用全局变量,这些”全局变量“事实上只在当次请求有效。当然,你也完全使用对象来管理你的代码。 如上述例子,如果使用对象方法而非函数的话,你可以定义这样一个对象在你的 var person = { yieldName : function(req) { var name = req.parameters.name; return "<!DOCTYPE html><html><head><title>Say Hello</title></head><body>My name is " + name + "!</body></html>"; }}; 你可以使用以下链接访问该 controller 方法: http://[your_server_host]:[your_server_port]/[your_servlet_context]/demo/person.yieldName?name=John You can add alias to routes, at your $webapp object, add a property named $webapp = { controller : { fileHome : "classpath:/org/keijack/kjservlet/demo/controller", fileSuffix : ".js", suffix : "", }, aliases : { // 别名 "/yieldMyName" : "/demo/person.yieldName", }, resources : [ "*.html", "/images/*" ], }; 增加了上述配置之后,你可以使用以下链接访问上面对象的例子。 http://[your_server_host]:[your_server_port]/[your_servlet_context]/yieldMyName?name=John 引入其他的文件上一章节说明了本框架的路由规则,通过路经来寻找处理一个请求的 javascript 文件。但是,将所有的逻辑放在一个文件中并不是最佳的实践,所以在实际业务当中,大部分情况下我们需要在一个文件中引入其他的文件。 Nashorn 内置提供了一个方法 load(文件全路经),不过我们并不推荐你使用这个方法,而是使用框架提供的 import(文件相对路经) 的方法。 以下是你应该使用 imports() 方法的理由:
编写 Controller 函数/方法Request 和 Response 参数就如你自己编写 Servlet 类一样,你的 controller 函数/方法会接收到两个参数,第一个参数是 request,第二个参数是 response。这两个参数本身就是对 HttpServletRequest 和 HttpServletResponse 进行了 js 对象封装。所以,理论上,你应该从 request 对象获取用户提交的内容,然后通过 response 对象将结果数据写回。但是,似乎你在快速开始章节的并不是这样,你只看到一个 request 参数,关于这一点,会在后续说明。 参考以下的 controller 方法 function dosth(req, res){ var someParamVal = req.parameters["name-defined-in-form-input"]; // 如果你的参数足够简单,你甚至只用 ".",例如你的参数名是"simpleName"的话,你可以使用: req.parameters.simpleName var serviceResult = someServiceObject.doService(someParamVal); res.write(serviceResult);} 如果你更习惯使用回调方法的话,你可以这样来编写: function dosth(req, res){ var someParamVal = req.parameters["name-defined-in-form-input"]; someServiceObject.doService(someParamVal, function (serviceCallbackResult) { res.write(serviceCallbackResult); });} 以下是 request 对象和 response 对象的完整属性、方法列表。 Request 对象的完整属性和方法(命名为 req)
{ "filename": "somePicture.jpg", // the file's name "contentType": "image/jpg", // the content type "content": "xxxxxx" // a string that get from the multipart content, // you can use getBytes() method to get the byte array, // and write it into a file.}
Response 对象的完整属性和方法(命名为 res)
另一种响应方式在此章节,将描述另一种响应的方式,你无须使用 正如你在快速开始章节所见,你根本无须使用 从原理上来说,如果方法返回的是一个符合格式的 JSON 数据,框架会自动进行响应的处理。而在编码过程中,我们并不需要知道具体的该 JSON 对象的结构,你只需使用框架提供的全局对象
以下是使用 function dosth(req){ var param = req.parameters; var serviceResult = someServiceObject.doService(param); return $renderer.json(serviceResult);} 事实上,在大部分情况下, function dosth(req){ var param = req.parameters; var serviceResult = someServiceObject.doService(param); return serviceResult; // 该返回值为一个 JSON 对象} 你还可以返回类似如下架构的数据: return "<!DOCTYPE html><html>....</html>"; // 会响应为 "text/html"。 return "<?xml version="1.0" encoding="UTF-8"?><tag>...</tag>"; // 会响应为 "text/xml"。 return "redirect:/url"; // 会重定向至 /url return "forward:/url"; // 会转发至 /url return "some text"; // 会响应为 "text/plain" MVCMVC 是一个非常常见的设计模式,该设计模式的主要原则在于分层,将代码分为逻辑层(Model)、控制层(Controller)和展示层(View)。代码分层的好处在于解耦,使得代码易读和更好维护。 网站开发者都喜欢使用 MVC 模式,特别是 Java Web 的开发者,也因此,用 Java 语言提供的模板引擎特别多。得益于此,在我们的框架中使用 MVC 模式也变得非常的容易。 KJServlet 支持 JSP、Freemarker 和 Velocity 模板引擎用做 View 层。 正如上章所见,你需要使用 $renderer.view 方法来使用 MVC 模式,参考例子如下: function dosth(req){ var param = req.parameters; var serviceResult = someServiceObject.doService(param); return $renderer.view("/WEB-INF/pages/view.jsp", serviceResult); // serviceResult 为 JSON 对象} 框架默认使用 JSP 作为模板引擎,不会你可以在 $webapp = { controller : { fileHome : "classpath:/org/keijack/kjservlet/demo/controller", // 你的 controller 方法的路径,默认为 "classpath:" fileSuffix : ".js", // 你的 controller 文件的后缀,默认为 ".js" suffix : "", // 你 controller 请求的后缀,默认为 "" }, resources : [ "*.html", "/images/*" ], view : { resolver : "jsp", // 模板引擎类型,可为"jsp"、"freemarker"或"velocity" prefix : "/WEB-INF/pages/", // view 文件路径的前缀。 suffix : ".jsp", // view 文件路径后缀。 }, } 如果你在 function dosth(req){ var param = req.parameters; var serviceResult = someServiceObject.doService(param); return $renderer.view("view", serviceResult); // 模板文件将为 /WEB-INF/pages/view.jsp } 注意!如果你使用 freemarker 或者 volocity 模板引擎,你需要自己增加这些引擎的依赖。如果你使用 maven 和 freemarker,你需要在你的 <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.26-incubating</version> </dependency> 在 $renderer.view(templateFileLocation, data, header) 中的第二个参数 假设你的 function dosth(req){ var data = {"userName": "Jhon", "sex": "male", "age": 28, "department": { "name": "HR", "phone": "+01xxxxxx", }, "subordinates": ["Mike", "Lily"] }; return $renderer.view("view", data);} 在你的模板文件中 -- 以 freemarker 为例 -- 可以这样来使用: <!DOCTYPE html><html><head> <title>Personal Details</title></head><body> <h1>Personal Details of ${userName}</h1> <p>sex: ${sex}</p> <p>age: ${age}</p> <p>department: ${department.name}</p> <p>subordinates: <#list subordinates as sbn>${sbn}, </#list></body></html> 再 注意! 假设你的 var data = {"userName": "Jhon", "sex": "male", "isAdult": function(){ // 注意:再这个方法中不要使用 `this`因为最终,这些方法会封装为一个 Java 对象。 return data.age >= 18; }, "age": 28, "department": { "name": "HR", "phone": "+01xxxxxx", }, "subordinates": ["Mike", "Lily"] }; 注意!不要再 那么,再你的模板文件中,你可以如下调用该方法: <!DOCTYPE html><html><head> <title>Personal Details</title></head><body> <h1>Personal Details of ${userName}</h1> <p>sex: ${sex}</p> <p>is adult: ${isAult.call()}</p> <p>age: ${age}</p> <p>department: ${department.name}</p> <p>subordinates: <#list subordinates as sbn>${sbn}, </#list></body></html> 如果你认为以上3个模板引擎都不适用,你甚至可以自定义你自己的模板引擎: $webapp = { controller : { fileHome : "classpath:/org/keijack/kjservlet/demo/controller", fileSuffix : ".js", suffix : "", }, resources : [ "*.html", "/images/*" ], view : { resolver : function(viewFileLocation, data, headers){ // viewFileLocation 已经拼装好以下配置中的 prefix 和suffix。 // 生成最终的 View 层渲染的 html 字符串 var html = ...; // 最后通过 $renderer 返回一个结构化的对象。 return $renderer.html(html, headers); }, prefix : "/WEB-INF/pages/", suffix : ".jsp", }, } 标注和面向切面编程(AOP)Javascript 不像 Java 一样具有内置的标注体系,并且,Javascript 的对象生成方式和 Java 有非常大的区别,你甚至无须使用 new 便可以定义对象,所以在 Javascript 中,其实很难如 Java 一样做到面向切面编程。然而,通过一些小把戏,我们还是勉强做了一些面向切面的内容。 如果你熟悉 Javascript,你必然对 strict mode有所耳闻。如果你向希望一个方法使用 strict mode 来执行,你需要在你的方法开始时加入一行 "use strict",我们的 AOP 灵感也时来源于此。请参考以下代码: function dosth(req){ "use strict"; "@post"; // KJSeverlet 内置的标注,该方法只接受 POST 类型的请求。 "@myOwnAnno"; // 用户自定义的标注 // 你的逻辑代码 ... "@Anno2"; // 如果你的标注放在逻辑体里,该标注不会被框架读取。 ...} 在以上的例子中,你获得的标注将是 ["@post", "@myOwnAnno"]。 注意!标注仅仅在 Controller 方法中生效! 我们已经知道如何进行标注了,那么如何使用这些标注呢?我们回到在 $webapp = { controller : { fileHome : "classpath:/org/keijack/kjservlet/demo/controller", fileSuffix : ".js", suffix : "", }, resources : [ "*.html", "/images/*" ], view : { resolver : "jsp", prefix : "/WEB-INF/pages/", suffix : ".jsp", }, interceptors : { intercept : "@myOwnAnno", // 如果你对多个标准进行相同的切面,该参数可以传入一个数组 // intercept: ["@myOwnAnno1", "@MyOwnAnno2"], before : function(req, res, ctx) { // controller 方法之前执行的代码 return true; // 如果希望方法继续执行,你必须返回 true,如果返回 false,那么,controller 方法将被终止,不再执行。 }, after : function(req, res, result, ctx) { // controller 方法执行之后执行的代码。 }, onError : function(req
|
请发表评论