日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

Spring 的优秀工具类盘点

發(fā)布時(shí)間:2025/3/8 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring 的优秀工具类盘点 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Spring 的優(yōu)秀工具類盤點(diǎn)---轉(zhuǎn)

第 1 部分: 文件資源操作和 Web 相關(guān)工具類

http://www.ibm.com/developerworks/cn/java/j-lo-spring-utils1/

文件資源操作

文件資源的操作是應(yīng)用程序中常見的功能,如當(dāng)上傳一個(gè)文件后將其保存在特定目錄下,從指定地址加載一個(gè)配置文件等等。我們一般使用 JDK 的 I/O 處理類完成這些操作,但對(duì)于一般的應(yīng)用程序來(lái)說(shuō),JDK 的這些操作類所提供的方法過(guò)于底層,直接使用它們進(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)容的操作方法。

訪問(wèn)文件資源

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

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

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

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

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

<%@ 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ù)器)的文件資源,您則可以方便地通過(guò) UrlResource 進(jìn)行訪問(wèn)。

為了方便訪問(wè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 文件資源加載的功能了。

本地化文件資源

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

清單 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)美國(guó)的本地化文件資源Resource msg_us = lrHalper.findLocalizedResource("i18n/message", ".properties", Locale.US); // ② 獲取對(duì)應(yīng)中國(guó)大陸的本地化文件資源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 類也可以通過(guò)相似的方式獲取本地化文件資源,但是其返回的是 ResourceBundle 類型的對(duì)象。如果您決定統(tǒng)一使用 Spring 的 Resource 接表征文件資源,那么 LocalizedResourceHelper 就是獲取文件資源的非常適合的幫助類了。

文件操作

在使用各種 Resource 接口的實(shí)現(xiàn)類加載文件資源后,經(jīng)常需要對(duì)文件資源進(jìn)行讀取、拷貝、轉(zhuǎn)存等不同類型的操作。您可以通過(guò) Resource 接口所提供了方法完成這些功能,不過(guò)在大多數(shù)情況下,通過(guò) 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); } }

往往我們都通過(guò)直接操作 InputStream 讀取文件的內(nèi)容,但是流操作的代碼是比較底層的,代碼的面向?qū)ο笮圆⒉粡?qiáng)。通過(guò) FileCopyUtils 讀取和拷貝文件內(nèi)容易于操作且相當(dāng)直觀。如在 ① 處,我們通過(guò) 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ì)象中的方法,這些方法包括:

方法說(shuō)明
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 沒(méi)有任何關(guān)系,您完全可以在基于 JDK I/O API 的程序中使用這個(gè)工具類。

屬性文件操作

我們知道可以通過(guò) java.util.Properties 的 load(InputStream inStream) 方法從一個(gè)輸入流中加載屬性資源。Spring 提供的 PropertiesLoaderUtils 允許您直接通過(guò)基于類路徑的文件地址加載屬性資源,請(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ì)象中加載屬性資源:

方法說(shuō)明
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í)必須事先通過(guò) EncodedResource 指定編碼格式,否則將會(huì)產(chǎn)生中文亂碼的問(wè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,但該方法返回的是通過(guò)構(gòu)造函數(shù)傳入的原 Resource 對(duì)象,所以必須通過(guò) EncodedResource#getReader() 獲取應(yīng)用編碼后的 Reader 對(duì)象,然后再通過(guò)該 Reader 讀取文件的內(nèi)容。

Web 相關(guān)工具類

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

操作 Servlet API 的工具類

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

WebApplicationContextUtils

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

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

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

WebApplicationContext wac =WebApplicationContextUtils. getWebApplicationContext(servletContext);

當(dāng) ServletContext 屬性列表中不存在 WebApplicationContext 時(shí),getWebApplicationContext() 方法不會(huì)拋出異常,它簡(jiǎn)單地返回 null。如果后續(xù)代碼直接訪問(wèn)返回的結(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 提供了易用的代理方法,降低了訪問(wèn) Servlet API 的復(fù)雜度,可以將其看成是常用 Servlet API 方法的門面類。

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

方法說(shuō)明
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ì)象,否則您必須通過(guò) request.getHttpSession.getAttribute(name) 完成相同的操作;
Object getRequiredSessionAttribute(HttpServletRequest request, String name) 和上一個(gè)方法類似,只不過(guò)強(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)求屬性就可以被訪問(wèn)到了;

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

方法說(shuō)明
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 所提供的過(guò)濾器和監(jiān)聽器

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

延遲加載過(guò)濾器

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 層訪問(wèn)到那些需要延遲加載的數(shù)據(jù)時(shí),由于加載領(lǐng)域?qū)ο蟮?Hibernate Session 已經(jīng)關(guān)閉,這些導(dǎo)致延遲加載數(shù)據(jù)的訪問(wèn)異常。

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

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

要啟用這個(gè)過(guò)濾器,必須在 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”模型。

中文亂碼過(guò)濾器

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

<web-app> <!---listener 的配置 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter ① Spring 編輯過(guò)濾器</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> ② 過(guò)濾器的匹配 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 編碼格式,表單中文亂碼的問(wèn)題就可以解決了。

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

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

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

以下是日志過(guò)濾器記錄的請(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 …

通過(guò)這個(gè)請(qǐng)求跟蹤日志,程度調(diào)試者可以詳細(xì)地查看到有哪些請(qǐng)求被調(diào)用,請(qǐng)求的參數(shù)是什么,請(qǐng)求是否正確返回等信息。雖然這兩個(gè)請(qǐng)求跟蹤日志過(guò)濾器一般在程序調(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)聽器,正確地使用它們可以完成一些特定需求的功能。比如某些第三方工具支持通過(guò) ${key} 的方式引用系統(tǒng)參數(shù)(即可以通過(guò) System.getProperty() 獲取的屬性),WebAppRootListener 可以將 Web 應(yīng)用根目錄添加到系統(tǒng)參數(shù)中,對(duì)應(yīng)的屬性名可以通過(guò)名為“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> …

這樣,您就可以在程序中通過(guò) System.getProperty("baobaotao.root") 獲取 Web 應(yīng)用的根目錄了。不過(guò)更常見的使用場(chǎng)景是在第三方工具的配置文件中通過(guò) ${baobaotao.root} 引用 Web 應(yīng)用的根目錄。比如以下的 log4j.properties 配置文件就通過(guò) ${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 允許您通過(guò) 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ù),也就是說(shuō),應(yīng)用服務(wù)器上所有的 Web 應(yīng)用都共享同一個(gè)系統(tǒng)參數(shù)對(duì)象。這時(shí),您必須通過(guò) webAppRootKey 上下文參數(shù)為不同 Web 應(yīng)用指定不同的屬性名:如第一個(gè) Web 應(yīng)用使用 webapp1.root 而第二個(gè) Web 應(yīng)用使用 webapp2.root 等,這樣才不會(huì)發(fā)生后者覆蓋前者的問(wèn)題。此外,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(wú)法指定對(duì)應(yīng)文件系統(tǒng)的 Web 應(yīng)用根目錄,使用這兩個(gè)監(jiān)聽器將會(huì)發(fā)生問(wèn)題。

Log4jConfigListener 監(jiān)聽器包括了 WebAppRootListener 的功能,也就是說(shuō),Log4jConfigListener 會(huì)自動(dòng)完成將 Web 應(yīng)用根目錄以 webAppRootKey 上下文參數(shù)指定的屬性名添加到系統(tǒng)參數(shù)中,所以當(dāng)您使用 Log4jConfigListener 后,就沒(méi)有必須再使用 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)闆](méi)法準(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)存資源泄露。但是一些類庫(kù)和框架往往會(huì)產(chǎn)生這個(gè)問(wèn)題。例如 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è)問(wèn)題的方法很簡(jiǎn)單,您僅需在 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 的文件操作類在訪問(wèn)類路徑相關(guān)、Web 上下文相關(guān)的文件資源時(shí),往往顯得拖泥帶水、拐彎抹角,Spring 的 Resource 實(shí)現(xiàn)類使這些工作變得輕松了許多。

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

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

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

