Работа с WebAssembly и Java: запуск Java-кода в браузере

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Зачем Java в WebAssembly?

WebAssembly (WASM) — это низкоуровневый бинарный формат инструкций, предназначенный для выполнения в браузере с почти нативной производительностью. Долгое время Java оставалась вне этой экосистемы из-за своей виртуальной машины (JVM). Однако современные инструменты, такие как TeaVM и CheerpJ, позволяют компилировать Java-код в WebAssembly, открывая новые возможности для веб-разработки.

В этой статье мы разберем, как работает связка Java + WebAssembly, рассмотрим практические примеры и оценим производительность. Вы узнаете, как перенести существующий Java-код в браузер без переписывания на JavaScript.

Основные подходы к интеграции Java и WebAssembly

Существует два основных способа запуска Java-кода в WebAssembly:

  • Компиляция байт-кода в WASM — используется TeaVM, который транслирует Java-байткод в WebAssembly.
  • Запуск JVM в браузере — CheerpJ эмулирует JVM в WebAssembly, позволяя запускать неизмененные Java-приложения.

Первый подход предпочтительнее для новых проектов, так как дает меньший размер бандла и лучшую производительность. Второй подходит для портирования legacy-приложений.

Настройка проекта с TeaVM

Для начала работы с TeaVM добавьте зависимость в pom.xml (Maven):

<dependency>
    <groupId>org.teavm</groupId>
    <artifactId>teavm-classlib</artifactId>
    <version>0.9.0</version>
</dependency>

Создайте простой Java-класс для компиляции:

import org.teavm.jso.JSBody;
import org.teavm.jso.JSExport;

public class Calculator {
    
    @JSExport
    public static int add(int a, int b) {
        return a + b;
    }
    
    @JSExport
    public static int factorial(int n) {
        if (n <= 1) return 1;
        return n * factorial(n - 1);
    }
}

Аннотация @JSExport делает методы доступными из JavaScript. После компиляции вы получите WASM-модуль и JavaScript-обертку.

Практический пример: Компиляция и вызов из JavaScript

Рассмотрим полный цикл от компиляции до вызова в браузере. Сначала скомпилируем Java-код с помощью плагина TeaVM:

<plugin>
    <groupId>org.teavm</groupId>
    <artifactId>teavm-maven-plugin</artifactId>
    <version>0.9.0</version>
    <configuration>
        <targetType>WEBASSEMBLY</targetType>
        <mainClass>com.example.Calculator</mainClass>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

После сборки (mvn package) в папке target появятся файлы: calculator.wasm и calculator.js. Теперь создадим HTML-страницу:

<!DOCTYPE html>
<html>
<head>
    <title>Java + WebAssembly Demo</title>
</head>
<body>
    <h1>Калькулятор на Java в WASM</h1>
    <p>Результат: <span id="result"></span></p>
    
    <script src="calculator.js"></script>
    <script>
        // Функция обратного вызова после загрузки WASM
        teavmMain().then(function(api) {
            var sum = api.com.example.Calculator.add(15, 27);
            var fact = api.com.example.Calculator.factorial(5);
            document.getElementById('result').innerHTML = 
                '15 + 27 = ' + sum + '<br>' +
                '5! = ' + fact;
        });
    </script>
</body>
</html>

Откройте страницу в браузере — вы увидите результат работы Java-методов, скомпилированных в WebAssembly.

Производительность и ограничения

Тестирование показывает, что Java-код, скомпилированный в WASM через TeaVM, работает в 1.5-3 раза медленнее нативного JavaScript для простых операций. Однако для сложных вычислений (математические алгоритмы, обработка данных) WASM может быть быстрее за счет оптимизаций низкоуровневого кода.

Основные ограничения:

  • Размер бандла — минимальный WASM-модуль занимает ~100 КБ, с библиотеками может достигать нескольких МБ.
  • Ограниченная поддержка Reflection — TeaVM не поддерживает динамическую загрузку классов.
  • Нет доступа к Java SE API — Swing, AWT и другие GUI-библиотеки недоступны.
  • Ограниченная работа с потоками — WebAssembly пока не поддерживает многопоточность в полном объеме.

Для обхода ограничений используйте следующие приемы:

  • Избегайте Reflection и динамической загрузки классов.
  • Используйте примитивные типы вместо объектов для критичных участков.
  • Применяйте @JSBody для вызова нативных JavaScript-функций из Java.

Пример оптимизированного кода

import org.teavm.jso.JSBody;
import org.teavm.jso.JSExport;

public class MatrixMul {
    
    @JSExport
    public static double[][] multiply(double[][] a, double[][] b) {
        int n = a.length;
        double[][] result = new double[n][n];
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                double sum = 0;
                for (int k = 0; k < n; k++) {
                    sum += a[i][k] * b[k][j];
                }
                result[i][j] = sum;
            }
        }
        return result;
    }
    
    @JSBody(params = {"message"}, script = "console.log(message);")
    public static native void log(String message);
}

Использование примитивных типов (

Блоги

Рекомендаций по книгам