Если вы занимались разработкой серьезного приложения на GWT, то вы наверняка сталкивались с проблемой, что компиляция GWT приложения занимает все больше и больше времени. Но при этом вы ещё не закончили разработку и осталось множество функциональности, которую нужно реализовать к определенному сроку.
Основываясь на том, что вы столкнулись с данной проблемой, то вы понимаете устройство и основы GWT, поэтому данная статья не руководство разработчика, а только обзор и попытка найти решение проблемы.
Возможно вы уже искали ответ на вопрос как ускорить компиляцию приложения и пробовали такую опцию как -draftCompile и некоторые другие способы, но что если этого все равно недостаточно?
Разделение приложения GWT на модули, в некотором роде решает проблему времени компиляции, но это уменьшает гибкость архитектуры, заставляет тратить время на поддержку, связь между модулями, а так же не дает пользоваться возможность Code Splitting представленной в GWT 2, которая очень эффективно позволяет бороться с размером JavaScript файла. Используя Code Splitting вы можете разрабатывать очень большое приложение без необходимости загружать полный JavaScript во время загрузки страницы. Главная проблема Code Splitting — это ещё большее увеличение времени компиляции.
В процессе разработки большого приложения я пришел к некоторым выводам, которые помогут вам сократить время тестовой компиляции. Идея проста. Представим, что у вашего приложения около 100 различных “экранов”, но во время разработки, вам интересен только тот, над которым вы сейчас работаете и несколько зависимых от него. Остальные же вы просто напросто не используете, поэтому их компиляция — зря потраченное время. Точно так же как не нужна вам на данном этапе компиляция со всеми локализациями (это в случает если вы используете стандартные средства GWT для интернационализации, а так же не используемые permutatuions). Поэтому было бы просто здорово если бы мы могли исключить ненужные Java классы из компиляции GWT приложения. Для простоты я делю Java классы на связанные группы, поэтому если я работаю над классом из группы A, то я исключаю из компиляции группы B, C и т.д. это позволяет уменьшить время компиляции и сделать работу более продуктивной. Я специально использую термин “Группа”, а не “Модуль”, так как модуль имеет несколько другое значение с точки зрения технологии GWT.
Давайте представим, что мы разрабатываем приложение Web Notepad. Данный проект легко разделить на группы, основываясь на основном меню: File, Edit, Format, View и Help. Сделаем следующее. Сначала создадим новый GWT проект, после создания получим структуру как на изображении ниже:
Автоматически будет сгенерирован следующий gwt.xml файл:
Учтите что для того чтобы включить Java классы в список на компиляцию в JavaScript эти классы должны храниться в соответствующих пакетах. При таком подходе мы можем добавлять и удалять необходимые нам группы из списка на компиляцию. Давайте создадим несколько групп для примера:
Эти пакеты не будут компилироваться пока мы не опишем их в gwt.xml файле следующим образом:
Мы хотели добиться того чтобы мы могли легко включать и выключать модули нужные нам именно на текущей стадии разработки. Для этого нам нужно организовать проект таким образом, чтобы во время компиляции они не зависели друг от друга. Так же как и EntryPoint класс (в нашем случае это Notepad.java) не должен напрямую зависеть от какого-либо из модулей. Чтобы этого добиться сделаем следующий интерфейс:
Этот интерфейс должен связывать пункты меню приложения с соответствующей группой, которая отвечает за реализацию того или иного пункта. Давайте напишем реализацию некоторых классов, для того чтобы все прояснить:
Как вы видите мы реализовали NotepadController для Edit и File групп. NotepadEditUndo, NoteadFileNew и NotepadFileOpen — это просто java классы, котоыре делают что-то в ответ на действие в меню. В вашем приложении это могут быть просто панели, которые необходимо поместить в главную часть экрана. Код NavigationController`а будет при этом будет следующим:
package com.careature.notepad.file;import com.careature.notepad.client.Notepad;import com.careature.notepad.shared.NotepadController;public class NotepadFileController implements NotepadController {
public boolean switchToScreen(String menuId, Object parameter) {
if ("New".equals(menuId)) {
Notepad.setCurrentContext(new NotepadFileNew());
return true;
} else if ("Open".equals(menuId)) {
Notepad.setCurrentContext(new NotepadFileOpen());
return true;
} // else if (....) {
// ......
// } return false;
}
}
А EntryPoint класс Notepad будет иметь вид:
private List controllers = new ArrayList();private void setupControllers() {
controllers.add(new NotepadEditController());
controllers.add(new NotepadFileController());
}private void processMenuAction(String menuId, Object parameter) {
for (NotepadController c : controllers) {
if (c.switchToScreen(menuId, parameter)) {
return;
}
}
}public static void setCurrentContext(Object context) {
/**
* Do whatever you need to do
*/
}
Теперь если я закомментирую соответствующую группу в gwt.xml и соответствующий import в Notepad.java это полностью исключит ту или иную группо java классов из компиляции. При этом приложение будет работать, за исключением отключенных групп.
Если же вам необходимо в некоторый момент протестировать полное приложение — не отчаивайтесь. Просто отключите ненужные permutations и локализации. Например, если вы тестируете приложение под Firefox добавьте строку:
<set-property name="user.agent" value="gecko"/>
в ваш gwt.xml файл. Таким же образом можно отключать ненужные вам в данный момент локализации.
Удачной и продуктивной работы!