第 2 部分: 特殊字符轉(zhuǎn)義和方法入?yún)z測(cè)工具類

http://www.ibm.com/developerworks/cn/java/j-lo-spring-utils2/

特殊字符轉(zhuǎn)義

由于 Web 應(yīng)用程序需要聯(lián)合使用到多種語(yǔ)言,每種語(yǔ)言都包含一些特殊的字符,對(duì)于動(dòng)態(tài)語(yǔ)言或標(biāo)簽式的語(yǔ)言而言,如果需要?jiǎng)討B(tài)構(gòu)造語(yǔ)言的內(nèi)容時(shí),一個(gè)我們經(jīng)常會(huì)碰到的問(wèn)題就是特殊字符轉(zhuǎn)義的問(wèn)題。下面是 Web 開發(fā)者最常面對(duì)需要轉(zhuǎn)義的特殊字符類型:

  • HTML 特殊字符;
  • JavaScript 特殊字符;
  • SQL 特殊字符;

如果不對(duì)這些特殊字符進(jìn)行轉(zhuǎn)義處理,則不但可能破壞文檔結(jié)構(gòu),還可以引發(fā)潛在的安全問(wèn)題。Spring 為 HTML 和 JavaScript 特殊字符提供了轉(zhuǎn)義操作工具類,它們分別是 HtmlUtils 和 JavaScriptUtils。

HTML 特殊字符轉(zhuǎn)義

HTML 中 <,>,& 等字符有特殊含義,它們是 HTML 語(yǔ)言的保留字,因此不能直接使用。使用這些個(gè)字符時(shí),應(yīng)使用它們的轉(zhuǎn)義序列:

  • &:&amp;
  • " :&quot;
  • < :&lt;
  • > :&gt;

由于 HTML 網(wǎng)頁(yè)本身就是一個(gè)文本型結(jié)構(gòu)化文檔,如果直接將這些包含了 HTML 特殊字符的內(nèi)容輸出到網(wǎng)頁(yè)中,極有可能破壞整個(gè) HTML 文檔的結(jié)構(gòu)。所以,一般情況下需要對(duì)動(dòng)態(tài)數(shù)據(jù)進(jìn)行轉(zhuǎn)義處理,使用轉(zhuǎn)義序列表示 HTML 特殊字符。下面的 JSP 網(wǎng)頁(yè)將一些變量動(dòng)態(tài)輸出到 HTML 網(wǎng)頁(yè)中:

清單 1. 未進(jìn)行 HTML 特殊字符轉(zhuǎn)義處理網(wǎng)頁(yè)
<%@ page language="java" contentType="text/html; charset=utf-8"%> <%!String userName = "</td><tr></table>";String address = " \" type=\"button";%> <table border="1"><tr><td>姓名:</td><td><%=userName%></td> ①</tr><tr><td>年齡:</td><td>28</td></tr> </table><input value="<%=address%>" type="text" /> ②

在 ① 和 ② 處,我們未經(jīng)任何轉(zhuǎn)義處理就直接將變量輸出到 HTML 網(wǎng)頁(yè)中,由于這些變量可能包含一些特殊的 HTML 的字符,它們將可能破壞整個(gè) HTML 文檔的結(jié)構(gòu)。我們可以從以上 JSP 頁(yè)面的一個(gè)具體輸出中了解這一問(wèn)題:

<table border="1"><tr><td>姓名:</td><td></td><tr></table></td> ① 破壞了 <table> 的結(jié)構(gòu)</tr><tr><td>年齡:</td><td>28</td></tr> </table><input value=" " type="button" type="text" /> ② 將本來(lái)是輸入框組件偷梁換柱為按鈕組件

融合動(dòng)態(tài)數(shù)據(jù)后的 HTML 網(wǎng)頁(yè)已經(jīng)面目全非,首先 ① 處的 <table> 結(jié)構(gòu)被包含 HTML 特殊字符的 userName 變量截?cái)嗔?#xff0c;造成其后的 <table> 代碼變成無(wú)效的內(nèi)容;其次,② 處 <input> 被動(dòng)態(tài)數(shù)據(jù)改換為按鈕類型的組件(type="button")。為了避免這一問(wèn)題,我們需要事先對(duì)可能破壞 HTML 文檔結(jié)構(gòu)的動(dòng)態(tài)數(shù)據(jù)進(jìn)行轉(zhuǎn)義處理。Spring 為我們提供了一個(gè)簡(jiǎn)單適用的 HTML 特殊字符轉(zhuǎn)義工具類,它就是 HtmlUtils。下面,我們通過(guò)一個(gè)簡(jiǎn)單的例子了解 HtmlUtils 的具體用法:

清單 2. HtmpEscapeExample
package com.baobaotao.escape; import org.springframework.web.util.HtmlUtils; public class HtmpEscapeExample {public static void main(String[] args) {String specialStr = "<div id=\"testDiv\">test1;test2</div>";String str1 = HtmlUtils.htmlEscape(specialStr); ①轉(zhuǎn)換為HTML轉(zhuǎn)義字符表示System.out.println(str1);String str2 = HtmlUtils.htmlEscapeDecimal(specialStr); ②轉(zhuǎn)換為數(shù)據(jù)轉(zhuǎn)義表示System.out.println(str2);String str3 = HtmlUtils.htmlEscapeHex(specialStr); ③轉(zhuǎn)換為十六進(jìn)制數(shù)據(jù)轉(zhuǎn)義表示System.out.println(str3);④下面對(duì)轉(zhuǎn)義后字符串進(jìn)行反向操作System.out.println(HtmlUtils.htmlUnescape(str1));System.out.println(HtmlUtils.htmlUnescape(str2));System.out.println(HtmlUtils.htmlUnescape(str3));} }

HTML 不但可以使用通用的轉(zhuǎn)義序列表示 HTML 特殊字符,還可以使用以 # 為前綴的數(shù)字序列表示 HTML 特殊字符,它們?cè)谧罱K的顯示效果上是一樣的。HtmlUtils 提供了三個(gè)轉(zhuǎn)義方法:

方法說(shuō)明
static String htmlEscape(String input) 將 HTML 特殊字符轉(zhuǎn)義為 HTML 通用轉(zhuǎn)義序列;
static String htmlEscapeDecimal(String input) 將 HTML 特殊字符轉(zhuǎn)義為帶 # 的十進(jìn)制數(shù)據(jù)轉(zhuǎn)義序列;
static String htmlEscapeHex(String input) 將 HTML 特殊字符轉(zhuǎn)義為帶 # 的十六進(jìn)制數(shù)據(jù)轉(zhuǎn)義序列;

此外,HtmlUtils 還提供了一個(gè)能夠?qū)⒔?jīng)過(guò)轉(zhuǎn)義內(nèi)容還原的方法:htmlUnescape(String input),它可以還原以上三種轉(zhuǎn)義序列的內(nèi)容。運(yùn)行以上代碼,您將可以看到以下的輸出:

str1:&lt;div id=&quot;testDiv&quot;&gt;test1;test2&lt;/div&gt; str2:&#60;div id=&#34;testDiv&#34;&#62;test1;test2&#60;/div&#62; str3:&#x3c;div id=&#x22;testDiv&#x22;&#x3e;test1;test2&#x3c;/div&#x3e; <div id="testDiv">test1;test2</div> <div id="testDiv">test1;test2</div> <div id="testDiv">test1;test2</div>

您只要使用 HtmlUtils 對(duì)代碼?清單 1?的 userName 和 address 進(jìn)行轉(zhuǎn)義處理,最終輸出的 HTML 頁(yè)面就不會(huì)遭受破壞了。

JavaScript 特殊字符轉(zhuǎn)義

JavaScript 中也有一些需要特殊處理的字符,如果直接將它們嵌入 JavaScript 代碼中,JavaScript 程序結(jié)構(gòu)將會(huì)遭受破壞,甚至被嵌入一些惡意的程序。下面列出了需要轉(zhuǎn)義的特殊 JavaScript 字符:

  • ' :\'
  • " :\"
  • \ :\\
  • 走紙換頁(yè): \f
  • 換行:\n
  • 換欄符:\t
  • 回車:\r
  • 回退符:\b

?

我們通過(guò)一個(gè)具體例子演示動(dòng)態(tài)變量是如何對(duì) JavaScript 程序進(jìn)行破壞的。假設(shè)我們有一個(gè) JavaScript 數(shù)組變量,其元素值通過(guò)一個(gè) Java List 對(duì)象提供,下面是完成這一操作的 JSP 代碼片斷:

清單 3. jsTest.jsp:未對(duì) JavaScript 特殊字符進(jìn)行處理
<%@ page language="java" contentType="text/html; charset=utf-8"%> <jsp:directive.page import="java.util.*"/> <%List textList = new ArrayList();textList.add("\";alert();j=\""); %> <script>var txtList = new Array();<% for ( int i = 0 ; i < textList.size() ; i++) { %>txtList[<%=i%>] = "<%=textList.get(i)%>"; ① 未對(duì)可能包含特殊 JavaScript 字符的變量進(jìn)行處理<% } %> </script>

當(dāng)客戶端調(diào)用這個(gè) JSP 頁(yè)面后,將得到以下的 HTML 輸出頁(yè)面:

<script>var txtList = new Array();txtList[0] = "";alert();j=""; ① 本來(lái)是希望接受一個(gè)字符串,結(jié)果被植入了一段JavaScript代碼 </script>

由于包含 JavaScript 特殊字符的 Java 變量直接合并到 JavaScript 代碼中,我們本來(lái)期望 ① 處所示部分是一個(gè)普通的字符串,但結(jié)果變成了一段 JavaScript 代碼,網(wǎng)頁(yè)將彈出一個(gè) alert 窗口。想像一下如果粗體部分的字符串是“";while(true)alert();j="”時(shí)會(huì)產(chǎn)生什么后果呢?

因此,如果網(wǎng)頁(yè)中的 JavaScript 代碼需要通過(guò)拼接 Java 變量動(dòng)態(tài)產(chǎn)生時(shí),一般需要對(duì)變量的內(nèi)容進(jìn)行轉(zhuǎn)義處理,可以通過(guò) Spring 的 JavaScriptUtils 完成這件工作。下面,我們使用 JavaScriptUtils 對(duì)以上代碼進(jìn)行改造:

<%@ page language="java" contentType="text/html; charset=utf-8"%> <jsp:directive.page import="java.util.*"/> <jsp:directive.page import="org.springframework.web.util.JavaScriptUtils"/> <%List textList = new ArrayList();textList.add("\";alert();j=\""); %> <script>var txtList = new Array();<% for ( int i = 0 ; i < textList.size() ; i++) { %>① 在輸出動(dòng)態(tài)內(nèi)容前事先進(jìn)行轉(zhuǎn)義處理txtList[<%=i%>] = "<%=JavaScriptUtils.javaScriptEscape(""+textList.get(i))%>";<% } %> </script>

通過(guò)轉(zhuǎn)義處理后,這個(gè) JSP 頁(yè)面輸出的結(jié)果網(wǎng)頁(yè)的 JavaScript 代碼就不會(huì)產(chǎn)生問(wèn)題了:

<script>var txtList = new Array();txtList[0] = "\";alert();j=\"";① 粗體部分僅是一個(gè)普通的字符串,而非一段 JavaScript 的語(yǔ)句了 </script>

SQL特殊字符轉(zhuǎn)義

應(yīng)該說(shuō),您即使沒(méi)有處理 HTML 或 JavaScript 的特殊字符,也不會(huì)帶來(lái)災(zāi)難性的后果,但是如果不在動(dòng)態(tài)構(gòu)造 SQL 語(yǔ)句時(shí)對(duì)變量中特殊字符進(jìn)行處理,將可能導(dǎo)致程序漏洞、數(shù)據(jù)盜取、數(shù)據(jù)破壞等嚴(yán)重的安全問(wèn)題。網(wǎng)絡(luò)中有大量講解 SQL 注入的文章,感興趣的讀者可以搜索相關(guān)的資料深入研究。

雖然 SQL 注入的后果很嚴(yán)重,但是只要對(duì)動(dòng)態(tài)構(gòu)造的 SQL 語(yǔ)句的變量進(jìn)行特殊字符轉(zhuǎn)義處理,就可以避免這一問(wèn)題的發(fā)生了。來(lái)看一個(gè)存在安全漏洞的經(jīng)典例子:

SELECT COUNT(userId) FROM t_user WHERE userName='"+userName+"' AND password ='"+password+"';

以上 SQL 語(yǔ)句根據(jù)返回的結(jié)果數(shù)判斷用戶提供的登錄信息是否正確,如果 userName 變量不經(jīng)過(guò)特殊字符轉(zhuǎn)義處理就直接合并到 SQL 語(yǔ)句中,黑客就可以通過(guò)將 userName 設(shè)置為 “1' or '1'='1”繞過(guò)用戶名/密碼的檢查直接進(jìn)入系統(tǒng)了。

所以除非必要,一般建議通過(guò) PreparedStatement 參數(shù)綁定的方式構(gòu)造動(dòng)態(tài) SQL 語(yǔ)句,因?yàn)檫@種方式可以避免 SQL 注入的潛在安全問(wèn)題。但是往往很難在應(yīng)用中完全避免通過(guò)拼接字符串構(gòu)造動(dòng)態(tài) SQL 語(yǔ)句的方式。為了防止他人使用特殊 SQL 字符破壞 SQL 的語(yǔ)句結(jié)構(gòu)或植入惡意操作,必須在變量拼接到 SQL 語(yǔ)句之前對(duì)其中的特殊字符進(jìn)行轉(zhuǎn)義處理。Spring 并沒(méi)有提供相應(yīng)的工具類,您可以通過(guò) jakarta commons lang 通用類包中(spring/lib/jakarta-commons/commons-lang.jar)的 StringEscapeUtils 完成這一工作:

清單 4. SqlEscapeExample
package com.baobaotao.escape; import org.apache.commons.lang.StringEscapeUtils; public class SqlEscapeExample {public static void main(String[] args) {String userName = "1' or '1'='1";String password = "123456";userName = StringEscapeUtils.escapeSql(userName);password = StringEscapeUtils.escapeSql(password);String sql = "SELECT COUNT(userId) FROM t_user WHERE userName='"+ userName + "' AND password ='" + password + "'";System.out.println(sql);} }

