Вы когда-нибудь задумывались над тем, почему в Java мы упаковываем веб-приложения в файлы WAR (или структуры каталогов WAR)? Это, безусловно, удобный способ перемещения приложения и его зависимостей из одного места в другое. Но разве не было бы неплохо, если бы все могло оставаться на прежнем месте и не было бы перемещения файлов? Разве не было бы неплохо, если бы вы указали требуемую версию Jetty или Tomcat, как и в случае с любой другой зависимостью? Подход без WAR — это тот, который завоевывает популярность в таких веб-фреймворках Java, как Play! угробить файлы WAR. С помощью стандартных веб-приложений на Java мы также можем отказаться от файлов WAR, просто запустив встроенный сервер Jetty или Tomcat. Давайте попробуем и посмотрим, как все пойдет.
Для этого эксперимента я собираюсь использовать Maven и Jetty. Он по-прежнему будет использовать ту же стандартную исходную структуру для файла WAR ( src / main / java , src / main / webapp и т. Д.). Основное отличие состоит в том, что я на самом деле запускаю Jetty, используя старый добрый статический void main . Это похоже на использование цели jetty: запустить цель, но позволит нам иметь одинаковую точную настройку при разработке и производстве. Статический материал будет в src / main / webapp , скомпилированные классы будут в target / classes, и зависимости будут правильными, если Maven загрузит их. Во-первых, вот небольшой класс Java ( src / main / java / foo / Main.java ), который устанавливает сервер Jetty и запускает его:
package foo; import java.io.File; import java.net.URL; import java.util.jar.Attributes; import java.util.jar.JarFile; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.*; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.webapp.WebAppContext; public class Main { public static void main(String[] args) throws Exception { String webappDirLocation = "src/main/webapp/"; Server server = new Server(8080); WebAppContext root = new WebAppContext(); root.setContextPath("/"); root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml"); root.setResourceBase(webappDirLocation); root.setParentLoaderPriority(true); server.setHandler(root); server.start(); server.join(); } }
Как видите, Main просто ссылается на каталог webapp, поэтому мне не нужно копировать материал оттуда в другое место. Далее у меня есть небольшой тестовый сервлет ( src / main / java / foo / HelloServlet.java ):
package foo; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.*; public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter out = resp.getWriter(); out.println("hello, world"); out.close(); } }
А теперь файл web.xml ( src / main / webapp / WEB-INF / web.xml ):
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>foo.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
И, наконец, файл pom.xml, который определяет Jetty как зависимость и предоставляет простой способ запуска класса Main:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jamesward</groupId> <version>1.0-SNAPSHOT</version> <name>warless_java_web_app</name> <artifactId>warless_java_web_app</artifactId> <packaging>jar</packaging> <properties> <jettyVersion>7.3.1.v20110307</jettyVersion> </properties> <dependencies> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> <version>${jettyVersion}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-webapp</artifactId> <version>${jettyVersion}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> <version>${jettyVersion}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.2</version> <configuration> <mainClass>foo.Main</mainClass> </configuration> </plugin> </plugins> </build> </project>
А теперь просто запустите:
mvn compile exec:java
Maven компилирует мои классы Java в target / classes, а затем цель exec: java запускает Main, который находит другие активы WAR в каталоге src / main / webapp . Если вы следили за этим, сделайте запрос на http: // localhost: 8080 /, чтобы убедиться, что он работает (что должно).
Есть две альтернативы запуску Jetty от Maven. Вы можете использовать плагин Maven appassembler для создания сценариев запуска, содержащих правильные ссылки CLASSPATH, а затем запустить основной класс, используя сгенерированные сценарии. Или вы можете использовать сборку Maven или плагин shade для создания JAR-файла, содержащего приложение и все его зависимости.
Вот пример раздела файла pom.xml для использования плагина appassembler:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>appassembler-maven-plugin</artifactId> <version>1.1.1</version> <configuration> <assembleDirectory>target</assembleDirectory> <generateRepository>false</generateRepository> <programs> <program> <mainClass>foo.Main</mainClass> <name>main</name> </program> </programs> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>assemble</goal> </goals> </execution> </executions> </plugin>
Для генерации стартовых скриптов просто запустите:
mvn install
Затем для запуска сценария установите переменную среды REPO в свой репозиторий Maven:
export REPO=~/.m2/repository
А затем просто запустите скрипт:
sh target/bin/main
Весь код для этого примера находится на github.com:
https://github.com/jamesward/warless_java_web_apps
Чтобы сделать все это еще проще, у Jetty есть архетип Maven для генерации всего для вас. Чтобы создать новый проект, содержащий эту настройку, выполните:
mvn archetype:generate -DarchetypeGroupId=org.mortbay.jetty.archetype -DarchetypeArtifactId=jetty-archetype-assembler -DarchetypeVersion=7.5.0-SNAPSHOT
И теперь вы готовы создать веб-приложение Java без войны!
Эта настройка действительно необходимый минимум для обработки запросов к веб-ресурсам и сервлетам, вам нужно будет проделать немного больше работы, если вы хотите добавить поддержку JSP. Узнайте больше об этом в документации Jetty .
Итак … Что вы думаете о веб-приложениях Java без файлов WAR и упаковки WAR? Я хотел бы услышать ваши мысли!