Перейти к основному содержимому

Введение в ActiveWeb

· 7 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

1. Обзор

В этой статье мы собираемся проиллюстрировать Activeweb — веб-фреймворк полного стека от JavaLite — предоставляющий все необходимое для разработки динамических веб-приложений или веб-сервисов с поддержкой REST.

2. Основные понятия и принципы

Activeweb использует «соглашение вместо конфигурации» — это означает, что его можно настраивать, но он имеет разумные значения по умолчанию и не требует дополнительной настройки. Нам просто нужно следовать нескольким предопределенным соглашениям, таким как имена классов, методов и полей в определенном предопределенном формате.

Это также упрощает разработку за счет перекомпиляции и перезагрузки исходного кода в работающий контейнер (Jetty по умолчанию).

Для управления зависимостями он использует Google Guice в качестве DI-фреймворка; чтобы узнать больше о Guice, ознакомьтесь с нашим руководством здесь .

3. Настройка Мавена

Для начала давайте сначала добавим необходимые зависимости:

<dependency>
<groupId>org.javalite</groupId>
<artifactId>activeweb</artifactId>
<version>1.15</version>
</dependency>

Последнюю версию можно найти здесь .

Кроме того, для тестирования приложения нам понадобится зависимость activeweb-testing :

<dependency>
<groupId>org.javalite</groupId>
<artifactId>activeweb-testing</artifactId>
<version>1.15</version>
<scope>test</scope>
</dependency>

Ознакомьтесь с последней версией здесь .

4. Структура приложения

Как мы уже говорили, структура приложения должна следовать определенному соглашению; вот как это выглядит для типичного приложения MVC:

./b632a3b45c266465a63c54acd5384ae2.png

Как мы видим, контроллеры , сервис , конфиг и модели должны находиться в своих собственных подпакетах в пакете приложения .

Представления должны быть расположены в каталоге WEB-INF/views , каждое из которых имеет собственный подкаталог на основе имени контроллера. Например , app.controllers.ArticleController должен иметь подкаталог article/ , содержащий все файлы представлений для этого контроллера.

Дескриптор развертывания или файл web.xml обычно должен содержать <filter> и соответствующий <filter-mapping>. Поскольку фреймворк является фильтром сервлета, вместо конфигурации <servlet> используется конфигурация фильтра:

...
<filter>
<filter-name>dispatcher</filter-name>
<filter-class>org.javalite.activeweb.RequestDispatcher</filter-class>
...
</filter>
...

Нам также нужен <init-param> root_controller , чтобы определить контроллер по умолчанию для приложения — аналог домашнего контроллера:

...
<init-param>
<param-name>root_controller</param-name>
<param-value>home</param-value>
</init-param>
...

5. Контроллеры

Контроллеры являются основными компонентами приложения ActiveWeb; и, как упоминалось ранее, все контроллеры должны находиться внутри пакета app.controllers :

public class ArticleController extends AppController {
// ...
}

Обратите внимание, что контроллер расширяет org.javalite.activeweb.AppController.

5.1. Сопоставление URL контроллера

Контроллеры сопоставляются с URL-адресом автоматически на основе соглашения. Например, ArticleController будет сопоставлен с:

http://host:port/contextroot/article

Теперь это будет сопоставлять их с действием по умолчанию в контроллере. Действия — это не что иное, как методы внутри контроллера. Назовите метод по умолчанию как index():

public class ArticleController extends AppController {
// ...
public void index() {
render("articles");
}
// ...
}

Для других методов или действий добавьте имя метода к URL-адресу:

public class ArticleController extends AppController {
// ...

public void search() {
render("search");
}
}

URL-адрес:

http://host:port/contextroot/article/search

У нас даже могут быть действия контроллера, основанные на методах HTTP. Просто аннотируйте метод с помощью @POST, @PUT, @DELETE, @GET, @HEAD. Если мы не аннотируем действие, оно по умолчанию считается GET.

5.2. Разрешение URL контроллера

Платформа использует имя контроллера и имя подпакета для создания URL-адреса контроллера. Например , URL-адрес app.controllers.ArticleController.java :

http://host:port/contextroot/article

Если контроллер находится внутри подпакета, URL просто становится:

http://host:port/contextroot/foreach/article

Для имени контроллера, состоящего более чем из одного слова (например , app.controllers.PublishedArticleController.java ), URL будет разделен символом подчеркивания:

http://host:port/contextroot/published_article

5.3. Получение параметров запроса

Внутри контроллера мы получаем доступ к параметрам запроса с помощью методов param() или params() из класса AppController. Первый метод принимает аргумент типа String — имя параметра, который необходимо получить:

public void search() {

String keyword = param("key");
view("search",articleService.search(keyword));

}

И мы можем использовать его позже, чтобы получить все параметры, если нам нужно:

public void search() {

Map<String, String[]> criterion = params();
// ...
}

6. Просмотры

В терминологии ActiveWeb представления часто называют шаблонами; это в основном потому, что он использует механизм шаблонов Apache FreeMarker вместо JSP. Подробнее о FreeMarker можно прочитать в нашем руководстве, здесь .

