Быстрая разработка веб-приложений на Java

asolntsev 12 августа 2011 в 00:36 112k
Как вы разрабатываете веб-приложение на Java?
После каждого изменения, как вы его запускаете и проверяете? Сколько времени занимает редеплой приложения и рестарт контейнера?

Мне довелось видеть разные варианты: от полной пересборки WAR-файла до использования плагинов для IDE типа MyEclipse, WTP и «коннекторов» для сервлет-контерйнеров. У некоторых из них есть явные недостатки, другие вполне работают — но есть способ проще!

Запускалка


Этот способ разработки позволяет максимально просто и гибко настроить приложение с минимальным временем редеплоя. Вам надо всего лишь написать один простенький Java-класс с main-методом, который запустит сервер Jetty сразу с нужными приложениями (т.н. Embedded Mode).



Вот как выглядит запускалка в минимальной комплектации:
import org.mortbay.jetty.*;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.webapp.WebAppContext;

public class Launcher {
  public static void main(String[] args) throws Exception {
    Server server = new Server();

    Connector connector = new SelectChannelConnector();
    connector.setPort(8080);
    server.addConnector(connector);

    WebAppContext root = new WebAppContext("root/src/main/webapp", "/");
    WebAppContext reports = new WebAppContext("reports/src/main/webapp", "/reports");
    WebAppContext petclinic = new WebAppContext("petclinic/src/main/webapp", "/petclinic");
    server.setHandlers(new Handler[]{root, reports, petclinic});

    server.start();
  }
}


Этот код запускает сервер (сервлет-контейнер), слушающий порт 8080, с тремя веб-приложениями, код для которых берётся прямо из папок проекта (root/src/main/webapp, reports/src/main/webapp и petclinic/src/main/webapp), то есть любые изменения в файлах вступают в силу сразу, без необходимости что-то пересобирать и передеплоить.

Понятное дело, при добавлении новых методов придётся рестартовать, но и в этом случае рестарт происходит максимально быстро, буквально в течение секунды (конечно, если ваши приложения не делают чего-то сложного при запуске). Если к этому ещё и прикрутить JRebel, будет вообще шоколадно.

Вот и вся хитрость.
Наверняка в продакшине вы используете не Jetty, а что-нибудь типа Tomcat, JBoss или WebLogic. Неважно, мы ведь сейчас говорим о разработке, где скорость, стабильность и т.д. неважны, а важна лёгкость настройки, скорость запуска и редеплоя. И тут Embedded Jetty — то, что доктор прописал.

Вы можете просто запускать этот main-класс из своей любимой IDE, отлаживать, тестировать, рестартовать; и не нужны никакие плагины, не нужно искать файлы конфигурации, не нужно копаться в XML'ах. Все настройки под рукой. Вот это жизнь!

Для запускали в минимальной комплектации достаточно всего трёх jar-файлов: servlet-api.jar, jetty.jar, jetty-util.jar.


[UPD] Как подсказывает kblcuk, приведённый код запускалки работает только для версии Jetty 6. Начиная с седьмой версии Jetty, названия пакетов и классов были поменяны с org.mortbay.jetty.* на org.eclipse.jetty.*, так что придётся подправить import.

Тюнинг


Рассмотрим некоторые дополнительные возможности, которые могут пригодиться в зависимости от проекта.

Загрузчики классов


