糊言亂語

          志未半斤, 才無八兩. 有苦有樂, 糊涂過活。
          posts - 25, comments - 7, trackbacks - 0, articles - 42
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          您幾乎總是使用 Spring 框架開發 Web 的應用,Spring 為 Web 應用提供了很多有用的工具類,這些工具類可以給您的程序開發帶來很多便利。在這節里,我們將逐一介紹這些工具類的使用方法。

          操作 Servlet API 的工具類

          當您在控制器、JSP 頁面中想直接訪問 Spring 容器時,您必須事先獲取 WebApplicationContext 對象。Spring 容器在啟動時將 WebApplicationContext 保存在 ServletContext的屬性列表中,通過 WebApplicationContextUtils 工具類可以方便地獲取 WebApplicationContext 對象。

          WebApplicationContextUtils

          當 Web 應用集成 Spring 容器后,代表 Spring 容器的 WebApplicationContext 對象將以 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 為鍵存放在 ServletContext 屬性列表中。您當然可以直接通過以下語句獲取 WebApplicationContext:

          WebApplicationContext wac = (WebApplicationContext)servletContext.
          getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
          

          但通過位于 org.springframework.web.context.support 包中的 WebApplicationContextUtils 工具類獲取 WebApplicationContext 更方便:

          WebApplicationContext wac =WebApplicationContextUtils.
          getWebApplicationContext(servletContext);
          

          當 ServletContext 屬性列表中不存在 WebApplicationContext 時,getWebApplicationContext() 方法不會拋出異常,它簡單地返回 null。如果后續代碼直接訪問返回的結果將引發一個 NullPointerException 異常,而 WebApplicationContextUtils 另一個 getRequiredWebApplicationContext(ServletContext sc) 方法要求 ServletContext 屬性列表中一定要包含一個有效的 WebApplicationContext 對象,否則馬上拋出一個 IllegalStateException 異常。我們推薦使用后者,因為它能提前發現錯誤的時間,強制開發者搭建好必備的基礎設施。

          WebUtils

          位于 org.springframework.web.util 包中的 WebUtils 是一個非常好用的工具類,它對很多 Servlet API 提供了易用的代理方法,降低了訪問 Servlet API 的復雜度,可以將其看成是常用 Servlet API 方法的門面類。

          下面這些方法為訪問 HttpServletRequest 和 HttpSession 中的對象和屬性帶來了方便:

          方法 說明
          Cookie getCookie(HttpServletRequest request, String name) 獲取 HttpServletRequest 中特定名字的 Cookie 對象。如果您需要創建 Cookie, Spring 也提供了一個方便的 CookieGenerator 工具類;
          Object getSessionAttribute(HttpServletRequest request, String name) 獲取 HttpSession 特定屬性名的對象,否則您必須通過request.getHttpSession.getAttribute(name) 完成相同的操作;
          Object getRequiredSessionAttribute(HttpServletRequest request, String name) 和上一個方法類似,只不過強制要求 HttpSession 中擁有指定的屬性,否則拋出異常;
          String getSessionId(HttpServletRequest request) 獲取 Session ID 的值;
          void exposeRequestAttributes(ServletRequest request, Map attributes) 將 Map 元素添加到 ServletRequest 的屬性列表中,當請求被導向(forward)到下一個處理程序時,這些請求屬性就可以被訪問到了;

          此外,WebUtils還提供了一些和ServletContext相關的方便方法:

          方法 說明
          String getRealPath(ServletContext servletContext, String path) 獲取相對路徑對應文件系統的真實文件路徑;
          File getTempDir(ServletContext servletContext) 獲取 ServletContex 對應的臨時文件地址,它以 File 對象的形式返回。

          下面的片斷演示了使用 WebUtils 從 HttpSession 中獲取屬性對象的操作:

          protected Object formBackingObject(HttpServletRequest request) throws Exception {
              UserSession userSession = (UserSession) WebUtils.getSessionAttribute(request, 
                  "userSession");
              if (userSession != null) {
                  return new AccountForm(this.petStore.getAccount(
                  userSession.getAccount().getUsername()));
              } else {
                  return new AccountForm();
              }
          }
          

          Spring 所提供的過濾器和監聽器

          Spring 為 Web 應用提供了幾個過濾器和監聽器,在適合的時間使用它們,可以解決一些常見的 Web 應用問題。

          延遲加載過濾器

          Hibernate 允許對關聯對象、屬性進行延遲加載,但是必須保證延遲加載的操作限于同一個 Hibernate Session 范圍之內進行。如果 Service 層返回一個啟用了延遲加載功能的領域對象給 Web 層,當 Web 層訪問到那些需要延遲加載的數據時,由于加載領域對象的 Hibernate Session 已經關閉,這些導致延遲加載數據的訪問異常。

          Spring 為此專門提供了一個 OpenSessionInViewFilter 過濾器,它的主要功能是使每個請求過程綁定一個 Hibernate Session,即使最初的事務已經完成了,也可以在 Web 層進行延遲加載的操作。

          OpenSessionInViewFilter 過濾器將 Hibernate Session 綁定到請求線程中,它將自動被 Spring 的事務管理器探測到。所以 OpenSessionInViewFilter 適用于 Service 層使用HibernateTransactionManager 或 JtaTransactionManager 進行事務管理的環境,也可以用于非事務只讀的數據操作中。

          要啟用這個過濾器,必須在 web.xml 中對此進行配置:

          …
          <filter>
              <filter-name>hibernateFilter</filter-name>
              <filter-class>
              org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
              </filter-class>
          </filter>
          <filter-mapping>
              <filter-name>hibernateFilter</filter-name>
              <url-pattern>*.html</url-pattern>
          </filter-mapping>
          …
          

          上面的配置,我們假設使用 .html 的后綴作為 Web 框架的 URL 匹配模式,如果您使用 Struts 等 Web 框架,可以將其改為對應的"*.do"模型。

          中文亂碼過濾器

          在您通過表單向服務器提交數據時,一個經典的問題就是中文亂碼問題。雖然我們所有的 JSP 文件和頁面編碼格式都采用 UTF-8,但這個問題還是會出現。解決的辦法很簡單,我們只需要在 web.xml 中配置一個 Spring 的編碼轉換過濾器就可以了:

          <web-app>
          <!---listener的配置-->
          <filter>
              <filter-name>encodingFilter</filter-name>
              <filter-class>
                  org.springframework.web.filter.CharacterEncodingFilter ① Spring 編輯過濾器
              </filter-class>
              <init-param> ② 編碼方式
                  <param-name>encoding</param-name>
                  <param-value>UTF-8</param-value>
              </init-param>
              <init-param> ③ 強制進行編碼轉換
                  <param-name>forceEncoding</param-name>
                  <param-value>true</param-value>
              </init-param>
              </filter>
              <filter-mapping> ② 過濾器的匹配 URL
                  <filter-name>encodingFilter</filter-name>
                  <url-pattern>*.html</url-pattern>
              </filter-mapping>
          
          <!---servlet的配置-->
          </web-app>
          

          這樣所有以 .html 為后綴的 URL 請求的數據都會被轉碼為 UTF-8 編碼格式,表單中文亂碼的問題就可以解決了。

          請求跟蹤日志過濾器

          除了以上兩個常用的過濾器外,還有兩個在程序調試時可能會用到的請求日志跟蹤過濾器,它們會將請求的一些重要信息記錄到日志中,方便程序的調試。這兩個日志過濾器只有在日志級別為 DEBUG 時才會起作用:

          方法 說明
          org.springframework.web.filter.ServletContextRequestLoggingFilter 該過濾器將請求的 URI 記錄到 Common 日志中(如通過 Log4J 指定的日志文件);
          org.springframework.web.filter.ServletContextRequestLoggingFilter 該過濾器將請求的 URI 記錄到 ServletContext 日志中。

          以下是日志過濾器記錄的請求跟蹤日志的片斷:

          (JspServlet.java:224) -     JspEngine --> /htmlTest.jsp
          (JspServlet.java:225) -              ServletPath: /htmlTest.jsp
          (JspServlet.java:226) -                 PathInfo: null
          (JspServlet.java:227) -                 RealPath: D:\masterSpring\chapter23\webapp\htmlTest.jsp
          (JspServlet.java:228) -               RequestURI: /baobaotao/htmlTest.jsp
          …
          

          通過這個請求跟蹤日志,程度調試者可以詳細地查看到有哪些請求被調用,請求的參數是什么,請求是否正確返回等信息。雖然這兩個請求跟蹤日志過濾器一般在程序調試時使用,但是即使程序部署不將其從 web.xml 中移除也不會有大礙,因為只要將日志級別設置為 DEBUG 以上級別,它們就不會輸出請求跟蹤日志信息了。

          轉存 Web 應用根目錄監聽器和 Log4J 監聽器

          Spring 在 org.springframework.web.util 包中提供了幾個特殊用途的 Servlet 監聽器,正確地使用它們可以完成一些特定需求的功能。比如某些第三方工具支持通過 ${key} 的方式引用系統參數(即可以通過 System.getProperty() 獲取的屬性),WebAppRootListener 可以將 Web 應用根目錄添加到系統參數中,對應的屬性名可以通過名為"webAppRootKey"的 Servlet 上下文參數指定,默認為"webapp.root"。下面是該監聽器的具體的配置:


          清單 6. WebAppRootListener 監聽器配置

          …
          <context-param>
              <param-name>webAppRootKey</param-name>
              <param-value>baobaotao.root</param-value> ① Web 應用根目錄以該屬性名添加到系統參數中
          </context-param>
          …
          ② 負責將 Web 應用根目錄以 webAppRootKey 上下文參數指定的屬性名添加到系統參數中
          <listener>
              <listener-class> 
              org.springframework.web.util.WebAppRootListener
              </listener-class>
          </listener>
          …
          

          這樣,您就可以在程序中通過 System.getProperty("baobaotao.root") 獲取 Web 應用的根目錄了。不過更常見的使用場景是在第三方工具的配置文件中通過${baobaotao.root} 引用 Web 應用的根目錄。比如以下的 log4j.properties 配置文件就通過 ${baobaotao.root} 設置了日志文件的地址:

          log4j.rootLogger=INFO,R
          log4j.appender.R=org.apache.log4j.RollingFileAppender
          log4j.appender.R.File=${baobaotao.root}/WEB-INF/logs/log4j.log ① 指定日志文件的地址
          log4j.appender.R.MaxFileSize=100KB
          log4j.appender.R.MaxBackupIndex=1
          log4j.appender.R.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
          

          另一個專門用于 Log4J 的監聽器是 Log4jConfigListener。一般情況下,您必須將 Log4J 日志配置文件以 log4j.properties 為文件名并保存在類路徑下。Log4jConfigListener 允許您通過 log4jConfigLocation Servlet 上下文參數顯式指定 Log4J 配置文件的地址,如下所示:

          ① 指定 Log4J 配置文件的地址
          <context-param>
              <param-name>log4jConfigLocation</param-name>
              <param-value>/WEB-INF/log4j.properties</param-value>
          </context-param>
          …
          ② 使用該監聽器初始化 Log4J 日志引擎
          <listener>
              <listener-class>
              org.springframework.web.util.Log4jConfigListener
              </listener-class>
          </listener>
          …
          

          提示

          一些Web應用服務器(如 Tomcat)不會為不同的Web應用使用獨立的系統參數,也就是說,應用服務器上所有的 Web 應用都共享同一個系統參數對象。這時,您必須通過webAppRootKey 上下文參數為不同Web應用指定不同的屬性名:如第一個 Web 應用使用 webapp1.root 而第二個 Web 應用使用 webapp2.root 等,這樣才不會發生后者覆蓋前者的問題。此外,WebAppRootListener 和 Log4jConfigListener 都只能應用在 Web 應用部署后 WAR 文件會解包的 Web 應用服務器上。一些 Web 應用服務器不會將Web 應用的 WAR 文件解包,整個 Web 應用以一個 WAR 包的方式存在(如 Weblogic),此時因為無法指定對應文件系統的 Web 應用根目錄,使用這兩個監聽器將會發生問題。

          Log4jConfigListener 監聽器包括了 WebAppRootListener 的功能,也就是說,Log4jConfigListener 會自動完成將 Web 應用根目錄以 webAppRootKey 上下文參數指定的屬性名添加到系統參數中,所以當您使用 Log4jConfigListener 后,就沒有必須再使用 WebAppRootListener了。

          Introspector 緩存清除監聽器

          Spring 還提供了一個名為 org.springframework.web.util.IntrospectorCleanupListener 的監聽器。它主要負責處理由 JavaBean Introspector 功能而引起的緩存泄露。IntrospectorCleanupListener 監聽器在 Web 應用關閉的時會負責清除 JavaBean Introspector 的緩存,在 web.xml 中注冊這個監聽器可以保證在 Web 應用關閉的時候釋放與其相關的 ClassLoader 的緩存和類引用。如果您使用了 JavaBean Introspector 分析應用中的類,Introspector 緩存會保留這些類的引用,結果在應用關閉的時候,這些類以及Web 應用相關的 ClassLoader 不能被垃圾回收。不幸的是,清除 Introspector 的唯一方式是刷新整個緩存,這是因為沒法準確判斷哪些是屬于本 Web 應用的引用對象,哪些是屬于其它 Web 應用的引用對象。所以刪除被緩存的 Introspection 會導致將整個 JVM 所有應用的 Introspection 都刪掉。需要注意的是,Spring 托管的 Bean 不需要使用這個監聽器,因為 Spring 的 Introspection 所使用的緩存在分析完一個類之后會馬上從 javaBean Introspector 緩存中清除掉,并將緩存保存在應用程序特定的 ClassLoader 中,所以它們一般不會導致內存資源泄露。但是一些類庫和框架往往會產生這個問題。例如 Struts 和 Quartz 的 Introspector 的內存泄漏會導致整個的 Web 應用的 ClassLoader 不能進行垃圾回收。在 Web 應用關閉之后,您還會看到此應用的所有靜態類引用,這個錯誤當然不是由這個類自身引起的。解決這個問題的方法很簡單,您僅需在 web.xml 中配置 IntrospectorCleanupListener 監聽器就可以了:

          <listener>
              <listener-class>
              org.springframework.web.util.IntrospectorCleanupListener
              </listener-class>
          </listener>
          



          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 乳山市| 颍上县| 吉首市| 太原市| 沁源县| 沧源| 阿合奇县| 富宁县| 和林格尔县| 友谊县| 宜城市| 芒康县| 台北县| 新营市| 壶关县| 托里县| 浪卡子县| 得荣县| 北流市| 五华县| 包头市| 隆尧县| 云南省| 武义县| 溧阳市| 昌吉市| 鄂州市| 门源| 建阳市| 宝坻区| 泗洪县| 丹棱县| 二连浩特市| 岳普湖县| 海宁市| 梓潼县| 仁怀市| 遵化市| 临漳县| 阳曲县| 特克斯县|