京山游俠

          專注技術,拒絕扯淡
          posts - 50, comments - 868, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理
          使用SpringSide 3.1.4.3開發Web項目的全過程(上)

          第七步、編寫Action和JSP。在SpringSide 3.1.4.3中,使用的是Struts 2及其Convention插件,已經不是前面使用的CodeBehind插件了,關于Convention插件,這里要再說幾句,該插件的大部分功能和CodeBehind相同,唯一讓人有點迷惑的就是該插件到哪里尋找Action類的問題,它會根據struts.convention.package.locators屬性的值來決定,在該項目中,其值為“web”,之需要查閱一下struts.xml文件即可知。這說明,Convention會尋找所有包含“web”這個單詞的包,并在該包及其子包中尋找Action類。這也正是Action層的包名為personal.youxia.web的原因。

          關于SpringSide 3種的Struts的探討,大家可以看看我之前寫的一篇文章SpringSide 3 中的 Struts 2

          ArticleAction的實現思路如下,修改index.jsp,使其重定向到article.action,該Action默認調用其list方法顯示所有文章,并返回article.jsp作為其視圖。在該視圖上,有添加文章的連接,點擊該連接則訪問article!input.action,這時會調用ArticleAction的input方法,并返回article-input.jsp作為其視圖,在該視圖中輸入文章的內容,點擊保存,調用article!save.action,這時會調用ArticleAction的save方法以保存數據,如果要刪除文章,則調用article!delete.action,這時會調用ArticleAction的delete方法。在調用以上方法的過程中,會自動調用prepare系列的方法。

          因此,該步驟涉及到三個JSP文件和一個Action類,它們分別是
          index.jsp
          article.jsp
          article-input.jsp
          ArticleAction.java

          index.jsp的修改很簡單,只是讓項目一啟動后就去訪問ArticleAction,而不是默認的UserAction。index.jsp的代碼如下:
          <% response.sendRedirect("article.action"); %>

          這時,重點進入到ArticleAction中,創建該Action,其代碼的框架如下:
          package personal.youxia.web;

          import personal.youxia.entity.entities.Article;

          public class ArticleAction extends CrudActionSupport<Article> {

              @Override
              
          public String delete() throws Exception {
                  
          // TODO Auto-generated method stub
                  return null;
              }


              @Override
              
          public String list() throws Exception {
                  
          // TODO Auto-generated method stub
                  return null;
              }


              @Override
              
          protected void prepareModel() throws Exception {
                  
          // TODO Auto-generated method stub

              }


              @Override
              
          public String save() throws Exception {
                  
          // TODO Auto-generated method stub
                  return null;
              }


              
          public Article getModel() {
                  
          // TODO Auto-generated method stub
                  return null;
              }


          }

          可以看到,該Action從CrudActionSupport類繼承,而CrudActionSupport又繼承自ActionSupport,并實現了ModelDriven和Preparable接口,這樣Struts 2的ModelDriven攔截器和Preparable攔截器就會對我們自己的Action發生作用。CrudActionSupport中的excute方法默認的實現是調用list方法,所以訪問article.action就等于訪問ArticleAction的list方法,該方法的目的是為了列出所有的文章,所以在該方法中使用了ArticleDao的分頁查詢,查詢結果放在一個page對象中。在Struts 2中,已經沒有了ActionForm的概念,可以直接把Action對象傳遞到視圖中,為了能夠在視圖中訪問page對象,只需要把page對象作為ArticleAction的一個屬性即可。先在ArticleAction.java中加入幾行代碼:
              @Autowired
              
          private ArticleManager articleManager;

              
          public void setArticleManager(ArticleManager articleManager) {
                  
          this.articleManager = articleManager;
              }
              
              
          private Page<Article> page = new Page<Article>(10);
              
              
          public Page<Article> getPage() {
                  
          return page;
              }

          可以看到該代碼的作用是為了注入ArticleManager和初始化Page對象,此時list方法的代碼就非常簡單,如下:
          @Override
              
          public String list() throws Exception {
                  page 
          = articleManager.getAll(page);
                  
          return SUCCESS;
              }

          由于該方法只是簡單獲取一個頁面的Acticle,所以代碼很簡單,使用articleManager.getAll方法即可。如果要實現復雜的條件查詢,就需要創建一個包含PropertyFilter對象的列表,然后使用articleManager.search方法進行查詢,為了簡化PropertyFilter對象列表的創建,白衣提供了HibernateWebUtils.buildPropertyFilters()靜態方法供大家使用。

          list方法返回的是SUCCESS,因此返回給用戶的視圖頁面為article.jsp,該頁面應該存放在WEB-INF目錄的content目錄中,這也是Convention插件的一個特性,這樣用戶就沒有辦法直接訪問到視圖頁面了。在該頁面中,可以通過訪問page對象來顯示數據,如下:
          <%@ page language="java" contentType="text/html; charset=UTF-8"
              pageEncoding
          ="UTF-8"%>
          <%@ include file="/common/taglibs.jsp"%>
          <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
          <html>
          <head>
          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
          <title>Insert title here</title>
          </head>
          <body>
          <table>
              
          <tr><td><href="article!input.action">添加文章</a></td></tr>
              
          <s:iterator value="page.result">
                  
          <tr>
                      
          <td>${subject}</td>
                      
          <td><href="article!delete.action?id=${id}">刪除</a></td>
                  
          </tr>
                  
          <tr>
                      
          <td>${content}</td>
                  
          </tr>
              
          </s:iterator>
          </table>
          </body>
          </html>

          如果數據庫中有初始數據的話,該項目運行效果如下圖:


          到目前為止,還沒有涉及到getModel()、prepareModel()、以及prepare系列的方法,但是,一旦需要添加或者刪除文章,這一系列的方法就有作用了。在Struts 2中,由于沒有了ActionForm的概念,所有的頁面傳入參數都會被注入到Action中,如果不想在Action中搞太多的getter和setter,最有效的方法就是提供一個Model對象,這時候攔截器會把頁面參數注入到Model中,而在目前的項目中,沒有比Entity類更適合做Model對象的了。通過觀察CrudActionSupport基類,可以發現只有在執行save和input方法之前,才會執行prepareModel方法,該方法可以保證getModel方法返回的對象不是一個空指針,而調用delete方法之前Model對象沒有初始化,但是delete方法只需要一個id作為參數,因此,可以在Action中增加一個id屬性來滿足要求。這時候,有改動的幾行代碼如下:
              private Long id;
              
          private Article article;

              
          public void setId(Long id) {
                  
          this.id = id;
              }

              @Override
              
          protected void prepareModel() throws Exception {
                  
          if (id != null) {
                      article 
          = articleManager.get(id);
                  } 
          else {
                      article 
          = new Article();
                  }
              }
              
          public Article getModel() {
                  
          return article;
              }

              @Override
              
          public String delete() throws Exception {
                  articleManager.delete(id);
                  
          return RELOAD;
              }

          這里需要特別關注的是delete方法返回的值,為RELOAD,這是一個在基類中定義好了的字符串。返回該字符串的目的,是為了在delete方法執行完之后,不返回任何視圖頁面,而是以redirect的方式再次調用article.action,以便顯示刪除文章后的結果。因此,需要在ArticleAction中使用@Result注解,如下:
          @Results( { @Result(name = CrudActionSupport.RELOAD, location = "article.action", type = "redirect") })

          經過如上修改,這時候再運行應用,就發現能夠刪除文章了。

          再來實現添加文章的功能,從上面的article.jsp中可以看出,添加文章的鏈接為article!input.action,此時,會運行ArticleAction的input方法,該方法只是簡單返回article-input.jsp視圖文件作為用戶輸入文章的接口,article-input.jsp的代碼如下:
          <%@ page language="java" contentType="text/html; charset=UTF-8"
              pageEncoding
          ="UTF-8"%>
          <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
          <html>
          <head>
          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
          <title>Insert title here</title>
          </head>
          <body>
          <form id="inputForm" action="article!save.action" method="post">
          <table class="inputView">
              
          <tr>
                  
          <td>主題:</td>
                  
          <td><input type="text" name="subject" size="40" id="subject" /></td>
              
          </tr>
              
          <tr>
                  
          <td>內容:</td>
                  
          <td><textarea name="content" id="subject"></textarea></td>
              
          </tr>
              
          <tr>
                  
          <td colspan="2">
                      
          <input type="submit" value="提交" />&nbsp; 
                      
          <input type="button" value="取消" onclick="history.back()"/>
                  
          </td>
              
          </tr>
          </table>
          </form>
          </body>
          </html>

          而ArticleAction中只需要修改如下幾行,由于ModelDriven攔截器已經把網頁中傳入的數據注入到了article對象中,所以save方法中只需要執行簡單的保存操作即可:
              @Override
              
          public String input() throws Exception {
                  
          return INPUT;
              }
              
              @Override
              
          public String save() throws Exception {
                  articleManager.save(article);
                  
          return RELOAD;
              }

          至于實現文章的修改功能,那也是通過input方法和save方法實現的,只不過此時網頁參數中會包含一個有效的id,而prepare系列的方法會根據該id先從數據庫中提取數據,然后顯示在article-input.jsp中,用戶修改后,再調用save方法保存到數據庫中。為減少本博文長度,該功能此處不做示范。

          通過上面的步驟可以發現,使用SpringSide 3中推薦的CRUD一體的模式,可以有效減少Action的數量和JSP文件的數量,每實現一個增刪查改功能,只需要一個Action和兩個JSP,但是,程序員一定要對其中的數據流向有充足的認識,才能理清它們之間的關系,不至于暈頭轉向。

          到這里大家會發現,ArticleAction誰都可以訪問,一點都不安全,所以第八步我會探討如何讓ArticleAction和SpringSecurity一起工作,至于第九步,當然是把項目從單數據庫環境更改到多數據庫環境了。具體內容,且看下回分解!

          評論

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2009-07-20 09:28 by YangL
          中寫的有點少了,呵呵

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)[未登錄]  回復  更多評論   

          2009-07-20 17:27 by Eric
          這個中確實是寫得太少了點...

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2009-07-20 20:29 by 個性藝術簽名
          中確實是寫得太少了點

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2009-07-21 16:29 by 虎嘯龍吟
          感覺這塊寫的不夠,比方說:prepare這塊。

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2009-07-21 18:06 by 海邊沫沫
          樓上幾位別心急,我還沒收工呢。

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2009-07-23 22:57 by zhj
          我在調用article!save.action時候報錯
          2009-07-23 22:50:16,937 [http-8080-2] ERROR [500.jsp] - The save() is not defined in action class com.opensymphony.xwork2.ActionSupport
          java.lang.IllegalArgumentException: The save() is not defined in action class com.opensymphony.xwork2.ActionSupport
          at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:453)
          at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:279)
          at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)
          at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:52)
          at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:468)
          at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
          at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:76)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:378)
          at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:99)
          at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
          at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.ui.SessionFixationProtectionFilter.doFilterHttp(SessionFixationProtectionFilter.java:67)
          at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
          at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
          at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:109)
          at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
          at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.ui.basicauth.BasicProcessingFilter.doFilterHttp(BasicProcessingFilter.java:174)
          at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:277)
          at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
          at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
          at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
          at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
          at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
          at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:236)
          at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
          at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
          at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
          at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
          at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
          at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
          at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
          at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
          at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
          at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
          at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
          at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
          at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
          at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
          at java.lang.Thread.run(Unknown Source)

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2009-07-25 12:55 by 海邊沫沫
          @zhj
          不要老在我的博客中發這么長的錯誤代碼呀,影響別人閱讀的。一般來說發前幾行就足夠了。

          這里的錯誤說的是你的ArticleAction中沒有定義save方法,你再檢查檢查。

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)[未登錄]  回復  更多評論   

          2009-08-04 17:17 by lx
          頂了,感謝樓主

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2009-11-04 21:32 by sina
          最近在用springside3做manytoone的時候遇到了很大麻煩,就是在修改many一方對象里的對應one方的外鍵的時候會報錯,與http://www.javaeye.com/problems/11098這個帖子里的狀況類似,還請海邊沫沫幫忙研究一下
          其間會報
          [com.opensymphony.xwork2.ognl.OgnlValueStack] - Error setting expression 'useType.id' with value '[Ljava.lang.String;@8fff06'
          ognl.OgnlException: target is null for setProperty(null, "id", [Ljava.lang.String;@8fff06)
          以及
          identifier of an instance of pplove.entity.entities.Status was altered from 5 to 2; nested exception is org.hibernate.HibernateException: identifier of an instance of pplove.entity.entities.Status was altered from 5 to 2
          的錯誤

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2009-11-27 10:27 by skyrocket
          aused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'databaseDefinitionSource': FactoryBean threw exception on object creation; nested exception is org.hibernate.hql.ast.QuerySyntaxException: Resource is not mapped [from Resource r left join fetch r.authorityList WHERE r.resourceType=? ORDER BY r.position ASC] 請問博主我在啟動Tomcat時候為什么報這個錯誤啊

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)[未登錄]  回復  更多評論   

          2009-11-27 11:08 by zhj
          經常報找不到action里的某方法。。。,明明有此方法 不知道為啥

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)[未登錄]  回復  更多評論   

          2009-12-24 14:17 by aaa
          啥時候出下

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2011-12-04 15:07 by plume
          session.delete()刪除的時候未刪除成功,并且后臺也沒有報錯。

          # re: 使用SpringSide 3.1.4.3開發Web項目的全過程(中)  回復  更多評論   

          2012-04-28 15:17 by 十四
          勁啊
          主站蜘蛛池模板: 浦江县| 广昌县| 鹰潭市| 彝良县| 桃源县| 吉林省| 远安县| 海盐县| 贵港市| 桑日县| 奉化市| 惠东县| 独山县| 萨嘎县| 江华| 莱州市| 肇东市| 上高县| 鹤岗市| 万全县| 甘泉县| 榕江县| 黑水县| 鱼台县| 宁国市| 都安| 新干县| 太原市| 乌鲁木齐县| 共和县| 罗田县| 高雄县| 禄丰县| 温州市| 红河县| 天祝| 汝州市| 大渡口区| 沾益县| 苗栗县| 晴隆县|