Приведённый выше код загружает классы и зависимости всех веб-приложений в одном загрузчике (ClassLoader). Если по какой-то причине вам важно, чтобы у веб-приложений были разные наборы классов и зависимостей (jar'ов), это тоже можно сделать, дописав немножко кода:

    WebAppContext petclinic = new WebAppContext("petclinic/src/main/webapp", "/petclinic");
    WebAppClassLoader classLoader = new WebAppClassLoader(petclinic);
    classLoader.addClassPath("petclinic-core/target/classes");
    classLoader.addClassPath("petclinic-services/target/classes");
    petclinic.setClassLoader(classLoader);


Я сам активно это использовал, когда мне надо было запускать сразу много веб-приложений, каждое со своими (конфликтующими) зависимостями. Очень просто, очень удобно.

JSP


Старожилы помнят, что в незабвенные времена для формирования HTML часто использовалась такая штука, как JSP. Если в вашем проекте JSP тоже используется, то придётся добавить ещё несколько зависимостей: eclipse-jdtcore.jar, jsp-api-2.1-glassfish.jar, jsp-2.1-glassfish.jar

Конфигурация JDBC-ресурсов



Возможно, есть решение попроще, но вот такой вариант у меня заработал:
import import javax.naming.InitialContext;
import com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean;

static void setupDataSources() { 
    System.setProperty("java.naming.factory.initial", "org.mortbay.naming.InitialContextFactory");
    AtomikosNonXADataSourceBean dataSource = new AtomikosNonXADataSourceBean();
    dataSource.setUniqueResourceName("jdbc/portal");
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
    dataSource.setUser("myusername");
    dataSource.setPassword("mypassword");
    dataSource.setMaxPoolSize(10);

    new InitialContext().createSubcontext("jdbc").bind("portal", dataSource);
}


Для этого вам дополнительно потребуется ещё немножко зависимостей: transactions.jar, jetty-naming.jar, transactions-api.jar, transactions-jta.jar, transactions-jdbc.jar, atomikos-util.jar


Нельзя ли это ещё немножко автоматизировать?


Можно. Но только если нужно.
Если адрес или пароль базы данных у вас часто меняется, возможно, вы захотите считывать их из какого-нибудь файла. Это можно. Но стоит ли? Главное, что все настройки сосредоточены в одном файле, а что это за файл — java-класс или *.properties — не всё ли равно?

Если список веб-приложений постоянно меняется, возможно, имеет смысл написать плагин для Eclipse или IDEA, который бы запускал Jetty со всеми проектами, которые есть в данный момент в Eclipse. Один такой плагин описан здесь — он находит все проекты, в которых есть файл web.xml, а остальные проекты добавляет к ним в classpath.

Минусы


Объективности ради попробую назвать некоторые подводные камни, таящиеся в этом подходе.
  • (Мнимый минус) Контейнер Jetty уступает в производительности Tomcat, JBoss, Resin и др.
    Ерунда. Мы сейчас говорим о разработке — тут производительность совершенно не важна.
    Кстати, это ещё вопрос, уступает ли.

  • (Мнимый минус) Контейнер Jetty нестабилен / иногда грохается / содержит утечки памяти.
    Ерунда. См. предыдущий пункт.

  • Если ваше приложение использует какие-то специфические особенности какого-то конкретного контейнера (особенно это касается WebLogic или Oracle AS), то на Jetty оно работать, естественно, не будет.
    Наверное, тут ничего не поделаешь. Могу только посоветовать не использовать такие возможности, чтобы ваше приложение не зависело от контейнера.

  • Иногда случается, что ваше приложение прекрасно работает на вашем компьютере в режиме Embedded Jetty, но грохается, будучи собранным в war и установленным на другой сервер.
    Это хоть и редко, но случается. В частости, может возникнуть ошибка из-за загрузчиков классов или версий jar'ов.


Альтернативы


Ради интереса я перечислю альтернативные способы разработки веб-приложений на Java со своей субъективной оценкой.
  • Полная пересборка WAR-файла и копирование в Tomcat/webapps.
    Самое надёжное и самое медленное решение. Представьте себе, ждать 1-15 минут после каждого изменения CSS! Нет уж, увольте.
    Минус: ужасно долго.
    Минус: дебаг придётся отдельно настраивать (remote debug). Пусть это не очень сложно, но всё-таки лишние усилия.
  • MyEclipse
    Платная IDE на базе Eclipse. При компиляции проекта копирует все ресурсы в отдельную папку — у меня это работало ужасно долго. Также у меня остались неприятные воспоминания от заморочек с настройкой.
  • Eclipse WTP (Web Tools Platform) — бесплатный, но тяжёлый плагин для Eclipse
    Я несколько раз пытался, но так и не смог настроить его так, как мне надо.
    Плюс: умеет работать с несколькими разными контейнерами (Tomcat, JBoss и т.д.)
    [UPD] Впрочем, вот тут Akvel рассказывает, как он безболезненно использует Eclipse WTP.
  • Application Server integrations
    аналог Eclipse WTP, доступный только в платной версии Intellij IDEA.
    Минус: платный.
  • JRebel
    Помимо своей основной задачи, JRebel позволяет конфигурировать ClassPath приложений прямо из проектов.
    Минус: JRebel платный.
    Плюс: если ваш работодатель на него раскошелится, JRebel может сильно упростить и ускорить работу разработчика.
  • Play! framework
    Умеет запускать приложение прямо из проекта и перегружать изменённый код на лету. Очень удобно.
    Минус: вы ограничены одним фреймворком, у которого есть свои особенности и ограничения.
  • Run Jetty Run
    Плагин для Eclipse, позволяющий запускать Jetty с приложением прямо из проекта. По сути, он делает в точности то же, что описано в этой статье.
    Плюс: содержит в себе все необходимые jar'ы, вам не придётся ничего дополнительно скачивать, кроме самого плагина.
    Минус: умеет запускать только один проект.
    Минус: не умеет настраивать соединение с базой данных. Придётся по-любому делать это руками.
  • [UPD] Jetty Maven plugin
    Как подсказывает 1nd1go, если в вашем проекте используется Maven, вы можете запускать веб-приложения командой "mvn jetty:run".
    Плюс: если используете плагин для Maven в своей IDE, можете запускать эту команду прямо из IDE; при этом можно и дебажить, т.к. jetty плагин запускается в том же процессе, что и мавен.
    Минус: вы привязаны к системе сборки (maven) и к IDE.
  • [UPD] Jetty Gradle plugin
    Если в вашем проекте используется Gradle, вы можете запускать веб-приложения командой "gradle jettyRun".
    Плюс: эту команду прямо из IDE (даже без всяких плагинов), при этом можно и дебажить.
    Минус: вы привязаны к системе сборки (gradle).
  • [UPD] Sysdeo Eclipse Tomcat Launcher plugin
    Как подсказывает helions8, для Eclipse есть хороший плагин Sysdeo Tomcat launcher, который позволяет совместить контекст томката и проекта, потому не нужно собирание war-файла и подкладывание его контейнеру, всякий css, html и прочий js правится сразу, на горячую. В большинстве случаев работает hot replace. По сути, этот плагин делает то же самое, что и наш Launcher
    Минус: вы привязаны к Eclipse.
    Минус: требует больше суеты. Надо и Tomcat самому скачать, и какой-то новый «tomcat project» создать — в общем, вполне неплохое средство, но запускалка проще.
  • [UPD] Spring source tool suite
    Как подсказывает alexeygrigorev, для Eclipse есть плагин Spring source tool suite, который имеет встроенный «навороченный томкат», умеющий редеплоить при сохранении файлов
    Минус: вы привязаны к Eclipse и STS.


Остаётся добавить, что в embedded режиме можно запускать не только Jetty, но и Tomcat, Glassfish и др. Конкретный контейнер неважен, важен принцип: никакой длительной пересборки, установки, распаковки и настройки. Всё запускается быстро и просто из одного класса.

Я сам разрабатываю веб-приложения таким образом уже несколько лет и всем остальным советую. Так вы будете тратить меньше времени на суету вокруг сборки и инсталляции и сможете сосредоточиться на собственно разработке.

Запускай!

Проголосовать:
+34
Сохранить: