«1. Обзор

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

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

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

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

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

3. Настройка Maven

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

<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:

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

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

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

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

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

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

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

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

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

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

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

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

http://host:port/contextroot/article

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

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

URL-адрес:

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

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

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

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

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

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

http://host:port/contextroot/article

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

http://host:port/contextroot/baeldung/article

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

http://host:port/contextroot/published_article

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

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

public void search() {

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

}

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

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

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

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

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

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

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

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

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

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

render("/common/error");

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

Отобразим данные в шаблоне:

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

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

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

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>

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

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

Наконец, зарегистрируем это в приложении context и внедрить его в контроллер по мере необходимости:

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

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", "baeldung");
        ar.set("words", "1250");
        ar.setDate("date", Instant.now());
        return ar;
    }
}

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

public class ArticleServiceModule extends AbstractModule {

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

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

public class AppBootstrap extends Bootstrap {

    public void init(AppContext context) {
    }

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

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

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

@Inject
private ArticleService articleService;

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

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

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

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

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

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

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

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

Последняя версия подключаемого модуля находится здесь.

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

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

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

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

...
<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>
...

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

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

mvn jetty:run

«

In this article, we learned about the basic concepts and conventions of the ActiveWeb framework. In addition to these, the framework has more features and capabilities than what we have discussed in here.

Please refer the official documentation for more details.

And, as always, the sample code used in the article is available over on GitHub.