簡單易用的Java MVC框架VRaptor使用教程
使用Java進行Web 開發時,有很多基于MVC的框架可供選擇。VRaptor就是其中之一。最新的VRaptor第四版基于CDI1.1。本文將帶你逐步了解這一框架的原理及新版本的新增特性。
在VRaptor框架中創建一個控制器,只需要在Java類中添加@Controller注解即可,框架將根據其約定的URL和JSP規范完成剩余的工作,這樣可以盡量減少配置文件的使用。例如:
@Controller public class UserController { public void list() { //... } }
規范簡介
VRaptor的URL格式規范為controllerName/methodName,因此可以通過user/list訪問到list方法。需要注意的是,后綴Controller并沒有包含在路徑中。
JSP調度器遵循另外一個有用的規范。與URL的規范類似,在控制器方法執行完畢后,VRaptor會在WEB-INF/jsp/controllerName/methodName.jsp這一路徑下查找JSP文件。
在我們的例子中就是WEB-INF/jsp/user/list.jsp
根據這些規范,Controller中所有的公有方法會被逐一映射用于應答HTTP請求,不限制請求動作的類型。
根據所選擇的請求動作,也可以通過使用@Get,@Post,@Put或@Delete注解限制對控制器方法的訪問。
如果不想使用默認的URL規范,也可以在注解中添加一個String類型的參數,修改控制器方法的訪問路徑。如果不希望限制HTTP請求動作的類型,可以使用@Path注解。
@Get("any/other/url") public void list() { //... }
獲取參數
控制器方法可以接收參數,VRaptor會嘗試為這些參數填入值。這對于簡單的值類型通常是可行的,例如下面search方法的long類型值。
@Get public void search(long id) { //... }
如果請求中帶有名為id的參數,也就是與方法的參數名具有相同的名字,VRaptor會嘗試將這個參數轉化為控制器方法所期望的類型。通過URL:user/search?id=1可以訪問到這個方法,或者也可以使用參數化的URL:
@Get("user/search/{id}") public void search(long id) { //... }
在這種情況下,URL路徑就是user/search/1。
不僅如此,我們還可以從視圖中得到更加復雜的對象,如下所示,這個add方法能夠得到一個User類型的對象:
@Post public void add(User user) { //... }
調用add方法的form.jsp的代碼如下:
<form action="user/add" method="post"> <input type="text" name="user.name"/> <input type="text" name="user.email"/> <input type="submit" value="Create user"> </form>
表單的請求參數樣例如下:
user.name = Rodrigo Turini user.email = rodrigo.turini@caelum.com.br ...
自定義結果集
如果想在JSP頁面中獲取對象列表,我們只需讓控制器方法將對象列表作為參數返回。
@Get public List<User> list(){ return userDao.list(); }
因為返回值是一個List<User>類型的對象,視圖中的變量名就應該是${userList}。如果返回類型是一個簡單的User 對象,視圖變量名就是${user}。另外一種將對象傳送給視圖的方式是使用專門的Result接口。我們可以將這個Bean注入到我們的控制器中,然后調用它的include方法。
@Controller public class UserController { @Inject private Result result; @Inject private UserDao userDao; @Get public void list(){ List<User> list = userDao.list(); result.include(list); result.include("users", list); } }
上述代碼將同一個list變量作為參數傳給include方法兩次,主要是為了說明這兩種方式的用法和它們之間的區別。第一個include方法將會生成一個在JSP頁面中可用的${userList}的變量,與方法的返回值一樣。第二個include方法顯式地提供了對象的名字,因此可以通過${users}訪問到它。
Result類中還有很多其他的方法可以幫助我們與視圖進行交互。從下面的例子可以看到,很容易就返回用JSON格式序列化后的對象列表。
@Get public void jsonList(){ List<User> users = userDao.list(); result.use(json()).from(users).serialize(); }
Result類為我們提供了包括json方法在內的多個方法,用于處理最為通用的一些結果類型,例如xml,html,jsonp等。Result類中還有一個representation方法,可以根據請求所能接受的格式序列化對象。
簡單的配置
目前為止,我們所看見的配置都非常簡單。因為VRaptor中所有的類都是由CDI管理的Bean,我們可以特化(specialize)任何一個 VRaptor組件——而且這些定制能夠被完美地封裝。例如,如果想更改默認的渲染視圖或VRaptor用于查找視圖的文件夾,只需要特化 DefaultPathResolver類即可。
@Specializes public class CustomPathResolver extends DefaultPathResolver { @Override protected String getPrefix() { return "/root/folder/"; } }
我們也可以通過重寫PathAnnotationRoutesParser類來修改URL的默認規范。
VRaptor與JPA的集成
CDI集成框架比較有趣的另一個方面就是它能夠讓你很方便地管理項目中的外部類。例如,VRaptor與JPA的整合就相當簡單。首先創建一個EntityManager的生產類:
public class EntityManagerCreator { @Inject private EntityManagerFactory factory; @Produces @RequestScoped public EntityManager getEntityManager() { return factory.createEntityManager(); } public void destroy(@Disposes EntityManager em) { if (em.isOpen()) { em.close(); } } }
然后就可以在應用程序中的任何一個Bean中注入這個對象的一個實例。
public class UserDao { @Inject private EntityManager em; public void add(User user) { em.getTransaction().begin(); em.persist(user); em.getTransaction().commit() } // ... }
我們也可以創建一個簡單的攔截器,將視圖的渲染包含在事務中。示例如下:
@Intercepts public class JPATransactionInterceptor { @Inject private EntityManager manager; @AroundCall public void intercept(SimpleInterceptorStack stack) { EntityTransaction transaction = manager.getTransaction(); transaction.begin(); stack.next(); transaction.commit(); } }
從上面的代碼中可以看到,使用@AroundCall注解的攔截器方法接收一個SimpleInterceptorStack對象作為參數。調用next()方法就會將請求分派給控制器,因此我們需要在調用next()方法之前打開事務,然后在調用之后關閉事務。
另外一種將視圖的渲染包含在事務中的方法就是支持@Transactional注解,然后僅在包含這個注解的方法上應用攔截器。這種方法和之前的方法一樣很簡單,只需要在攔截器中添加一個accepts方法即可。
@Accepts public boolean accepts() { return method.containsAnnotation(Transactional.class); }
在UserController類的add方法上添加@Transactional注解:
@Controller public class UserController { @Inject private Result result; @Inject private UserDao userDao; @Get public void list() { List<User> list = userDao.list(); result.include(list); result.include("users", list); } @Post @Transactional public void add(User user) { userDao.add(user); } }
然后UserDao類的方法只需要將持久化的工作代理給EntityManager.persist即可:
public void add(User user) { em.persist(user); }
添加插件
與許多其他的功能一樣,上述功能已經以VRaptor4插件的形式實現并發布。上文所提及的攔截器和生產者分別位于vraptor-jpa和vraptor-hibernate插件中,只需要將相關的jar包添加到項目中(或通過配置自己熟悉的依賴管理工具)就可以正常使用它們,而無需更多的配置。
只要插件中包含beans.xml文件,CDI框架就可以管理插件中的類并且能夠讓這些類在VRaptor上被注入。由于創建擴展非常簡單,插件開發的社區參與非常活躍并且已經創建了很多插件。在VRaptor的框架文檔中羅列了其中一部分插件。
深入挖掘VRaptor在JavaEE中的使用
在應用服務器中集成VRaptor與JPA和事務控制更容易。可以使用注解@PersistenceContext替代CDI的@Inject注入 EntityManager。這樣就可以使用注解javax.transaction.Transactional來控制方法中的事務。
@Controller public class UserController { @Inject private Result result; @Inject private UserDao userDao; @Get public void list(){ List<User> list = userDao.list(); result.include("users", list); } @Post @Transactional public void add(User user){ userDao.add(user); } @Post @Transactional public void remove(User user){ userDao.delete(user); } }
或者也可以像下面這段代碼一樣將@Transactional注解直接添加到VRaptor控制器上,而不是對方法做注解:
@Controller @Transactional public class UserController { @Inject private Result result; @Inject private UserDao userDao; // remaining code }
之所以可以這樣,是因為VRaptor控制器和其他的類一樣,都是由CDI管理的Bean。
VRaptor示例
通過基于VRaptor框架的開源項目,可以在實踐中了解更多關于VRaptor框架的功能。Mamute問答引擎就是一個很好地利用Vraptor4新特性的例子,具體內容可以參見項目代碼庫。也可以通過VRaptor框架的示例應用vraptor-music-jungle和已經配置好的基礎項目blank-project快速展開學習。
關于VRaptor4的更多信息
從VRaptor官方文檔中可以了解到更多關于VRaptor4框架的更多信息:從一分鐘指南和十分鐘指南開始,然后學習從老版本向新版本遷移的教程,再之后可以瀏覽由社區編寫的詳細說明文檔。如果想更加深入地學習,可以閱讀與VRaptor內核無縫集成的CDI1.1的規格說明書。
來自:http://www.infoq.com/cn/articles/VRaptor-MVC-Framework-Version-4