Maitriser les Variables Globales avec Nashorn en Java : Guide Complet

Apprenez a utiliser les variables globales dans Nashorn avec Java. Decouvrez comment definir, acceder et manipuler des donnees entre Java et JavaScript via l'API Scripting.

Mahmoud DEVO
Mahmoud DEVO
December 27, 2025 7 min read
Maitriser les Variables Globales avec Nashorn en Java : Guide Complet

Integration de scripts JavaScript avec Java : une approche pratique

Introduction

L’integration de scripts JavaScript avec Java est un sujet essentiel pour les developpeurs qui souhaitent exploiter les avantages des deux langages. Nashorn, le moteur JavaScript integre a Java 8, permet la prise en charge de scripts JavaScript dans n’importe quelle application Java. Ce moteur haute performance offre une interoperabilite transparente entre Java et JavaScript, permettant aux developpeurs de tirer parti de la flexibilite de JavaScript tout en conservant la robustesse de Java.

Dans cet article approfondi, nous allons explorer en detail comment utiliser les variables globales avec Nashorn. Vous apprendrez a definir des variables depuis Java, a les utiliser dans vos scripts JavaScript, et a recuperer les resultats. Nous couvrirons egalement les bonnes pratiques et les pieges courants a eviter.

Pourquoi utiliser Nashorn ?

Nashorn presente plusieurs avantages majeurs :

  • Performance : Compile JavaScript en bytecode Java pour une execution rapide
  • Interoperabilite : Acces direct aux classes et objets Java depuis JavaScript
  • Simplicite : API intuitive via javax.script
  • Securite : Controle fin sur l’execution des scripts

Note importante : Nashorn a ete deprecie dans Java 11 et supprime dans Java 15. Pour les projets modernes, considerez GraalJS comme alternative. Cependant, Nashorn reste pertinent pour la maintenance de projets existants sur Java 8-10.

Configuration de l’Environnement Nashorn

Avant de travailler avec les variables globales, configurons correctement notre environnement Nashorn :

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.Bindings;
import javax.script.ScriptContext;

public class NashornSetup {

    private ScriptEngine engine;

    public NashornSetup() {
        ScriptEngineManager manager = new ScriptEngineManager();
        engine = manager.getEngineByName("nashorn");

        if (engine == null) {
            throw new RuntimeException("Nashorn engine not available. " +
                "Ensure you're using Java 8-14.");
        }
    }

    public ScriptEngine getEngine() {
        return engine;
    }
}

Definition des Variables Globales

Pour commencer, nous devons definir une variable globale dans le script JavaScript. Pour cela, nous utilisons la methode put() de l’objet ScriptEngine. Cette methode place une variable dans le scope global du moteur de script.

Methode Simple avec put()

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

// Definition d'une variable String
engine.put("textToPrint", "Donnees definies en Java.");

// Definition de variables de differents types
engine.put("compteur", 42);
engine.put("estActif", true);
engine.put("prix", 19.99);

Dans cet exemple, nous definissons plusieurs variables de types differents qui seront accessibles dans le script JavaScript.

Utilisation des Bindings pour un Controle Avance

Les Bindings offrent un controle plus fin sur la portee des variables :

// Creer des bindings personnalises
Bindings bindings = engine.createBindings();
bindings.put("nom", "Jean");
bindings.put("age", 30);
bindings.put("ville", "Paris");

// Appliquer les bindings au scope ENGINE
engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);

// Ou utiliser les bindings globaux (partages entre moteurs)
Bindings globalBindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
if (globalBindings != null) {
    globalBindings.put("appVersion", "1.0.0");
}

Passer des Objets Java Complexes

Vous pouvez egalement passer des objets Java complexes :

import java.util.HashMap;
import java.util.ArrayList;
import java.util.Map;
import java.util.List;

// Passer une Map
Map<String, Object> config = new HashMap<>();
config.put("maxConnections", 100);
config.put("timeout", 30000);
config.put("debug", false);
engine.put("config", config);

// Passer une List
List<String> utilisateurs = new ArrayList<>();
utilisateurs.add("Alice");
utilisateurs.add("Bob");
utilisateurs.add("Charlie");
engine.put("utilisateurs", utilisateurs);

