隨筆 - 63  文章 - 0  trackbacks - 0
          <2009年4月>
          2930311234
          567891011
          12131415161718
          19202122232425
          262728293012
          3456789

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          Spring 不但提供了一個(gè)功能全面的應(yīng)用開發(fā)框架,本身還擁有眾多可以在程序編寫時(shí)直接使用的工具類,您不但可以在 Spring 應(yīng)用中使用這些工具類,也可以在其它的應(yīng)用中使用,這些工具類中的大部分是可以在脫離 Spring 框架時(shí)使用的。了解 Spring 中有哪些好用的工具類并在程序編寫時(shí)適當(dāng)使用,將有助于提高開發(fā)效率、增強(qiáng)代碼質(zhì)量。

          在這個(gè)分為兩部分的文章中,我們將從眾多的 Spring 工具類中遴選出那些好用的工具類介紹給大家。第 1 部分將介紹與文件資源操作和 Web 相關(guān)的工具類。在 第 2 部分 中將介紹特殊字符轉(zhuǎn)義和方法入?yún)z測(cè)工具類。

          文件資源操作

          文件資源的操作是應(yīng)用程序中常見的功能,如當(dāng)上傳一個(gè)文件后將其保存在特定目錄下,從指定地址加載一個(gè)配置文件等等。我們一般使用 JDK 的 I/O 處理類完成這些操作,但對(duì)于一般的應(yīng)用程序來說,JDK 的這些操作類所提供的方法過于底層,直接使用它們進(jìn)行文件操作不但程序編寫復(fù)雜而且容易產(chǎn)生錯(cuò)誤。相比于 JDK 的 File,Spring 的 Resource 接口(資源概念的描述接口)抽象層面更高且涵蓋面更廣,Spring 提供了許多方便易用的資源操作工具類,它們大大降低資源操作的復(fù)雜度,同時(shí)具有更強(qiáng)的普適性。這些工具類不依賴于 Spring 容器,這意味著您可以在程序中象一般普通類一樣使用它們。

          加載文件資源

          Spring 定義了一個(gè) org.springframework.core.io.Resource 接口,Resource 接口是為了統(tǒng)一各種類型不同的資源而定義的,Spring 提供了若干 Resource 接口的實(shí)現(xiàn)類,這些實(shí)現(xiàn)類可以輕松地加載不同類型的底層資源,并提供了獲取文件名、URL 地址以及資源內(nèi)容的操作方法。

          訪問文件資源

          假設(shè)有一個(gè)文件地位于 Web 應(yīng)用的類路徑下,您可以通過以下方式對(duì)這個(gè)文件資源進(jìn)行訪問:

          • 通過 FileSystemResource 以文件系統(tǒng)絕對(duì)路徑的方式進(jìn)行訪問;
          • 通過 ClassPathResource 以類路徑的方式進(jìn)行訪問;
          • 通過 ServletContextResource 以相對(duì)于Web應(yīng)用根目錄的方式進(jìn)行訪問。

          相比于通過 JDK 的 File 類訪問文件資源的方式,Spring 的 Resource 實(shí)現(xiàn)類無疑提供了更加靈活的操作方式,您可以根據(jù)情況選擇適合的 Resource 實(shí)現(xiàn)類訪問資源。下面,我們分別通過 FileSystemResource 和 ClassPathResource 訪問同一個(gè)文件資源:


          清單 1. FileSourceExample
          package com.baobaotao.io;
                      import java.io.IOException;
                      import java.io.InputStream;
                      import org.springframework.core.io.ClassPathResource;
                      import org.springframework.core.io.FileSystemResource;
                      import org.springframework.core.io.Resource;
                      public class FileSourceExample {
                      public static void main(String[] args) {
                      try {
                      String filePath =
                      "D:/masterSpring/chapter23/webapp/WEB-INF/classes/conf/file1.txt";
                      // ① 使用系統(tǒng)文件路徑方式加載文件
                      Resource res1 = new FileSystemResource(filePath);
                      // ② 使用類路徑方式加載文件
                      Resource res2 = new ClassPathResource("conf/file1.txt");
                      InputStream ins1 = res1.getInputStream();
                      InputStream ins2 = res2.getInputStream();
                      System.out.println("res1:"+res1.getFilename());
                      System.out.println("res2:"+res2.getFilename());
                      } catch (IOException e) {
                      e.printStackTrace();
                      }
                      }
                      }
                      

          在獲取資源后,您就可以通過 Resource 接口定義的多個(gè)方法訪問文件的數(shù)據(jù)和其它的信息:如您可以通過 getFileName() 獲取文件名,通過 getFile() 獲取資源對(duì)應(yīng)的 File 對(duì)象,通過 getInputStream() 直接獲取文件的輸入流。此外,您還可以通過 createRelative(String relativePath) 在資源相對(duì)地址上創(chuàng)建新的資源。

          在 Web 應(yīng)用中,您還可以通過 ServletContextResource 以相對(duì)于 Web 應(yīng)用根目錄的方式訪問文件資源,如下所示:

          <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
                      <jsp:directive.page import="
                      org.springframework.web.context.support.ServletContextResource"/>
                      <jsp:directive.page import="org.springframework.core.io.Resource"/>
                      <%
                      // ① 注意文件資源地址以相對(duì)于 Web 應(yīng)用根路徑的方式表示
                      Resource res3 = new ServletContextResource(application,
                      "/WEB-INF/classes/conf/file1.txt");
                      out.print(res3.getFilename());
                      %>
                      

          對(duì)于位于遠(yuǎn)程服務(wù)器(Web 服務(wù)器或 FTP 服務(wù)器)的文件資源,您則可以方便地通過 UrlResource 進(jìn)行訪問。

          為了方便訪問不同類型的資源,您必須使用相應(yīng)的 Resource 實(shí)現(xiàn)類,是否可以在不顯式使用 Resource 實(shí)現(xiàn)類的情況下,僅根據(jù)帶特殊前綴的資源地址直接加載文件資源呢?Spring 提供了一個(gè) ResourceUtils 工具類,它支持“classpath:”和“file:”的地址前綴,它能夠從指定的地址加載文件資源,請(qǐng)看下面的例子:


          清單 2. ResourceUtilsExample
          package com.baobaotao.io;
                      import java.io.File;
                      import org.springframework.util.ResourceUtils;
                      public class ResourceUtilsExample {
                      public static void main(String[] args) throws Throwable{
                      File clsFile = ResourceUtils.getFile("classpath:conf/file1.txt");
                      System.out.println(clsFile.isFile());
                      String httpFilePath = "file:D:/masterSpring/chapter23/src/conf/file1.txt";
                      File httpFile = ResourceUtils.getFile(httpFilePath);
                      System.out.println(httpFile.isFile());
                      }
                      }
                      

          ResourceUtils 的 getFile(String resourceLocation) 方法支持帶特殊前綴的資源地址,這樣,我們就可以在不和 Resource 實(shí)現(xiàn)類打交道的情況下使用 Spring 文件資源加載的功能了。

          本地化文件資源

          本地化文件資源是一組通過本地化標(biāo)識(shí)名進(jìn)行特殊命名的文件,Spring 提供的 LocalizedResourceHelper 允許通過文件資源基名和本地化實(shí)體獲取匹配的本地化文件資源并以 Resource 對(duì)象返回。假設(shè)在類路徑的 i18n 目錄下,擁有一組基名為 message 的本地化文件資源,我們通過以下實(shí)例演示獲取對(duì)應(yīng)中國大陸和美國的本地化文件資源:


          清單 3. LocaleResourceTest
          package com.baobaotao.io;
                      import java.util.Locale;
                      import org.springframework.core.io.Resource;
                      import org.springframework.core.io.support.LocalizedResourceHelper;
                      public class LocaleResourceTest {
                      public static void main(String[] args) {
                      LocalizedResourceHelper lrHalper = new LocalizedResourceHelper();
                      // ① 獲取對(duì)應(yīng)美國的本地化文件資源
                      Resource msg_us = lrHalper.findLocalizedResource("i18n/message", ".properties",
                      Locale.US);
                      // ② 獲取對(duì)應(yīng)中國大陸的本地化文件資源
                      Resource msg_cn = lrHalper.findLocalizedResource("i18n/message", ".properties",
                      Locale.CHINA);
                      System.out.println("fileName(us):"+msg_us.getFilename());
                      System.out.println("fileName(cn):"+msg_cn.getFilename());
                      }
                      }
                      

          雖然 JDK 的 java.util.ResourceBundle 類也可以通過相似的方式獲取本地化文件資源,但是其返回的是 ResourceBundle 類型的對(duì)象。如果您決定統(tǒng)一使用 Spring 的 Resource 接表征文件資源,那么 LocalizedResourceHelper 就是獲取文件資源的非常適合的幫助類了。

          文件操作

          在使用各種 Resource 接口的實(shí)現(xiàn)類加載文件資源后,經(jīng)常需要對(duì)文件資源進(jìn)行讀取、拷貝、轉(zhuǎn)存等不同類型的操作。您可以通過 Resource 接口所提供了方法完成這些功能,不過在大多數(shù)情況下,通過 Spring 為 Resource 所配備的工具類完成文件資源的操作將更加方便。

          文件內(nèi)容拷貝

          第一個(gè)我們要認(rèn)識(shí)的是 FileCopyUtils,它提供了許多一步式的靜態(tài)操作方法,能夠?qū)⑽募?nèi)容拷貝到一個(gè)目標(biāo) byte[]、String 甚至一個(gè)輸出流或輸出文件中。下面的實(shí)例展示了 FileCopyUtils 具體使用方法:


          清單 4. FileCopyUtilsExample
          package com.baobaotao.io;
                      import java.io.ByteArrayOutputStream;
                      import java.io.File;
                      import java.io.FileReader;
                      import java.io.OutputStream;
                      import org.springframework.core.io.ClassPathResource;
                      import org.springframework.core.io.Resource;
                      import org.springframework.util.FileCopyUtils;
                      public class FileCopyUtilsExample {
                      public static void main(String[] args) throws Throwable {
                      Resource res = new ClassPathResource("conf/file1.txt");
                      // ① 將文件內(nèi)容拷貝到一個(gè) byte[] 中
                      byte[] fileData = FileCopyUtils.copyToByteArray(res.getFile());
                      // ② 將文件內(nèi)容拷貝到一個(gè) String 中
                      String fileStr = FileCopyUtils.copyToString(new FileReader(res.getFile()));
                      // ③ 將文件內(nèi)容拷貝到另一個(gè)目標(biāo)文件
                      FileCopyUtils.copy(res.getFile(),
                      new File(res.getFile().getParent()+ "/file2.txt"));
                      // ④ 將文件內(nèi)容拷貝到一個(gè)輸出流中
                      OutputStream os = new ByteArrayOutputStream();
                      FileCopyUtils.copy(res.getInputStream(), os);
                      }
                      }
                      

          往往我們都通過直接操作 InputStream 讀取文件的內(nèi)容,但是流操作的代碼是比較底層的,代碼的面向?qū)ο笮圆⒉粡?qiáng)。通過 FileCopyUtils 讀取和拷貝文件內(nèi)容易于操作且相當(dāng)直觀。如在 ① 處,我們通過 FileCopyUtils 的 copyToByteArray(File in) 方法就可以直接將文件內(nèi)容讀到一個(gè) byte[] 中;另一個(gè)可用的方法是 copyToByteArray(InputStream in),它將輸入流讀取到一個(gè) byte[] 中。

          如果是文本文件,您可能希望將文件內(nèi)容讀取到 String 中,此時(shí)您可以使用 copyToString(Reader in) 方法,如 ② 所示。使用 FileReader 對(duì) File 進(jìn)行封裝,或使用 InputStreamReader 對(duì) InputStream 進(jìn)行封裝就可以了。

          FileCopyUtils 還提供了多個(gè)將文件內(nèi)容拷貝到各種目標(biāo)對(duì)象中的方法,這些方法包括:

          方法 說明
          static void copy(byte[] in, File out) 將 byte[] 拷貝到一個(gè)文件中
          static void copy(byte[] in, OutputStream out) 將 byte[] 拷貝到一個(gè)輸出流中
          static int copy(File in, File out) 將文件拷貝到另一個(gè)文件中
          static int copy(InputStream in, OutputStream out) 將輸入流拷貝到輸出流中
          static int copy(Reader in, Writer out) 將 Reader 讀取的內(nèi)容拷貝到 Writer 指向目標(biāo)輸出中
          static void copy(String in, Writer out) 將字符串拷貝到一個(gè) Writer 指向的目標(biāo)中

          在實(shí)例中,我們雖然使用 Resource 加載文件資源,但 FileCopyUtils 本身和 Resource 沒有任何關(guān)系,您完全可以在基于 JDK I/O API 的程序中使用這個(gè)工具類。

          屬性文件操作

          我們知道可以通過 java.util.Properties的load(InputStream inStream) 方法從一個(gè)輸入流中加載屬性資源。Spring 提供的 PropertiesLoaderUtils 允許您直接通過基于類路徑的文件地址加載屬性資源,請(qǐng)看下面的例子:

          package com.baobaotao.io;
                      import java.util.Properties;
                      import org.springframework.core.io.support.PropertiesLoaderUtils;
                      public class PropertiesLoaderUtilsExample {
                      public static void main(String[] args) throws Throwable {
                      // ① jdbc.properties 是位于類路徑下的文件
                      Properties props = PropertiesLoaderUtils.loadAllProperties("jdbc.properties");
                      System.out.println(props.getProperty("jdbc.driverClassName"));
                      }
                      }
                      

          一般情況下,應(yīng)用程序的屬性文件都放置在類路徑下,所以 PropertiesLoaderUtils 比之于 Properties#load(InputStream inStream) 方法顯然具有更強(qiáng)的實(shí)用性。此外,PropertiesLoaderUtils 還可以直接從 Resource 對(duì)象中加載屬性資源:

          方法 說明
          static Properties loadProperties(Resource resource) 從 Resource 中加載屬性
          static void fillProperties(Properties props, Resource resource) 將 Resource 中的屬性數(shù)據(jù)添加到一個(gè)已經(jīng)存在的 Properties 對(duì)象中

          特殊編碼的資源

          當(dāng)您使用 Resource 實(shí)現(xiàn)類加載文件資源時(shí),它默認(rèn)采用操作系統(tǒng)的編碼格式。如果文件資源采用了特殊的編碼格式(如 UTF-8),則在讀取資源內(nèi)容時(shí)必須事先通過 EncodedResource 指定編碼格式,否則將會(huì)產(chǎn)生中文亂碼的問題。


          清單 5. EncodedResourceExample
          package com.baobaotao.io;
                      import org.springframework.core.io.ClassPathResource;
                      import org.springframework.core.io.Resource;
                      import org.springframework.core.io.support.EncodedResource;
                      import org.springframework.util.FileCopyUtils;
                      public class EncodedResourceExample {
                      public static void main(String[] args) throws Throwable  {
                      Resource res = new ClassPathResource("conf/file1.txt");
                      // ① 指定文件資源對(duì)應(yīng)的編碼格式(UTF-8)
                      EncodedResource encRes = new EncodedResource(res,"UTF-8");
                      // ② 這樣才能正確讀取文件的內(nèi)容,而不會(huì)出現(xiàn)亂碼
                      String content  = FileCopyUtils.copyToString(encRes.getReader());
                      System.out.println(content);
                      }
                      }
                      

          EncodedResource 擁有一個(gè) getResource() 方法獲取 Resource,但該方法返回的是通過構(gòu)造函數(shù)傳入的原 Resource 對(duì)象,所以必須通過 EncodedResource#getReader() 獲取應(yīng)用編碼后的 Reader 對(duì)象,然后再通過該 Reader 讀取文件的內(nèi)容。





          回頁首


          Web 相關(guān)工具類

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

          操作 Servlet API 的工具類

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

          WebApplicationContextUtils

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

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

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

          WebApplicationContext wac =WebApplicationContextUtils.
                      getWebApplicationContext(servletContext);
                      

          當(dāng) ServletContext 屬性列表中不存在 WebApplicationContext 時(shí),getWebApplicationContext() 方法不會(huì)拋出異常,它簡單地返回 null。如果后續(xù)代碼直接訪問返回的結(jié)果將引發(fā)一個(gè) NullPointerException 異常,而 WebApplicationContextUtils 另一個(gè) getRequiredWebApplicationContext(ServletContext sc) 方法要求 ServletContext 屬性列表中一定要包含一個(gè)有效的 WebApplicationContext 對(duì)象,否則馬上拋出一個(gè) IllegalStateException 異常。我們推薦使用后者,因?yàn)樗芴崆鞍l(fā)現(xiàn)錯(cuò)誤的時(shí)間,強(qiáng)制開發(fā)者搭建好必備的基礎(chǔ)設(shè)施。

          WebUtils

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

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

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

          此外,WebUtils還提供了一些和ServletContext相關(guān)的方便方法:

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

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

          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 所提供的過濾器和監(jiān)聽器

          Spring 為 Web 應(yīng)用提供了幾個(gè)過濾器和監(jiān)聽器,在適合的時(shí)間使用它們,可以解決一些常見的 Web 應(yīng)用問題。

          延遲加載過濾器

          Hibernate 允許對(duì)關(guān)聯(lián)對(duì)象、屬性進(jìn)行延遲加載,但是必須保證延遲加載的操作限于同一個(gè) Hibernate Session 范圍之內(nèi)進(jìn)行。如果 Service 層返回一個(gè)啟用了延遲加載功能的領(lǐng)域?qū)ο蠼o Web 層,當(dāng) Web 層訪問到那些需要延遲加載的數(shù)據(jù)時(shí),由于加載領(lǐng)域?qū)ο蟮?Hibernate Session 已經(jīng)關(guān)閉,這些導(dǎo)致延遲加載數(shù)據(jù)的訪問異常。

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

          OpenSessionInViewFilter 過濾器將 Hibernate Session 綁定到請(qǐng)求線程中,它將自動(dòng)被 Spring 的事務(wù)管理器探測(cè)到。所以 OpenSessionInViewFilter 適用于 Service 層使用HibernateTransactionManager 或 JtaTransactionManager 進(jìn)行事務(wù)管理的環(huán)境,也可以用于非事務(wù)只讀的數(shù)據(jù)操作中。

          要啟用這個(gè)過濾器,必須在 web.xml 中對(duì)此進(jìn)行配置:

          …
                      <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>
                      …
                      

          上面的配置,我們假設(shè)使用 .html 的后綴作為 Web 框架的 URL 匹配模式,如果您使用 Struts 等 Web 框架,可以將其改為對(duì)應(yīng)的“*.do”模型。

          中文亂碼過濾器

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

          <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> ③ 強(qiáng)制進(jìn)行編碼轉(zhuǎn)換
                      <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 請(qǐng)求的數(shù)據(jù)都會(huì)被轉(zhuǎn)碼為 UTF-8 編碼格式,表單中文亂碼的問題就可以解決了。

          請(qǐng)求跟蹤日志過濾器

          除了以上兩個(gè)常用的過濾器外,還有兩個(gè)在程序調(diào)試時(shí)可能會(huì)用到的請(qǐng)求日志跟蹤過濾器,它們會(huì)將請(qǐng)求的一些重要信息記錄到日志中,方便程序的調(diào)試。這兩個(gè)日志過濾器只有在日志級(jí)別為 DEBUG 時(shí)才會(huì)起作用:

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

          以下是日志過濾器記錄的請(qǐng)求跟蹤日志的片斷:

          (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
                      …
                      

          通過這個(gè)請(qǐng)求跟蹤日志,程度調(diào)試者可以詳細(xì)地查看到有哪些請(qǐng)求被調(diào)用,請(qǐng)求的參數(shù)是什么,請(qǐng)求是否正確返回等信息。雖然這兩個(gè)請(qǐng)求跟蹤日志過濾器一般在程序調(diào)試時(shí)使用,但是即使程序部署不將其從 web.xml 中移除也不會(huì)有大礙,因?yàn)橹灰獙⑷罩炯?jí)別設(shè)置為 DEBUG 以上級(jí)別,它們就不會(huì)輸出請(qǐng)求跟蹤日志信息了。

          轉(zhuǎn)存 Web 應(yīng)用根目錄監(jiān)聽器和 Log4J 監(jiān)聽器

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


          清單 6. WebAppRootListener 監(jiān)聽器配置
          …
                      <context-param>
                      <param-name>webAppRootKey</param-name>
                      <param-value>baobaotao.root</param-value> ① Web 應(yīng)用根目錄以該屬性名添加到系統(tǒng)參數(shù)中
                      </context-param>
                      …
                      ② 負(fù)責(zé)將 Web 應(yīng)用根目錄以 webAppRootKey 上下文參數(shù)指定的屬性名添加到系統(tǒng)參數(shù)中
                      <listener>
                      <listener-class>
                      org.springframework.web.util.WebAppRootListener
                      </listener-class>
                      </listener>
                      …
                      

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

          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
                      

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

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

          提示

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

          Log4jConfigListener 監(jiān)聽器包括了 WebAppRootListener 的功能,也就是說,Log4jConfigListener 會(huì)自動(dòng)完成將 Web 應(yīng)用根目錄以 webAppRootKey 上下文參數(shù)指定的屬性名添加到系統(tǒng)參數(shù)中,所以當(dāng)您使用 Log4jConfigListener 后,就沒有必須再使用 WebAppRootListener了。

          Introspector 緩存清除監(jiān)聽器

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

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





          回頁首


          小結(jié)

          本文介紹了一些常用的 Spring 工具類,其中大部分 Spring 工具類不但可以在基于 Spring 的應(yīng)用中使用,還可以在其它的應(yīng)用中使用。使用 JDK 的文件操作類在訪問類路徑相關(guān)、Web 上下文相關(guān)的文件資源時(shí),往往顯得拖泥帶水、拐彎抹角,Spring 的 Resource 實(shí)現(xiàn)類使這些工作變得輕松了許多。

          在 Web 應(yīng)用中,有時(shí)你希望直接訪問 Spring 容器,獲取容器中的 Bean,這時(shí)使用 WebApplicationContextUtils 工具類從 ServletContext 中獲取 WebApplicationContext 是非常方便的。WebUtils 為訪問 Servlet API 提供了一套便捷的代理方法,您可以通過 WebUtils 更好的訪問 HttpSession 或 ServletContext 的信息。

          Spring 提供了幾個(gè) Servlet 過濾器和監(jiān)聽器,其中 ServletContextRequestLoggingFilter 和 ServletContextRequestLoggingFilter 可以記錄請(qǐng)求訪問的跟蹤日志,你可以在程序調(diào)試時(shí)使用它們獲取請(qǐng)求調(diào)用的詳細(xì)信息。WebAppRootListener 可以將 Web 應(yīng)用的根目錄以特定屬性名添加到系統(tǒng)參數(shù)中,以便第三方工具類通過 ${key} 的方式進(jìn)行訪問。Log4jConfigListener 允許你指定 Log4J 日志配置文件的地址,且可以在配置文件中通過 ${key} 的方式引用 Web 應(yīng)用根目錄,如果你需要在 Web 應(yīng)用相關(guān)的目錄創(chuàng)建日志文件,使用 Log4jConfigListener 可以很容易地達(dá)到這一目標(biāo)。

          Web 應(yīng)用的內(nèi)存泄漏是最讓開發(fā)者頭疼的問題,雖然不正確的程序編寫可能是這一問題的根源,也有可能是一些第三方框架的 JavaBean Introspector 緩存得不到清除而導(dǎo)致的,Spring 專門為解決這一問題配備了 IntrospectorCleanupListener 監(jiān)聽器,它只要簡單在 web.xml 中聲明該監(jiān)聽器就可以了。

          posted on 2009-04-05 08:44 lanxin1020 閱讀(155) 評(píng)論(0)  編輯  收藏 所屬分類: spring
          主站蜘蛛池模板: 凤山县| 亳州市| 鄂托克旗| 双牌县| 霸州市| 庆元县| 宜良县| 北安市| 南通市| 嘉祥县| 江阴市| 临漳县| 甘泉县| 柘城县| 扬中市| 承德县| 湄潭县| 兰西县| 嘉黎县| 霍林郭勒市| 岑巩县| 长寿区| 沧州市| 宁都县| 噶尔县| 娄烦县| 柳江县| 日土县| 若尔盖县| 遵化市| 石河子市| 四子王旗| 清河县| 和静县| 太白县| 镇安县| 隆尧县| 通辽市| 那坡县| 峡江县| 广州市|