领先一步
VMware 提供培训和认证,助您加速进步。
了解更多Java 企业应用程序可以有多种形态和形式。根据其需求,开发人员需要决定应用程序需要哪些特定的架构层。到目前为止,Spring Roo 采取了实用方法,以减少服务门面、仓库或 DAO 层引入的往往不必要的复杂性。新发布的 Spring Roo 1.2.0.M1(查看公告)包含了频繁请求的对可根据应用程序需求量身定制的架构层的支持。本文概述了 Roo 的新服务和仓库层功能。

虽然有许多新的分层和持久化选择,但Roo默认将继续支持JPA Active Record实体。但是,您可以通过添加额外的服务或仓库层(详情如下)轻松更改现有应用程序。如果您添加新的层,Roo将自动更改其在消费层或服务层中的ITD。例如,Roo将自动更改您的应用程序,以在现有MVC控制器、GWT定位器、集成测试或给定域类型的数据点播中注入并调用新的服务层。
通过Roo 1.2.0.M1版本,Roo核心现在提供三种选项来支持数据持久化:JPA实体(Active Record风格)、JPA仓库和MongoDB仓库。对Spring Data Neo4J的支持目前正在开发中,很快将作为Roo插件提供。
Active Record风格的JPA实体自Spring Roo的第一个版本以来一直是默认设置,并将保持不变。要为JPA持久化配置您的项目,您可以运行jpa setup命令
roo> jpa setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT
这会将您的项目配置为使用Hibernate对象关系映射器以及内存数据库(HSQLDB)。Roo支持的Active Record风格的JPA实体使用@RooEntity进行注解,该注解负责提供持久化标识符字段及其访问器和修改器。此外,此注解还会创建典型的CRUD方法以支持数据访问。
roo> entity --class ~.domain.Pizza
此命令将创建一个Pizza域类型,以及用于持久化、更新、读取和删除实体的Active Record风格方法。以下示例还包含一些字段,可以直接添加到Java源文件,或通过Roo shell中的field命令添加。
@RooJavaBean
@RooToString
@RooEntity
public class Pizza {
@NotNull
@Size(min = 2)
private String name;
private BigDecimal price;
@ManyToMany(cascade = CascadeType.ALL)
private Set<Topping> toppings = new HashSet<Topping>();
@ManyToOne
private Base base;
}
需要仓库/DAO层而不是默认Roo JPA“Active Record”持久化方法的开发人员可以通过为给定JPA域类型创建Spring Data JPA支持的仓库来实现。通过jpa setup命令为JPA持久化配置项目后,此功能通过使用Roo的@RooJpaEntity注解域类型自动提供。
roo> entity --class ~.domain.Pizza --activeRecord false
通过定义--activeRecord false,您可以选择退出默认的“Active Record”风格。以下示例还包含一些字段,可以通过Roo shell中的field命令添加。
@RooJavaBean
@RooToString
@RooJpaEntity
public class Pizza {
@NotNull
@Size(min = 2)
private String name;
private BigDecimal price;
@ManyToMany(cascade = CascadeType.ALL)
private Set<Topping> toppings = new HashSet<Topping>();
@ManyToOne
private Base base;
}
有了域类型之后,您现在可以使用repository jpa命令为此类型创建新的仓库
roo> repository jpa --interface ~.repository.PizzaRepository --entity ~.domain.Pizza
这将创建一个利用Spring Data JPA的简单接口定义
@RooRepositoryJpa(domainType = Pizza.class)
public interface PizzaRepository {
}
当然,您可以在任何接口上手动添加@RooRepositoryJpa注解,而无需在Roo shell中发出repository jpa命令。
添加@RooRepositoryJpa注解将触发一个相当简单的AspectJ ITD的创建,该ITD会向PizzaRepository接口添加一个extends语句,从而产生与此接口定义等效的结果
public interface PizzaRepository extends JpaRepository<Pizza, Long> {}
JpaRepository接口是Spring Data JPA API的一部分,并提供所有开箱即用的CRUD功能。
作为JPA持久化的替代方案,Spring Roo 1.2现在通过利用Spring Data MongoDB项目提供MongoDB支持。MongoDB受Cloud Foundry支持,这是一个免费开发和托管Spring Roo应用程序的好地方。除了MongoDB,您还可以使用MySQL和PostgreSQL与Cloud Foundry。要为MongoDB持久化配置项目,您可以使用mongo setup命令
roo> mongo setup
这将配置您的Spring应用程序上下文以使用运行在localhost和默认端口上的MongoDB安装。可选的命令属性允许您定义主机、端口、数据库名称、用户名和密码。如果您在Cloud Foundry上使用MongoDB,只需在“mongo setup”后添加--cloudFoundry,Roo就会自动为您配置所有内容
一旦应用程序配置了MongoDB支持,entity mongo和repository mongo命令就可用了
roo> entity mongo --class ~.domain.Pizza
此命令将创建一个用@RooMongoEntity注解的Pizza域类型。此注解负责触发ITD的创建,该ITD提供一个Spring Data @Id注解字段及其访问器和修改器。以下示例还包含一些字段,可以通过Roo shell中的field命令添加。
@RooJavaBean
@RooToString
@RooMongoEntity
public class Pizza {
@NotNull
@Size(min = 2)
private String name;
private BigDecimal price;
@ManyToMany(cascade = CascadeType.ALL)
private Set<Topping> toppings = new HashSet<Topping>();
@ManyToOne
private Base base;
}
有了域类型之后,您现在可以使用repository mongo命令为此类型创建新的仓库(或通过将@RooRepositoryMongo注解应用于任意接口)
roo> repository mongo --interface ~.repository.PizzaRepository --entity ~.domain.Pizza
这将创建一个利用Spring Data MongoDB的简单接口定义
@RooRepositoryMongo(domainType = Pizza.class)
public interface PizzaRepository {
List<Pizza> findAll();
}
与上面看到的Spring Data JPA驱动的仓库类似,此接口通过一个ITD进行增强,该ITD引入了Spring Data API提供的PagingAndSortingRepository,并负责提供所有必要的CRUD功能。此外,此接口定义了一个“自定义”查找器,PagingAndSortingRepository实现不提供此查找器:List findAll();。此方法是Spring Roo的UI脚手架所必需的,并由Spring Data MongoDB提供的查询构建器机制自动实现。
与使用JPA持久化的应用程序类似(有关使用JPA与Postgres的详细信息,请参阅这篇博客文章),MongoDB应用程序可以轻松部署到VMware Cloud Foundry。以下脚本提供了一个Pizza Shop示例应用程序(参见/sample/pizzashop.roo)的示例,该应用程序已调整为与MongoDB支持的仓库层一起使用
// Create a new project.
project com.springsource.pizzashop
// Create configuration for MongoDB peristence
mongo setup --cloudFoundry true
// Define domain model.
entity mongo --class ~.domain.Base --testAutomatically
field string --fieldName name --sizeMin 2 --notNull --class ~.domain.Base
entity mongo --class ~.domain.Topping --testAutomatically
field string --fieldName name --sizeMin 2 --notNull --class ~.domain.Topping
entity mongo --class ~.domain.Pizza --testAutomatically
field string --fieldName name --notNull --sizeMin 2 --class ~.domain.Pizza
field number --fieldName price --type java.lang.Float
field set --fieldName toppings --type ~.domain.Topping
field reference --fieldName base --type ~.domain.Base
entity mongo --class ~.domain.PizzaOrder --testAutomatically
field string --fieldName name --notNull --sizeMin 2 --class ~.domain.PizzaOrder
field string --fieldName address --sizeMax 30
field number --fieldName total --type java.lang.Float
field date --fieldName deliveryDate --type java.util.Date
field set --fieldName pizzas --type ~.domain.Pizza
// Add layer support.
repository mongo --interface ~.repository.ToppingRepository --entity ~.domain.Topping
repository mongo --interface ~.repository.BaseRepository --entity ~.domain.Base
repository mongo --interface ~.repository.PizzaRepository --entity ~.domain.Pizza
repository mongo --interface ~.repository.PizzaOrderRepository --entity ~.domain.PizzaOrder
service --interface ~.service.ToppingService --entity ~.domain.Topping
service --interface ~.service.BaseService --entity ~.domain.Base
service --interface ~.service.PizzaService --entity ~.domain.Pizza
service --interface ~.service.PizzaOrderService --entity ~.domain.PizzaOrder
// Create a Web UI.
web mvc setup
web mvc all --package ~.web
// Package the application into a war file.
perform package
// Deploy and start the application in Cloud Foundry
cloud foundry login
cloud foundry deploy --appName roo-pizzashop --path /target/pizzashop-0.1.0.BUILD-SNAPSHOT.war --memory 512
cloud foundry create service --serviceName pizzashop-mongo --serviceType mongodb
cloud foundry bind service --serviceName pizzashop-mongo --appName roo-pizzashop
cloud foundry start app --appName roo-pizzashop
“实时”示例应用程序目前部署在Cloud Foundry上:http://roo-pizzashop.cloudfoundry.com/
根据Roo的约定,所有功能都将通过AspectJ ITD引入,从而为开发人员提供了一个干净的画布,用于实现不自然地适合域实体的自定义业务逻辑。服务层的其他常见用例是安全性或远程集成,例如Web服务。有关更详细的讨论,请参阅Spring Roo参考指南中的架构章节。
服务层集成到Roo项目中与仓库层类似,通过直接使用@RooService注解或Roo shell中的service命令
roo> service --interface ~.service.PizzaService --entity ~.domain.Pizza
此命令将在定义的包中创建PizzaService接口,并在同一包中创建PizzaServiceImpl(PizzaServiceImpl的名称和包可以通过可选的--class属性自定义)。
@RooService(domainTypes = { Pizza.class })
public interface PizzaService {
}
根据Roo约定,默认的CRUD方法定义可以在AspectJ ITD中找到
void savePizza(Pizza pizza);
Pizza findPizza(Long id);
List<Pizza> findAllPizzas();
List<Pizza> findPizzaEntries(int firstResult, int maxResults);
long countAllPizzas();
Pizza updatePizza(pizza pizza);
void deletePizza(Pizza pizza);
同样,PizzaServiceImpl也相当简单
public class PizzaServiceImpl implements PizzaService {}
通过AspectJ ITD,PizzaServiceImpl类型默认用@Service和@Transactional注解。此外,ITD将向目标类型引入以下方法和字段
@Autowired PizzaRepository pizzaRepository;
public void savePizza(Pizza pizza) {
pizzaRepository.save(pizza);
}
public Pizza findPizza(Long id) {
return pizzaRepository.findOne(id);
}
public List<Pizza> findAllPizzas() {
return pizzaRepository.findAll();
}
public List<Pizza> findPizzaEntries(int firstResult, int maxResults) {
return pizzaRepository.findAll(new PageRequest(firstResult / maxResults, maxResults)).getContent();
}
public long countAllPizzas() {
return pizzaRepository.count();
}
public Pizza updatePizza(Pizza pizza) {
return pizzaRepository.save(pizza);
}
public void deletePizza(Pizza pizza) {
pizzaRepository.delete(pizza);
}
如您所见,Roo将检测是否存在给定域类型的持久化提供程序层,并自动注入此组件以将所有服务层调用委托给此层。如果不存在持久化(或其他“较低级别”)层,服务层ITD将简单地提供方法存根。
使用Spring Roo 1.2,向新的或现有的Spring Roo管理应用程序添加架构层或持久化选项变得几乎微不足道。无需考虑为新的持久化提供程序配置应用程序,或将对新层的引用注入到Spring MVC控制器、GWT UI或集成测试中——Roo将为您完成所有这些工作!
随着Spring Roo 1.2中可用的分层支持,我们预计未来会有更多的持久化提供程序。对Spring Data Neo4J的仓库分层集成目前正在开发中,很快将作为Roo插件提供。
如果您想轻松试用这些新功能,为什么不构建您自己的MongoDB驱动的Pizza Shop应用程序版本并将其部署到Cloud Foundry呢?得益于这些新的Roo 1.2.0.M1功能,这只需几分钟。
鉴于Spring Roo 1.2.0.M1是一个里程碑版本,您应该继续将Roo 1.1.5用于生产项目。但是,我们相信Roo 1.2 M1适合探索新功能或快速项目。
Roo团队始终欢迎社区的反馈。