// Execution du script utilisant ces objets
engine.eval("print('Max connections: ' + config.get('maxConnections'));");
engine.eval("print('Premier utilisateur: ' + utilisateurs.get(0));");

Execution de Scripts JavaScript

Une fois que nous avons defini les variables globales, nous pouvons executer le script JavaScript. Pour cela, nous utilisons la methode eval() de l’objet ScriptEngine.

Execution Simple

try {
    // Utiliser une variable definie en Java
    engine.eval("print(textToPrint);");

    // Scripts multi-lignes
    String script = """
        var message = 'Bonjour ' + nom + '!';
        var anneeNaissance = 2024 - age;
        print(message);
        print('Annee de naissance estimee: ' + anneeNaissance);
        """;
    engine.eval(script);

} catch (ScriptException ex) {
    System.err.println("Erreur de script: " + ex.getMessage());
    System.err.println("Ligne: " + ex.getLineNumber());
    System.err.println("Colonne: " + ex.getColumnNumber());
}

Execution avec Retour de Valeur

// Recuperer une valeur calculee par JavaScript
Object resultat = engine.eval("40 + 2");
System.out.println("Resultat: " + resultat);  // 42

// Recuperer un objet complexe
engine.eval("var personne = { nom: 'Alice', age: 25 };");
Object personne = engine.get("personne");

// Acceder aux proprietes de l'objet JavaScript
if (personne instanceof jdk.nashorn.api.scripting.ScriptObjectMirror) {
    jdk.nashorn.api.scripting.ScriptObjectMirror mirror =
        (jdk.nashorn.api.scripting.ScriptObjectMirror) personne;
    System.out.println("Nom: " + mirror.get("nom"));
    System.out.println("Age: " + mirror.get("age"));
}

Utilisation d’Objets Java dans JavaScript

Nashorn permet l’utilisation bidirectionnelle : vous pouvez non seulement passer des objets Java a JavaScript, mais aussi acceder a des classes Java depuis vos scripts.

Recuperer des Valeurs depuis JavaScript

// Definir une variable en JavaScript
engine.eval("var resultatCalcul = 'Traitement termine avec succes';");

// Recuperer la valeur en Java
String value = (String) engine.get("resultatCalcul");
System.out.println(value);  // "Traitement termine avec succes"

// Recuperer des types numeriques
engine.eval("var score = 95.5;");
Double score = (Double) engine.get("score");
System.out.println("Score: " + score);

Acceder aux Classes Java depuis JavaScript

String script = """
    // Importer des classes Java
    var ArrayList = Java.type('java.util.ArrayList');
    var HashMap = Java.type('java.util.HashMap');
    var System = Java.type('java.lang.System');

    // Creer et utiliser des objets Java
    var liste = new ArrayList();
    liste.add('Premier');
    liste.add('Deuxieme');
    liste.add('Troisieme');

    // Afficher via System.out
    System.out.println('Taille de la liste: ' + liste.size());

    // Retourner la liste a Java
    liste;
    """;

Object result = engine.eval(script);
List<String> listeJava = (List<String>) result;

Prise en Charge des Interfaces Java

Nashorn prend en charge les interfaces Java, notamment les interfaces fonctionnelles. Cela permet d’implementer des interfaces Java directement en JavaScript.

Definition d’une Interface Fonctionnelle

@FunctionalInterface
public interface Calculateur {
    double calculer(double a, double b);
}

public interface Pet {
    void eat();
    void sleep();
    String getName();
}

Implementation en JavaScript

// Interface fonctionnelle - syntaxe simplifiee
engine.eval("var addition = function(a, b) { return a + b; };");
Calculateur calc = (Calculateur) engine.get("addition");
System.out.println("5 + 3 = " + calc.calculer(5, 3));

// Interface avec plusieurs methodes
String petScript = """
    var monChat = new Pet() {
        eat: function() { print('Miam miam!'); },
        sleep: function() { print('Zzz...'); },
        getName: function() { return 'Felix'; }
    };
    monChat;
    """;