事實(shí)上,StringEscapeUtils 不但提供了 SQL 特殊字符轉(zhuǎn)義處理的功能,還提供了 HTML、XML、JavaScript、Java 特殊字符的轉(zhuǎn)義和還原的方法。如果您不介意引入 jakarta commons lang 類包,我們更推薦您使用 StringEscapeUtils 工具類完成特殊字符轉(zhuǎn)義處理的工作。

方法入?yún)z測(cè)工具類

Web 應(yīng)用在接受表單提交的數(shù)據(jù)后都需要對(duì)其進(jìn)行合法性檢查,如果表單數(shù)據(jù)不合法,請(qǐng)求將被駁回。類似的,當(dāng)我們?cè)诰帉戭惖姆椒〞r(shí),也常常需要對(duì)方法入?yún)⑦M(jìn)行合法性檢查,如果入?yún)⒉环弦?#xff0c;方法將通過(guò)拋出異常的方式拒絕后續(xù)處理。舉一個(gè)例子:有一個(gè)根據(jù)文件名獲取輸入流的方法:InputStream getData(String file),為了使方法能夠成功執(zhí)行,必須保證 file 入?yún)⒉荒転?null 或空白字符,否則根本無(wú)須進(jìn)行后繼的處理。這時(shí)方法的編寫者通常會(huì)在方法體的最前面編寫一段對(duì)入?yún)⑦M(jìn)行檢測(cè)的代碼,如下所示:

public InputStream getData(String file) {if (file == null || file.length() == 0|| file.replaceAll("\\s", "").length() == 0) {throw new IllegalArgumentException("file入?yún)⒉皇怯行У奈募刂?#34;);} … }

類似以上檢測(cè)方法入?yún)⒌拇a是非常常見,但是在每個(gè)方法中都使用手工編寫檢測(cè)邏輯的方式并不是一個(gè)好主意。閱讀 Spring 源碼,您會(huì)發(fā)現(xiàn) Spring 采用一個(gè) org.springframework.util.Assert 通用類完成這一任務(wù)。

Assert 翻譯為中文為“斷言”,使用過(guò) JUnit 的讀者都熟知這個(gè)概念,它斷定某一個(gè)實(shí)際的運(yùn)行值和預(yù)期想一樣,否則就拋出異常。Spring 對(duì)方法入?yún)⒌臋z測(cè)借用了這個(gè)概念,其提供的 Assert 類擁有眾多按規(guī)則對(duì)方法入?yún)⑦M(jìn)行斷言的方法,可以滿足大部分方法入?yún)z測(cè)的要求。這些斷言方法在入?yún)⒉粷M足要求時(shí)就會(huì)拋出 IllegalArgumentException。下面,我們來(lái)認(rèn)識(shí)一下 Assert 類中的常用斷言方法:

斷言方法說(shuō)明
notNull(Object object) 當(dāng) object 不為 null 時(shí)拋出異常,notNull(Object object, String message) 方法允許您通過(guò) message 定制異常信息。和 notNull() 方法斷言規(guī)則相反的方法是 isNull(Object object)/isNull(Object object, String message),它要求入?yún)⒁欢ㄊ?null;
isTrue(boolean expression) / isTrue(boolean expression, String message) 當(dāng) expression 不為 true 拋出異常;
notEmpty(Collection collection) / notEmpty(Collection collection, String message) 當(dāng)集合未包含元素時(shí)拋出異常。notEmpty(Map map) / notEmpty(Map map, String message) 和 notEmpty(Object[] array, String message) / notEmpty(Object[] array, String message) 分別對(duì) Map 和 Object[] 類型的入?yún)⑦M(jìn)行判斷;
hasLength(String text) / hasLength(String text, String message) 當(dāng) text 為 null 或長(zhǎng)度為 0 時(shí)拋出異常;
hasText(String text) / hasText(String text, String message) text 不能為 null 且必須至少包含一個(gè)非空格的字符,否則拋出異常;
isInstanceOf(Class clazz, Object obj) / isInstanceOf(Class type, Object obj, String message) 如果 obj 不能被正確造型為 clazz 指定的類將拋出異常;
isAssignable(Class superType, Class subType) / isAssignable(Class superType, Class subType, String message) subType 必須可以按類型匹配于 superType,否則將拋出異常;

使用 Assert 斷言類可以簡(jiǎn)化方法入?yún)z測(cè)的代碼,如 InputStream getData(String file) 在應(yīng)用 Assert 斷言類后,其代碼可以簡(jiǎn)化為以下的形式:

public InputStream getData(String file){Assert.hasText(file,"file入?yún)⒉皇怯行У奈募刂?#34;); ① 使用 Spring 斷言類進(jìn)行方法入?yún)z測(cè) … }

可見使用 Spring 的 Assert 替代自編碼實(shí)現(xiàn)的入?yún)z測(cè)邏輯后,方法的簡(jiǎn)潔性得到了不少的提高。Assert 不依賴于 Spring 容器,您可以大膽地在自己的應(yīng)用中使用這個(gè)工具類。

小結(jié)

本文介紹了一些常用的 Spring 工具類,其中大部分 Spring 工具類不但可以在基于 Spring 的應(yīng)用中使用,還可以在其它的應(yīng)用中使用。

對(duì)于 Web 應(yīng)用來(lái)說(shuō),由于有很多關(guān)聯(lián)的腳本代碼,如果這些代碼通過(guò)拼接字符串的方式動(dòng)態(tài)產(chǎn)生,就需要對(duì)動(dòng)態(tài)內(nèi)容中特殊的字符進(jìn)行轉(zhuǎn)義處理,否則就有可能產(chǎn)生意想不到的后果。Spring 為此提供了 HtmlUtils 和 JavaScriptUtils 工具類,只要將動(dòng)態(tài)內(nèi)容在拼接之前使用工具類進(jìn)行轉(zhuǎn)義處理,就可以避免類似問(wèn)題的發(fā)生了。如果您不介意引入一個(gè)第三方類包,那么 jakarta commons lang 通用類包中的 StringEscapeUtils 工具類可能更加適合,因?yàn)樗峁┝烁尤娴霓D(zhuǎn)義功能。

最后我們還介紹了 Spring 的 Assert 工具類,Assert 工具類是通用性很強(qiáng)的工具類,它使用面向?qū)ο蟮姆绞浇鉀Q方法入?yún)z測(cè)的問(wèn)題,您可以在自己的應(yīng)用中使用 Assert 對(duì)方法入?yún)⑦M(jìn)行檢查。

總結(jié)

以上是生活随笔為你收集整理的Spring 的优秀工具类盘点的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