Поместите шаблоны в каталог WEB-INF/views . Каждый контроллер должен иметь подкаталог по имени, содержащий все необходимые ему шаблоны.

6.1. Отображение представления контроллера

При попадании в контроллер выполняется действие по умолчанию index() , и платформа выбирает шаблон WEB-INF/views/article/ index.ftl из каталога представлений для этого контроллера. Точно так же для любого другого действия представление будет выбираться на основе имени действия.

Это не всегда то, что нам хотелось бы. Иногда мы можем захотеть вернуть некоторые представления на основе внутренней бизнес-логики. В этом сценарии мы можем управлять процессом с помощью метода render() из родительского класса org.javalite.activeweb.AppController :

public void index() {
render("articles");
}

Обратите внимание, что расположение пользовательских представлений также должно быть в том же каталоге представлений для этого контроллера. Если это не так, добавьте к имени шаблона префикс имени каталога, в котором находится шаблон, и передайте его методу render() :

render("/common/error");

6.3. Представления с данными

Для отправки данных в представления org.javalite.activeweb.AppController предоставляет метод view() :

view("articles", articleService.getArticles());

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

Мы также можем использовать метод assign() для передачи данных в представления. Между методами view() и assign() нет абсолютно никакой разницы — мы можем выбрать любой из них:

assign("article", articleService.search(keyword));

Сопоставим данные в шаблоне:

<@content for="title">Articles</@content>
...
<#list articles as article>
<tr>
<td>${article.title}</td>
<td>${article.author}</td>
<td>${article.words}</td>
<td>${article.date}</td>
</tr>
</#list>
</table>

7. Управление зависимостями

Для управления объектами и экземплярами ActiveWeb использует Google Guice в качестве среды управления зависимостями.

Допустим, нам нужен класс обслуживания в нашем приложении; это отделит бизнес-логику от контроллеров.

Давайте сначала создадим сервисный интерфейс:

public interface ArticleService {

List<Article> getArticles();
Article search(String keyword);

}

И реализация:

public class ArticleServiceImpl implements ArticleService {

public List<Article> getArticles() {
return fetchArticles();
}

public Article search(String keyword) {
Article ar = new Article();
ar.set("title", "Article with "+keyword);
ar.set("author", "foreach");
ar.set("words", "1250");
ar.setDate("date", Instant.now());
return ar;
}
}

Теперь давайте свяжем этот сервис как модуль Guice:

public class ArticleServiceModule extends AbstractModule {

@Override
protected void configure() {
bind(ArticleService.class).to(ArticleServiceImpl.class)
.asEagerSingleton();
}
}

Наконец, зарегистрируйте это в контексте приложения и вставьте в контроллер, как требуется:

public class AppBootstrap extends Bootstrap {

public void init(AppContext context) {
}

public Injector getInjector() {
return Guice.createInjector(new ArticleServiceModule());
}
}

Обратите внимание, что это имя класса конфигурации должно быть AppBootstrap и оно должно находиться в пакете app.config .

Наконец, вот как мы внедряем его в контроллер:

@Inject
private ArticleService articleService;

8. Тестирование

Модульные тесты для приложения ActiveWeb пишутся с использованием библиотеки JSpec из JavaLite.

Мы будем использовать класс org.javalite.activeweb.ControllerSpec из JSpec для тестирования нашего контроллера и назовем тестовые классы, следуя аналогичному соглашению:

public class ArticleControllerSpec extends ControllerSpec {
// ...
}

Обратите внимание, что имя похоже на тестируемый контроллер с «Spec» в конце.

Вот тестовый пример:

@Test
public void whenReturnedArticlesThenCorrect() {
request().get("index");
a(responseContent())
.shouldContain("<td>Introduction to Mule</td>");
}

Обратите внимание, что метод request() имитирует вызов контроллера, а соответствующий HTTP-метод get() принимает имя действия в качестве аргумента.

Мы также можем передать параметры контроллеру с помощью метода params() :

@Test
public void givenKeywordWhenFoundArticleThenCorrect() {
request().param("key", "Java").get("search");
a(responseContent())
.shouldContain("<td>Article with Java</td>");
}

Чтобы передать несколько параметров, мы также можем использовать цепочку методов с помощью этого гибкого API.

9. Развертывание приложения

Приложение можно развернуть в любом контейнере сервлетов, таком как Tomcat, WildFly или Jetty. Конечно, самым простым способом развертывания и тестирования будет использование плагина Maven Jetty:

...
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.8.v20171121</version>
<configuration>
<reload>manual</reload>
<scanIntervalSeconds>10000</scanIntervalSeconds>
</configuration>
</plugin>
...

Последняя версия плагина здесь .

Теперь, наконец, мы можем запустить его:

mvn jetty:run

10. Заключение

В этой статье мы узнали об основных концепциях и соглашениях фреймворка ActiveWeb. В дополнение к этому, фреймворк имеет больше функций и возможностей, чем мы обсуждали здесь.

Пожалуйста, обратитесь к официальной документации для получения более подробной информации.

И, как всегда, пример кода, использованный в статье, доступен на GitHub .