Original address: Http://www.infoq.com/cn/articles/nashorn
Starting with JDK 6, Java is already bundled with the JavaScript engine, which is based on Mozilla's rhino. This feature allows developers to embed JavaScript code in Java and even call Java from embedded JavaScript. In addition, it provides the ability to run JavaScript from the command line using Jrunscript. If you don't need very good performance, and you can accept the ECMAScript 3 Limited feature set, it's pretty good.
Starting with JDK 8, Nashorn replaced Rhino as the embedded JavaScript engine for Java. Nashorn fully supports the ECMAScript 5.1 specification as well as some extensions. It uses the new language feature based on JSR 292, which contains the invokedynamic introduced in JDK 7, which compiles JavaScript into Java bytecode.
This brings a performance boost of 2 to 10 times times compared to the previous rhino implementations, although it is still worse than the V8 engine in chrome and node. js. If you are interested in implementation details, you can look at these slides from the JVM language summit.
Because Nashorn comes with JDK 8, it also adds a simple function-type interface support. Next, we'll see more details soon.
Let's start with a small example. First, you may need to install JDK 8 and NetBeans, IntelliJ idea, or eclipse. For integrated JavaScript development, they provide at least basic support. Let's create a simple Java project that contains the following two sample files and runs it:
(Click on the image to see a larger image)
In line 12th, we use the "eval" method of the engine to evaluate any JavaScript code. In this example, we just loaded the above JavaScript file and evaluated it. You may find that "print" is unfamiliar. It is not a built-in function of JavaScript, but is provided by Nashorn, and it provides other handy functions that are useful in scripting environments. You can also embed the "Hello World" print code directly into a string passed to the "eval" method, but place JavaScript in its own file to open a new tool world for it.
Eclipse does not currently provide dedicated support for Nashorn, however, with JavaScript development tools (JSDT) projects, it already supports basic JavaScript tools and editing.
(Click on the image to see a larger image)
IntelliJ Idea 13.1 (Community Edition and Ultimate Edition) offers excellent JavaScript and nashorn support. It has a full-featured debugger and even allows refactoring synchronization between Java and JavaScript, so for example, if you rename a Java class that is referenced by JavaScript, or rename a JavaScript file for Java source code, The IDE will then modify the appropriate references across languages.
Here's an example of how to debug JavaScript called from Java (Note that NetBeans also provides the JavaScript debugger, as shown below):
(Click on the image to see a larger image)
You might say that the tool looks good, and the new implementation fixes the performance and consistency issues, but why should I use it? One reason is the generic scripting. Sometimes it is convenient to be able to insert any type of string directly and allow it to be interpreted. Sometimes, there is no way to be in the way of the compiler, or not to worry about static types, may also be good. Or, you might be interested in the node. JS programming model, which can also be used with Java, as we'll see at the end of this article. In addition, there is a case to mention that the use of JavaScript for JavaFX development is much faster than Java.
Shell Script
The Nashorn engine can be called from the command line using the JJS command. You can call it without any arguments, which will take you into an interactive mode, or you can pass a JavaScript file name that you want to execute, or you can use it as a substitute for a shell script like this:
#!/usr/bin/env JJS var name = $ARG [0]; Print (name?) "Hello, ${name}!": "Hello, world!");
The "-" prefix is required to pass the program parameters to the JJS. So for example, you can call this:
./hello-script.js–joe
If there is no "-" prefix, the parameter is interpreted as the file name.
Passing data to Java or sending data out of Java
As mentioned above, you can call JavaScript directly from Java code, just get an engine object and invoke its "eval" method. You can explicitly pass data as a string ...
Scriptenginemanager Scriptenginemanager = new Scriptenginemanager (); ScriptEngine Nashorn = scriptenginemanager.getenginebyname ("Nashorn"); String name = "Olli"; Nashorn.eval ("Print ('" + name + "')");
...... Or you can pass bindings in Java, which are global variables that can be accessed from within the JavaScript engine:
int Valuein = 10; Simplebindings simplebindings = new Simplebindings (); Simplebindings.put ("Globalvalue", Valuein); Nashorn.eval ("Print (Globalvalue)", simplebindings);
The evaluation result of JavaScript Eval will be returned from the "eval" method of the engine:
Integer result = (integer) nashorn.eval ("1 + 2"); ASSERT (Result = = 3);
Using Java classes in Nashorn
As mentioned earlier, one of the most powerful features of Nashorn stems from the invocation of Java classes in JavaScript. Not only can you access classes and create instances, you can also inherit them, invoke their static methods, and do almost anything you can do in Java.
As an example, let's take a look at the ins and outs. JavaScript does not have any language features that are concurrency-oriented, and all common run-time environments are single-threaded or at least without any shared state. Interestingly, in a nashorn environment, JavaScript does run concurrently and has a shared state, just like in Java:
Access Java class thread var thread = Java.type ("Java.lang.Thread"); The subclass Var MyThread with the Run method = Java.extend (thread, { run:function () { print ("Run in separate Thread"), }), Var th = new MyThread (); Th.start (); Th.join ();
Note that the canonical practice of accessing classes from Nashorn is to use Java.type, and you can use Java.extend to extend a class.
Pleasing functional
In all respects, with the release of JDK 8, java--at least to some extent-has become a functional language. Developers can use higher-order functions on the collection, such as traversing all elements. A higher order function is a function that treats another function as a parameter, and it can do something meaningful with this function parameter. Take a look at examples of higher-order functions in Java below:
list<integer> list = Arrays.aslist (3, 4, 1, 2); List.foreach (New Consumer () { @Override public void Accept (Object o) { System.out.println (o); }});
For this example, our traditional implementation is to iterate through the elements using an "external" loop, but for now, instead of doing so, we have passed a "Consumer" function to the "ForEach" operation, a high-order "inner Loop" The operation passes each element in the collection one by one to consumer's "accept" method and executes it.
As mentioned above, for such a higher order function, the practice of a functional language is to receive a function parameter instead of an object. Although the transfer function reference itself is traditionally beyond the scope of Java, JDK 8 now has some syntactic sugars that allow it to implement that representation using lambda expressions (also known as "closures"). For example:
list<integer> list = Arrays.aslist (3, 4, 1, 2); List.foreach (El-System.out.println (EL));
In this case, the parameter of "ForEach" is the form of such a function reference. This is possible because the customer is a functional interface (sometimes referred to as a single abstract method) type or "SAM").
So why do we talk about lambda expressions when we talk about Nashorn? Because in JavaScript, developers can write code like this, and in this case, Nashorn can be particularly good at narrowing the gap between Java and JavaScript. In particular, it even allows developers to pass pure JavaScript functions as implementations of functional interfaces (Sam types).
Let's look at some of the pure JavaScript code, which functions like the Java code above. Note that there is no built-in list type in JavaScript, only arrays, but the size of these arrays is dynamically allocated and has a similar approach to Java lists. So, in this example, we call the "for each" method of a JavaScript array:
var jsarray = [4,1,3,2]; Jsarray.foreach (function (EL) {print (EL)});
Similarities are obvious, but that is not the whole point. Developers can also convert such a JavaScript array into a Java list:
var list = java.util.Arrays.asList (Jsarray);
Did you see it? Yes, this is the JavaScript that runs in Nashorn. Now that it's a Java list, developers can call their "ForEach" method. Note that this "foreach" method is different from the one we call on the JavaScript array, which is the "foreach" method defined on the Java collection. Here, we still pass a pure JavaScript function:
List.foreach (function (EL) {print (EL)});
Nashorn allows developers to provide pure JavaScript function references where they need to use a functional interface (SAM type). This adapts not only to Java, but also to JavaScript.
The next version of ECMAScript--which is expected to be the last version of the year--will include the function's phrase, which allows the developer to write the function in the form of an approximate Java lambda expression, except that it uses the double arrow = =. This further enhances consistency.
Nashorn JavaScript-specific dialects
As mentioned in the Introduction section, the Nashorn supported JavaScript implements the ECMAScript 5.1 version and some extensions. I don't recommend using these extensions, because they're neither Java nor JavaScript, and both types of developers will find it unhealthy. On the other hand, there are two extensions that are used extensively throughout the Oracle documentation, so we should be aware of them. First, let's make some preparations for understanding the first extension. As mentioned earlier, developers can use Java.extend to extend a Java class from JavaScript. If you need to inherit an abstract Java class or implement an interface, you can use a simpler syntax. In this case, the developer can actually invoke the constructor of the abstract class or interface and pass in a JavaScript object constant that describes the implementation of the method. This constant is just name/value, and you probably know the JSON format, which is similar to that. This allows us to implement the Runnable interface as follows:
var r = new Java.lang.Runnable ({ run:function () { print ("running...\n"); }});
In this example, an object constant specifies the implementation of the Run method, which we actually use to invoke the Runnable constructor. Note that this is a way that Nashorn implementations provide to us, otherwise we cannot do so in JavaScript.
The sample code is similar to the way we implement an interface in Java in an anonymous inner class, but it's not exactly the same. This brings us to the first extension, which allows the developer to pass the last argument after the right parenthesis ")" when the constructor is called. The code for this practice is as follows:
var r = new Java.lang.Runnable () { run:function () { print ("running...\n"); }};
...... It implements exactly the same functionality, but more like Java.
The second commonly used extension is a simple way of writing a function that allows you to delete two curly braces and return statements in a single-line function method body. So, the example in the previous section:
List.foreach (function (EL) {print (EL)});
Can be expressed in a more concise way:
List.foreach (function (EL) print (EL));
Avatar.js
We've seen that with Nashorn, we have a good JavaScript engine embedded in Java. We have also seen that we can access any Java class from Nashorn. Avatar.js Further, it "brings the node programming model, API, and module ecosystem to the Java platform." To understand what this means and why it is exciting, we must first understand what node is. In essence, node is stripped of Chrome's V8 JavaScript engine, allowing it to run from the command line without needing a browser. In this way, JavaScript is not only run in the browser, but can be run on the server side. Running JavaScript on the server side in any meaningful way requires at least access to the file system and network. To do this, node embeds a library called LIBNV, which implements this function asynchronously. In fact, this means that the operating system call never blocks, even if it takes a while to return. Developers need to provide a callback function instead of blocking. The function fires immediately upon completion of the call and returns if there is any result.
Several companies have used node in important applications, including Walmart and PayPal.
Let's look at a small example of JavaScript, which I've rewritten based on the example on the node site:
Load the "HTTP" module (which is blocked) to handle HTTP requests for var HTTP = require (' http '); When requested, returns "hello,world\n" function handlerequest (req, res) { Res.writehead ($, {' Content-type ': ' Text/plain '}); Res.end (' Hello, world\n '); }//monitoring localhost, port 1337//and providing a callback function handlerequest//here embodies its non-blocking/asynchronous feature Http.createserver (HandleRequest). Listen (1337, ' 127.0.0.1 '); Log to the console and make sure we're moving in the right direction console.log (' Get your Hello at http://127.0.0.1:1337/');
To run this code, you need to install node and then save the above JavaScript code to a file. Finally, the file is called as a parameter to node.
By binding Libuv to Java classes and making JavaScript accessible to them, Avatar.js is designed to provide the same core API as node in this way. While this may sound tedious, this approach works well. Avatar.js supports many node modules. Support for node's mainstream web framework "Express" suggests that this approach does apply to many existing projects.
Unfortunately, there is no avatar.js binary distribution package at the time of writing this article. There is a Readme file that explains how to build from the source code, but if you do not have that much time to build from scratch, you can also download the binaries from here instead of building them yourself. Both ways are possible, but in order to get results faster, I recommend choosing the second way.
Once you have created the binaries and placed them in the Lib folder, you can invoke the Avatar.js framework using statements such as the following:
Java-djava.library.path=lib-jar Lib/avatar-js.jar Helloworld.js
Assume that the demo server (the code above) is saved to a file named "Helloworld.js".
Let's ask again, why is this useful? Oracle's experts (slide 10) point out several scenarios for the library. I have roughly the same view of the two points, namely:
- Have a node application and want to use a Java library as a supplement to the node API
- Want to switch to JavaScript and node API, but need to embed some or all of the remaining Java code
Two scenarios can be implemented by using avatar.js and invoking any required Java classes from JavaScript code. We have seen that Nashorn supports this approach.
Let me give you an example of the first application scenario. JavaScript currently has only one type that represents a numeric value, named "Number." This is equivalent to the "double" precision of Java and has the same limitations. The number of JavaScript, like a double in Java, does not represent arbitrary ranges and precision, such as when measuring currency.
In Java, we can use BigDecimal, which is exactly what it is for this kind of situation. But JavaScript does not have a built-in equivalent type, so we can access the BigDecimal class directly from JavaScript code to handle currency values safely.
Let's look at a Web service example that calculates how many percent of a certain number is. First, you need a function to perform the actual calculation:
var BigDecimal = java.type (' Java.math.BigDecimal '); function Calculatepercentage (amount, percentage) { var result = new BigDecimal (amount). Multiply ( new BigDecimal (percentage)). Divide ( new BigDecimal ("the"), 2, bigdecimal.round_half_even); return result.toplainstring (); }
JavaScript does not have a type declaration, except that the code above is very similar to the Java code I wrote for the task:
public static string calculate (string amount, String percentage) { BigDecimal result = new BigDecimal (amount). multipl Y ( new BigDecimal (percentage)). Divide ( new BigDecimal ("X"), 2, bigdecimal.round_half_even); return result.toplainstring (); }
We just need to replace the HandleRequest function in the node example above to complete the code. The following code is replaced:
Load tool module "url" to parse urlvar URL = require (' URL '); function HandleRequest (req, res) { //'/calculate ' Web service address if (Url.parse (req.url). Pathname = = = '/calculate ') { C2/>var query = Url.parse (Req.url, true). query; Quantities and percentages are passed in as query parameters to var result = Calculatepercentage (Query.amount, query.percentage); Res.writehead ($, {' Content-type ': ' Text/plain '}); Res.end (result + ' \ n '); } }
We have also used the node core module to process the request URL, which resolves the query parameters amount and percentage.
When you start the server (as described earlier) and use the browser to issue a request such as the one below,
http://localhost:1337/calculate?amount=99700000000000000086958613&percentage=7.59
Will get the correct result "7567230000000000006600158.73". This is not possible when you are simply using the "number" type of JavaScript.
When you decide to migrate your existing JEE applications to JavaScript and node, the second scenario is meaningful. In this case, you can easily access all of the existing services from within the JavaScript code. Another related scenario is that when you use JavaScript and node to build new server features, you can still benefit from existing JEE services.
In addition, Avatar.js-based avatar projects are also moving in the same direction. The details of this project are beyond the scope of this article, but readers can read this Oracle announcement for a cursory understanding. The basic idea of the project is to write the application in JavaScript and access the JEE service. The Avatar project contains a binary bundle of avatar.js, but it requires glassfish for installation and development.
Summary
The Nashorn project enhances the rhino implementation in JDK 6, greatly improving the performance of long-running applications such as those used in Web servers. Nashorn integrates Java with JavaScript and even considers new lambda expressions for JDK 8. Avatar.js brings true innovation, built on these features, and provides an integration of enterprise-class Java and JavaScript code, and is largely compatible with the de facto standard of JavaScript server-side programming.
The full instance and Avatar.js binaries for Mac OS X can be downloaded from GitHub.
nashorn--the power of Java and JavaScript in JDK 8-turn