Sitebricks(仍处于测试阶段)是一个新的Java™Web应用程序框架。 您可能会想,“为什么还需要另一个Web框架?” 使用Google Sitebricks,您可以快速构建可以由其他人维护或使用的Web应用程序。 Sitebricks建立在Guice之上。 它将Guice的许多原理扩展和扩展到了Web。 与Guice一样,它积极使用批注将配置保留为代码的一部分。 您无需创建或编辑大量XML文件即可使用Guice。 相反,Sitebricks允许您在编写更少代码的同时创建Web应用程序。 您编写的代码将很简单。 您可以查看Sitebricks代码并快速了解发生了什么。 Sitebricks不会损害类型安全性或性能。
在本文中,通过创建示例Web应用程序来了解Sitebricks的功能。 名为Reviewr的示例应用程序将允许用户读取和撰写餐厅评论。 您将使用数据库来保留应用程序中使用的数据(但不会在此上花费太多时间)。 该示例重点介绍利用Sitebricks功能的应用程序部分。
从本文底部的表中下载 Reviewer应用程序示例代码。
Guice由Sitebricks和示例应用程序使用。 熟悉Guice或其他依赖注入框架(如Spring)将很有用。
像许多现代Java框架一样,Sitebricks也利用Maven。 许多框架使用Maven的原型系统来建立一个新项目。 这也是Sitebricks的计划,但是在撰写本文时,还没有准备好。 在本文的示例中,您将更多地采用手动方法。 您将遵循Maven最佳实践,并首先创建一个业务逻辑项目,如清单1所示。
$ mvn archetype:create -DgroupId=org.developerworks.reviewr -DartifactId=reviewrMain
[INFO] Scanning for projects...
该代码创建了一个简单的项目结构,这是典型的Java应用程序,最终将被打包为一个JAR文件。 下一步是创建第二个项目,它将是Web应用程序,如下所示。
$ mvn archetype:create -DgroupId=org.developerworks.reviewr -
DartifactId=reviewrWeb -DarchetypeArtifactId=maven-archetype-webapp
[INFO] Scanning for projects...
这与清单1相似,但是这次您提供了maven-archetype-webapp
的原型ID。 它是Maven的核心原型,它创建了一个基本的Java Web应用程序项目。 现在,您已经创建了项目,打开它们的pom文件以向其中添加Sitebricks。 首先对main
业务逻辑项目执行此操作,如清单3所示。
<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>org.developerworks.reviewr</groupId>
<artifactId>reviewrMain</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>reviewrMain</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>sonatype-google-snapshots</id>
<name>Sonatype Google Snapshots Repository</name>
<url>http://oss.sonatype.org/content/repositories/
google-snapshots/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.sitebricks</groupId>
<artifactId>sitebricks</artifactId>
<version>0.8-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.2.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
从清单1生成的原始文件开始,已对pom文件进行了多种修改。
http://oss.sonatype.org/content/repositories/google-snapshots/
,您可以在其中找到Sitebricks和Guice的Maven工件。 Web应用程序项目的pom文件如下所示。
<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>org.developerworks.reviewr</groupId>
<artifactId>reviewrWeb</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>reviewrWeb Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.developerworks.reviewr</groupId>
<artifactId>reviewrMain</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>reviewrWeb</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
清单4显示了两个小的修改。 添加了对主项目的依赖关系,它将引入您在主项目中编写的代码以及主项目的所有依赖关系(包括对Sitebricks的依赖关系)。 您还添加了一个Jetty插件,该插件可以启动嵌入式Jetty Web服务器并在其上运行应用程序。 现在已经设置了项目,您可以开始使用Sitebricks。
使用Sitebricks的好处之一是它需要最少的配置。 首先,您需要修改应用程序的web.xml,如清单5所示。
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Reviewr</display-name>
<filter>
<filter-name>webFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>webFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.developerworks.reviewr.AppInjector</listener-class>
</listener>
</web-app>
上面的代码创建了一个Servlet过滤器,该过滤器将拦截传入的HTTP请求。 您不是使用自己的过滤器,而是使用Guice随附的GuiceFilter。 您正在映射过滤器/*
(将所有请求发送到该过滤器)。
清单5还设置了一个Servlet上下文侦听器。 这是启动Web应用程序时将调用的侦听器。 这里的基本原理是在应用程序启动时配置Sitebricks。 因此,您将编写一个名为AppInjector
的自定义类,如下所示。
AppInjector
Sitebricks配置 public class AppInjector extends GuiceServletContextListener {
@Override
public Injector getInjector() {
Module module = new SitebricksModule() {
@Override
protected void configureSitebricks() {
scan(ShowRestaurants.class.getPackage());
bind(RestaurantDao.class).to(RestaurantEmbeddedJdbcDao.class);
bind(ReviewDao.class).to(ReviewEmbeddedJdbcDao.class);
bind(String.class)
.annotatedWith(Names.named("connStr"))
.toInstance("jdbc:derby:reviewrDb;create=true");
bind(DbManager.class).to(EmbeddedDbManager.class);
}
};
Injector injector = Guice.createInjector(module);
return injector;
}
}
如果您熟悉Guice,则上面的代码应该看起来很熟悉。 此代码创建一个Guice模块。 通常,您在Guice中实现configure
方法,但是在这种情况下,您将使用特定于Sitebricks的Module
子类,并实现其configureSitebricks
方法。 在这里,您可以完成所有典型的Guice配置:将接口绑定到其实现并配置应用程序属性。
该示例使用scan
,这是Sitebricks独有的API。 它采用Java程序包,并将其内容添加到Sitebricks管理的类中。 稍后您将看到如何使用它。
您已经完成了应用程序所需的所有配置。 此配置的大多数实际上是特定于应用程序的,例如所有在AppInjector
进行的绑定调用。 它们不是必需的,但已完成,因此您可以使用Guice简化编写业务逻辑的过程。 如果只需要Sitebricks,则只需要web.xml和scan
调用。 现在配置已完成,您可以编写一些应用程序逻辑。
应用程序的第一页将显示系统中的所有餐厅,并使用户可以根据餐厅提供的食物种类来过滤餐厅列表。 Sitebricks是模型-视图-控件(MVC)样式的框架,但它专注于应用程序中的控制器和视图。 控制器是简单的Java类。 清单7显示了用于显示所有餐厅的控制器。
ShowRestaurants
控制器 @At("/")
public class ShowRestaurants {
private List<Restaurant> restaurants;
private String category;
private LinkedHashSet<String> categories;
private final RestaurantDao dao;
@Inject
public ShowRestaurants(RestaurantDao dao){
this.dao = dao;
}
@Get
public void get(){
this.restaurants = dao.findAll();
categories = new LinkedHashSet<String>(restaurants.size());
for (Restaurant restaurant : restaurants){
categories.add(restaurant.getCategory());
}
if (category != null && category.trim().length() > 0){
List<Restaurant> filtered = new ArrayList<Restaurant>
(restaurants.size());
for (Restaurant restaurant : restaurants){
if (restaurant.getCategory().equalsIgnoreCase(category)){
filtered.add(restaurant);
}
}
restaurants = filtered;
}
}
// getters and setters omitted for brevity
}
请注意,此类由@At
注释,这是一个Sitebricks注释。 该"/"
值告诉Sitebricks任何传入请求映射为"/"
这一类。 该类将充当应用程序“主页”的控制器。
Sitebricks严格遵循HTTP和REST约定,因此允许将不同的方法用于不同的HTTP方法,例如GET和POST。 @Get
批注指示用于GET请求的方法。 您可以根据需要命名方法。 “获取”没有什么特别的。 注释决定了将调用哪种方法。
上面的类有几个成员变量。 RestaurantDao
是用于检索页面所需数据的对象。 ShowRestaurants
的构造函数上的@Inject
注释告诉Guice注入RestaurantDao
。 其他成员变量, 餐厅 , 类别和类别 ,形成页的数据模型。 在视图中使用变量restaurant和category 。
类别变量实际上是一个请求参数。 Sitebricks会自动将同名的请求变量与此绑定。 例如,如果请求http://<app>?category=Thai
,则类别将等于Thai。 根据代码,如果给出了一个类别,则将其用于过滤显示的餐馆列表。
清单8显示了将使用由控制器构建的数据模型的视图代码。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<title>Reviewr</title>
</head>
<body>
<h1>All Restaurants</h1>
<form method="get">
<label for="category">Select category:</label>
<select name="category" id="category">
<option value="" label="Any"/>
@Repeat(items=categories, var="category")
<option>${category}</option>
</select>
<input type="submit" value="Filter"/>
</form>
<table border="1">
<thead>
<tr>
<td>Name</td>
<td>Category</td>
<td>Average Rating</td>
</tr>
</thead>
<tbody>
@Repeat(items=restaurants, var="restaurant")
<tr><td><a
href="/restaurant/${restaurant.name}">${restaurant.name}</a></td>
<td>${restaurant.category}</td><td>${restaurant.averageRating}
</td></tr>
</tbody>
</table>
<div class="msg">Not in the list?
<a href="/restaurants/new">Add a restaurant!</a>
</div>
</body>
</html>
上面显示的名称ShowRestaurants.html与控制器匹配。 Sitebricks使用此约定,因此您不必通过配置即可。 HTML主要是纯HTML; 没有脚本(这不是JSP)。 Sitebricks小部件@Repeat
用于迭代集合。 为了评估小部件,它使用MVEL表达式语言。 MVEL是典型JSP / JSF表达语言的超集。 该代码创建一个餐厅表,其中每个餐厅的名称也是/restaurant/${restaurant.name}
的超链接。 例如,如果有一个名为TCBY的餐厅,则该链接将指向/restaurant/TCBY
。 像这样的动态URL是Sitebricks的基本组成部分。 清单9显示了它们如何与处理该示例的控制器一起使用。
RestaurantDetails
控制器 @At("/restaurant/:name")
public class RestaurantDetails {
private final RestaurantDao dao;
private final ReviewDao reviewDao;
private final Logger logger;
private Restaurant restaurant;
private String text;
private String author;
private Integer rating;
private Integer restaurantId;
@Inject
public RestaurantDetails(RestaurantDao dao, ReviewDao reviewDao, Logger logger){
this.dao = dao;
this.reviewDao = reviewDao;
this.logger = logger;
}
@Get
public void get(@Named("name") String name){
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (UnsupportedEncodingException ex) {
logger.log(Level.SEVERE, null, ex);
}
this.restaurant = dao.findByName(name);
reviewDao.getReviewsForRestaurant(restaurant);
}
@Post
public String addReview(@Named("name") String name){
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (UnsupportedEncodingException ex) {
logger.log(Level.SEVERE, null, ex);
}
restaurant = dao.findByName(name);
reviewDao.create(restaurant, text, author, rating);
return "/reviewrWeb/restaurant/"+restaurant.getName();
}
public boolean getNoReviews() {
return this.restaurant.getReviews().size() == 0;
}
// getters and setters omitted for brevity
}
再次注意@At
注释。 这次,它的值为"/restaurant/:name"
。 :name
用于告诉Sitebricks这是一个动态变量。 它将使URL与此模式匹配,并将URL的最后部分放在一个名为name的变量中。 这是Guice中一个普通的命名变量。
get
方法的输入参数已使用@Named("name")
注释。 这将导致Guice在调用get
方法时将从URL提取的值注入此变量(该方法将被GET
请求调用,因为它上面带有@Get
批注)。
该示例对变量进行URL解码,因为该变量是URL的一部分,而不是已为您完成解码的HTTP请求参数。 没有它, Elephant Bar
将作为Elephant%20Bar
。 您可能会注意到,该类还具有addReview
方法,该方法具有@Post
批注。
处理表单可能是Web应用程序中最繁琐的任务之一。 如您所料,Sitebricks也在这一领域大放异彩。 清单9显示了餐厅详细信息页面的控制器。 视图代码显示在下面的清单10中。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<title></title>
</head>
<body>
<h1>${restaurant.name}</h1>
<h2>Category: ${restaurant.category}</h2>
<h2>Average Rating: ${restaurant.averageRating}</h2>
<h2>Reviews</h2>
@ShowIf(noReviews)
<p>This restaurant has no reviews yet.</p>
@Repeat(items=restaurant.reviews, var="review")
<div class="review">${review.author}:${review.rating}<br/>${review.text}</div>
<form method="post">
<label for="author">Your name:</label>
<input type="text" id="author" name="author"/>
<label for="rating">Your rating:</label>
<select name="rating">
<option value="0" label="0"/>
<option value="1" label="1"/>
<option value="2" label="2"/>
<option value="3" label="3"/>
<option value="4" label="4"/>
<option value="5" label="5"/>
</select><br/>
<label for="text">Write your review:</label><br/>
<textarea id="text" name="text" cols="60" rows="40"/><br/>
<input type="hidden" name="restaurantId" value="${restaurant.id}"/>
<input type="submit" value="Submit your review"/>
</form>
</body>
</html>
该文件遵循命名约定,因此为RestaurantDetails.html。 文件顶部有HTML代码,用于显示有关餐厅及其所有评论的信息。 @ShowIf
是另一个Sitebricks核心小部件。 此小部件的参数是一个布尔变量,在这种情况下,它称为noReviews
。 回到清单9 ,您将不会看到该名称的变量,但是有一个名为getNoReviews
的方法。 这是根据JavaBeans约定进行的,因此在评估@ShowIf
小部件时将调用该方法。
HTML模板中最有趣的部分是底部的表单。 表单使用post
方法,就像更改服务器的任何表单一样(在这种情况下,向数据库添加评论)。 它提交回自己,因此将使用相同的控制器。 在清单9中,您可以看到还有一个不同的方法addReview
,它用@Post
注释。 这是提交表单时将调用的方法。 与get
使用相同的命名值模式。
看一下每个表单元素及其名称。 这些元素与RestaurantDetails
类的成员变量匹配。 Sitebricks会自动将表单数据绑定到成员变量,就像处理请求参数一样。 这使您作为开发人员的工作更加轻松。 addReview
方法是Sitebricks中的重定向方法,它返回一个字符串。 该方法仅返回一个字符串,该字符串是应重定向到的相对URL。 在这种情况下,该URL与餐厅的详细信息URL相同,因此将重新显示详细信息页面。 您可以轻松地将其更改为主页URL或任何其他所需的URL。
在本文中,您了解了使用Google Sitebricks开发Java Web应用程序有多方便。 您探索了所有基本要素:创建一个使用Sitebricks的项目,配置Sitebricks,使用其小部件以及映射URL。 本文还介绍了其他一些方便的功能,例如带有命名变量和表单绑定的动态URL。
Sitebricks是一个轻量级的框架,提供了用于快速创建Web应用程序的通用工具。 它将Guice理念扩展到了网络。 最棒的是,Sitebricks仍然是一个年轻的框架。 您需要密切关注即将发布的功能以及围绕Sitebricks开发的生态系统。
翻译自: https://www.ibm.com/developerworks/java/library/wa-sitebricks/index.html