亚洲精品乱码久久 | 五月婷婷视频在线观看 | 丁香九月婷婷综合 | 久久男人影院 | 国产又粗又硬又长又爽的视频 | 亚洲国产精品久久久久 | 91精品成人久久 | 91精品少妇偷拍99 | 免费一区在线 | 日韩中文字幕视频在线观看 | www.天天色.com | 免费a视频在线观看 | 亚洲一级电影在线观看 | 天天干天天操天天射 | 96精品视频 | 中文字幕国语官网在线视频 | 国产青青青 | av理论电影| 天天做天天爱天天爽综合网 | 成人精品视频久久久久 | 亚洲成a人片77777潘金莲 | 最新av观看 | 黄网站污 | 日本免费久久高清视频 | 日韩欧美在线视频一区二区 | 91在线播放国产 | 日日夜夜天天久久 | 久久久免费观看完整版 | 日韩剧情 | 五月婷色 | 欧美在线观看视频免费 | 精品一二区 | 一区二区不卡 | 人人干97 | 91在线麻豆 | 97超碰人人模人人人爽人人爱 | 久久久午夜精品理论片中文字幕 | 日韩精品免费在线观看 | av在线a | 免费网站看av片 | 91精品国产乱码久久 | 亚洲 综合 精品 | 免费看的毛片 | 精品久久久久久久久久岛国gif | 最近免费中文字幕大全高清10 | 国产在线精品国自产拍影院 | 国产成人久久精品亚洲 | 国产免费黄色 | 99超碰在线观看 | 91精品国产电影 | 国产精品一区二区久久 | 九九九九九精品 | 久草在线视频免赞 | 国产精品久久久久久久久久久久午 | 亚洲日本在线视频观看 | 天天摸夜夜添 | 高清不卡一区二区三区 | 99久久免费看 | 国产精品久久久久久久久久99 | 婷婷开心久久网 | 国产精品小视频网站 | 国产网站在线免费观看 | 久久精品视频网 | 欧美成人视 | 国产高清视频 | 成人中心免费视频 | 欧美激情视频在线观看免费 | 国产91在线免费视频 | 97在线视频免费 | 亚洲综合射 | 欧美日韩国产精品一区二区三区 | 成人网在线免费视频 | 超碰国产97 | 午夜视频导航 | 午夜久操 | 亚洲高清在线视频 | 亚洲国产成人高清精品 | 日韩免费在线视频观看 | 日本在线视频网址 | 日韩在线电影观看 | 亚洲人在线 | 免费大片黄在线 | 五月天综合在线 | 成人av资源网 | www.色国产 | 国产在线观看91 | 久久九九久久九九 | 欧美午夜精品久久久久久浪潮 | 天天搞夜夜骑 | 欧美日韩在线电影 | 国产一级大片在线观看 | 天天操天天摸天天干 | 91av视频播放 | 欧美资源在线观看 | 色美女在线 | 欧美日韩中文国产一区发布 | 亚洲四虎在线 | 免费看三片 | 欧美日韩91 | 国产精品色婷婷视频 | 久久久麻豆视频 | 久久精品99国产精品日本 | 国产成人三级 | 97超碰免费在线 | 特级黄色片免费看 | 在线观看av中文字幕 | 亚洲 欧洲av| 97视频精品 | 国产高清第一页 | 九九热精品视频在线观看 | 日韩精品一区在线播放 | 久久久久久毛片精品免费不卡 | 色欧美88888久久久久久影院 | 黄色在线观看免费 | 91精品久久久久久久久久入口 | 在线观看的av | 亚洲,播放 | 丝袜制服综合网 | 欧美在线一二区 | 免费观看av| 久久久精品一区二区三区 | 国产人成在线视频 | 中文字幕电影高清在线观看 | 在线 你懂| 亚洲一级电影 | 毛片永久新网址首页 | 国产一区免费在线观看 | 亚洲精品777 | 久久免费99精品久久久久久 | 欧美福利视频一区 | a√天堂资源 | 久久与婷婷 | 国产精品免费不卡 | 2019天天干夜夜操 | 天天综合网久久 | 欧美精品久久久久久久免费 | 国产精品久久久免费 | 久久久久区 | 婷婷国产v亚洲v欧美久久 | 欧美日韩性视频 | 狠狠干我 | 激情综合啪| 欧美少妇xx | 九九免费精品视频在线观看 | 亚洲精品免费在线观看视频 | www.夜夜草 | 一区三区视频在线观看 | 92中文资源在线 | 亚洲高清网站 | 色网站免费在线看 | 夜色资源网 | 亚洲欧美日韩在线看 | 日日干综合| 欧美色插 | 99re久久资源最新地址 | 毛片网在线观看 | 婷婷网五月天 | 天天色天天骑天天射 | 亚洲专区一二三 | 久在线 | 97精品国产手机 | 国产精品久免费的黄网站 | 黄色aaaaa| 91av电影网 | 婷婷中文字幕综合 | 国产精品美女 | 日韩免费看片 | 免费看三级 | 91九色网站 | 精品国产精品一区二区夜夜嗨 | 欧美精品久久久久久久久久白贞 | 久久精品观看 | 欧美日韩精品网站 | 国产在线观看xxx | 日本精品一 | 亚洲精品国产精品久久99 | 中文字幕一区二区三区四区 | 丁香 久久 综合 | 五月婷婷丁香网 | 欧美亚洲一区二区在线 | 日韩视频免费在线观看 | 不卡中文字幕在线 | 免费看色视频 | 国产做a爱一级久久 | 婷婷在线网 | 色是在线视频 | 亚洲日本成人网 | 免费午夜在线视频 | 久久免费视频网 | 精品国产免费看 | 成人久久电影 | 96精品视频| 国产精品成人免费一区久久羞羞 | 国产精久久久久久久 | 天天干天天干天天 | 色婷婷综合五月 | 韩国精品视频在线观看 | 国产区av在线 | 国产一区二区三区免费在线 | 91精品久久久久久粉嫩 | 日韩免 | 欧日韩在线视频 | 日韩av线观看 | 日韩久久网站 | 国产区精品视频 | 国产精品高清在线观看 | 久久精品视频观看 | 国产精品免费观看视频 | 免费在线观看午夜视频 | 国产一级免费播放 | 国产精品一区二区三区四区在线观看 | 少妇bbb搡bbbb搡bbbb′ | 久久成人福利 | 亚洲一区二区三区毛片 | 日韩成人免费在线电影 | 中文字幕视频三区 | 欧美不卡视频在线 | 免费黄色网址大全 | 亚洲欧美日韩国产精品一区午夜 | 欧美日韩免费一区二区三区 | 香蕉视频网址 | 亚洲欧洲精品一区 | 久久国产精品影视 | 草久草久 | 精品二区久久 | 国产综合婷婷 | 92精品国产成人观看免费 | 色综合狠狠干 | 日本在线视频网址 | 在线观看视频黄 | 超碰免费在线公开 | 久久久久久视频 | 午夜精品久久久久久久99水蜜桃 | 色资源网免费观看视频 | 亚洲精品国偷拍自产在线观看蜜桃 | 亚洲精品一区二区三区高潮 | 中文字幕免费看 | 亚洲另类视频在线 | 91精品婷婷国产综合久久蝌蚪 | 色网站在线免费观看 | 天天插视频 | 黄色不卡av | 国产精品激情偷乱一区二区∴ | 国产91免费在线观看 | 国产亚洲精品久久久网站好莱 | 麻豆 91 在线 | 欧美日本一二三 | av电影亚洲| 亚洲影视九九影院在线观看 | 国产精品麻豆免费版 | 国产免费嫩草影院 | 国产黄色片在线免费观看 | 在线91播放 | 久久久久久高潮国产精品视 | 亚洲精品永久免费视频 | 成人久久18免费网站 | 久久色亚洲 | 国产免费叼嘿网站免费 | 国产精品毛片网 | 视频一区二区三区视频 | 国产亚洲精品xxoo | 亚洲精品国产精品久久99热 | 不卡视频在线 | 在线观看成人一级片 | 国产精品毛片久久久 | 超碰激情在线 | 国产人成一区二区三区影院 | 热99久久精品 | 欧美激情综合五月 | 色爽网站 | 99c视频高清免费观看 | 99精品热| 日本精品久久久久久 | 日韩av中文在线 | 西西4444www大胆视频 | 免费高清在线一区 | 国产自产在线视频 | 午夜视频在线观看一区二区三区 | 欧美国产91 | 色av男人的天堂免费在线 | 毛片网在线观看 | 国产亚洲字幕 | 亚洲精品美女久久久久 | 91在线最新| 97超碰成人在线 | 中文字幕国产亚洲 | 婷婷日日 | 亚洲成av人影片在线观看 | 黄色在线观看www | 欧美日韩国产一区 | 国产精品成人av久久 | 俺要去色综合狠狠 | 综合久久婷婷 | 久草剧场| 在线免费观看黄色 | 久久优 | 在线观看精品视频 | 亚洲一区av | 天天干人人 | 国产一区二区久久 | 激情五月五月婷婷 | 色婷婷综合久久久 | 亚洲精品中文在线 | 日韩三级一区 | 国产一区二区三区免费观看视频 | 69精品久久久 | 国产中文视频 | 欧美精品在线视频 | 日韩视频在线观看视频 | 一区在线电影 | 97超碰资源站 | 99在线视频网站 | 国产精品福利无圣光在线一区 | 久久手机在线视频 | 日韩激情第一页 | 综合色在线观看 | 韩国精品一区二区三区六区色诱 | 麻豆免费视频网站 | 日韩精品一区二区在线观看 | 超碰97在线资源 | 麻豆91在线播放 | 免费午夜在线视频 | 色资源在线观看 | 婷婷开心久久网 | 久久久在线免费观看 | 婷婷av电影 | 国产精品国产三级国产不产一地 | 国产区精品在线 | 97超在线视频 | 久久不射电影院 | 国产视| 久久精品亚洲一区二区三区观看模式 | 欧美精品久久久久久久 | 亚洲影院国产 | 精品xxx| 亚洲一区欧美精品 | 久久亚洲免费 | 91最新在线 | 久久久久激情视频 | 婷婷去俺也去六月色 | 久草视频中文在线 | 天天干,夜夜爽 | 九七视频在线观看 | 91精品久久久久 | 成人在线免费视频观看 | 一二三久久久 | 狠狠干天天色 | 国产黄色片网站 | 亚洲闷骚少妇在线观看网站 | 一级一片免费视频 | 天天草网站 | 久久综合久久综合九色 | 日韩一级片大全 | 日韩高清观看 | 亚洲天堂自拍视频 | 成人免费xxx在线观看 | 蜜臀av夜夜澡人人爽人人 | 欧美成人猛片 | a视频免费看 | 九九色网 | 久久午夜影视 | a天堂中文在线 | 久久精品视频网站 | 色小说av| 91影视成人 | 久久婷婷激情 | 午夜免费久久看 | 日韩免费看的电影 | 婷婷亚洲五月 | 亚洲一区日韩 | 国产尤物在线视频 | 久久久人人人 | 人人澡人人爽 | 成人一级片免费看 | 91在线在线观看 | 色婷婷综合激情 | 国产乱码精品一区二区蜜臀 | 色综合久久中文字幕综合网 | 日韩精品免费在线播放 | 黄色成人小视频 | av再线观看 | 91av原创| 国产一区二区在线播放视频 | 视频一区二区国产 | 91人人爽久久涩噜噜噜 | 91黄色在线看 | 久久资源在线 | 成年人视频在线观看免费 | 日日夜夜精品免费观看 | 欧美一区二区免费在线观看 | 久草国产视频 | 色婷婷狠狠干 | 亚洲欧美色婷婷 | 国产日韩欧美在线一区 | 日韩精品一区二区在线 | 高清一区二区 | 天天干夜夜操视频 | 国内精品视频在线播放 | 中文字幕高清在线播放 | 中文字幕日本在线 | 99久久99久久 | 久久99亚洲网美利坚合众国 | 天天爽天天爽 | 日韩免费在线播放 | 黄色av网站在线免费观看 | 片网站| 99一区二区三区 | 亚洲精品久久久蜜桃 | 最新av在线网址 | 久久久久久久久久久久久国产精品 | 免费在线一区二区 | 日韩一二区在线 | 国产视频精品免费 | 成人97人人超碰人人99 | 国产精品a成v人在线播放 | 国产精品毛片完整版 | 国产又黄又爽又猛视频日本 | 亚洲精品在线免费观看视频 | 日韩三级中文字幕 | 福利一区二区 | 亚洲精品视频在线观看免费视频 | 色天天天| 91香蕉视频在线下载 | 亚洲精品在线网站 | 97人人爽人人 | 中文字幕有码在线播放 | 国产99久久精品一区二区永久免费 | 伊人激情网 | 日本aa在线 | 日本动漫做毛片一区二区 | 日日爱视频 | 91在线小视频 | 二区视频在线观看 | 国产精品网在线观看 | 日韩大片在线 | 天天摸日日摸人人看 | 在线视频a| 亚洲精品久久久久999中文字幕 | 在线观看视频一区二区三区 | 国产一级电影在线 | 国产中出在线观看 | 国产伦精品一区二区三区四区视频 | 91黄在线看 | 又黄又爽的免费高潮视频 | 伊人国产视频 | 婷婷中文字幕综合 | 免费高清在线视频一区· | 久久a久久 | 国产又粗又长又硬免费视频 | 国产视频18 | 99这里都是精品 | 国产尤物一区二区三区 | 国产91免费在线观看 | 天天色天天草天天射 | 天天干天天插 | 亚洲 精品在线视频 | 久久美女精品 | 久久久久久久久久久久影院 | 精品一区在线 | 日韩精品在线观看视频 | 久久久穴| 亚洲精品视频大全 | 青青草久草在线 | 日韩欧美一级二级 | 国产视频一区二区在线播放 | 国产成人久久av免费高清密臂 | 黄色一级大片在线免费看国产一 | 欧美精品xx | 国产精品密入口果冻 | 婷婷色亚洲 | 日韩v欧美v日本v亚洲v国产v | 亚洲 欧美 综合 在线 精品 | 国产美腿白丝袜足在线av | 夜夜视频资源 | 国产精品丝袜久久久久久久不卡 | 五月婷网站 | 91麻豆精品91久久久久同性 | 中文字幕亚洲五码 | 欧美 日韩 国产 成人 在线 | 久久久九色精品国产一区二区三区 | 久久精品视频国产 | 国产xvideos免费视频播放 | 91日韩在线专区 | 在线观看黄网站 | 国产免费国产 | 欧美在线99 | 97成人在线免费视频 | 免费高清在线一区 | 美女视频免费一区二区 | 一本一道久久a久久精品蜜桃 | 日韩免费看视频 | 久久视频免费看 | 日韩精品一区电影 | 成人免费一区二区三区在线观看 | 在线精品视频免费播放 | 91成品视频 | 国产区精品视频 | 麻豆影视在线播放 | 午夜色大片在线观看 | 日韩色视频在线观看 | 人人超碰免费 | 美女黄久久| 99视频这里有精品 | 天天射,天天干 | 日韩精品1区2区 | 亚洲动漫在线观看 | 中文亚洲欧美日韩 | 久久99热久久99精品 | 国产午夜精品一区 | 最新av网址在线观看 | 99热9| 亚洲精品88欧美一区二区 | 国产电影黄色av | 伊香蕉大综综综合久久啪 | 99精品色| 国产精品久久久久久五月尺 | 丁香六月色 | 久久一精品 | 亚洲免费在线看 | 日韩精品一区二区三区在线播放 | av电影免费观看 | 99在线视频观看 | 91成人黄色 | 99在线热播精品免费 | 日日干天天操 | 热久在线 | 日韩精品国产一区 | 美女国产网站 | 999视频网站 | 在线免费观看黄 | 久久中文网 | 少妇av网 | 国产精品涩涩屋www在线观看 | 在线91视频| 国产粉嫩在线 | 亚洲国产中文字幕 | 午夜精品一区二区三区在线播放 | 99亚洲精品在线 | 黄色一级大片在线免费看产 | 日韩激情综合 | 欧美视频在线二区 | 婷婷丁香激情综合 | 久久精品一区八戒影视 | 欧美日韩国产一区 | 成年人黄色大片在线 | 国产在线 一区二区三区 | 久久亚洲私人国产精品 | 日日爱夜夜爱 | 五月天婷亚洲天综合网鲁鲁鲁 | 国产精品免费在线播放 | 欧美国产日韩一区二区 | 国产女人40精品一区毛片视频 | 亚洲免费观看在线视频 | 91亚洲成人 | 99视频在线看 | 日韩激情在线视频 | av免费电影在线 | 免费在线色 | 久久人人爽人人爽人人片av软件 | 国产欧美精品在线观看 | 精品国产午夜 | 亚洲综合五月天 | 五月天激情综合 | 天堂成人在线 | 亚洲va欧洲va国产va不卡 | 欧美人交a欧美精品 | 亚洲首页 | 精品久久久久久久久久久久久久久久久久 | 在线视频91 | 国产最新视频在线 | 国产资源中文字幕 | 色婷婷亚洲精品 | 在线观看视频中文字幕 | 日韩中文字幕第一页 | 三级黄色大片在线观看 | 精品亚洲男同gayvideo网站 | 欧美日本一区 | 香蕉网在线播放 | 欧美午夜性 | 麻豆精品在线视频 | 久久成人国产精品免费软件 | 欧美a在线看 | 一区二区三区在线不卡 | 蜜桃视频日本 | 91九色国产在线 | 色视频 在线 | 亚洲高清av | 亚洲影院天堂 | 日韩欧美视频免费看 | 国产精品五月天 | 日韩欧美一区二区三区视频 | 亚洲一区二区三区四区精品 | 亚洲精品乱码久久久久久蜜桃91 | 激情久久影院 | 亚洲精品三级 | 免费在线观看一区二区三区 | 狠狠躁夜夜a产精品视频 | 国产一级一片免费播放放 | 欧美激情视频一区二区三区免费 | 国产97色在线 | 亚洲一区二区三区四区在线视频 | 久久久久久免费网 | 在线国产中文 | 久久精品国产亚洲精品2020 | 91精品一区国产高清在线gif | 麻豆影音先锋 | 国产99亚洲| 免费一级特黄毛大片 | 奇米影视8888在线观看大全免费 | 亚洲激精日韩激精欧美精品 | 国产黄a三级| 亚洲经典视频 | 日韩最新理论电影 | 狠狠干狠狠艹 | 久久久久久国产一区二区三区 | 午夜 久久 tv | 91x色| 亚洲激情综合 | 亚洲国产精彩中文乱码av | 亚洲精品综合欧美二区变态 | 中文字幕色婷婷在线视频 | 91.精品高清在线观看 | 日韩电影在线观看中文字幕 | 精品视频123区在线观看 | 久久成 | 超碰在97| 日韩视频三区 | 在线观看日韩中文字幕 | 国产在线观看二区 | 日本精品一 | 欧美日韩高清免费 | 午夜av免费在线观看 | 天天干夜夜想 | 福利视频第一页 | 久精品一区 | 6080yy午夜一二三区久久 | 狠狠色丁香婷婷综合 | 在线观看免费视频你懂的 | 蜜臀av在线一区二区三区 | 久久久影院官网 | 二区三区精品 | 亚洲高清在线精品 | 91亚洲国产成人久久精品网站 | 久久久久这里只有精品 | 天天干天天做 | 一级黄色电影网站 | 色91在线| 中文字幕免费成人 | 福利一区二区 | www四虎影院 | 亚洲国产视频网站 | 欧美精品在线视频 | 久久精国产 | 高清国产午夜精品久久久久久 | 久久天天拍 | 欧美激情综合五月色丁香 | 天天干,天天射,天天操,天天摸 | 日韩乱码中文字幕 | 国产精品九九久久久久久久 | 成年人免费在线观看网站 | 免费中文字幕视频 | 黄色片毛片 | 婷婷色综合网 | 激情电影影院 | 日韩视 | 久久一区二区三区日韩 | ,午夜性刺激免费看视频 | 久久色在线播放 | 亚洲精品在线视频观看 | 黄色国产高清 | 日韩精品免费在线观看 | 少妇高潮冒白浆 | 99久久er热在这里只有精品15 | 91av在线不卡 | 午夜精品99久久免费 | 日韩欧美一区二区不卡 | 亚洲 欧美 成人 | 欧美日韩精品区 | 国产精品久久99综合免费观看尤物 | 亚洲成a人片77777潘金莲 | 国产拍揄自揄精品视频麻豆 | 国产免费精彩视频 | 99久久久久久 | 国产精品视频内 | 在线观看av大片 | 国产成本人视频在线观看 | 久久久久久国产精品亚洲78 | 亚洲国产高清在线观看视频 | 精品亚洲va在线va天堂资源站 | www.色com | 四虎国产精品免费 | 亚洲午夜精品久久久久久久久 | 国产成人av福利 | 亚洲一区二区视频 | 欧美性色网站 | 激情网五月天 | 久久99久久99 | 五月婷网| 亚洲欧洲成人精品av97 | 操操操日日 | 久久久精品国产一区二区电影四季 | 69视频在线| 91精品天码美女少妇 | av免费在线免费观看 | 日本99热 | 天堂av高清 | 99精品国产一区二区三区麻豆 | 黄色亚洲免费 | 中文字幕一区二区三区四区 | 国产精品免费在线 | 在线视频观看你懂的 | 国产成人av一区二区三区在线观看 | 久久精品站 | 日韩久久视频 | 欧美日韩在线免费观看 | 一级α片 | 日本精品久久久久 | 国产在线精品一区二区 | 久久久国产精品一区二区中文 | 亚洲h在线播放在线观看h | 亚洲精品欧美精品 | 青春草国产视频 | 日韩在线电影一区二区 | 中文字幕影片免费在线观看 | 欧美美女视频在线观看 | 99久久99久久精品国产片果冰 | 亚洲欧美婷婷六月色综合 | 欧美日韩国产网站 | av解说在线 | 婷婷六月天在线 | 色综合夜色一区 | 超碰97在线资源 | 中文字幕人成不卡一区 | 亚洲精品在线观看网站 | av3级在线 | 九九热在线视频免费观看 | 国产精品精品国产婷婷这里av | 日本三级香港三级人妇99 | 午夜免费福利视频 | 91精品久久久久久久91蜜桃 | 亚洲一区二区三区精品在线观看 | 国产免费观看高清完整版 | 日韩精品一区电影 | 亚洲午夜大片 | www.午夜 | 久久精选视频 | 国模视频一区二区 | 久久久久久久久亚洲精品 | 日韩精品一区二区在线视频 | 欧美在线视频一区二区三区 | 精品播放 | 亚洲专区中文字幕 | 国产精品视频99 | 小草av在线播放 | 国精产品满18岁在线 | 免费色视频网站 | 免费在线视频一区二区 | 97人人看| 高清av在线免费观看 | 欧美日韩国产成人 | av日韩av| 91中文字幕在线播放 | 天天操天天色综合 | 国产精品日韩在线 | 国内精品在线观看视频 | 激情欧美丁香 | 亚洲精选在线 | 久久96国产精品久久99漫画 | 成人一区二区在线 | 亚洲国产成人精品在线观看 | 亚洲欧美一区二区三区孕妇写真 | 国产激情免费 | 日日爱夜夜爱 | 人交video另类hd | 综合天天久久 | 久操久 | 91视频免费看网站 | 成人精品99 | 国产亚洲午夜高清国产拍精品 | 免费看成人 | 久草在线高清 | 人人插人人搞 | 97香蕉久久国产在线观看 | 免费看污网站 | 国产一级片毛片 | 天堂网一区 | 欧美日韩免费网站 | 亚洲精品国产精品久久99热 | 久久久久免费精品国产小说色大师 | 91爱爱免费观看 | 天天射日| 中文字幕 国产视频 | 色av男人的天堂免费在线 | 亚洲狠狠婷婷 | av电影在线播放 | 亚洲一区美女视频在线观看免费 | 久久国产免费视频 | 麻豆影视在线观看 | 粉嫩aⅴ一区二区三区 | 欧美精品二区 | 日韩国产精品久久久久久亚洲 | 国产麻豆果冻传媒在线观看 | 91大神精品视频在线观看 | 久久综合婷婷国产二区高清 | 色噜噜在线观看视频 | 久久国产网| www.在线看片.com | 狠狠色噜噜狠狠狠狠 | 欧美aaaxxxx做受视频 | 黄色av电影一级片 | 日韩精品一区二区三区丰满 | 波多在线视频 | a级国产乱理伦片在线播放 久久久久国产精品一区 | 久久伊人国产精品 | 欧美性精品 | 91视频一8mav | 国产精品美女免费 | 国产精品网红直播 | 色操插| 欧美一级电影片 | 久久久精品免费看 | 五月天亚洲激情 | 黄色网中文字幕 | 国产精品久久久久一区二区 | 美国三级黄色大片 | 中文字幕在线国产 | 97视频免费在线观看 | 涩涩爱夜夜爱 | 日韩欧美成人网 | 国外成人在线视频网站 | 国产在线第三页 | 一本一道波多野毛片中文在线 | 国产精品麻豆果冻传媒在线播放 | 日韩免费在线观看 | 日韩在线视频播放 | 手机在线看永久av片免费 | 久久高清毛片 | 成人精品亚洲 | 亚洲视频456| 91亚洲视频在线观看 | 久久免费国产视频 | 久久这里精品视频 | 人人插人人做 | 欧美激情奇米色 | 国产一级二级视频 | 99在线热播精品免费99热 | 成人免费在线视频观看 | 免费视频 你懂的 | 在线成人观看 | 亚洲精品国偷拍自产在线观看蜜桃 | 亚洲91中文字幕无线码三区 | 在线观看黄a | 国产又粗又硬又爽的视频 | 日本久草电影 | 在线亚洲高清视频 | 久久字幕 | 国产中文视 | 色福利网| 91精品在线免费视频 | 亚洲激情一区二区三区 | 91中文在线视频 | 成人av一区二区三区 | 美女黄频在线观看 | 一区二区 不卡 | 美女露久久 | 日韩在线观看不卡 | 探花视频在线观看免费 | 免费在线观看av网址 | 奇米导航 | 久久污视频 | 午夜性盈盈 | 亚洲国产视频在线 | 91成人精品一区在线播放69 | 国产一区在线免费观看视频 | 日韩欧美视频在线免费观看 | 亚洲午夜精品久久久久久久久久久久 | 久久久在线免费观看 | 麻豆视频免费网站 | 国产五月婷婷 | 91精品视频在线免费观看 | 久精品视频免费观看2 | 久久久五月天 | 深夜免费福利在线 | 精品国产黄色片 | 亚洲黄色在线免费观看 | 亚洲激情 欧美激情 | 69av网| 日本精品va在线观看 | 日韩性色 | 99久久精品国产网站 | 欧美精品久久久久久久久老牛影院 | 这里只有精品视频在线 | 欧美精品久久天天躁 | 91在线视频免费播放 | 在线观看免费国产小视频 | 欧美一级片在线 | 999视频网站 | www夜夜| 99爱视频 | 91视频啊啊啊 | 国内一区二区视频 | 国产精品毛片久久蜜 | 99超碰在线观看 | 丁香久久综合 | 精品国产乱码一区二 | 9999亚洲 | 开心丁香婷婷深爱五月 | 91精品在线看 | 国产123区在线观看 国产精品麻豆91 | 欧美成人一区二区 | 亚洲专区欧美专区 | 国产精品久久99精品毛片三a | 91免费日韩 | 色欧美成人精品a∨在线观看 | 国产一级视频在线观看 | 在线视频区| 狠狠狠狠干 | 中文字幕色在线视频 | 狠狠干美女 | 国产日本亚洲 | 天天综合网天天 | 中国美女一级看片 | 亚洲毛片一区二区三区 | 成人观看视频 | 激情欧美丁香 | 欧美性生活一级片 | 九9热这里真品2 | 国产成人一区三区 | 精品 激情 | 黄色录像av| 国产一区二区在线播放视频 | 国内精品视频久久 | 国产麻豆果冻传媒在线观看 | 久久九九影视网 | 国产综合在线观看视频 | 亚洲第一区在线播放 | 欧美在线一二区 | 中文字幕在线观看网站 | 六月丁香色婷婷 | 国产精品不卡一区 | 东方av在线免费观看 | 久久精品官网 | 精品人人人人 | 日韩在线观看视频一区二区三区 | 亚洲九九精品 | 在线看黄网站 | www.狠狠干| 日韩av电影中文字幕 | 国产成人久久精品77777综合 | 久草网在线观看 | 色av资源网 | 天天天干天天射天天天操 | 欧美日本国产在线观看 | 国产一区国产精品 | 日韩精品一区二区三区高清免费 | 天天天天爱天天躁 | 韩国av一区| 91少妇精拍在线播放 | 日韩亚洲精品电影 | 久久久亚洲精华液 | 狠狠色噜噜狠狠 | 日韩啪啪小视频 | 91黄色在线视频 | 久久不卡国产精品一区二区 | 久久视频在线观看中文字幕 | 在线观看国产日韩 | 五月婷婷一区 | 欧美精品久久久久久久亚洲调教 | 国产精品一区二区久久久久 | 亚洲不卡123 | 久草精品在线观看 | 国产 日韩 欧美 在线 | 成人免费看黄 | 成人午夜黄色 | 成人久久久久 | 久草在线视频中文 | 久久精品观看 | 97超碰网| 欧美激情综合色 | 亚洲一区av| 国产伦精品一区二区三区免费 | 久久色中文字幕 | 亚洲国产精品va在线看 | 91九色在线视频观看 | 中文字幕亚洲欧美日韩 | 午夜电影久久久 | 亚洲欧美日韩国产一区二区 | 午夜三级毛片 | 超碰在线个人 | 国产原创中文在线 | 久久久黄视频 | 一区在线播放 | 国产在线精品福利 | 国产69精品久久久久99 | 天天操人人要 | 成人久久18免费 | 91在线视频免费观看 | 成人久久|