1. 概述
在本文中,我們將介紹 Activeweb – JavaLite 提供的全棧 Web 框架,它提供了開發動態 Web 應用程序或 RESTful Web 服務所需的一切。
2. 基本概念和原則
Activeweb 採用“約定優於配置” – 這意味着它可配置,但具有合理的默認值,並且不需要額外的配置。我們只需要遵循幾個預定義的約定,例如命名類、方法和字段的特定格式。
它還通過重新編譯和加載源代碼到正在運行的容器(默認情況下為 Jetty)簡化了開發。
對於依賴管理,它使用 Google Guice 作為 DI 框架;要了解更多關於 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 應用程序的結構:
如我們所見,控制器、服務、配置和模型應該位於app包中的各自子包中。
視圖應該位於WEB-INF/views目錄中,每個視圖擁有自己的子目錄,基於控制器名稱。例如,app.controllers.ArticleController應該有一個article/子目錄,其中包含所有針對該控制器的視圖文件。
部署描述符或web.xml通常包含一個<filter>和一個對應的<filter-mapping>。由於框架是一個 Servlet 過濾器,而不是<servlet>配置,因此有過濾器配置:
...
<filter>
<filter-name>dispatcher</filter-name>
<filter-class>org.javalite.activeweb.RequestDispatcher</filter-class>
...
</filter>
...
我們還需要一個<init-param>,root_controller,用於定義應用程序的默認控制器——類似於home控制器:
...
<init-param>
<param-name>root_controller</param-name>
<param-value>home</param-value>
</init-param>
...
5. Controllers
Controllers是ActiveWeb應用程序的主要組件;並且,正如前面提到的,所有Controllers都應該位於
public class ArticleController extends AppController {
// ...
}
請注意,Controller繼承了
5.1. Controller URL Mapping
Controllers會根據約定自動映射到URL。例如,
http://host:port/contextroot/article
現在,這將映射到Controller中的默認動作。動作只是Controller中的方法。將默認方法命名為
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方法具有Controller動作。只需使用
5.2. Controller URL Resolution
框架使用Controller名稱和子包名稱來生成Controller URL。例如,
http://host:port/contextroot/article
如果Controller位於子包中,URL將簡化為:
http://host:port/contextroot/baeldung/article
對於具有多個單詞的Controller名稱(例如,
http://host:port/contextroot/published_article
5.3. Retrieving Request Parameters
在Controller中,我們可以使用
public void search() {
String keyword = param("key");
view("search",articleService.search(keyword));
}
我們可以使用後者來獲取所有參數,如果需要:
public void search() {
Map<String, String[]> criterion = params();
// ...
}
6. Views
在 ActiveWeb 術語中,視圖通常被稱為模板;這主要是因為它使用了 Apache FreeMarker 模板引擎而不是 JSPs。 您可以在我們的指南中瞭解更多關於 FreeMarker 的信息,這裏。
將模板放在 WEB-INF/views 目錄中。 每個控制器都應該有一個子目錄,名稱與該控制器相對應,幷包含該控制器所需的所有模板。
6.1. Controller View Mapping
當控制器被訪問時,默認動作 index() 會被執行,並且框架會從 WEB-INF/views/article/ 目錄中選擇 index.ftl 模板用於該控制器。 同樣,對於任何其他動作,視圖將根據動作名稱進行選擇。
這並不總是我們想要的結果。 有時我們可能想要根據內部業務邏輯返回一些視圖。 在這種情況下,render() 方法來控制該過程。 從父類 org.javalite.activeweb.AppController 中:
public void index() {
render("articles");
}
請注意,自定義視圖的位置也應在與該控制器同一視圖目錄中。 如果不是這樣,請在將模板名稱與模板所在的目錄名稱前綴並將其傳遞給 render() 方法:
render("/common/error");
6.3. Views With Data
要將數據發送到視圖中,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", "baeldung");
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)進行編寫。
我們將使用 JSpec 中的 org.javalite.activeweb.ControllerSpec 類來測試我們的控制器,並且我們將按照類似的約定命名測試類:
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. 部署應用程序
可以將應用程序部署到任何servlet容器,例如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 框架的基本概念和約定。除此之外,該框架還具有我們討論過的更多功能和能力。
請參閲官方 文檔 以獲取更多詳細信息。