// Note: Definir l'interface Pet dans le scope
engine.put("Pet", Pet.class);
Pet pet = (Pet) engine.eval(petScript);
pet.eat();    // Affiche: Miam miam!
pet.sleep();  // Affiche: Zzz...
System.out.println("Nom: " + pet.getName());  // Felix

Exemple Complet : Application Pratique

Voici un exemple complet et fonctionnel montrant toutes les techniques abordees :

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.Invocable;
import java.util.HashMap;
import java.util.Map;

public class NashornGlobalVariablesDemo {

    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        if (engine == null) {
            System.err.println("Nashorn non disponible!");
            return;
        }

        try {
            // 1. Definition des variables globales
            engine.put("appName", "MonApplication");
            engine.put("version", "2.0.0");
            engine.put("debugMode", true);

            // 2. Passer un objet de configuration
            Map<String, Object> config = new HashMap<>();
            config.put("maxUsers", 1000);
            config.put("timeout", 30000);
            engine.put("config", config);

            // 3. Definir des fonctions JavaScript
            String functions = """
                function formatMessage(prefix, message) {
                    return '[' + prefix + '] ' + message;
                }

                function processConfig() {
                    var result = {
                        app: appName,
                        ver: version,
                        debug: debugMode,
                        maxUsers: config.get('maxUsers')
                    };
                    return JSON.stringify(result);
                }
                """;
            engine.eval(functions);

            // 4. Appeler les fonctions JavaScript depuis Java
            Invocable invocable = (Invocable) engine;

            String message = (String) invocable.invokeFunction(
                "formatMessage", "INFO", "Application demarree"
            );
            System.out.println(message);

            String configJson = (String) invocable.invokeFunction("processConfig");
            System.out.println("Config: " + configJson);

            // 5. Recuperer des variables modifiees
            engine.eval("var resultat = 'Traitement OK';");
            System.out.println("Resultat: " + engine.get("resultat"));

        } catch (ScriptException e) {
            System.err.println("Erreur de script: " + e.getMessage());
        } catch (NoSuchMethodException e) {
            System.err.println("Methode non trouvee: " + e.getMessage());
        }
    }
}

Sortie attendue :

[INFO] Application demarree
Config: {"app":"MonApplication","ver":"2.0.0","debug":true,"maxUsers":1000}
Resultat: Traitement OK

Bonnes Pratiques

Suivez ces recommandations pour une utilisation optimale de Nashorn avec les variables globales :

1. Gestion des Erreurs Robuste

public Object executeScript(ScriptEngine engine, String script) {
    try {
        return engine.eval(script);
    } catch (ScriptException e) {
        // Logger l'erreur avec contexte
        System.err.printf("Erreur ligne %d, colonne %d: %s%n",
            e.getLineNumber(),
            e.getColumnNumber(),
            e.getMessage());

        // Retourner une valeur par defaut ou relancer
        return null;
    }
}

2. Reutiliser les Instances ScriptEngine

// BIEN: Reutiliser l'engine pour plusieurs evaluations
public class ScriptService {
    private final ScriptEngine engine;

    public ScriptService() {
        ScriptEngineManager manager = new ScriptEngineManager();
        this.engine = manager.getEngineByName("nashorn");
    }

    public Object evaluate(String script) throws ScriptException {
        return engine.eval(script);
    }
}

// MAL: Creer un nouvel engine a chaque fois (couteux)
public Object evaluateBad(String script) throws ScriptException {
    ScriptEngine engine = new ScriptEngineManager()
        .getEngineByName("nashorn");
    return engine.eval(script);
}

3. Isolation des Contextes

// Utiliser des Bindings separes pour isoler les executions
public Object evaluateIsolated(ScriptEngine engine, String script,
                                Map<String, Object> variables) throws ScriptException {
    Bindings bindings = engine.createBindings();
    bindings.putAll(variables);
    return engine.eval(script, bindings);
}

4. Validation des Entrees

public void setVariable(ScriptEngine engine, String name, Object value) {
    if (name == null || name.isEmpty()) {
        throw new IllegalArgumentException("Le nom de variable ne peut etre vide");
    }
    if (!name.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
        throw new IllegalArgumentException("Nom de variable invalide: " + name);
    }
    engine.put(name, value);
}

Pieges Courants

Evitez ces erreurs frequentes lors de l’utilisation de Nashorn :

1. Oublier de Verifier la Disponibilite du Moteur

// PROBLEME: NullPointerException si Nashorn n'est pas disponible
ScriptEngine engine = manager.getEngineByName("nashorn");
engine.eval("print('test');");  // NPE potentiel!

// SOLUTION: Toujours verifier
ScriptEngine engine = manager.getEngineByName("nashorn");
if (engine == null) {
    throw new RuntimeException("Moteur Nashorn non disponible. " +
        "Utilisez Java 8-14 ou ajoutez GraalJS.");
}

2. Confusion entre Types Java et JavaScript

// PROBLEME: Les nombres JavaScript sont des Double
engine.eval("var nombre = 42;");
Integer nombre = (Integer) engine.get("nombre");  // ClassCastException!

// SOLUTION: Utiliser Number ou Double
Number nombre = (Number) engine.get("nombre");
int valeur = nombre.intValue();  // OK

3. Fuite de Memoire avec les Variables Globales

// PROBLEME: Les variables s'accumulent
for (int i = 0; i < 10000; i++) {
    engine.put("temp_" + i, "data");  // Memoire non liberee!
}

// SOLUTION: Nettoyer apres usage
engine.put("tempData", "processing");
// ... traitement ...
engine.getBindings(ScriptContext.ENGINE_SCOPE).remove("tempData");

4. Problemes de Thread-Safety

// PROBLEME: ScriptEngine n'est PAS thread-safe par defaut
// Plusieurs threads utilisant le meme engine = comportement imprevisible

// SOLUTION: Un engine par thread ou synchronisation
private final ThreadLocal<ScriptEngine> engineHolder =
    ThreadLocal.withInitial(() -> {
        ScriptEngineManager manager = new ScriptEngineManager();
        return manager.getEngineByName("nashorn");
    });

public Object evaluateThreadSafe(String script) throws ScriptException {
    return engineHolder.get().eval(script);
}

5. Ne Pas Gerer les Exceptions JavaScript

// PROBLEME: Exception JavaScript non capturee correctement
try {
    engine.eval("throw new Error('Erreur volontaire');");
} catch (ScriptException e) {
    // Le message peut etre peu informatif
    System.err.println(e.getMessage());
}

// SOLUTION: Extraire plus d'informations
try {
    engine.eval("throw new Error('Erreur volontaire');");
} catch (ScriptException e) {
    Throwable cause = e.getCause();
    if (cause != null) {
        System.err.println("Cause: " + cause.getMessage());
    }
    System.err.println("Script: ligne " + e.getLineNumber());
}

Conclusion

L’integration de scripts JavaScript avec Java via Nashorn offre une flexibilite remarquable pour les applications necessitant du scripting dynamique. Dans cet article, nous avons couvert :

  • Configuration : Comment initialiser correctement le moteur Nashorn
  • Variables globales : Les methodes put() et get() pour l’echange de donnees
  • Bindings : Le controle avance de la portee des variables
  • Objets complexes : Le passage de Maps, Lists et objets personnalises
  • Interfaces Java : L’implementation d’interfaces depuis JavaScript
  • Bonnes pratiques : Gestion des erreurs, reutilisation, isolation
  • Pieges courants : Thread-safety, types, fuites memoire

Alternatives Modernes

Pour les projets sur Java 15+, considerez ces alternatives :

AlternativeAvantages
GraalJSCompatible ECMAScript 2022, performances elevees
RhinoMature, fonctionne sur toutes versions Java
J2V8Binding V8, tres rapide

Pour Aller Plus Loin

  • GraalVM : graalvm.org - Runtime polyglotte moderne
  • JSR 223 : Specification de l’API Scripting Java
  • ECMAScript : Comprendre les differences entre ES5 (Nashorn) et ES6+

N’hesitez pas a partager vos experiences et questions dans les commentaires!

Advertisement

In-Article Ad

Dev Mode

Share this article

Mahmoud DEVO

Mahmoud DEVO

Senior Full-Stack Developer

I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.

Enjoyed this article?

Subscribe to get more tech content delivered to your inbox.

Related Articles