久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring 事务管理高级应用难点剖析--转

發(fā)布時間:2025/4/5 javascript 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring 事务管理高级应用难点剖析--转 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

第 1 部分

http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN&en=utf&hpp=20&dws=cndw&lo=zh

概述

Spring 最成功,最吸引人的地方莫過于輕量級的聲明式事務(wù)管理,僅此一點,它就宣告了重量級 EJB 容器的覆滅。Spring 聲明式事務(wù)管理將開發(fā)者從繁復(fù)的事務(wù)管理代碼中解脫出來,專注于業(yè)務(wù)邏輯的開發(fā)上,這是一件可以被拿來頂禮膜拜的事情。但是,世界并未從此消停,開發(fā)人員需要面對的是層出不窮的應(yīng)用場景,這些場景往往逾越了普通 Spring 技術(shù)書籍的理想界定。因此,隨著應(yīng)用開發(fā)的深入,在使用經(jīng)過 Spring 層層封裝的聲明式事務(wù)時,開發(fā)人員越來越覺得自己墜入了迷霧,陷入了沼澤,體會不到外界所宣稱的那種暢快淋漓。本系列文章的目標(biāo)旨在整理并剖析實際應(yīng)用中種種讓我們迷茫的場景,讓陽光照進云遮霧障的山頭。

DAO 和事務(wù)管理的牽絆

很少有使用 Spring 但不使用 Spring 事務(wù)管理器的應(yīng)用,因此常常有人會問:是否用了 Spring,就一定要用 Spring 事務(wù)管理器,否則就無法進行數(shù)據(jù)的持久化操作呢?事務(wù)管理器和 DAO 是什么關(guān)系呢?

也許是 DAO 和事務(wù)管理如影隨行的緣故吧,這個看似簡單的問題實實在在地存在著,從初學(xué)者心中涌出,縈繞在開發(fā)老手的腦際。答案當(dāng)然是否定的!我們都知道:事務(wù)管理是保證數(shù)據(jù)操作的事務(wù)性(即原子性、一致性、隔離性、持久性,也即所謂的 ACID),脫離了事務(wù)性,DAO 照樣可以順利地進行數(shù)據(jù)的操作。

下面,我們來看一段使用 Spring JDBC 進行數(shù)據(jù)訪問的代碼:

清單 1. UserJdbcWithoutTransManagerService.java
package user.withouttm;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.apache.commons.dbcp.BasicDataSource;@Service("service1") public class UserJdbcWithoutTransManagerService {@Autowiredprivate JdbcTemplate jdbcTemplate;public void addScore(String userName,int toAdd){String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?";jdbcTemplate.update(sql,toAdd,userName);}public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("user/withouttm/jdbcWithoutTransManager.xml");UserJdbcWithoutTransManagerService service = (UserJdbcWithoutTransManagerService)ctx.getBean("service1");JdbcTemplate jdbcTemplate = (JdbcTemplate)ctx.getBean("jdbcTemplate");BasicDataSource basicDataSource = (BasicDataSource)jdbcTemplate.getDataSource();//①.檢查數(shù)據(jù)源autoCommit的設(shè)置System.out.println("autoCommit:"+ basicDataSource.getDefaultAutoCommit());//②.插入一條記錄,初始分?jǐn)?shù)為10jdbcTemplate.execute("INSERT INTO t_user(user_name,password,score) VALUES('tom','123456',10)");//③.調(diào)用工作在無事務(wù)環(huán)境下的服務(wù)類方法,將分?jǐn)?shù)添加20分service.addScore("tom",20);//④.查看此時用戶的分?jǐn)?shù)int score = jdbcTemplate.queryForInt("SELECT score FROM t_user WHERE user_name ='tom'");System.out.println("score:"+score);jdbcTemplate.execute("DELETE FROM t_user WHERE user_name='tom'");} }

jdbcWithoutTransManager.xml 的配置文件如下所示:

清單 2. jdbcWithoutTransManager.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"><context:component-scan base-package="user.withouttm"/><!-- 數(shù)據(jù)源默認(rèn)將autoCommit設(shè)置為true --><bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"p:driverClassName="oracle.jdbc.driver.OracleDriver"p:url="jdbc:oracle:thin:@localhost:1521:orcl"p:username="test"p:password="test"/><bean id="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource"/> </beans>

運行 UserJdbcWithoutTransManagerService,在控制臺上打出如下的結(jié)果:

defaultAutoCommit:true score:30

在 jdbcWithoutTransManager.xml 中,沒有配置任何事務(wù)管理器,但是數(shù)據(jù)已經(jīng)成功持久化到數(shù)據(jù)庫中。在默認(rèn)情況下,dataSource 數(shù)據(jù)源的 autoCommit 被設(shè)置為 true ―― 這也意謂著所有通過 JdbcTemplate 執(zhí)行的語句馬上提交,沒有事務(wù)。如果將 dataSource 的 defaultAutoCommit 設(shè)置為 false,再次運行 UserJdbcWithoutTransManagerService,將拋出錯誤,原因是新增及更改數(shù)據(jù)的操作都沒有提交到數(shù)據(jù)庫,所以 ④ 處的語句因無法從數(shù)據(jù)庫中查詢到匹配的記錄而引發(fā)異常。

對于強調(diào)讀速度的應(yīng)用,數(shù)據(jù)庫本身可能就不支持事務(wù),如使用 MyISAM 引擎的 MySQL 數(shù)據(jù)庫。這時,無須在 Spring 應(yīng)用中配置事務(wù)管理器,因為即使配置了,也是沒有實際用處的。

不過,對于 Hibernate 來說,情況就有點復(fù)雜了。因為 Hibernate 的事務(wù)管理擁有其自身的意義,它和 Hibernate 一級緩存有密切的關(guān)系:當(dāng)我們調(diào)用 Session 的 save、update 等方法時,Hibernate 并不直接向數(shù)據(jù)庫發(fā)送 SQL 語句,而是在提交事務(wù)(commit)或 flush 一級緩存時才真正向數(shù)據(jù)庫發(fā)送 SQL。所以,即使底層數(shù)據(jù)庫不支持事務(wù),Hibernate 的事務(wù)管理也是有一定好處的,不會對數(shù)據(jù)操作的效率造成負(fù)面影響。所以,如果是使用 Hibernate 數(shù)據(jù)訪問技術(shù),沒有理由不配置 HibernateTransactionManager 事務(wù)管理器。

但是,不使用 Hibernate 事務(wù)管理器,在 Spring 中,Hibernate 照樣也可以工作,來看下面的例子:

清單 3.UserHibernateWithoutTransManagerService.java
package user.withouttm;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.orm.hibernate3.HibernateTemplate; import org.apache.commons.dbcp.BasicDataSource; import user.User;@Service("service2") public class UserHibernateWithoutTransManagerService {@Autowiredprivate HibernateTemplate hibernateTemplate;public void addScore(String userName,int toAdd){User user = (User)hibernateTemplate.get(User.class,userName);user.setScore(user.getScore()+toAdd);hibernateTemplate.update(user);}public static void main(String[] args) {//參考UserJdbcWithoutTransManagerService相應(yīng)代碼…} }

此時,采用 hiberWithoutTransManager.xml 的配置文件,其配置內(nèi)容如下:

清單 4.hiberWithoutTransManager.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd"><!--省略掉包掃描,數(shù)據(jù)源,JdbcTemplate配置部分,參見jdbcWithoutTransManager.xml -->…<bean id="sessionFactory"class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"p:dataSource-ref="dataSource"><property name="annotatedClasses"><list><value>user.User</value></list></property><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop><prop key="hibernate.show_sql">true</prop></props></property></bean><bean id="hibernateTemplate"class="org.springframework.orm.hibernate3.HibernateTemplate"p:sessionFactory-ref="sessionFactory"/> </beans>

運行 UserHibernateWithoutTransManagerService,程序正確執(zhí)行,并得到類似于 UserJdbcWithoutTransManagerService 的執(zhí)行結(jié)果,這說明 Hibernate 在 Spring 中,在沒有事務(wù)管理器的情況下,依然可以正常地進行數(shù)據(jù)的訪問。

應(yīng)用分層的迷惑

Web、Service 及 DAO 三層劃分就像西方國家的立法、行政、司法三權(quán)分立一樣被奉為金科玉律,甚至有開發(fā)人員認(rèn)為如果要使用 Spring 的事務(wù)管理就一定先要進行三層的劃分。這個看似荒唐的論調(diào)在開發(fā)人員中頗有市場。更有甚者,認(rèn)為每層必須先定義一個接口,然后再定義一個實現(xiàn)類。其結(jié)果是:一個很簡單的功能,也至少需要 3 個接口,3 個類,再加上視圖層的 JSP 和 JS 等,打牌都可以轉(zhuǎn)上兩桌了,這種誤解貽害不淺。

對將“面向接口編程”奉為圭臬,認(rèn)為放之四海而皆準(zhǔn)的論調(diào),筆者深不以為然。是的,“面向接口編程”是 Martin Fowler,Rod Johnson 這些大師提倡的行事原則。如果拿這條原則去開發(fā)架構(gòu),開發(fā)產(chǎn)品,怎么強調(diào)都不為過。但是,對于我們一般的開發(fā)人員來說,做的最多的是普通工程項目,往往最多的只是一些對數(shù)據(jù)庫增、刪、查、改的功能。此時,“面向接口編程”除了帶來更多的類文件外,看不到更多其它的好處。

Spring 框架提供的所有附加的好處(AOP、注解增強、注解 MVC 等)唯一的前提就是讓 POJO 的類變成一個受 Spring 容器管理的 Bean,除此以外沒有其它任何的要求。下面的實例用一個 POJO 完成所有的功能,既是 Controller,又是 Service,還是 DAO:

清單 5. MixLayerUserService.java
package user.mixlayer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; //①.將POJO類通過注解變成Spring MVC的Controller @Controller public class MixLayerUserService {//②.自動注入JdbcTemplate@Autowiredprivate JdbcTemplate jdbcTemplate;//③.通過Spring MVC注解映URL請求@RequestMapping("/logon.do") public String logon(String userName,String password){if(isRightUser(userName,password)){String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?";jdbcTemplate.update(sql,20,userName);return "success";}else{return "fail";}}private boolean isRightUser(String userName,String password){//do sth...return true;} }

通過 @Controller 注解將 MixLayerUserService 變成 Web 層的 Controller,同時也是 Service 層的服務(wù)類。此外,由于直接使用 JdbcTemplate 訪問數(shù)據(jù),所以 MixLayerUserService 還是一個 DAO。來看一下對應(yīng)的 Spring 配置文件:

清單 6.applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"><!--掃描Web類包,通過注釋生成Bean--><context:component-scan base-package="user.mixlayer"/><!--①.啟動Spring MVC的注解功能,完成請求和注解POJO的映射--><bean class="org.springframework.web.servlet.mvc.annotation .AnnotationMethodHandlerAdapter"/><!--模型視圖名稱的解析,即在模型視圖名稱添加前后綴 --><bean class="org.springframework.web.servlet.view .InternalResourceViewResolver"p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/><!--普通數(shù)據(jù)源 --><bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"p:driverClassName="oracle.jdbc.driver.OracleDriver"p:url="jdbc:oracle:thin:@localhost:1521:orcl"p:username="test"p:password="test"/><bean id="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource"/><!--事務(wù)管理器 --><bean id="jdbcManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource"/><!--②使用aop和tx命名空間語法為MixLayerUserService所有公用方法添加事務(wù)增強 --><aop:config proxy-target-class="true"><aop:pointcut id="serviceJdbcMethod"expression="execution(public * user.mixlayer.MixLayerUserService.*(..))"/><aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0"/></aop:config><tx:advice id="jdbcAdvice" transaction-manager="jdbcManager"><tx:attributes><tx:method name="*"/></tx:attributes></tx:advice> </beans>

在 ① 處,我們定義配置了 AnnotationMethodHandlerAdapter,以便啟用 Spring MVC 的注解驅(qū)動功能。而②和③處通過 Spring 的 aop 及 tx 命名空間,以及 Aspject 的切點表達(dá)式語法進行事務(wù)增強的定義,對 MixLayerUserService 的所有公有方法進行事務(wù)增強。要使程序能夠運行起來還必須進行 web.xml 的相關(guān)配置:

清單 7.web.xml
<?xml version="1.0" encoding="GB2312"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"><context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:user/mixlayer/applicationContext.xml</param-value></context-param><context-param><param-name>log4jConfigLocation</param-name><param-value>/WEB-INF/classes/log4j.properties</param-value></context-param><listener><listener-class>org.springframework.web.util.Log4jConfigListener</listener-class></listener><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>user</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--①通過contextConfigLocation參數(shù)指定Spring配置文件的位置 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:user/mixlayer/applicationContext.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>user</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping> </web-app>

這個配置文件很簡單,唯一需要注意的是 DispatcherServlet 的配置。默認(rèn)情況下 Spring MVC 根據(jù) Servlet 的名字查找 WEB-INF 下的 <servletName>-servlet.xml 作為 Spring MVC 的配置文件,在此,我們通過 contextConfigLocation 參數(shù)顯式指定 Spring MVC 配置文件的確切位置。

將 org.springframework.jdbc 及 org.springframework.transaction 的日志級別設(shè)置為 DEBUG,啟動項目,并訪問 http://localhost:8088/logon.do?userName=tom 應(yīng)用,MixLayerUserService#logon 方法將作出響應(yīng),查看后臺輸出日志:

清單 8 執(zhí)行日志
13:24:22,625 DEBUG (AbstractPlatformTransactionManager.java:365) - Creating new transaction with name [user.mixlayer.MixLayerUserService.logon]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 13:24:22,906 DEBUG (DataSourceTransactionManager.java:205) - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@6e1cbf] for JDBC transaction 13:24:22,921 DEBUG (DataSourceTransactionManager.java:222) - Switching JDBC Connection [org.apache.commons.dbcp.PoolableConnection@6e1cbf] to manual commit 13:24:22,921 DEBUG (JdbcTemplate.java:785) - Executing prepared SQL update 13:24:22,921 DEBUG (JdbcTemplate.java:569) - Executing prepared SQL statement [UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?] 13:24:23,140 DEBUG (JdbcTemplate.java:794) - SQL update affected 0 rows 13:24:23,140 DEBUG (AbstractPlatformTransactionManager.java:752) - Initiating transaction commit 13:24:23,140 DEBUG (DataSourceTransactionManager.java:265) - Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@6e1cbf] 13:24:23,140 DEBUG (DataSourceTransactionManager.java:323) - Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection@6e1cbf] after transaction 13:24:23,156 DEBUG (DataSourceUtils.java:312) - Returning JDBC Connection to DataSource

日志中粗體部分說明了 MixLayerUserService#logon 方法已經(jīng)正確運行在事務(wù)上下文中。

Spring 框架本身不應(yīng)該是復(fù)雜化代碼的理由,使用 Spring 的開發(fā)者應(yīng)該是無拘無束的:從實際應(yīng)用出發(fā),去除掉那些所謂原則性的接口,去除掉強制分層的束縛,簡單才是硬道理。

事務(wù)方法嵌套調(diào)用的迷茫

Spring 事務(wù)一個被訛傳很廣說法是:一個事務(wù)方法不應(yīng)該調(diào)用另一個事務(wù)方法,否則將產(chǎn)生兩個事務(wù)。結(jié)果造成開發(fā)人員在設(shè)計事務(wù)方法時束手束腳,生怕一不小心就踩到地雷。

其實這種是不認(rèn)識 Spring 事務(wù)傳播機制而造成的誤解,Spring 對事務(wù)控制的支持統(tǒng)一在 TransactionDefinition 類中描述,該類有以下幾個重要的接口方法:

  • int getPropagationBehavior():事務(wù)的傳播行為
  • int getIsolationLevel():事務(wù)的隔離級別
  • int getTimeout():事務(wù)的過期時間
  • boolean isReadOnly():事務(wù)的讀寫特性。

很明顯,除了事務(wù)的傳播行為外,事務(wù)的其它特性 Spring 是借助底層資源的功能來完成的,Spring 無非只充當(dāng)個代理的角色。但是事務(wù)的傳播行為卻是 Spring 憑借自身的框架提供的功能,是 Spring 提供給開發(fā)者最珍貴的禮物,訛傳的說法玷污了 Spring 事務(wù)框架最美麗的光環(huán)。

所謂事務(wù)傳播行為就是多個事務(wù)方法相互調(diào)用時,事務(wù)如何在這些方法間傳播。Spring 支持 7 種事務(wù)傳播行為:

  • PROPAGATION_REQUIRED 如果當(dāng)前沒有事務(wù),就新建一個事務(wù),如果已經(jīng)存在一個事務(wù)中,加入到這個事務(wù)中。這是最常見的選擇。
  • PROPAGATION_SUPPORTS 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
  • PROPAGATION_MANDATORY 使用當(dāng)前的事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。
  • PROPAGATION_REQUIRES_NEW 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
  • PROPAGATION_NOT_SUPPORTED 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
  • PROPAGATION_NEVER 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
  • PROPAGATION_NESTED 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則執(zhí)行與 PROPAGATION_REQUIRED 類似的操作。

Spring 默認(rèn)的事務(wù)傳播行為是 PROPAGATION_REQUIRED,它適合于絕大多數(shù)的情況。假設(shè) ServiveX#methodX() 都工作在事務(wù)環(huán)境下(即都被 Spring 事務(wù)增強了),假設(shè)程序中存在如下的調(diào)用鏈:Service1#method1()->Service2#method2()->Service3#method3(),那么這 3 個服務(wù)類的 3 個方法通過 Spring 的事務(wù)傳播機制都工作在同一個事務(wù)中。

下面,我們來看一下實例,UserService#logon() 方法內(nèi)部調(diào)用了 UserService#updateLastLogonTime() 和 ScoreService#addScore() 方法,這兩個類都繼承于 BaseService。它們之間的類結(jié)構(gòu)說明如下:

圖 1. UserService 和 ScoreService

具體的代碼如下所示:

清單 9 UserService.java
@Service("userService") public class UserService extends BaseService {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate ScoreService scoreService;public void logon(String userName) {updateLastLogonTime(userName);scoreService.addScore(userName, 20);}public void updateLastLogonTime(String userName) {String sql = "UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?";jdbcTemplate.update(sql, System.currentTimeMillis(), userName);} }

UserService 中注入了 ScoreService 的 Bean,ScoreService 的代碼如下所示:

清單 10 ScoreService.java
@Service("scoreUserService") public class ScoreService extends BaseService{@Autowiredprivate JdbcTemplate jdbcTemplate;public void addScore(String userName, int toAdd) {String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?";jdbcTemplate.update(sql, toAdd, userName);} }

通過 Spring 的事務(wù)配置為 ScoreService 及 UserService 中所有公有方法都添加事務(wù)增強,讓這些方法都工作于事務(wù)環(huán)境下。下面是關(guān)鍵的配置代碼:

清單 11 事務(wù)增強配置
<!-- 添加Spring事務(wù)增強 --> <aop:config proxy-target-class="true"><aop:pointcut id="serviceJdbcMethod"<!-- 所有繼承于BaseService類的子孫類的public方法都進行事務(wù)增強-->expression="within(user.nestcall.BaseService+)"/><aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0"/> </aop:config> <tx:advice id="jdbcAdvice" transaction-manager="jdbcManager"><tx:attributes><tx:method name="*"/></tx:attributes> </tx:advice>

將日志級別設(shè)置為 DEBUG,啟動 Spring 容器并執(zhí)行 UserService#logon() 的方法,仔細(xì)觀察如下的輸出日志:

清單 12 執(zhí)行日志
16:25:04,765 DEBUG (AbstractPlatformTransactionManager.java:365) - Creating new transaction with name [user.nestcall.UserService.logon]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT ①為UserService#logon方法啟動一個事務(wù)16:25:04,765 DEBUG (DataSourceTransactionManager.java:205) - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@32bd65] for JDBC transactionlogon method...updateLastLogonTime... ②直接執(zhí)行updateLastLogonTime方法16:25:04,781 DEBUG (JdbcTemplate.java:785) - Executing prepared SQL update16:25:04,781 DEBUG (JdbcTemplate.java:569) - Executing prepared SQL statement [UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?]16:25:04,828 DEBUG (JdbcTemplate.java:794) - SQL update affected 0 rows16:25:04,828 DEBUG (AbstractPlatformTransactionManager.java:470) - Participating in existing transaction ③ScoreService#addScore方法加入到UserService#logon的事務(wù)中addScore...16:25:04,828 DEBUG (JdbcTemplate.java:785) - Executing prepared SQL update16:25:04,828 DEBUG (JdbcTemplate.java:569) - Executing prepared SQL statement [UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?]16:25:04,828 DEBUG (JdbcTemplate.java:794) - SQL update affected 0 rows16:25:04,828 DEBUG (AbstractPlatformTransactionManager.java:752) - Initiating transaction commit④提交事務(wù)16:25:04,828 DEBUG (DataSourceTransactionManager.java:265) - Committing JDBC transactionon Connection [org.apache.commons.dbcp.PoolableConnection@32bd65]16:25:04,828 DEBUG (DataSourceTransactionManager.java:323) - Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection@32bd65] after transaction16:25:04,828 DEBUG (DataSourceUtils.java:312) - Returning JDBC Connection to DataSource

從上面的輸入日志中,可以清楚地看到 Spring 為 UserService#logon() 方法啟動了一個新的事務(wù),而 UserSerive#updateLastLogonTime() 和 UserService#logon() 是在相同的類中,沒有觀察到有事務(wù)傳播行為的發(fā)生,其代碼塊好像“直接合并”到 UserService#logon() 中。接著,當(dāng)執(zhí)行到 ScoreService#addScore() 方法時,我們就觀察到了發(fā)生了事務(wù)傳播的行為:Participating in existing transaction,這說明 ScoreService#addScore() 添加到 UserService#logon() 的事務(wù)上下文中,兩者共享同一個事務(wù)。所以最終的結(jié)果是 UserService 的 logon(), updateLastLogonTime() 以及 ScoreService 的 addScore 都工作于同一事務(wù)中。

多線程的困惑

由于 Spring 的事務(wù)管理器是通過線程相關(guān)的 ThreadLocal 來保存數(shù)據(jù)訪問基礎(chǔ)設(shè)施,再結(jié)合 IOC 和 AOP 實現(xiàn)高級聲明式事務(wù)的功能,所以 Spring 的事務(wù)天然地和線程有著千絲萬縷的聯(lián)系。

我們知道 Web 容器本身就是多線程的,Web 容器為一個 Http 請求創(chuàng)建一個獨立的線程,所以由此請求所牽涉到的 Spring 容器中的 Bean 也是運行于多線程的環(huán)境下。在絕大多數(shù)情況下,Spring 的 Bean 都是單實例的(singleton),單實例 Bean 的最大的好處是線程無關(guān)性,不存在多線程并發(fā)訪問的問題,也即是線程安全的。

一個類能夠以單實例的方式運行的前提是“無狀態(tài)”:即一個類不能擁有狀態(tài)化的成員變量。我們知道,在傳統(tǒng)的編程中,DAO 必須執(zhí)有一個 Connection,而 Connection 即是狀態(tài)化的對象。所以傳統(tǒng)的 DAO 不能做成單實例的,每次要用時都必須 new 一個新的實例。傳統(tǒng)的 Service 由于將有狀態(tài)的 DAO 作為成員變量,所以傳統(tǒng)的 Service 本身也是有狀態(tài)的。

但是在 Spring 中,DAO 和 Service 都以單實例的方式存在。Spring 是通過 ThreadLocal 將有狀態(tài)的變量(如 Connection 等)本地線程化,達(dá)到另一個層面上的“線程無關(guān)”,從而實現(xiàn)線程安全。Spring 不遺余力地將狀態(tài)化的對象無狀態(tài)化,就是要達(dá)到單實例化 Bean 的目的。

由于 Spring 已經(jīng)通過 ThreadLocal 的設(shè)施將 Bean 無狀態(tài)化,所以 Spring 中單實例 Bean 對線程安全問題擁有了一種天生的免疫能力。不但單實例的 Service 可以成功運行于多線程環(huán)境中,Service 本身還可以自由地啟動獨立線程以執(zhí)行其它的 Service。下面,通過一個實例對此進行描述:

清單 13 UserService.java 在事務(wù)方法中啟動獨立線程運行另一個事務(wù)方法
@Service("userService") public class UserService extends BaseService {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate ScoreService scoreService;//① 在logon方法體中啟動一個獨立的線程,在該獨立的線程中執(zhí)行ScoreService#addScore()方法public void logon(String userName) {System.out.println("logon method...");updateLastLogonTime(userName);Thread myThread = new MyThread(this.scoreService,userName,20);myThread.start();}public void updateLastLogonTime(String userName) {System.out.println("updateLastLogonTime...");String sql = "UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?";jdbcTemplate.update(sql, System.currentTimeMillis(), userName);}//② 封裝ScoreService#addScore()的線程private class MyThread extends Thread{private ScoreService scoreService;private String userName;private int toAdd;private MyThread(ScoreService scoreService,String userName,int toAdd) {this.scoreService = scoreService;this.userName = userName;this.toAdd = toAdd;}public void run() {scoreService.addScore(userName,toAdd);}} }

將日志級別設(shè)置為 DEBUG,執(zhí)行 UserService#logon() 方法,觀察以下輸出的日志:

清單 14 執(zhí)行日志
[main] (AbstractPlatformTransactionManager.java:365) - Creating new transaction with name[user.multithread.UserService.logon]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT ①[main] (DataSourceTransactionManager.java:205) - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@1353249] for JDBC transactionlogon method...updateLastLogonTime...[main] (JdbcTemplate.java:785) - Executing prepared SQL update[main] (JdbcTemplate.java:569) - Executing prepared SQL statement [UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?][main] (JdbcTemplate.java:794) - SQL update affected 0 rows[main] (AbstractPlatformTransactionManager.java:752) - Initiating transaction commit[Thread-2](AbstractPlatformTransactionManager.java:365) - Creating new transaction with name [user.multithread.ScoreService.addScore]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT ②[main] (DataSourceTransactionManager.java:265) - Committing JDBC transactionon Connection [org.apache.commons.dbcp.PoolableConnection@1353249] ③[main] (DataSourceTransactionManager.java:323) - Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection@1353249] after transaction[main] (DataSourceUtils.java:312) - Returning JDBC Connection to DataSource[Thread-2] (DataSourceTransactionManager.java:205) - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@10dc656] for JDBC transactionaddScore...[main] (JdbcTemplate.java:416) - Executing SQL statement [DELETE FROM t_user WHERE user_name='tom'][main] (DataSourceUtils.java:112) - Fetching JDBC Connection from DataSource[Thread-2] (JdbcTemplate.java:785) - Executing prepared SQL update[Thread-2] (JdbcTemplate.java:569) - Executing prepared SQL statement [UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?][main] (DataSourceUtils.java:312) - Returning JDBC Connection to DataSource[Thread-2] (JdbcTemplate.java:794) - SQL update affected 0 rows[Thread-2] (AbstractPlatformTransactionManager.java:752) - Initiating transaction commit[Thread-2] (DataSourceTransactionManager.java:265) - Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@10dc656] ④[Thread-2] (DataSourceTransactionManager.java:323) - Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection@10dc656] after transaction

在 ① 處,在主線程(main)執(zhí)行的 UserService#logon() 方法的事務(wù)啟動,在 ③ 處,其對應(yīng)的事務(wù)提交,而在子線程(Thread-2)執(zhí)行的 ScoreService#addScore() 方法的事務(wù)在 ② 處啟動,在 ④ 處對應(yīng)的事務(wù)提交。

所以,我們可以得出這樣的結(jié)論:在?相同線程中進行相互嵌套調(diào)用的事務(wù)方法工作于相同的事務(wù)中。如果這些相互嵌套調(diào)用的方法工作在不同的線程中,不同線程下的事務(wù)方法工作在獨立的事務(wù)中。

小結(jié)

Spring 聲明式事務(wù)是 Spring 最核心,最常用的功能。由于 Spring 通過 IOC 和 AOP 的功能非常透明地實現(xiàn)了聲明式事務(wù)的功能,一般的開發(fā)者基本上無須了解 Spring 聲明式事務(wù)的內(nèi)部細(xì)節(jié),僅需要懂得如何配置就可以了。

但是在實際應(yīng)用開發(fā)過程中,Spring 的這種透明的高階封裝在帶來便利的同時,也給我們帶來了迷惑。就像通過流言傳播的消息,最終聽眾已經(jīng)不清楚事情的真相了,而這對于應(yīng)用開發(fā)來說是很危險的。本系列文章通過剖析實際應(yīng)用中給開發(fā)者造成迷惑的各種難點,通過分析 Spring 事務(wù)管理的內(nèi)部運作機制將真相還原出來。

在本文中,我們通過剖析了解到以下的真相:

  • 在沒有事務(wù)管理的情況下,DAO 照樣可以順利進行數(shù)據(jù)操作;
  • 將應(yīng)用分成 Web,Service 及 DAO 層只是一種參考的開發(fā)模式,并非是事務(wù)管理工作的前提條件;
  • Spring 通過事務(wù)傳播機制可以很好地應(yīng)對事務(wù)方法嵌套調(diào)用的情況,開發(fā)者無須為了事務(wù)管理而刻意改變服務(wù)方法的設(shè)計;
  • 由于單實例的對象不存在線程安全問題,所以進行事務(wù)管理增強的 Bean 可以很好地工作在多線程環(huán)境下。

在?下一篇?文章中,筆者將繼續(xù)分析 Spring 事務(wù)管理的以下難點:

  • 混合使用多種數(shù)據(jù)訪問技術(shù)(如 Spring JDBC + Hibernate)的事務(wù)管理問題;
  • 進行 Spring AOP 增強的 Bean 存在哪些特殊的情況。

第 2 部分

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

聯(lián)合軍種作戰(zhàn)的混亂

Spring 抽象的 DAO 體系兼容多種數(shù)據(jù)訪問技術(shù),它們各有特色,各有千秋。像 Hibernate 是非常優(yōu)秀的 ORM 實現(xiàn)方案,但對底層 SQL 的控制不太方便;而 iBatis 則通過模板化技術(shù)讓您方便地控制 SQL,但沒有 Hibernate 那樣高的開發(fā)效率;自由度最高的當(dāng)然是直接使用 Spring JDBC 莫屬了,但是它也是最底層的,靈活的代價是代碼的繁復(fù)。很難說哪種數(shù)據(jù)訪問技術(shù)是最優(yōu)秀的,只有在某種特定的場景下,才能給出答案。所以在一個應(yīng)用中,往往采用多個數(shù)據(jù)訪問技術(shù):一般是兩種,一種采用 ORM 技術(shù)框架,而另一種采用偏 JDBC 的底層技術(shù),兩者珠聯(lián)璧合,形成聯(lián)合軍種,共同御敵。

但是,這種聯(lián)合軍種如何應(yīng)對事務(wù)管理的問題呢?我們知道 Spring 為每種數(shù)據(jù)訪問技術(shù)提供了相應(yīng)的事務(wù)管理器,難道需要分別為它們配置對應(yīng)的事務(wù)管理器嗎?它們到底是如何協(xié)作,如何工作的呢?這些層出不窮的問題往往壓制了開發(fā)人員使用聯(lián)合軍種的想法。

其實,在這個問題上,我們低估了 Spring 事務(wù)管理的能力。如果您采用了一個高端 ORM 技術(shù)(Hibernate,JPA,JDO),同時采用一個 JDBC 技術(shù)(Spring JDBC,iBatis),由于前者的會話(Session)是對后者連接(Connection)的封裝,Spring 會“足夠智能地”在同一個事務(wù)線程讓前者的會話封裝后者的連接。所以,我們只要直接采用前者的事務(wù)管理器就可以了。下表給出了混合數(shù)據(jù)訪問技術(shù)所對應(yīng)的事務(wù)管理器:

表 1. 混合數(shù)據(jù)訪問技術(shù)的事務(wù)管理器
混合數(shù)據(jù)訪問技術(shù)事務(wù)管理器
ORM 技術(shù)框架JDBC 技術(shù)框架
HibernateSpring JDBC 或 iBatisHibernateTransactionManager
JPASpring JDBC 或 iBatisJpaTransactionManager
JDOSpring JDBC 或 iBatisJdoTransactionManager

由于一般不會出現(xiàn)同時使用多個 ORM 框架的情況(如 Hibernate + JPA),我們不擬對此命題展開論述,只重點研究 ORM 框架 + JDBC 框架的情況。Hibernate + Spring JDBC 可能是被使用得最多的組合,下面我們通過實例觀察事務(wù)管理的運作情況。

清單 1.User.java:使用了注解聲明的實體類
import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Column; import javax.persistence.Id; import java.io.Serializable; @Entity @Table(name="T_USER") public class User implements Serializable{ @Id@Column(name = "USER_NAME") private String userName; private String password; private int score; @Column(name = "LAST_LOGON_TIME")private long lastLogonTime = 0; }

再來看下 UserService 的關(guān)鍵代碼:

清單 2.UserService.java:使用 Hibernate 數(shù)據(jù)訪問技術(shù)
package user.mixdao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service; import org.springframework.orm.hibernate3.HibernateTemplate; import org.apache.commons.dbcp.BasicDataSource; import user.User;@Service("userService") public class UserService extends BaseService {@Autowiredprivate HibernateTemplate hibernateTemplate;@Autowiredprivate ScoreService scoreService;public void logon(String userName) {System.out.println("logon method...");updateLastLogonTime(userName); //①使用Hibernate數(shù)據(jù)訪問技術(shù)scoreService.addScore(userName, 20); //②使用Spring JDBC數(shù)據(jù)訪問技術(shù)}public void updateLastLogonTime(String userName) {System.out.println("updateLastLogonTime...");User user = hibernateTemplate.get(User.class,userName);user.setLastLogonTime(System.currentTimeMillis());hibernateTemplate.flush(); //③請看下文的分析} }

在①處,使用 Hibernate 操作數(shù)據(jù),而在②處調(diào)用 ScoreService#addScore(),該方法內(nèi)部使用 Spring JDBC 操作數(shù)據(jù)。

在③處,我們顯式調(diào)用了 flush() 方法,將 Session 中的緩存同步到數(shù)據(jù)庫中,這個操作將即時向數(shù)據(jù)庫發(fā)送一條更新記錄的 SQL 語句。之所以要在此顯式執(zhí)行 flush() 方法,原因是:默認(rèn)情況下,Hibernate 要在事務(wù)提交時才將數(shù)據(jù)的更改同步到數(shù)據(jù)庫中,而事務(wù)提交發(fā)生在 logon() 方法返回前。如果所有針對數(shù)據(jù)庫的更改都使用 Hibernate,這種數(shù)據(jù)同步延遲的機制不會產(chǎn)生任何問題。但是,我們在 logon() 方法中同時采用了 Hibernate 和 Spring JDBC 混合數(shù)據(jù)訪問技術(shù)。Spring JDBC 無法自動感知 Hibernate 一級緩存,所以如果不及時調(diào)用 flush() 方法將數(shù)據(jù)更改同步到數(shù)據(jù)庫,則②處通過 Spring JDBC 進行數(shù)據(jù)更改的結(jié)果將被 Hibernate 一級緩存中的更改覆蓋掉,因為,一級緩存在 logon() 方法返回前才同步到數(shù)據(jù)庫!

ScoreService 使用 Spring JDBC 數(shù)據(jù)訪問技術(shù),其代碼如下:

清單 3.ScoreService.java:使用 Spring JDBC 數(shù)據(jù)訪問技術(shù)
package user.mixdao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.apache.commons.dbcp.BasicDataSource;@Service("scoreUserService") public class ScoreService extends BaseService{@Autowiredprivate JdbcTemplate jdbcTemplate;public void addScore(String userName, int toAdd) {System.out.println("addScore...");String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?";jdbcTemplate.update(sql, toAdd, userName);//① 查看此處數(shù)據(jù)庫激活的連接數(shù)BasicDataSource basicDataSource = (BasicDataSource) jdbcTemplate.getDataSource();System.out.println("激活連接數(shù)量:"+basicDataSource.getNumActive());} }

Spring 關(guān)鍵的配置文件代碼如下所示:

清單 4. applicationContext.xml 事務(wù)配置代碼部分
<!-- 使用Hibernate事務(wù)管理器 --> <bean id="hiberManager"class="org.springframework.orm.hibernate3.HibernateTransactionManager"p:sessionFactory-ref="sessionFactory"/><!-- 對所有繼承BaseService類的公用方法實施事務(wù)增強 --> <aop:config proxy-target-class="true"><aop:pointcut id="serviceJdbcMethod"expression="within(user.mixdao.BaseService+)"/><aop:advisor pointcut-ref="serviceJdbcMethod"advice-ref="hiberAdvice"/> </aop:config><tx:advice id="hiberAdvice" transaction-manager="hiberManager"><tx:attributes><tx:method name="*"/></tx:attributes> </tx:advice>

啟動 Spring 容器,執(zhí)行 UserService#logon() 方法,可以查看到如下的執(zhí)行日志:

清單 5. 代碼運行日志
12:38:57,062 (AbstractPlatformTransactionManager.java:365) - Creating new transaction with name [user.mixdao.UserService.logon]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT12:38:57,093 (SessionImpl.java:220) - opened session at timestamp: 1266640737012:38:57,093 (HibernateTransactionManager.java:493) - Opened new Session [org.hibernate.impl.SessionImpl@83020] for Hibernate transaction ①12:38:57,093 (HibernateTransactionManager.java:504) - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@83020]12:38:57,109 (JDBCTransaction.java:54) - begin…logon method... updateLastLogonTime... …12:38:57,109 (AbstractBatcher.java:401) - select user0_.USER_NAME as USER1_0_0_, user0_.LAST_LOGON_TIME as LAST2_0_0_, user0_.password as password0_0_, user0_.score as score0_0_ from T_USER user0_ where user0_.USER_NAME=?Hibernate: select user0_.USER_NAME as USER1_0_0_, user0_.LAST_LOGON_TIME as LAST2_0_0_, user0_.password as password0_0_, user0_.score as score0_0_ from T_USER user0_ where user0_.USER_NAME=?…12:38:57,187 (HibernateTemplate.java:422) - Not closing pre-bound Hibernate Session after HibernateTemplate12:38:57,187 (HibernateTemplate.java:397) - Found thread-bound Sessionfor HibernateTemplateHibernate: update T_USER set LAST_LOGON_TIME=?, password=?, score=? where USER_NAME=?…2010-02-20 12:38:57,203 DEBUG [main] (AbstractPlatformTransactionManager.java:470) - Participating in existing transaction ② addScore...2010-02-20 12:38:57,203 DEBUG [main] (JdbcTemplate.java:785) - Executing prepared SQL update2010-02-20 12:38:57,203 DEBUG [main] (JdbcTemplate.java:569)- Executing prepared SQL statement [UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?]2010-02-20 12:38:57,203 DEBUG [main] (JdbcTemplate.java:794) - SQL update affected 1 rows激活連接數(shù)量:1 ③ 2010-02-20 12:38:57,203 DEBUG [main] (AbstractPlatformTransactionManager.java:752) - Initiating transaction commit 2010-02-20 12:38:57,203 DEBUG [main] (HibernateTransactionManager.java:652) - Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@83020] ④2010-02-20 12:38:57,203 DEBUG [main] (JDBCTransaction.java:103) - commit ⑤

仔細(xì)觀察這段輸出日志,在①處 UserService#logon() 開啟一個新的事務(wù),在②處 ScoreService#addScore() 方法加入到①處開啟的事務(wù)上下文中。③處的輸出是 ScoreService#addScore() 方法內(nèi)部的輸出,匯報此時數(shù)據(jù)源激活的連接數(shù)為 1,這清楚地告訴我們 Hibernate 和 JDBC 這兩種數(shù)據(jù)訪問技術(shù)在同一事務(wù)上下文中“共用”一個連接。在④處,提交 Hibernate 事務(wù),接著在⑤處觸發(fā)調(diào)用底層的 Connection 提交事務(wù)。

從以上的運行結(jié)果,我們可以得出這樣的結(jié)論:使用 Hibernate 事務(wù)管理器后,可以混合使用 Hibernate 和 Spring JDBC 數(shù)據(jù)訪問技術(shù),它們將工作于同一事務(wù)上下文中。但是使用 Spring JDBC 訪問數(shù)據(jù)時,Hibernate 的一級或二級緩存得不到同步,此外,一級緩存延遲數(shù)據(jù)同步機制可能會覆蓋 Spring JDBC 數(shù)據(jù)更改的結(jié)果。

由于混合數(shù)據(jù)訪問技術(shù)的方案的事務(wù)同步而緩存不同步的情況,所以最好用 Hibernate 完成讀寫操作,而用 Spring JDBC 完成讀的操作。如用 Spring JDBC 進行簡要列表的查詢,而用 Hibernate 對查詢出的數(shù)據(jù)進行維護。如果確實要同時使用 Hibernate 和 Spring JDBC 讀寫數(shù)據(jù),則必須充分考慮到 Hibernate 緩存機制引發(fā)的問題:必須充分分析數(shù)據(jù)維護邏輯,根據(jù)需要,及時調(diào)用 Hibernate 的 flush() 方法,以免覆蓋 Spring JDBC 的更改,在 Spring JDBC 更改數(shù)據(jù)庫時,維護 Hibernate 的緩存。

可以將以上結(jié)論推廣到其它混合數(shù)據(jù)訪問技術(shù)的方案中,如 Hibernate+iBatis,JPA+Spring JDBC,JDO+Spring JDBC 等。

特殊方法成漏網(wǎng)之魚

由于 Spring 事務(wù)管理是基于接口代理或動態(tài)字節(jié)碼技術(shù),通過 AOP 實施事務(wù)增強的。雖然,Spring 還支持 AspectJ LTW 在類加載期實施增強,但這種方法很少使用,所以我們不予關(guān)注。

對于基于接口動態(tài)代理的 AOP 事務(wù)增強來說,由于接口的方法是 public 的,這就要求實現(xiàn)類的實現(xiàn)方法必須是 public 的(不能是 protected,private 等),同時不能使用 static 的修飾符。所以,可以實施接口動態(tài)代理的方法只能是使用“public”或“public final”修飾符的方法,其它方法不可能被動態(tài)代理,相應(yīng)的也就不能實施 AOP 增強,也不能進行 Spring 事務(wù)增強了。

基于 CGLib 字節(jié)碼動態(tài)代理的方案是通過擴展被增強類,動態(tài)創(chuàng)建子類的方式進行 AOP 增強植入的。由于使用 final、static、private 修飾符的方法都不能被子類覆蓋,相應(yīng)的,這些方法將不能被實施 AOP 增強。所以,必須特別注意這些修飾符的使用,以免不小心成為事務(wù)管理的漏網(wǎng)之魚。

下面通過具體的實例說明基于 CGLib 字節(jié)碼動態(tài)代理無法享受 Spring AOP 事務(wù)增強的特殊方法。

清單 6.UserService.java:4 個不同修飾符的方法
package user.special; import org.springframework.stereotype.Service;@Service("userService") public class UserService {//① private方法因訪問權(quán)限的限制,無法被子類覆蓋private void method1() {System.out.println("method1");}//② final方法無法被子類覆蓋public final void method2() {System.out.println("method2");}//③ static是類級別的方法,無法被子類覆蓋public static void method3() {System.out.println("method3");}//④ public方法可以被子類覆蓋,因此可以被動態(tài)字節(jié)碼增強public void method4() {System.out.println("method4");} }

Spring 通過 CGLib 動態(tài)代理技術(shù)對 UserService Bean 實施 AOP 事務(wù)增強的配置如下所示:

清單 7.applicationContext.xml:對 UserService 用 CGLib 實施事務(wù)增強
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"><!-- 省略聲明數(shù)據(jù)源及DataSourceTransactionManager事務(wù)管理器-->…<aop:config proxy-target-class="true"><!-- ①顯式使用CGLib動態(tài)代理 --><!-- ②希望對UserService所有方法實施事務(wù)增強 --><aop:pointcut id="serviceJdbcMethod"expression="execution(* user.special.UserService.*(..))"/><aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0"/></aop:config><tx:advice id="jdbcAdvice" transaction-manager="jdbcManager"><tx:attributes><tx:method name="*"/></tx:attributes></tx:advice> </beans>

在 ① 處,我們通過 proxy-target-class="true"顯式使用 CGLib 動態(tài)代理技術(shù),在 ② 處通過 AspjectJ 切點表達(dá)式表達(dá) UserService 所有的方法,希望對 UserService 所有方法都實施 Spring AOP 事務(wù)增強。

在 UserService 添加一個可執(zhí)行的方法,如下所示:

清單 8.UserService.java 添加 main 方法
package user.special; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service;@Service("userService") public class UserService {…public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("user/special/applicationContext.xml");UserService service = (UserService) ctx.getBean("userService");System.out.println("before method1");service.method1();System.out.println("after method1");System.out.println("before method2");service.method2();System.out.println("after method2");System.out.println("before method3");service.method3();System.out.println("after method3");System.out.println("before method4");service.method4();System.out.println("after method4");} }

在運行 UserService 之前,將 Log4J 日志級別設(shè)置為 DEBUG,運行以上代碼查看輸出日志,如下所示:

17:24:10,953 (AbstractBeanFactory.java:241) - Returning cached instance of singleton bean 'userService'before method1 method1 after method1 before method2 method2 after method2 before method3 method3 after method3 before method417:24:10,953 (AbstractPlatformTransactionManager.java:365) - Creating new transaction with name [user.special.UserService.method4]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT17:24:11,109 (DataSourceTransactionManager.java:205) - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@165b7e] for JDBC transaction…17:24:11,109 (DataSourceTransactionManager.java:265) - Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@165b7e]17:24:11,125 (DataSourceTransactionManager.java:323) - Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection@165b7e] after transaction17:24:11,125 (DataSourceUtils.java:312) - Returning JDBC Connection to DataSourceafter method4

觀察以上輸出日志,很容易發(fā)現(xiàn) method1~method3 這 3 個方法都沒有被實施 Spring 的事務(wù)增強,只有 method4 被實施了事務(wù)增強。這個結(jié)果剛才驗證了我們前面的論述。

我們通過下表描述哪些特殊方法將成為 Spring AOP 事務(wù)增強的漏網(wǎng)之魚:

表 2. 不能被 Spring AOP 事務(wù)增強的方法
動態(tài)代理策略不能被事務(wù)增強的方法
基于接口的動態(tài)代理除 public 外的其它所有的方法,此外 public static 也不能被增強
基于 CGLib 的動態(tài)代理private、static、final 的方法

不過,需要特別指出的是,這些不能被 Spring 事務(wù)增強的特殊方法并非就不工作在事務(wù)環(huán)境下。只要它們被外層的事務(wù)方法調(diào)用了,由于 Spring 的事務(wù)管理的傳播特殊,內(nèi)部方法也可以工作在外部方法所啟動的事務(wù)上下文中。我們說,這些方法不能被 Spring 進行 AOP 事務(wù)增強,是指這些方法不能啟動事務(wù),但是外層方法的事務(wù)上下文依就可以順利地傳播到這些方法中。

這些不能被 Spring 事務(wù)增強的方法和可被 Spring 事務(wù)增強的方法唯一的區(qū)別在?“是否可以主動啟動一個新事務(wù)”:前者不能而后者可以。對于事務(wù)傳播行為來說,二者是完全相同的,前者也和后者一樣不會造成數(shù)據(jù)連接的泄漏問題。換句話說,如果這些“特殊方法”被無事務(wù)上下文的方法調(diào)用,則它們就工作在無事務(wù)上下文中;反之,如果被具有事務(wù)上下文的方法調(diào)用,則它們就工作在事務(wù)上下文中。

對于 private 的方法,由于最終都會被 public 方法封裝后再開放給外部調(diào)用,而 public 方法是可以被事務(wù)增強的,所以基本上沒有什么問題。在實際開發(fā)中,最容易造成隱患的是基于 CGLib 的動態(tài)代理時的“public static”和“public final”這兩種特殊方法。原因是它們本身是 public 的,所以可以直接被外部類(如 Web 層的 Controller 類)調(diào)用,只要調(diào)用者沒有事務(wù)上下文,這些特殊方法也就以無事務(wù)的方式運作。

小結(jié)

在本文中,我們通過剖析了解到以下的真相:

  • 混合使用多個數(shù)據(jù)訪問技術(shù)框架的最佳組合是一個 ORM 技術(shù)框架(如 Hibernate 或 JPA 等)+ 一個 JDBC 技術(shù)框架(如 Spring JDBC 或 iBatis)。直接使用 ORM 技術(shù)框架對應(yīng)的事務(wù)管理器就可以了,但必須考慮 ORM 緩存同步的問題;
  • Spring AOP 增強有兩個方案:其一為基于接口的動態(tài)代理,其二為基于 CGLib 動態(tài)生成子類的代理。由于 Java 語法的特性,有些特殊方法不能被 Spring AOP 代理,所以也就無法享受 AOP 織入帶來的事務(wù)增強。

在下一篇文章中,筆者將繼續(xù)分析 Spring 事務(wù)管理的以下難點:

  • 直接獲取 Connection 時,哪些情況會造成數(shù)據(jù)連接的泄漏,以及如何應(yīng)對;
  • 除 Spring JDBC 外,其它數(shù)據(jù)訪問技術(shù)數(shù)據(jù)連接泄漏的應(yīng)對方案。

第 3 部分

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

概述

對于應(yīng)用開發(fā)者來說,數(shù)據(jù)連接泄漏無疑是一個可怕的夢魘。如果存在數(shù)據(jù)連接泄漏問題,應(yīng)用程序?qū)⒁驍?shù)據(jù)連接資源的耗盡而崩潰,甚至還可能引起數(shù)據(jù)庫的崩潰。數(shù)據(jù)連接泄漏像黑洞一樣讓開發(fā)者避之唯恐不及。

Spring DAO 對所有支持的數(shù)據(jù)訪問技術(shù)框架都使用模板化技術(shù)進行了薄層的封裝。只要您的程序都使用 Spring DAO 模板(如 JdbcTemplate、HibernateTemplate 等)進行數(shù)據(jù)訪問,一定不會存在數(shù)據(jù)連接泄漏的問題 ―― 這是 Spring 給予我們鄭重的承諾!因此,我們無需關(guān)注數(shù)據(jù)連接(Connection)及其衍生品(Hibernate 的 Session 等)的獲取和釋放的操作,模板類已經(jīng)通過其內(nèi)部流程替我們完成了,且對開發(fā)者是透明的。

但是由于集成第三方產(chǎn)品,整合遺產(chǎn)代碼等原因,可能需要直接訪問數(shù)據(jù)源或直接獲取數(shù)據(jù)連接及其衍生品。這時,如果使用不當(dāng),就可能在無意中創(chuàng)造出一個魔鬼般的連接泄漏問題。

我們知道:當(dāng) Spring 事務(wù)方法運行時,就產(chǎn)生一個事務(wù)上下文,該上下文在本事務(wù)執(zhí)行線程中針對同一個數(shù)據(jù)源綁定了一個唯一的數(shù)據(jù)連接(或其衍生品),所有被該事務(wù)上下文傳播的方法都共享這個數(shù)據(jù)連接。這個數(shù)據(jù)連接從數(shù)據(jù)源獲取及返回給數(shù)據(jù)源都在 Spring 掌控之中,不會發(fā)生問題。如果在需要數(shù)據(jù)連接時,能夠獲取這個被 Spring 管控的數(shù)據(jù)連接,則使用者可以放心使用,無需關(guān)注連接釋放的問題。

那么,如何獲取這些被 Spring 管控的數(shù)據(jù)連接呢? Spring 提供了兩種方法:其一是使用數(shù)據(jù)資源獲取工具類,其二是對數(shù)據(jù)源(或其衍生品如 Hibernate SessionFactory)進行代理。在具體介紹這些方法之前,讓我們先來看一下各種引發(fā)數(shù)據(jù)連接泄漏的場景。

Spring JDBC 數(shù)據(jù)連接泄漏

如果直接從數(shù)據(jù)源獲取連接,且在使用完成后不主動歸還給數(shù)據(jù)源(調(diào)用 Connection#close()),則將造成數(shù)據(jù)連接泄漏的問題。

一個具體的實例

下面,來看一個具體的實例:

清單 1.JdbcUserService.java:主體代碼
package user.connleak; import org.apache.commons.dbcp.BasicDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import java.sql.Connection; @Service("jdbcUserService") public class JdbcUserService { @Autowired private JdbcTemplate jdbcTemplate; public void logon(String userName) { try { // ①直接從數(shù)據(jù)源獲取連接,后續(xù)程序沒有顯式釋放該連接Connection conn = jdbcTemplate.getDataSource().getConnection(); String sql = "UPDATE t_user SET last_logon_time=? WHERE user_name =?"; jdbcTemplate.update(sql, System.currentTimeMillis(), userName); Thread.sleep(1000);// ②模擬程序代碼的執(zhí)行時間} catch (Exception e) { e.printStackTrace(); } } }

JdbcUserService 通過 Spring AOP 事務(wù)增強的配置,讓所有 public 方法都工作在事務(wù)環(huán)境中。即讓 logon() 和 updateLastLogonTime() 方法擁有事務(wù)功能。在 logon() 方法內(nèi)部,我們在①處通過調(diào)用?jdbcTemplate.getDataSource().getConnection()顯式獲取一個連接,這個連接不是 logon() 方法事務(wù)上下文線程綁定的連接,所以如果開發(fā)者如果沒有手工釋放這連接(顯式調(diào)用 Connection#close() 方法),則這個連接將永久被占用(處于 active 狀態(tài)),造成連接泄漏!下面,我們編寫模擬運行的代碼,查看方法執(zhí)行對數(shù)據(jù)連接的實際占用情況:

清單 2.JdbcUserService.java:模擬運行代碼
… @Service("jdbcUserService") public class JdbcUserService {…//①以異步線程的方式執(zhí)行JdbcUserService#logon()方法,以模擬多線程的環(huán)境public static void asynchrLogon(JdbcUserService userService, String userName) {UserServiceRunner runner = new UserServiceRunner(userService, userName);runner.start();}private static class UserServiceRunner extends Thread {private JdbcUserService userService;private String userName;public UserServiceRunner(JdbcUserService userService, String userName) {this.userService = userService;this.userName = userName;}public void run() {userService.logon(userName);}}//② 讓主執(zhí)行線程睡眠一段指定的時間public static void sleep(long time) {try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}}//③ 匯報數(shù)據(jù)源的連接占用情況public static void reportConn(BasicDataSource basicDataSource) {System.out.println("連接數(shù)[active:idle]-[" +basicDataSource.getNumActive()+":"+basicDataSource.getNumIdle()+"]");}public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("user/connleak/applicatonContext.xml");JdbcUserService userService = (JdbcUserService) ctx.getBean("jdbcUserService");BasicDataSource basicDataSource = (BasicDataSource) ctx.getBean("dataSource");//④匯報數(shù)據(jù)源初始連接占用情況JdbcUserService.reportConn(basicDataSource);JdbcUserService.asynchrLogon(userService, "tom");JdbcUserService.sleep(500);//⑤此時線程A正在執(zhí)行JdbcUserService#logon()方法JdbcUserService.reportConn(basicDataSource); JdbcUserService.sleep(2000);//⑥此時線程A所執(zhí)行的JdbcUserService#logon()方法已經(jīng)執(zhí)行完畢JdbcUserService.reportConn(basicDataSource);JdbcUserService.asynchrLogon(userService, "john");JdbcUserService.sleep(500);//⑦此時線程B正在執(zhí)行JdbcUserService#logon()方法JdbcUserService.reportConn(basicDataSource);JdbcUserService.sleep(2000);//⑧此時線程A和B都已完成JdbcUserService#logon()方法的執(zhí)行JdbcUserService.reportConn(basicDataSource);}

在 JdbcUserService 中添加一個可異步執(zhí)行 logon() 方法的 asynchrLogon() 方法,我們通過異步執(zhí)行 logon() 以及讓主線程睡眠的方式模擬多線程環(huán)境下的執(zhí)行場景。在不同的執(zhí)行點,通過 reportConn() 方法匯報數(shù)據(jù)源連接的占用情況。

使用如下的 Spring 配置文件對 JdbcUserServie 的方法進行事務(wù)增強:

清單 3.applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"><context:component-scan base-package="user.connleak"/><bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"p:driverClassName="oracle.jdbc.driver.OracleDriver"p:url="jdbc:oracle:thin:@localhost:1521:orcl"p:username="test"p:password="test"p:defaultAutoCommit="false"/><bean id="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource"/><bean id="jdbcManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource"/><!-- 對JdbcUserService的所有方法實施事務(wù)增強 --><aop:config proxy-target-class="true"><aop:pointcut id="serviceJdbcMethod"expression="within(user.connleak.JdbcUserService+)"/><aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0"/></aop:config><tx:advice id="jdbcAdvice" transaction-manager="jdbcManager"><tx:attributes><tx:method name="*"/></tx:attributes></tx:advice> </beans>

保證 BasicDataSource 數(shù)據(jù)源的配置默認(rèn)連接為 0,運行以上程序代碼,在控制臺中將輸出以下的信息:

清單 4. 輸出日志
連接數(shù) [active:idle]-[0:0] 連接數(shù) [active:idle]-[2:0] 連接數(shù) [active:idle]-[1:1] 連接數(shù) [active:idle]-[3:0] 連接數(shù) [active:idle]-[2:1]

我們通過下表對數(shù)據(jù)源連接的占用和泄漏情況進行描述:

表 1. 執(zhí)行過程數(shù)據(jù)源連接占用情況
時間執(zhí)行線程 1執(zhí)行線程 2數(shù)據(jù)源連接activeidleleak
T0未啟動未啟動000
T1正在執(zhí)行方法未啟動200
T2執(zhí)行完畢未啟動111
T3執(zhí)行完畢正式執(zhí)行方法301
T4執(zhí)行完畢執(zhí)行完畢212

可見在執(zhí)行線程 1 執(zhí)行完畢后,只釋放了一個數(shù)據(jù)連接,還有一個數(shù)據(jù)連處于 active 狀態(tài),說明泄漏了一個連接。相似的,執(zhí)行線程 2 執(zhí)行完畢后,也泄漏了一個連接:原因是直接通過數(shù)據(jù)源獲取連接(jdbcTemplate.getDataSource().getConnection())而沒有顯式釋放造成的。

通過 DataSourceUtils 獲取數(shù)據(jù)連接

Spring 提供了一個能從當(dāng)前事務(wù)上下文中獲取綁定的數(shù)據(jù)連接的工具類,那就是 DataSourceUtils。Spring 強調(diào)必須使用 DataSourceUtils 工具類獲取數(shù)據(jù)連接,Spring 的 JdbcTemplate 內(nèi)部也是通過 DataSourceUtils 來獲取連接的。DataSourceUtils 提供了若干獲取和釋放數(shù)據(jù)連接的靜態(tài)方法,說明如下:

  • static Connection doGetConnection(DataSource dataSource):首先嘗試從事務(wù)上下文中獲取連接,失敗后再從數(shù)據(jù)源獲取連接;
  • static Connection getConnection(DataSource dataSource):和 doGetConnection 方法的功能一樣,實際上,它內(nèi)部就是調(diào)用 doGetConnection 方法獲取連接的;
  • static void doReleaseConnection(Connection con, DataSource dataSource):釋放連接,放回到連接池中;
  • static void releaseConnection(Connection con, DataSource dataSource):和 doReleaseConnection 方法的功能一樣,實際上,它內(nèi)部就是調(diào)用 doReleaseConnection 方法獲取連接的;

來看一下 DataSourceUtils 從數(shù)據(jù)源獲取連接的關(guān)鍵代碼:

清單 5. DataSourceUtils.java 獲取連接的工具類
public abstract class DataSourceUtils {…public static Connection doGetConnection(DataSource dataSource) throws SQLException {Assert.notNull(dataSource, "No DataSource specified");//①首先嘗試從事務(wù)同步管理器中獲取數(shù)據(jù)連接ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) { conHolder.requested();if (!conHolder.hasConnection()) {logger.debug("Fetching resumed JDBC Connection from DataSource");conHolder.setConnection(dataSource.getConnection());}return conHolder.getConnection();}//②如果獲取不到,則直接從數(shù)據(jù)源中獲取連接Connection con = dataSource.getConnection();//③如果擁有事務(wù)上下文,則將連接綁定到事務(wù)上下文中if (TransactionSynchronizationManager.isSynchronizationActive()) {ConnectionHolder holderToUse = conHolder;if (holderToUse == null) {holderToUse = new ConnectionHolder(con);}else {holderToUse.setConnection(con);}holderToUse.requested();TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(holderToUse, dataSource));holderToUse.setSynchronizedWithTransaction(true);if (holderToUse != conHolder) {TransactionSynchronizationManager.bindResource(dataSource, holderToUse);}}return con;}… }

它首先查看當(dāng)前是否存在事務(wù)管理上下文,并嘗試從事務(wù)管理上下文獲取連接,如果獲取失敗,直接從數(shù)據(jù)源中獲取連接。在獲取連接后,如果當(dāng)前擁有事務(wù)上下文,則將連接綁定到事務(wù)上下文中。

我們在清單 1 的 JdbcUserService 中,使用 DataSourceUtils.getConnection() 替換直接從數(shù)據(jù)源中獲取連接的代碼:

清單 6. JdbcUserService.java:使用 DataSourceUtils 獲取數(shù)據(jù)連接
public void logon(String userName) {try {//Connection conn = jdbcTemplate.getDataSource().getConnection();//①使用DataSourceUtils獲取數(shù)據(jù)連接Connection conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());String sql = "UPDATE t_user SET last_logon_time=? WHERE user_name =?";jdbcTemplate.update(sql, System.currentTimeMillis(), userName);Thread.sleep(1000); } catch (Exception e) {e.printStackTrace();} }

重新運行代碼,得到如下的執(zhí)行結(jié)果:

清單 7. 輸出日志
連接數(shù) [active:idle]-[0:0] 連接數(shù) [active:idle]-[1:0] 連接數(shù) [active:idle]-[0:1] 連接數(shù) [active:idle]-[1:0] 連接數(shù) [active:idle]-[0:1]

對照清單 4 的輸出日志,我們可以看到已經(jīng)沒有連接泄漏的現(xiàn)象了。一個執(zhí)行線程在運行 JdbcUserService#logon() 方法時,只占用一個連接,而且方法執(zhí)行完畢后,該連接馬上釋放。這說明通過 DataSourceUtils.getConnection() 方法確實獲取了方法所在事務(wù)上下文綁定的那個連接,而不是像原來那樣從數(shù)據(jù)源中獲取一個新的連接。

使用 DataSourceUtils 獲取數(shù)據(jù)連接也可能造成泄漏!

是否使用 DataSourceUtils 獲取數(shù)據(jù)連接就可以高枕無憂了呢?理想很美好,但現(xiàn)實很殘酷:如果 DataSourceUtils 在沒有事務(wù)上下文的方法中使用 getConnection() 獲取連接,依然會造成數(shù)據(jù)連接泄漏!

保持代碼清單 6 的代碼不變,調(diào)整 Spring 配置文件,將清單 3 中 Spring AOP 事務(wù)增強配置的代碼注釋掉,重新運行清單 6 的代碼,將得到如下的輸出日志:

清單 8. 輸出日志
連接數(shù) [active:idle]-[0:0] 連接數(shù) [active:idle]-[1:1] 連接數(shù) [active:idle]-[1:1] 連接數(shù) [active:idle]-[2:1] 連接數(shù) [active:idle]-[2:1]

我們通過下表對數(shù)據(jù)源連接的占用和泄漏情況進行描述:

表 2. 執(zhí)行過程數(shù)據(jù)源連接占用情況
時間執(zhí)行線程 1執(zhí)行線程 2數(shù)據(jù)源連接activeidleleak
T0未啟動未啟動000
T1正在執(zhí)行方法未啟動110
T2執(zhí)行完畢未啟動111
T3執(zhí)行完畢正式執(zhí)行方法211
T4執(zhí)行完畢執(zhí)行完畢212

仔細(xì)對照表 1 的執(zhí)行過程,我們發(fā)現(xiàn)在 T1 時,有事務(wù)上下文時的 active 為 2,idle 為 0,而此時由于沒有事務(wù)管理,則 active 為 1 而 idle 也為 1。這說明有事務(wù)上下文時,需要等到整個事務(wù)方法(即 logon())返回后,事務(wù)上下文綁定的連接才釋放。但在沒有事務(wù)上下文時,logon() 調(diào)用 JdbcTemplate 執(zhí)行完數(shù)據(jù)操作后,馬上就釋放連接。

在 T2 執(zhí)行線程完成 logon() 方法的執(zhí)行后,有一個連接沒有被釋放(active),所以發(fā)生了連接泄漏。到 T4 時,兩個執(zhí)行線程都完成了 logon() 方法的調(diào)用,但是出現(xiàn)了兩個未釋放的連接。

要堵上這個連接泄漏的漏洞,需要對 logon() 方法進行如下的改造:

清單 9.JdbcUserService.java:手工釋放獲取的連接
public void logon(String userName) {Connection conn = null;try {conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());String sql = "UPDATE t_user SET last_logon_time=? WHERE user_name =?";jdbcTemplate.update(sql, System.currentTimeMillis(), userName);Thread.sleep(1000);// ①} catch (Exception e) {e.printStackTrace();}finally {// ②顯式使用DataSourceUtils釋放連接DataSourceUtils.releaseConnection(conn,jdbcTemplate.getDataSource());} }

在 ② 處顯式調(diào)用?DataSourceUtils.releaseConnection()?方法釋放獲取的連接。特別需要指出的是:一定不能在 ① 處釋放連接!因為如果 logon() 在獲取連接后,① 處代碼前這段代碼執(zhí)行時發(fā)生異常,則①處釋放連接的動作將得不到執(zhí)行。這將是一個非常具有隱蔽性的連接泄漏的隱患點。

JdbcTemplate 如何做到對連接泄漏的免疫

分析 JdbcTemplate 的代碼,我們可以清楚地看到它開放的每個數(shù)據(jù)操作方法,首先都使用 DataSourceUtils 獲取連接,在方法返回之前使用 DataSourceUtils 釋放連接。

來看一下 JdbcTemplate 最核心的一個數(shù)據(jù)操作方法 execute():

清單 10.JdbcTemplate#execute()
public <T> T execute(StatementCallback<T> action) throws DataAccessException {//① 首先根據(jù)DataSourceUtils獲取數(shù)據(jù)連接Connection con = DataSourceUtils.getConnection(getDataSource());Statement stmt = null;try {Connection conToUse = con;…h(huán)andleWarnings(stmt);return result;}catch (SQLException ex) {JdbcUtils.closeStatement(stmt);stmt = null;DataSourceUtils.releaseConnection(con, getDataSource());con = null;throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);}finally {JdbcUtils.closeStatement(stmt);//② 最后根據(jù)DataSourceUtils釋放數(shù)據(jù)連接DataSourceUtils.releaseConnection(con, getDataSource());} }

在 ① 處通過 DataSourceUtils.getConnection() 獲取連接,在 ② 處通過 DataSourceUtils.releaseConnection() 釋放連接。所有 JdbcTemplate 開放的數(shù)據(jù)訪問方法最終都是通過?execute(StatementCallback<T> action)執(zhí)行數(shù)據(jù)訪問操作的,因此這個方法代表了 JdbcTemplate 數(shù)據(jù)操作的最終實現(xiàn)方式。

正是因為 JdbcTemplate 嚴(yán)謹(jǐn)?shù)墨@取連接,釋放連接的模式化流程保證了 JdbcTemplate 對數(shù)據(jù)連接泄漏問題的免疫性。所以,如有可能盡量使用 JdbcTemplate,HibernateTemplate 等這些模板進行數(shù)據(jù)訪問操作,避免直接獲取數(shù)據(jù)連接的操作。

使用 TransactionAwareDataSourceProxy

如果不得已要顯式獲取數(shù)據(jù)連接,除了使用 DataSourceUtils 獲取事務(wù)上下文綁定的連接外,還可以通過 TransactionAwareDataSourceProxy 對數(shù)據(jù)源進行代理。數(shù)據(jù)源對象被代理后就具有了事務(wù)上下文感知的能力,通過代理數(shù)據(jù)源的 getConnection() 方法獲取的連接和使用 DataSourceUtils.getConnection() 獲取連接的效果是一樣的。

下面是使用 TransactionAwareDataSourceProxy 對數(shù)據(jù)源進行代理的配置:

清單 11.applicationContext.xml:對數(shù)據(jù)源進行代理
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"p:driverClassName="oracle.jdbc.driver.OracleDriver"p:url="jdbc:oracle:thin:@localhost:1521:orcl"p:username="test"p:password="test"p:defaultAutoCommit="false"/><!-- ①對數(shù)據(jù)源進行代理--> <bean id="dataSourceProxy" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy"p:targetDataSource-ref="dataSource"/><!-- ②直接使用數(shù)據(jù)源的代理對象--> <bean id="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSourceProxy"/><!-- ③直接使用數(shù)據(jù)源的代理對象--> <bean id="jdbcManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSourceProxy"/>

對數(shù)據(jù)源進行代理后,我們就可以通過數(shù)據(jù)源代理對象的 getConnection() 獲取事務(wù)上下文中綁定的數(shù)據(jù)連接了。

因此,如果數(shù)據(jù)源已經(jīng)進行了 TransactionAwareDataSourceProxy 的代理,而且方法存在事務(wù)上下文,那么清單 1 的代碼也不會生產(chǎn)連接泄漏的問題。

其它數(shù)據(jù)訪問技術(shù)的等價類

理解了 Spring JDBC 的數(shù)據(jù)連接泄漏問題,其中的道理可以平滑地推廣到其它框架中去。Spring 為每個數(shù)據(jù)訪問技術(shù)框架都提供了一個獲取事務(wù)上下文綁定的數(shù)據(jù)連接(或其衍生品)的工具類和數(shù)據(jù)源(或其衍生品)的代理類。

DataSourceUtils 的等價類

下表列出了不同數(shù)據(jù)訪問技術(shù)對應(yīng) DataSourceUtils 的等價類:

表 3. 不同數(shù)據(jù)訪問框架 DataSourceUtils 的等價類
數(shù)據(jù)訪問技術(shù)框架連接 ( 或衍生品 ) 獲取工具類
Spring JDBCorg.springframework.jdbc.datasource.DataSourceUtils
Hibernateorg.springframework.orm.hibernate3.SessionFactoryUtils
iBatisorg.springframework.jdbc.datasource.DataSourceUtils
JPAorg.springframework.orm.jpa.EntityManagerFactoryUtils
JDOorg.springframework.orm.jdo.PersistenceManagerFactoryUtils

TransactionAwareDataSourceProxy 的等價類

下表列出了不同數(shù)據(jù)訪問技術(shù)框架下 TransactionAwareDataSourceProxy 的等價類:

表 4. 不同數(shù)據(jù)訪問框架 TransactionAwareDataSourceProxy 的等價類
數(shù)據(jù)訪問技術(shù)框架連接 ( 或衍生品 ) 獲取工具類
Spring JDBCorg.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
Hibernateorg.springframework.orm.hibernate3.LocalSessionFactoryBean
iBatisorg.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
JPA
JDOorg.springframework.orm.jdo.
TransactionAwarePersistenceManagerFactoryProxy

小結(jié)

在本文中,我們通過剖析了解到以下的真相:

    • 使用 Spring JDBC 時如果直接獲取 Connection,可能會造成連接泄漏。為降低連接泄漏的可能,盡量使用 DataSourceUtils 獲取數(shù)據(jù)連接。也可以對數(shù)據(jù)源進行代理,以便將其擁有事務(wù)上下文的感知能力;
    • 可以將 Spring JDBC 防止連接泄漏的解決方案平滑應(yīng)用到其它的數(shù)據(jù)訪問技術(shù)框架中。

?

?

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/3832949.html

總結(jié)

以上是生活随笔為你收集整理的Spring 事务管理高级应用难点剖析--转的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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

久久国产精品萌白酱免费 | 日韩精品一区二区av在线 | 日韩精品一区二区av在线 | 无码国产乱人伦偷精品视频 | 欧美日韩视频无码一区二区三 | 国产成人一区二区三区在线观看 | 久久久久se色偷偷亚洲精品av | 精品久久8x国产免费观看 | 日产精品高潮呻吟av久久 | 国产黄在线观看免费观看不卡 | 国产又粗又硬又大爽黄老大爷视 | 成熟妇人a片免费看网站 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 欧美zoozzooz性欧美 | 18禁止看的免费污网站 | aa片在线观看视频在线播放 | 色婷婷av一区二区三区之红樱桃 | 东京一本一道一二三区 | 人妻少妇精品无码专区动漫 | 中文无码精品a∨在线观看不卡 | 黑人粗大猛烈进出高潮视频 | 黑森林福利视频导航 | 少妇太爽了在线观看 | 十八禁真人啪啪免费网站 | 福利一区二区三区视频在线观看 | 荫蒂添的好舒服视频囗交 | 日本成熟视频免费视频 | 国产精品无码一区二区桃花视频 | 中文字幕日产无线码一区 | 国产精品久久久久久无码 | 青草青草久热国产精品 | 黄网在线观看免费网站 | 青青青爽视频在线观看 | 女人和拘做爰正片视频 | 熟妇女人妻丰满少妇中文字幕 | 综合网日日天干夜夜久久 | 婷婷六月久久综合丁香 | 日本在线高清不卡免费播放 | 精品久久久中文字幕人妻 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 无码人妻丰满熟妇区五十路百度 | 欧美老熟妇乱xxxxx | 偷窥村妇洗澡毛毛多 | 久久亚洲中文字幕精品一区 | 亚洲日韩一区二区三区 | 色偷偷人人澡人人爽人人模 | 国产精品国产三级国产专播 | 大肉大捧一进一出视频出来呀 | 国产亚洲精品久久久久久久久动漫 | 男女爱爱好爽视频免费看 | 亚洲s色大片在线观看 | 国产人妻人伦精品 | 1000部夫妻午夜免费 | 婷婷综合久久中文字幕蜜桃三电影 | 国产97人人超碰caoprom | 东京一本一道一二三区 | 国产一区二区三区精品视频 | 自拍偷自拍亚洲精品10p | 色偷偷人人澡人人爽人人模 | 亚洲 欧美 激情 小说 另类 | 亚洲高清偷拍一区二区三区 | 日本一卡二卡不卡视频查询 | 成人精品一区二区三区中文字幕 | 久久久国产精品无码免费专区 | 免费视频欧美无人区码 | 欧洲欧美人成视频在线 | 2020最新国产自产精品 | www国产亚洲精品久久网站 | 国产黄在线观看免费观看不卡 | 青青草原综合久久大伊人精品 | 亚洲色欲色欲欲www在线 | 一本大道伊人av久久综合 | 中文无码成人免费视频在线观看 | av无码不卡在线观看免费 | 欧美日韩综合一区二区三区 | 久久精品人人做人人综合试看 | 无套内射视频囯产 | 久久www免费人成人片 | 久久久无码中文字幕久... | 亚洲国产欧美国产综合一区 | 欧美激情一区二区三区成人 | 亚洲精品综合五月久久小说 | 成人精品一区二区三区中文字幕 | 亚洲精品国产精品乱码不卡 | 亚洲の无码国产の无码步美 | 99久久99久久免费精品蜜桃 | 国产精品无码mv在线观看 | 国产小呦泬泬99精品 | 亚洲中文字幕成人无码 | 亚洲中文字幕无码中文字在线 | 狠狠cao日日穞夜夜穞av | 色诱久久久久综合网ywww | 久久久无码中文字幕久... | 中文字幕乱码亚洲无线三区 | 国产精品久久国产三级国 | 国产欧美熟妇另类久久久 | 国产黑色丝袜在线播放 | 色狠狠av一区二区三区 | 老熟妇乱子伦牲交视频 | 久久天天躁夜夜躁狠狠 | 中国大陆精品视频xxxx | 乱中年女人伦av三区 | 精品久久综合1区2区3区激情 | 成人无码视频免费播放 | 一本久道久久综合婷婷五月 | 国产免费无码一区二区视频 | 日本一区二区三区免费播放 | 免费人成网站视频在线观看 | 无码人妻av免费一区二区三区 | 成 人影片 免费观看 | 人妻互换免费中文字幕 | 国产精品美女久久久 | 最新国产乱人伦偷精品免费网站 | а√资源新版在线天堂 | 亚洲 激情 小说 另类 欧美 | 久久久久成人片免费观看蜜芽 | 青春草在线视频免费观看 | 久久国产劲爆∧v内射 | 欧美兽交xxxx×视频 | 18禁止看的免费污网站 | 国产偷抇久久精品a片69 | 久久久久久国产精品无码下载 | 一本大道伊人av久久综合 | 精品国产一区二区三区四区 | 97无码免费人妻超级碰碰夜夜 | 国产av久久久久精东av | 午夜时刻免费入口 | 亚洲高清偷拍一区二区三区 | 精品国产乱码久久久久乱码 | 亚洲综合另类小说色区 | 亚洲国产午夜精品理论片 | 成年女人永久免费看片 | 97色伦图片97综合影院 | 性欧美牲交xxxxx视频 | 人人爽人人澡人人高潮 | 国产sm调教视频在线观看 | 77777熟女视频在线观看 а天堂中文在线官网 | 国内精品久久久久久中文字幕 | 亚洲无人区一区二区三区 | 精品成在人线av无码免费看 | 麻豆国产丝袜白领秘书在线观看 | 国产综合在线观看 | 大肉大捧一进一出好爽视频 | 天堂久久天堂av色综合 | 久久亚洲a片com人成 | 久在线观看福利视频 | aⅴ在线视频男人的天堂 | 欧美日韩一区二区综合 | 熟妇人妻中文av无码 | 国产在线aaa片一区二区99 | 亚洲成av人在线观看网址 | 鲁大师影院在线观看 | 人妻中文无码久热丝袜 | 在线播放免费人成毛片乱码 | 亚洲精品国产精品乱码不卡 | 欧美性色19p | 久久久久免费看成人影片 | 久久这里只有精品视频9 | 四十如虎的丰满熟妇啪啪 | 国产免费久久久久久无码 | 国产香蕉97碰碰久久人人 | 亚洲一区二区三区香蕉 | 麻豆精产国品 | 丰满人妻一区二区三区免费视频 | 免费乱码人妻系列无码专区 | 国产成人精品一区二区在线小狼 | 欧美兽交xxxx×视频 | 久久久久久亚洲精品a片成人 | 欧美国产日韩久久mv | 全黄性性激高免费视频 | 亚洲日本在线电影 | 国产在线精品一区二区高清不卡 | 十八禁视频网站在线观看 | 欧美刺激性大交 | 国产特级毛片aaaaaaa高清 | 亚洲成a人片在线观看无码 | 中文久久乱码一区二区 | 日本成熟视频免费视频 | 男人的天堂av网站 | 国产电影无码午夜在线播放 | 日产国产精品亚洲系列 | 久久久国产一区二区三区 | 漂亮人妻洗澡被公强 日日躁 | 午夜不卡av免费 一本久久a久久精品vr综合 | 国产精品多人p群无码 | 欧美成人高清在线播放 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 色偷偷人人澡人人爽人人模 | 欧美三级不卡在线观看 | 国产亚洲欧美在线专区 | 国产网红无码精品视频 | 美女张开腿让人桶 | 2019nv天堂香蕉在线观看 | 熟妇女人妻丰满少妇中文字幕 | 无码国内精品人妻少妇 | 99久久99久久免费精品蜜桃 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 狠狠综合久久久久综合网 | 亚洲人交乣女bbw | 久激情内射婷内射蜜桃人妖 | 97se亚洲精品一区 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲人成网站在线播放942 | 国产激情综合五月久久 | 国产av久久久久精东av | 无码人妻av免费一区二区三区 | 亚洲爆乳大丰满无码专区 | 鲁大师影院在线观看 | 亚洲综合另类小说色区 | 精品国产麻豆免费人成网站 | 永久免费精品精品永久-夜色 | 又粗又大又硬又长又爽 | 午夜丰满少妇性开放视频 | 精品人人妻人人澡人人爽人人 | 久久久久国色av免费观看性色 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 日本一区二区更新不卡 | 国产精品久久久 | 丁香花在线影院观看在线播放 | 福利一区二区三区视频在线观看 | 精品欧洲av无码一区二区三区 | 中文毛片无遮挡高清免费 | 免费人成网站视频在线观看 | 人妻互换免费中文字幕 | 成人无码精品1区2区3区免费看 | 18黄暴禁片在线观看 | 免费男性肉肉影院 | 好男人www社区 | 亚洲熟女一区二区三区 | 无码人妻久久一区二区三区不卡 | 无码播放一区二区三区 | 俺去俺来也www色官网 | 人妻插b视频一区二区三区 | 国产成人综合美国十次 | 日本欧美一区二区三区乱码 | 国产亚洲tv在线观看 | 无码帝国www无码专区色综合 | 亚洲国产av美女网站 | 狠狠cao日日穞夜夜穞av | 99精品无人区乱码1区2区3区 | 在线观看欧美一区二区三区 | 国产成人一区二区三区在线观看 | 天下第一社区视频www日本 | 亚洲爆乳无码专区 | 日产精品99久久久久久 | 亚洲高清偷拍一区二区三区 | 久久国产精品偷任你爽任你 | 丁香啪啪综合成人亚洲 | 精品欧美一区二区三区久久久 | 青草视频在线播放 | 无码av岛国片在线播放 | 日日摸夜夜摸狠狠摸婷婷 | 国产在线精品一区二区高清不卡 | 中文字幕无码视频专区 | 爆乳一区二区三区无码 | 18无码粉嫩小泬无套在线观看 | 2020最新国产自产精品 | 中文字幕无码av激情不卡 | 亚洲成熟女人毛毛耸耸多 | 色一情一乱一伦 | 麻豆国产人妻欲求不满谁演的 | 国产极品视觉盛宴 | 日产国产精品亚洲系列 | 国产精品第一区揄拍无码 | 久久99精品久久久久婷婷 | 久久精品国产99久久6动漫 | 色婷婷久久一区二区三区麻豆 | 曰韩少妇内射免费播放 | 清纯唯美经典一区二区 | 国产又爽又黄又刺激的视频 | 性生交片免费无码看人 | 激情亚洲一区国产精品 | 国产精品毛片一区二区 | 十八禁视频网站在线观看 | 扒开双腿吃奶呻吟做受视频 | 四虎影视成人永久免费观看视频 | 狂野欧美性猛交免费视频 | 国产激情无码一区二区 | 黄网在线观看免费网站 | 日本大乳高潮视频在线观看 | 国内综合精品午夜久久资源 | 少妇人妻av毛片在线看 | 亚洲日本在线电影 | 国产精品无码永久免费888 | 99麻豆久久久国产精品免费 | 麻豆人妻少妇精品无码专区 | 国产色在线 | 国产 | 又紧又大又爽精品一区二区 | 国产99久久精品一区二区 | 中文字幕久久久久人妻 | 国产高清不卡无码视频 | 亚洲色无码一区二区三区 | 欧美自拍另类欧美综合图片区 | 精品国产一区二区三区av 性色 | 亚洲色欲色欲天天天www | 国产办公室秘书无码精品99 | 在线欧美精品一区二区三区 | 国产精品99久久精品爆乳 | 高潮毛片无遮挡高清免费视频 | 国产精品第一国产精品 | 欧美日韩视频无码一区二区三 | 国产日产欧产精品精品app | 日本一区二区三区免费播放 | 久久综合给久久狠狠97色 | 麻豆md0077饥渴少妇 | 无码免费一区二区三区 | 人妻少妇被猛烈进入中文字幕 | 久久久久久久久蜜桃 | 水蜜桃亚洲一二三四在线 | 亚洲综合精品香蕉久久网 | 丰满肥臀大屁股熟妇激情视频 | 欧美第一黄网免费网站 | 波多野结衣乳巨码无在线观看 | 亚洲无人区一区二区三区 | 免费无码的av片在线观看 | 玩弄中年熟妇正在播放 | 熟妇激情内射com | 免费播放一区二区三区 | 亚洲欧美综合区丁香五月小说 | 中国女人内谢69xxxx | 欧美 日韩 亚洲 在线 | 亚洲狠狠婷婷综合久久 | 中文字幕无码日韩专区 | 欧洲精品码一区二区三区免费看 | 中文字幕av无码一区二区三区电影 | 日本一卡2卡3卡四卡精品网站 | 亚洲熟妇自偷自拍另类 | 无码av岛国片在线播放 | 成人无码影片精品久久久 | 久久精品无码一区二区三区 | 天堂无码人妻精品一区二区三区 | 欧美兽交xxxx×视频 | 国产av一区二区精品久久凹凸 | 粉嫩少妇内射浓精videos | 自拍偷自拍亚洲精品被多人伦好爽 | 国产午夜视频在线观看 | 国产亚洲精品精品国产亚洲综合 | 中文字幕av伊人av无码av | 国产在线aaa片一区二区99 | 人人妻人人澡人人爽人人精品 | 精品久久久久久亚洲精品 | 九九热爱视频精品 | 一本久久伊人热热精品中文字幕 | 又色又爽又黄的美女裸体网站 | 麻豆人妻少妇精品无码专区 | 男人扒开女人内裤强吻桶进去 | 日本一本二本三区免费 | 综合网日日天干夜夜久久 | 高清不卡一区二区三区 | 精品一区二区三区无码免费视频 | www国产精品内射老师 | 乱人伦人妻中文字幕无码 | 精品国产麻豆免费人成网站 | 日韩在线不卡免费视频一区 | 久久综合激激的五月天 | 国产人妻精品午夜福利免费 | 国产黄在线观看免费观看不卡 | 久久精品人妻少妇一区二区三区 | 久久熟妇人妻午夜寂寞影院 | 国产成人一区二区三区在线观看 | 18无码粉嫩小泬无套在线观看 | 7777奇米四色成人眼影 | 亚洲国产av精品一区二区蜜芽 | 免费看男女做好爽好硬视频 | 玩弄少妇高潮ⅹxxxyw | 久久午夜无码鲁丝片秋霞 | 无码播放一区二区三区 | 亚洲中文字幕在线观看 | 久久久精品456亚洲影院 | 精品欧洲av无码一区二区三区 | 少妇厨房愉情理9仑片视频 | 久久精品国产日本波多野结衣 | 国产 浪潮av性色四虎 | 老子影院午夜伦不卡 | 国产精品久久久久影院嫩草 | 国产美女极度色诱视频www | 国产精品久久福利网站 | 高潮喷水的毛片 | 牲欲强的熟妇农村老妇女视频 | 精品人妻中文字幕有码在线 | 永久免费精品精品永久-夜色 | 扒开双腿吃奶呻吟做受视频 | 亚洲熟女一区二区三区 | 麻豆国产人妻欲求不满 | 亚洲a无码综合a国产av中文 | 又粗又大又硬又长又爽 | 中文字幕无码视频专区 | 国产人妻人伦精品1国产丝袜 | 亚洲欧洲日本无在线码 | 中文字幕av无码一区二区三区电影 | 亚洲s码欧洲m码国产av | 久久精品国产精品国产精品污 | 久久久无码中文字幕久... | 国产午夜手机精彩视频 | 成人aaa片一区国产精品 | 久久久无码中文字幕久... | 少妇人妻大乳在线视频 | 久久精品国产精品国产精品污 | 久久综合激激的五月天 | 性欧美大战久久久久久久 | 国产在线aaa片一区二区99 | 亚洲综合无码一区二区三区 | 女人被男人躁得好爽免费视频 | 国产成人一区二区三区在线观看 | 日韩欧美群交p片內射中文 | 国产猛烈高潮尖叫视频免费 | 一本久久a久久精品亚洲 | 天天做天天爱天天爽综合网 | 欧美刺激性大交 | 东北女人啪啪对白 | 久久久久久国产精品无码下载 | 国产日产欧产精品精品app | 中文字幕人妻丝袜二区 | 色窝窝无码一区二区三区色欲 | 亚洲综合精品香蕉久久网 | 在线天堂新版最新版在线8 | 国产麻豆精品精东影业av网站 | 日韩精品无码一区二区中文字幕 | 精品亚洲成av人在线观看 | 精品人妻人人做人人爽 | 18无码粉嫩小泬无套在线观看 | 免费网站看v片在线18禁无码 | 日本大乳高潮视频在线观看 | 搡女人真爽免费视频大全 | 蜜桃视频韩日免费播放 | 四虎国产精品一区二区 | 伊人久久大香线焦av综合影院 | 国产精品国产自线拍免费软件 | 国产绳艺sm调教室论坛 | 亚洲精品国产第一综合99久久 | 一区二区三区乱码在线 | 欧洲 | 国产精品亚洲综合色区韩国 | 国产亚洲精品久久久久久国模美 | 欧美日本免费一区二区三区 | 在线播放无码字幕亚洲 | 无码人妻精品一区二区三区下载 | 国产精品久久精品三级 | 成在人线av无码免费 | 亲嘴扒胸摸屁股激烈网站 | 亚洲国精产品一二二线 | 又紧又大又爽精品一区二区 | 亚洲成色在线综合网站 | 青青青手机频在线观看 | 无码国内精品人妻少妇 | 搡女人真爽免费视频大全 | 亚洲国产精品久久久久久 | 久久久久亚洲精品中文字幕 | 国产精品高潮呻吟av久久4虎 | 久久久久亚洲精品中文字幕 | 亚洲男女内射在线播放 | 国内揄拍国内精品人妻 | 学生妹亚洲一区二区 | 亚洲va欧美va天堂v国产综合 | 精品久久久久香蕉网 | 少妇久久久久久人妻无码 | 国产性生交xxxxx无码 | 男人的天堂av网站 | 国产精品久久久久久久9999 | 男女超爽视频免费播放 | 3d动漫精品啪啪一区二区中 | 色综合久久中文娱乐网 | 日韩亚洲欧美中文高清在线 | 日韩亚洲欧美精品综合 | 亚洲国产成人av在线观看 | 久久国产精品二国产精品 | 国产成人无码一二三区视频 | 国产香蕉97碰碰久久人人 | 97精品人妻一区二区三区香蕉 | 亚洲色欲久久久综合网东京热 | 日韩亚洲欧美中文高清在线 | 高清无码午夜福利视频 | 欧美一区二区三区 | 免费网站看v片在线18禁无码 | 国产人妻久久精品二区三区老狼 | 亚洲精品综合一区二区三区在线 | 亚洲中文字幕av在天堂 | 宝宝好涨水快流出来免费视频 | 国产午夜亚洲精品不卡 | 久久精品人人做人人综合 | 红桃av一区二区三区在线无码av | 国产精华av午夜在线观看 | 鲁大师影院在线观看 | 久久久精品国产sm最大网站 | 300部国产真实乱 | 在线视频网站www色 | 中文字幕 亚洲精品 第1页 | 日韩少妇白浆无码系列 | 久青草影院在线观看国产 | 国产精品二区一区二区aⅴ污介绍 | 日本又色又爽又黄的a片18禁 | 骚片av蜜桃精品一区 | 久久亚洲中文字幕精品一区 | 国产精品无码永久免费888 | 亚洲精品无码人妻无码 | 色诱久久久久综合网ywww | 久久久精品欧美一区二区免费 | 亚洲一区二区三区四区 | 亚洲天堂2017无码中文 | 久久精品国产大片免费观看 | 丰满少妇熟乱xxxxx视频 | 丰腴饱满的极品熟妇 | 亚洲国产精品无码一区二区三区 | av香港经典三级级 在线 | 奇米影视7777久久精品 | 国产内射老熟女aaaa | 国产无遮挡又黄又爽免费视频 | 国产特级毛片aaaaaaa高清 | 日韩精品无码免费一区二区三区 | а√天堂www在线天堂小说 | 国产偷国产偷精品高清尤物 | 精品国产aⅴ无码一区二区 | 国产免费久久久久久无码 | 国产精品成人av在线观看 | 国产美女极度色诱视频www | 久久综合狠狠综合久久综合88 | 日本丰满护士爆乳xxxx | 国产午夜视频在线观看 | 欧美放荡的少妇 | 丰满少妇女裸体bbw | 亚洲 另类 在线 欧美 制服 | 国产九九九九九九九a片 | 色婷婷综合激情综在线播放 | 内射爽无广熟女亚洲 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 色 综合 欧美 亚洲 国产 | 国产精品a成v人在线播放 | 免费中文字幕日韩欧美 | 久久精品丝袜高跟鞋 | 亚洲精品久久久久久久久久久 | 永久免费精品精品永久-夜色 | 一个人看的www免费视频在线观看 | 欧美35页视频在线观看 | 亚洲午夜无码久久 | 无人区乱码一区二区三区 | 天堂亚洲2017在线观看 | 午夜精品久久久久久久久 | 国产偷国产偷精品高清尤物 | 在线观看免费人成视频 | 欧洲精品码一区二区三区免费看 | 亚洲精品综合一区二区三区在线 | 亚洲人交乣女bbw | 四虎影视成人永久免费观看视频 | 内射爽无广熟女亚洲 | 国产人成高清在线视频99最全资源 | 九一九色国产 | 人人妻人人澡人人爽精品欧美 | 国产亚洲精品久久久闺蜜 | 好爽又高潮了毛片免费下载 | 亚洲色成人中文字幕网站 | 黑人粗大猛烈进出高潮视频 | 亚洲 另类 在线 欧美 制服 | 97久久国产亚洲精品超碰热 | 日韩av激情在线观看 | 亚洲 日韩 欧美 成人 在线观看 | 天天做天天爱天天爽综合网 | 亚洲国产日韩a在线播放 | 天天躁夜夜躁狠狠是什么心态 | 天天做天天爱天天爽综合网 | 中文字幕 人妻熟女 | 又色又爽又黄的美女裸体网站 | 精品人人妻人人澡人人爽人人 | 日韩人妻系列无码专区 | 四十如虎的丰满熟妇啪啪 | 老司机亚洲精品影院无码 | 国产又爽又黄又刺激的视频 | 欧美日韩人成综合在线播放 | 亚洲另类伦春色综合小说 | 亚洲春色在线视频 | 国产精品久久精品三级 | 色欲人妻aaaaaaa无码 | 99久久人妻精品免费二区 | 熟妇激情内射com | 亚洲综合精品香蕉久久网 | 亚洲国产精品久久久天堂 | 中文字幕 亚洲精品 第1页 | 波多野结衣av一区二区全免费观看 | 国产乡下妇女做爰 | 国产偷自视频区视频 | 99国产精品白浆在线观看免费 | 国产69精品久久久久app下载 | 亚洲国产欧美日韩精品一区二区三区 | 成年美女黄网站色大免费视频 | 免费人成在线观看网站 | 欧美zoozzooz性欧美 | 亚洲人成网站色7799 | 少妇太爽了在线观看 | 国产精品无码一区二区三区不卡 | 亚洲精品www久久久 | 欧美兽交xxxx×视频 | 日本精品高清一区二区 | 啦啦啦www在线观看免费视频 | 日本护士毛茸茸高潮 | 十八禁真人啪啪免费网站 | 国产精品二区一区二区aⅴ污介绍 | 亚洲男人av天堂午夜在 | 亚洲欧美日韩综合久久久 | 亚洲国产成人a精品不卡在线 | 全黄性性激高免费视频 | 亚洲欧美色中文字幕在线 | 亚洲va欧美va天堂v国产综合 | 亚洲天堂2017无码 | 日本大乳高潮视频在线观看 | 国产办公室秘书无码精品99 | 在线а√天堂中文官网 | 国产香蕉97碰碰久久人人 | 又大又黄又粗又爽的免费视频 | 国产精品人妻一区二区三区四 | 欧美精品国产综合久久 | 国产精品亚洲五月天高清 | 国产无遮挡又黄又爽免费视频 | 亚洲成a人片在线观看无码3d | 男女超爽视频免费播放 | 国内精品久久久久久中文字幕 | 色一情一乱一伦一区二区三欧美 | 天堂亚洲免费视频 | 久久久精品成人免费观看 | 奇米影视7777久久精品人人爽 | 领导边摸边吃奶边做爽在线观看 | 樱花草在线播放免费中文 | 久久精品人人做人人综合试看 | 十八禁视频网站在线观看 | 国产精品-区区久久久狼 | 亚洲熟悉妇女xxx妇女av | 色一情一乱一伦一视频免费看 | 性欧美videos高清精品 | 中文字幕无线码免费人妻 | 久久久久久久人妻无码中文字幕爆 | 丁香花在线影院观看在线播放 | 国产成人无码专区 | 国产亚av手机在线观看 | 欧美一区二区三区视频在线观看 | 中文无码成人免费视频在线观看 | 女人和拘做爰正片视频 | 影音先锋中文字幕无码 | 久久精品国产一区二区三区 | 好屌草这里只有精品 | 亚洲娇小与黑人巨大交 | 99精品无人区乱码1区2区3区 | 欧美人与动性行为视频 | 亚洲精品综合五月久久小说 | 国产亚洲精品久久久久久久久动漫 | 精品人妻av区 | 国产精品久久久久无码av色戒 | 中文字幕无线码 | 成 人 免费观看网站 | 丰满少妇人妻久久久久久 | 77777熟女视频在线观看 а天堂中文在线官网 | 精品国产福利一区二区 | 国产在线aaa片一区二区99 | 久久久久成人精品免费播放动漫 | 欧美老熟妇乱xxxxx | 亚洲中文无码av永久不收费 | 97久久超碰中文字幕 | 午夜精品久久久久久久久 | 国产99久久精品一区二区 | 牲欲强的熟妇农村老妇女视频 | 亚洲爆乳精品无码一区二区三区 | 日本丰满熟妇videos | 男女下面进入的视频免费午夜 | 东京热男人av天堂 | 亚洲自偷自偷在线制服 | 日本爽爽爽爽爽爽在线观看免 | 日产精品99久久久久久 | 国产xxx69麻豆国语对白 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 自拍偷自拍亚洲精品10p | 日本一区二区三区免费高清 | 亚洲精品一区二区三区在线观看 | 色欲人妻aaaaaaa无码 | 丰满肥臀大屁股熟妇激情视频 | 日韩av激情在线观看 | 麻豆国产人妻欲求不满谁演的 | 国产人妻精品一区二区三区不卡 | 77777熟女视频在线观看 а天堂中文在线官网 | 国产内射爽爽大片视频社区在线 | 四虎影视成人永久免费观看视频 | 亚洲人成无码网www | 国产亚洲精品久久久久久国模美 | 麻豆国产人妻欲求不满谁演的 | 99久久人妻精品免费一区 | 色欲av亚洲一区无码少妇 | 55夜色66夜色国产精品视频 | 少妇性l交大片欧洲热妇乱xxx | 无码精品国产va在线观看dvd | 疯狂三人交性欧美 | 亚洲国产精品无码一区二区三区 | 中文字幕乱妇无码av在线 | 亚洲日本va中文字幕 | 激情爆乳一区二区三区 | 国产情侣作爱视频免费观看 | 久久久中文字幕日本无吗 | 纯爱无遮挡h肉动漫在线播放 | 日本精品高清一区二区 | 国产精品久久久久久亚洲影视内衣 | 国产精品久免费的黄网站 | 性色欲网站人妻丰满中文久久不卡 | 荫蒂被男人添的好舒服爽免费视频 | 亚洲一区二区三区含羞草 | 国产做国产爱免费视频 | 天海翼激烈高潮到腰振不止 | aa片在线观看视频在线播放 | 亚洲欧美日韩成人高清在线一区 | 白嫩日本少妇做爰 | 4hu四虎永久在线观看 | 久久久久国色av免费观看性色 | 久久综合色之久久综合 | 国产精品手机免费 | 亚洲熟妇色xxxxx欧美老妇y | 偷窥村妇洗澡毛毛多 | 中文无码精品a∨在线观看不卡 | 国产精品-区区久久久狼 | 日韩精品无码一区二区中文字幕 | 无码av中文字幕免费放 | 日本丰满熟妇videos | 国产精品无码成人午夜电影 | 久久精品国产亚洲精品 | 亚洲中文字幕无码中文字在线 | 亚洲人成人无码网www国产 | 牲欲强的熟妇农村老妇女视频 | 四虎影视成人永久免费观看视频 | 久久人人爽人人爽人人片ⅴ | 少妇无套内谢久久久久 | 18禁止看的免费污网站 | 午夜精品一区二区三区在线观看 | 亚洲综合久久一区二区 | 国产午夜福利100集发布 | 蜜臀aⅴ国产精品久久久国产老师 | 18无码粉嫩小泬无套在线观看 | 国产精品无码成人午夜电影 | 天干天干啦夜天干天2017 | 综合网日日天干夜夜久久 | 亚洲色www成人永久网址 | 日韩精品成人一区二区三区 | 国产99久久精品一区二区 | 国产女主播喷水视频在线观看 | 久久久久国色av免费观看性色 | 午夜福利试看120秒体验区 | 日韩av激情在线观看 | 秋霞成人午夜鲁丝一区二区三区 | 国精产品一品二品国精品69xx | 乌克兰少妇xxxx做受 | 国产乱码精品一品二品 | 天堂无码人妻精品一区二区三区 | 亚洲の无码国产の无码影院 | 夜夜高潮次次欢爽av女 | 最近中文2019字幕第二页 | 成人女人看片免费视频放人 | 成人三级无码视频在线观看 | 亚洲精品无码人妻无码 | 青青青爽视频在线观看 | 无码人妻精品一区二区三区下载 | 国产人妻久久精品二区三区老狼 | 欧洲精品码一区二区三区免费看 | 俺去俺来也在线www色官网 | 国产成人无码区免费内射一片色欲 | 免费看男女做好爽好硬视频 | 久久99热只有频精品8 | 精品国产一区二区三区四区在线看 | 国产精品亚洲lv粉色 | 亚洲欧美中文字幕5发布 | 日本精品久久久久中文字幕 | 国产欧美熟妇另类久久久 | 成人亚洲精品久久久久软件 | 麻豆精品国产精华精华液好用吗 | 国产性生交xxxxx无码 | 国产 浪潮av性色四虎 | 97无码免费人妻超级碰碰夜夜 | 大胆欧美熟妇xx | 精品一二三区久久aaa片 | 男人的天堂2018无码 | 一个人看的www免费视频在线观看 | 精品人妻中文字幕有码在线 | 自拍偷自拍亚洲精品被多人伦好爽 | 欧美xxxx黑人又粗又长 | 久久亚洲中文字幕无码 | 国产午夜亚洲精品不卡下载 | 97色伦图片97综合影院 | 日韩精品无码一区二区中文字幕 | 国产在线aaa片一区二区99 | 色综合久久久无码中文字幕 | 国产两女互慰高潮视频在线观看 | 乌克兰少妇xxxx做受 | 亚洲啪av永久无码精品放毛片 | 亚洲 日韩 欧美 成人 在线观看 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 亚洲熟女一区二区三区 | 中文久久乱码一区二区 | 国产suv精品一区二区五 | 无码精品人妻一区二区三区av | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 成人精品一区二区三区中文字幕 | 中文毛片无遮挡高清免费 | 天天躁夜夜躁狠狠是什么心态 | 4hu四虎永久在线观看 | av无码久久久久不卡免费网站 | 人人妻人人澡人人爽欧美精品 | 无码av中文字幕免费放 | 97夜夜澡人人双人人人喊 | 国产精品.xx视频.xxtv | 国产一精品一av一免费 | 亚洲精品久久久久久久久久久 | 日本护士xxxxhd少妇 | 亚洲国产欧美日韩精品一区二区三区 | 中文字幕精品av一区二区五区 | 少妇被粗大的猛进出69影院 | 波多野结衣av一区二区全免费观看 | 永久免费观看美女裸体的网站 | 一区二区三区乱码在线 | 欧洲 | 午夜男女很黄的视频 | 久久久久人妻一区精品色欧美 | 最新国产乱人伦偷精品免费网站 | 理论片87福利理论电影 | 亚洲乱码中文字幕在线 | 欧洲精品码一区二区三区免费看 | 美女扒开屁股让男人桶 | 国产激情一区二区三区 | 亚洲国产欧美日韩精品一区二区三区 | 成人影院yy111111在线观看 | 国产精品人人爽人人做我的可爱 | 激情亚洲一区国产精品 | 亚洲国产精品一区二区第一页 | 久久午夜无码鲁丝片 | 少妇性俱乐部纵欲狂欢电影 | 在线观看国产午夜福利片 | 免费无码av一区二区 | 暴力强奷在线播放无码 | 欧美精品在线观看 | 内射老妇bbwx0c0ck | 久久久久成人片免费观看蜜芽 | 国产xxx69麻豆国语对白 | 国产真人无遮挡作爱免费视频 | 鲁鲁鲁爽爽爽在线视频观看 | 久久综合给合久久狠狠狠97色 | 水蜜桃av无码 | 大乳丰满人妻中文字幕日本 | 国产偷抇久久精品a片69 | 亚洲熟妇色xxxxx欧美老妇 | 久久婷婷五月综合色国产香蕉 | 未满成年国产在线观看 | 亚洲精品无码人妻无码 | 亚拍精品一区二区三区探花 | 牲欲强的熟妇农村老妇女视频 | 国产无遮挡又黄又爽免费视频 | 日本熟妇大屁股人妻 | 国产乱人伦av在线无码 | 精品国精品国产自在久国产87 | 欧美熟妇另类久久久久久不卡 | 欧美性黑人极品hd | 丰满少妇女裸体bbw | 国精产品一品二品国精品69xx | 樱花草在线社区www | 国产av一区二区三区最新精品 | 国产两女互慰高潮视频在线观看 | 漂亮人妻洗澡被公强 日日躁 | 久久无码专区国产精品s | 伊人色综合久久天天小片 | 国产欧美熟妇另类久久久 | 狠狠色丁香久久婷婷综合五月 | 婷婷五月综合缴情在线视频 | 国产精品久久久久久久9999 | 欧洲vodafone精品性 | 国产欧美精品一区二区三区 | 又大又黄又粗又爽的免费视频 | 亚洲va欧美va天堂v国产综合 | 小鲜肉自慰网站xnxx | 久久综合色之久久综合 | 思思久久99热只有频精品66 | 初尝人妻少妇中文字幕 | 国产精品沙发午睡系列 | 又大又紧又粉嫩18p少妇 | 婷婷丁香五月天综合东京热 | 亚欧洲精品在线视频免费观看 | 亚洲成色www久久网站 | ass日本丰满熟妇pics | www一区二区www免费 | 天堂久久天堂av色综合 | 人妻无码αv中文字幕久久琪琪布 | 日韩无套无码精品 | 久精品国产欧美亚洲色aⅴ大片 | 国产两女互慰高潮视频在线观看 | 97se亚洲精品一区 | 亚洲精品综合一区二区三区在线 | 色婷婷av一区二区三区之红樱桃 | 丰满人妻一区二区三区免费视频 | 久久亚洲日韩精品一区二区三区 | 亚洲综合在线一区二区三区 | 人人妻人人澡人人爽人人精品浪潮 | 高清不卡一区二区三区 | 无码av中文字幕免费放 | 麻豆果冻传媒2021精品传媒一区下载 | 亚洲日韩中文字幕在线播放 | 亚洲欧美综合区丁香五月小说 | 欧美性黑人极品hd | 日韩精品无码一区二区中文字幕 | 老子影院午夜精品无码 | 成人无码精品1区2区3区免费看 | 久久这里只有精品视频9 | 捆绑白丝粉色jk震动捧喷白浆 | 67194成是人免费无码 | 极品尤物被啪到呻吟喷水 | 亚洲国产成人av在线观看 | 天天躁日日躁狠狠躁免费麻豆 | 亲嘴扒胸摸屁股激烈网站 | 精品久久久久久人妻无码中文字幕 | 久久久久久a亚洲欧洲av冫 | 图片区 小说区 区 亚洲五月 | 亚洲日韩av一区二区三区中文 | 亚洲无人区一区二区三区 | 日本丰满护士爆乳xxxx | 国产精品无码永久免费888 | 欧美黑人巨大xxxxx | 性生交大片免费看女人按摩摩 | 欧美阿v高清资源不卡在线播放 | 激情亚洲一区国产精品 | 国产成人精品必看 | 久久国语露脸国产精品电影 | 偷窥日本少妇撒尿chinese | 亚洲国产精品美女久久久久 | 亚洲国产午夜精品理论片 | av无码电影一区二区三区 | 人人妻人人藻人人爽欧美一区 | 亚洲一区二区三区四区 | 色狠狠av一区二区三区 | 亚洲aⅴ无码成人网站国产app | 色婷婷综合激情综在线播放 | 毛片内射-百度 | 在线看片无码永久免费视频 | 久久精品99久久香蕉国产色戒 | 狠狠色噜噜狠狠狠7777奇米 | 成人影院yy111111在线观看 | 性色av无码免费一区二区三区 | 日本大香伊一区二区三区 | 无码av免费一区二区三区试看 | 欧美成人免费全部网站 | 欧美性生交活xxxxxdddd | 亚洲中文无码av永久不收费 | 水蜜桃av无码 | 伊人色综合久久天天小片 | yw尤物av无码国产在线观看 | 麻豆精品国产精华精华液好用吗 | 亚洲人成网站免费播放 | 麻豆蜜桃av蜜臀av色欲av | 精品无人国产偷自产在线 | 亚洲最大成人网站 | 一个人免费观看的www视频 | 国产精品第一区揄拍无码 | 乱码午夜-极国产极内射 | 国产亚洲日韩欧美另类第八页 | 日韩欧美成人免费观看 | 亚洲成熟女人毛毛耸耸多 | 国精产品一品二品国精品69xx | 亚洲精品一区二区三区在线观看 | 国产精品va在线观看无码 | 亚洲人亚洲人成电影网站色 | 亚洲欧洲日本无在线码 | 亚洲午夜无码久久 | 亚洲欧美精品aaaaaa片 | 国产熟妇另类久久久久 | 免费观看又污又黄的网站 | 日韩av无码一区二区三区不卡 | 欧美喷潮久久久xxxxx | 精品无码av一区二区三区 | 日韩av激情在线观看 | 无码一区二区三区在线观看 | 国产亚洲精品精品国产亚洲综合 | 亚洲精品午夜国产va久久成人 | 无套内谢的新婚少妇国语播放 | 日韩av无码中文无码电影 | 日韩精品无码一本二本三本色 | 亚欧洲精品在线视频免费观看 | 东京无码熟妇人妻av在线网址 | 精品夜夜澡人妻无码av蜜桃 | 丰满人妻翻云覆雨呻吟视频 | 午夜精品久久久久久久久 | 精品一区二区不卡无码av | 欧美大屁股xxxxhd黑色 | 九月婷婷人人澡人人添人人爽 | 黄网在线观看免费网站 | 色一情一乱一伦一区二区三欧美 | 精品国产国产综合精品 | 无码人妻出轨黑人中文字幕 | 国产人妻精品一区二区三区不卡 | 色偷偷人人澡人人爽人人模 | 亚洲一区二区三区国产精华液 | 爆乳一区二区三区无码 | 老熟女重囗味hdxx69 | 亚洲最大成人网站 | 国产成人精品久久亚洲高清不卡 | 成人无码精品一区二区三区 | 久久精品丝袜高跟鞋 | 国色天香社区在线视频 | 国产亚洲精品久久久久久 | 精品欧洲av无码一区二区三区 | 女人被男人爽到呻吟的视频 | 国产精品无码成人午夜电影 | 欧美熟妇另类久久久久久不卡 | 一二三四社区在线中文视频 | 久在线观看福利视频 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 波多野结衣一区二区三区av免费 | 在线欧美精品一区二区三区 | 三级4级全黄60分钟 | 人妻与老人中文字幕 | 国产香蕉97碰碰久久人人 | a片在线免费观看 | 亚洲人成人无码网www国产 | 99久久久无码国产精品免费 | 国产成人精品无码播放 | 日日摸日日碰夜夜爽av | 一区二区三区高清视频一 | 妺妺窝人体色www婷婷 | 国内综合精品午夜久久资源 | 欧美丰满老熟妇xxxxx性 | 精品水蜜桃久久久久久久 | 人妻少妇被猛烈进入中文字幕 | 亚洲精品鲁一鲁一区二区三区 | 国产精品亚洲一区二区三区喷水 | 国产av剧情md精品麻豆 | 亚洲中文字幕在线无码一区二区 | 国产亲子乱弄免费视频 | 日日橹狠狠爱欧美视频 | 国産精品久久久久久久 | 国产乱码精品一品二品 | 亚洲精品久久久久久一区二区 | 好屌草这里只有精品 | 国产免费久久精品国产传媒 | 亚洲经典千人经典日产 | 欧美丰满熟妇xxxx性ppx人交 | 少妇无套内谢久久久久 | 亚洲s色大片在线观看 | 国产人妻精品午夜福利免费 | 人妻aⅴ无码一区二区三区 | 精品无码一区二区三区的天堂 | 亚洲国产欧美日韩精品一区二区三区 | 国产偷自视频区视频 | 国产亚洲欧美在线专区 | 啦啦啦www在线观看免费视频 | 2020最新国产自产精品 | 亚洲国产精品久久人人爱 | 国产av无码专区亚洲a∨毛片 | 国精品人妻无码一区二区三区蜜柚 | 欧美 日韩 人妻 高清 中文 | 狠狠综合久久久久综合网 | 亚洲国产精品无码久久久久高潮 | 国产人成高清在线视频99最全资源 | 中文毛片无遮挡高清免费 | 99久久久无码国产aaa精品 | 亚洲s码欧洲m码国产av | 又粗又大又硬又长又爽 | 大胆欧美熟妇xx | 欧美怡红院免费全部视频 | 好男人www社区 | 成人影院yy111111在线观看 | 精品人妻人人做人人爽夜夜爽 | 国产亚洲欧美在线专区 | 在线观看欧美一区二区三区 | 亚洲自偷自拍另类第1页 | 夜夜夜高潮夜夜爽夜夜爰爰 | 日本大香伊一区二区三区 | aⅴ亚洲 日韩 色 图网站 播放 | а√资源新版在线天堂 | 麻豆md0077饥渴少妇 | 曰韩无码二三区中文字幕 | 精品夜夜澡人妻无码av蜜桃 | 麻花豆传媒剧国产免费mv在线 | 天天躁日日躁狠狠躁免费麻豆 | 国产亚洲日韩欧美另类第八页 | 国产无套内射久久久国产 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产婷婷色一区二区三区在线 | 亚洲熟熟妇xxxx | 在线观看欧美一区二区三区 | 亚洲国产成人a精品不卡在线 | 国产精品高潮呻吟av久久 | 国产午夜亚洲精品不卡下载 | 精品国产av色一区二区深夜久久 | 国产成人无码av一区二区 | 女人高潮内射99精品 | 亚洲国产欧美日韩精品一区二区三区 | 爽爽影院免费观看 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | √天堂中文官网8在线 | 国产精品亚洲专区无码不卡 | 亚洲日韩一区二区三区 | 亚洲综合无码久久精品综合 | 激情内射亚州一区二区三区爱妻 | 精品水蜜桃久久久久久久 | 久久天天躁夜夜躁狠狠 | 欧美日韩一区二区三区自拍 | 日日麻批免费40分钟无码 | 妺妺窝人体色www婷婷 | 丰满人妻翻云覆雨呻吟视频 | 四虎影视成人永久免费观看视频 | 国产综合色产在线精品 | 无码人妻精品一区二区三区不卡 | 亚洲va中文字幕无码久久不卡 | 女高中生第一次破苞av | 亚洲日本va午夜在线电影 | 亚洲性无码av中文字幕 | 欧美日韩综合一区二区三区 | 国产精品国产自线拍免费软件 | 全球成人中文在线 | 自拍偷自拍亚洲精品10p | 给我免费的视频在线观看 | 亚洲日韩av一区二区三区中文 | 人人妻人人澡人人爽欧美一区九九 | 丰满人妻翻云覆雨呻吟视频 | 久久综合色之久久综合 | 巨爆乳无码视频在线观看 | 久久精品中文字幕大胸 | 久久久久国色av免费观看性色 | а√资源新版在线天堂 | 亚洲综合在线一区二区三区 | 亚洲娇小与黑人巨大交 | 思思久久99热只有频精品66 | 亚洲熟女一区二区三区 | 久久久精品国产sm最大网站 | 成人免费无码大片a毛片 | 久久精品国产一区二区三区肥胖 | 少妇无码吹潮 | 少妇高潮一区二区三区99 | 国产高潮视频在线观看 | 牲欲强的熟妇农村老妇女视频 | 好屌草这里只有精品 | 中文字幕乱码中文乱码51精品 | 一区二区三区高清视频一 | 又湿又紧又大又爽a视频国产 | 亚洲中文字幕在线观看 | 成人亚洲精品久久久久软件 | 天堂无码人妻精品一区二区三区 | 国内综合精品午夜久久资源 | 日本精品人妻无码免费大全 | 国产亚洲精品精品国产亚洲综合 | 人妻尝试又大又粗久久 | 亚洲成a人一区二区三区 | 免费无码肉片在线观看 | 玩弄少妇高潮ⅹxxxyw | 精品国产一区二区三区四区在线看 | 麻豆蜜桃av蜜臀av色欲av | 女人被男人爽到呻吟的视频 | 久久午夜无码鲁丝片午夜精品 | 亚洲成av人在线观看网址 | 国产九九九九九九九a片 | 亚洲色欲久久久综合网东京热 | 午夜精品久久久久久久久 | 老熟妇乱子伦牲交视频 | 丰满肥臀大屁股熟妇激情视频 | 国产九九九九九九九a片 | 国产人妻久久精品二区三区老狼 | 免费男性肉肉影院 | 国产精品久久精品三级 | 在线观看免费人成视频 | 亚拍精品一区二区三区探花 | 国产电影无码午夜在线播放 | 无码av岛国片在线播放 | 色欲综合久久中文字幕网 | 精品久久久无码人妻字幂 | 亚洲啪av永久无码精品放毛片 | 精品成人av一区二区三区 | 亚洲第一网站男人都懂 | 久久精品女人的天堂av | 国产精品久久久久久久9999 | 强伦人妻一区二区三区视频18 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 乱码午夜-极国产极内射 | 亚洲另类伦春色综合小说 | 亚洲成av人综合在线观看 | 丰满人妻一区二区三区免费视频 | 亚洲人成影院在线观看 | 99re在线播放 | 久久国产精品精品国产色婷婷 | 国产精品第一区揄拍无码 | 水蜜桃av无码 | 西西人体www44rt大胆高清 | 99久久精品无码一区二区毛片 | 国产精品久久久久久亚洲影视内衣 | 亚洲精品国产a久久久久久 | 精品偷自拍另类在线观看 | 日韩视频 中文字幕 视频一区 | 国产香蕉尹人视频在线 | 亚洲精品国产a久久久久久 | 无码免费一区二区三区 | 色偷偷人人澡人人爽人人模 | 人人妻人人澡人人爽人人精品 | 亚洲成av人影院在线观看 | 久久精品国产一区二区三区肥胖 | 67194成是人免费无码 | 午夜成人1000部免费视频 | 欧美激情综合亚洲一二区 | 欧美日韩在线亚洲综合国产人 | 欧美亚洲日韩国产人成在线播放 | 成人无码影片精品久久久 | 亚洲国产av美女网站 | 无码人妻精品一区二区三区不卡 | 国产亚洲精品久久久久久大师 | а天堂中文在线官网 | 丰满人妻一区二区三区免费视频 | 欧美激情内射喷水高潮 | 国产人成高清在线视频99最全资源 | 欧美人与善在线com | 一本久久伊人热热精品中文字幕 | 免费国产成人高清在线观看网站 | 午夜嘿嘿嘿影院 | 动漫av一区二区在线观看 | 国产一区二区三区精品视频 | 无码福利日韩神码福利片 | 99久久久无码国产精品免费 | 宝宝好涨水快流出来免费视频 | 精品国产一区二区三区四区 | 亚洲最大成人网站 | 精品国产一区二区三区四区在线看 | 欧美丰满熟妇xxxx性ppx人交 | 麻豆国产人妻欲求不满 | 亚洲综合伊人久久大杳蕉 | 精品人人妻人人澡人人爽人人 | 欧洲熟妇精品视频 | 波多野结衣高清一区二区三区 | 国产麻豆精品精东影业av网站 | 久久久久久a亚洲欧洲av冫 | 人人妻在人人 | 国产精品第一国产精品 | 久久99热只有频精品8 | 久久久久亚洲精品男人的天堂 | 国产精品久久国产三级国 | 国产 浪潮av性色四虎 | 大地资源网第二页免费观看 | 无码人妻精品一区二区三区不卡 | 色情久久久av熟女人妻网站 | 中文字幕日韩精品一区二区三区 | 玩弄少妇高潮ⅹxxxyw | 夜先锋av资源网站 | 18禁黄网站男男禁片免费观看 | 影音先锋中文字幕无码 | 99国产精品白浆在线观看免费 | 久激情内射婷内射蜜桃人妖 | 嫩b人妻精品一区二区三区 | 亚洲国产精品久久久天堂 | 亚洲乱亚洲乱妇50p | 国产人成高清在线视频99最全资源 | 色 综合 欧美 亚洲 国产 | 亚洲高清偷拍一区二区三区 | 精品少妇爆乳无码av无码专区 | 午夜精品一区二区三区在线观看 | 久久99精品久久久久婷婷 | 色 综合 欧美 亚洲 国产 | 精品国偷自产在线 | 青青草原综合久久大伊人精品 | 波多野结衣av一区二区全免费观看 | 精品国产青草久久久久福利 | 给我免费的视频在线观看 | 国产va免费精品观看 | 久久综合激激的五月天 | 国产无套内射久久久国产 | 亚洲综合久久一区二区 | 国产精品视频免费播放 | 亚洲国产精品美女久久久久 | 成人片黄网站色大片免费观看 | 伊人色综合久久天天小片 | 亚洲中文无码av永久不收费 | 无码播放一区二区三区 | 国产成人av免费观看 | 亚洲成av人片在线观看无码不卡 | 欧美熟妇另类久久久久久不卡 | 激情综合激情五月俺也去 | 网友自拍区视频精品 | 日韩视频 中文字幕 视频一区 | 女人和拘做爰正片视频 | 国产超碰人人爽人人做人人添 | 午夜免费福利小电影 | 99久久久无码国产aaa精品 | 最近中文2019字幕第二页 | 欧美人与动性行为视频 | 国产精品久久久久久亚洲毛片 | 亚洲人成人无码网www国产 | 精品午夜福利在线观看 | 亚洲精品国产a久久久久久 | 国产精品久久国产精品99 | 99麻豆久久久国产精品免费 | 国产亚洲美女精品久久久2020 | 国产午夜无码精品免费看 | 欧美自拍另类欧美综合图片区 | 色婷婷综合激情综在线播放 | 99久久精品日本一区二区免费 | 人妻体内射精一区二区三四 | 无码人妻av免费一区二区三区 | 在线 国产 欧美 亚洲 天堂 | 久久久久99精品成人片 | 精品国产一区av天美传媒 | 亚洲熟妇色xxxxx欧美老妇y | 色婷婷久久一区二区三区麻豆 | 国产后入清纯学生妹 | 伊人久久大香线焦av综合影院 | 大肉大捧一进一出视频出来呀 | 漂亮人妻洗澡被公强 日日躁 | 波多野结衣av一区二区全免费观看 | 欧美亚洲国产一区二区三区 | 免费观看黄网站 | 久久亚洲日韩精品一区二区三区 | 天堂久久天堂av色综合 | 国产精品成人av在线观看 | 4hu四虎永久在线观看 | 亚洲国产综合无码一区 | 日日天干夜夜狠狠爱 | 美女扒开屁股让男人桶 | 伊人久久大香线蕉av一区二区 | 国产精品无码一区二区三区不卡 | 国产乡下妇女做爰 | 老司机亚洲精品影院 | 乱中年女人伦av三区 | 青青青爽视频在线观看 | 色诱久久久久综合网ywww | 青草青草久热国产精品 | 欧洲欧美人成视频在线 | 欧美性猛交xxxx富婆 | 四虎影视成人永久免费观看视频 | 亚洲男女内射在线播放 | 大屁股大乳丰满人妻 | 妺妺窝人体色www在线小说 | 小sao货水好多真紧h无码视频 | 99久久久无码国产精品免费 | 国产精品久久久 | 色欲久久久天天天综合网精品 | 熟妇人妻中文av无码 | 激情爆乳一区二区三区 | 亚洲中文字幕在线观看 | 亚洲精品综合一区二区三区在线 | 国产9 9在线 | 中文 | 欧洲vodafone精品性 | 十八禁真人啪啪免费网站 | 亚洲色www成人永久网址 | 无码人妻精品一区二区三区不卡 | 精品国产一区二区三区四区 | 中文字幕乱码亚洲无线三区 | 亚洲国产精品一区二区第一页 | 欧美日韩人成综合在线播放 | 国产在线一区二区三区四区五区 | 久久久久久国产精品无码下载 | 99麻豆久久久国产精品免费 | aⅴ在线视频男人的天堂 | 欧美兽交xxxx×视频 | 久久精品国产大片免费观看 | 精品厕所偷拍各类美女tp嘘嘘 | 成人试看120秒体验区 | 无遮无挡爽爽免费视频 | 欧洲熟妇精品视频 | 中文字幕av日韩精品一区二区 | 欧美日韩一区二区综合 | 亚洲综合精品香蕉久久网 | 亚洲精品中文字幕乱码 | 欧美自拍另类欧美综合图片区 | 97夜夜澡人人爽人人喊中国片 | 免费无码的av片在线观看 | 国产乡下妇女做爰 | 亚洲区欧美区综合区自拍区 | 亚洲中文字幕久久无码 | 全球成人中文在线 | 欧美亚洲日韩国产人成在线播放 | 成人精品一区二区三区中文字幕 | 三上悠亚人妻中文字幕在线 | 成在人线av无码免观看麻豆 | 亚洲欧美国产精品专区久久 | 2019午夜福利不卡片在线 | 国产精品二区一区二区aⅴ污介绍 | 鲁鲁鲁爽爽爽在线视频观看 | 国产精品久久久久久亚洲影视内衣 | 中文字幕人妻丝袜二区 | 国产乱人伦av在线无码 | 亚洲大尺度无码无码专区 | 初尝人妻少妇中文字幕 | 亚洲乱码国产乱码精品精 | 亚洲精品一区二区三区在线 | 亚洲精品成a人在线观看 | 帮老师解开蕾丝奶罩吸乳网站 | 曰韩少妇内射免费播放 | 免费无码午夜福利片69 | 丰满少妇高潮惨叫视频 | 97精品国产97久久久久久免费 | 性欧美疯狂xxxxbbbb | 性史性农村dvd毛片 | 黑人巨大精品欧美黑寡妇 | 超碰97人人射妻 | 国产精品高潮呻吟av久久4虎 | 水蜜桃av无码 | 国产亚洲视频中文字幕97精品 | 国产舌乚八伦偷品w中 | 人妻中文无码久热丝袜 | 奇米影视888欧美在线观看 | 国产国产精品人在线视 | 男人和女人高潮免费网站 | 一本久久伊人热热精品中文字幕 | 国产一区二区三区日韩精品 | 成人无码精品一区二区三区 | 中文字幕乱码人妻无码久久 | 国内少妇偷人精品视频 | 福利一区二区三区视频在线观看 | 亚洲日韩av一区二区三区四区 | 狠狠色噜噜狠狠狠7777奇米 | 日韩精品乱码av一区二区 | 亚洲国产成人av在线观看 | 国内精品人妻无码久久久影院蜜桃 | 人妻体内射精一区二区三四 | 欧美日本免费一区二区三区 | 亚洲日本在线电影 | 久久午夜无码鲁丝片秋霞 | 精品久久久无码人妻字幂 | 少女韩国电视剧在线观看完整 | 亚洲综合另类小说色区 | 好男人www社区 | 国产午夜无码精品免费看 | 亚洲国产av美女网站 | 成熟女人特级毛片www免费 | 国产精品无码成人午夜电影 | 午夜福利一区二区三区在线观看 | 久久精品国产一区二区三区肥胖 | 国语精品一区二区三区 | 国产麻豆精品一区二区三区v视界 | 东京热一精品无码av | 青青草原综合久久大伊人精品 | 亚洲精品中文字幕乱码 | 日本精品少妇一区二区三区 | 蜜臀av无码人妻精品 | 欧美人与禽zoz0性伦交 | 丝袜人妻一区二区三区 | 亚洲一区二区观看播放 | 欧美猛少妇色xxxxx | 日日摸日日碰夜夜爽av | 国产av无码专区亚洲a∨毛片 | 精品国产福利一区二区 | 国产免费久久精品国产传媒 | 麻豆精品国产精华精华液好用吗 | 亚洲熟妇色xxxxx亚洲 | 成人无码精品一区二区三区 | 男女爱爱好爽视频免费看 | 国产精品国产自线拍免费软件 | 日产精品99久久久久久 | 好爽又高潮了毛片免费下载 | 日本乱偷人妻中文字幕 | 一本色道久久综合狠狠躁 | 亚洲精品鲁一鲁一区二区三区 | 高潮毛片无遮挡高清免费 | 国产精品久久久一区二区三区 | 7777奇米四色成人眼影 | 国产精品无码一区二区桃花视频 | 久久久久99精品国产片 | 亚洲成av人片在线观看无码不卡 | 女人被男人躁得好爽免费视频 | 无码av中文字幕免费放 | 久久zyz资源站无码中文动漫 | 熟女少妇人妻中文字幕 | 国产一区二区三区日韩精品 | 四十如虎的丰满熟妇啪啪 | 亚洲欧美日韩综合久久久 | 日产精品高潮呻吟av久久 | 国产成人综合在线女婷五月99播放 | 狂野欧美性猛xxxx乱大交 | 亚洲精品美女久久久久久久 | 亚洲大尺度无码无码专区 | 99久久久无码国产精品免费 | 一本久久伊人热热精品中文字幕 | 亚洲国产欧美在线成人 | 亚洲va欧美va天堂v国产综合 | 亚洲国产精品一区二区美利坚 | 国产极品美女高潮无套在线观看 | 亚洲 激情 小说 另类 欧美 | 天海翼激烈高潮到腰振不止 | 97无码免费人妻超级碰碰夜夜 | 99久久精品日本一区二区免费 | 图片区 小说区 区 亚洲五月 | 亚洲小说图区综合在线 | 丝袜美腿亚洲一区二区 | 久久久国产一区二区三区 | 在线观看免费人成视频 | 97夜夜澡人人双人人人喊 | 国产免费无码一区二区视频 | 午夜男女很黄的视频 | 日韩精品a片一区二区三区妖精 | 白嫩日本少妇做爰 | 老司机亚洲精品影院无码 | 午夜熟女插插xx免费视频 | 精品亚洲成av人在线观看 | v一区无码内射国产 | 国产熟女一区二区三区四区五区 | 精品久久8x国产免费观看 | 大肉大捧一进一出视频出来呀 | 亚洲男人av香蕉爽爽爽爽 | 白嫩日本少妇做爰 | 丰满岳乱妇在线观看中字无码 | 又大又硬又爽免费视频 | 日韩av无码一区二区三区 | 99久久人妻精品免费二区 | 亚洲国产日韩a在线播放 | 久久无码人妻影院 | 无套内谢老熟女 | 76少妇精品导航 | 国产特级毛片aaaaaa高潮流水 | 国产精品亚洲一区二区三区喷水 | 欧美 日韩 亚洲 在线 | 亚洲中文字幕成人无码 | 久久久www成人免费毛片 | 色婷婷久久一区二区三区麻豆 | 兔费看少妇性l交大片免费 | 日本爽爽爽爽爽爽在线观看免 | www国产精品内射老师 | 亚洲中文字幕无码中文字在线 | 亚洲中文字幕无码一久久区 | 免费国产成人高清在线观看网站 | 国产高清av在线播放 | 啦啦啦www在线观看免费视频 | 国产精品无码一区二区三区不卡 | 午夜丰满少妇性开放视频 | 领导边摸边吃奶边做爽在线观看 | 精品乱子伦一区二区三区 | 亚洲日本一区二区三区在线 | 国产特级毛片aaaaaa高潮流水 | 成年美女黄网站色大免费视频 | 日本一卡2卡3卡四卡精品网站 | 又大又硬又爽免费视频 | 亚洲小说图区综合在线 | 小鲜肉自慰网站xnxx | 成人女人看片免费视频放人 | 成熟女人特级毛片www免费 | 丰满岳乱妇在线观看中字无码 | 性色欲情网站iwww九文堂 | 自拍偷自拍亚洲精品10p | 中文字幕人妻无码一夲道 | 国产av久久久久精东av | 亚洲一区二区三区国产精华液 | 在线观看免费人成视频 | 最近免费中文字幕中文高清百度 | 曰韩无码二三区中文字幕 | 99久久精品午夜一区二区 | 亚洲乱码日产精品bd | 亚洲色欲久久久综合网东京热 | 精品成人av一区二区三区 | 国产精品99爱免费视频 | 国产乱人伦偷精品视频 | 欧美人与动性行为视频 | 婷婷色婷婷开心五月四房播播 | 久在线观看福利视频 | 男女猛烈xx00免费视频试看 | 中文字幕无码乱人伦 | 伊人久久大香线蕉av一区二区 | 亚洲欧洲中文日韩av乱码 | 狠狠噜狠狠狠狠丁香五月 | 在线视频网站www色 | 亚洲成a人片在线观看日本 | 亚洲精品欧美二区三区中文字幕 | 精品国产一区二区三区av 性色 | 日本一区二区更新不卡 | 国产深夜福利视频在线 | 精品日本一区二区三区在线观看 | 欧美 日韩 亚洲 在线 | 成人欧美一区二区三区 | 久久这里只有精品视频9 | 亚洲一区二区三区无码久久 | 国产疯狂伦交大片 | 欧美日韩一区二区免费视频 | 色婷婷欧美在线播放内射 | 国内老熟妇对白xxxxhd | 精品无码国产自产拍在线观看蜜 | 国产精品永久免费视频 | 99久久精品国产一区二区蜜芽 | 风流少妇按摩来高潮 | 自拍偷自拍亚洲精品10p | 国产av一区二区三区最新精品 | 99麻豆久久久国产精品免费 | 欧美性生交xxxxx久久久 | 熟女体下毛毛黑森林 | 中文字幕无码免费久久99 | 99精品国产综合久久久久五月天 | 强奷人妻日本中文字幕 | 中文字幕无码av波多野吉衣 | 精品aⅴ一区二区三区 | 丝袜美腿亚洲一区二区 | 男女作爱免费网站 | 免费看男女做好爽好硬视频 | 成人精品视频一区二区三区尤物 | 婷婷五月综合缴情在线视频 | 18黄暴禁片在线观看 | 青春草在线视频免费观看 | 国产猛烈高潮尖叫视频免费 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 色一情一乱一伦一区二区三欧美 | 国产在热线精品视频 | 中文亚洲成a人片在线观看 | 久久久久久亚洲精品a片成人 | 亚洲一区二区三区偷拍女厕 | 亚洲精品一区国产 | 爱做久久久久久 | 永久免费精品精品永久-夜色 | 夜夜夜高潮夜夜爽夜夜爰爰 | 日产国产精品亚洲系列 | 亚洲国产精品久久人人爱 | 国产在线aaa片一区二区99 | 国产亚洲人成a在线v网站 | 奇米影视888欧美在线观看 | 97夜夜澡人人爽人人喊中国片 | 在线播放无码字幕亚洲 | 成人毛片一区二区 | 亚洲区小说区激情区图片区 | 人妻少妇精品无码专区二区 | 亚洲综合色区中文字幕 | 国产精品嫩草久久久久 | 欧美丰满熟妇xxxx | 国产人妻久久精品二区三区老狼 | 亚洲色大成网站www | 99国产欧美久久久精品 | 国内精品久久毛片一区二区 | 97久久超碰中文字幕 | 天堂а√在线地址中文在线 | 亚洲va欧美va天堂v国产综合 | 亚洲精品国产a久久久久久 | 日本熟妇大屁股人妻 | 曰韩无码二三区中文字幕 | 亚洲va中文字幕无码久久不卡 | 成人免费视频在线观看 | 麻豆精品国产精华精华液好用吗 | 精品夜夜澡人妻无码av蜜桃 | 女人被爽到呻吟gif动态图视看 | aⅴ在线视频男人的天堂 | 国产精品美女久久久 | 国产无遮挡又黄又爽又色 | 黑森林福利视频导航 | 亚洲精品国产品国语在线观看 | 荫蒂添的好舒服视频囗交 | 欧美性生交xxxxx久久久 | 免费人成在线观看网站 | 少妇久久久久久人妻无码 | 国产人妻精品一区二区三区 | 日韩人妻无码一区二区三区久久99 | 中文无码伦av中文字幕 | 中文字幕无码av激情不卡 | 亚洲国产精品美女久久久久 | 无码吃奶揉捏奶头高潮视频 | 国内老熟妇对白xxxxhd | 国产精品无码成人午夜电影 | 国产极品视觉盛宴 | 熟女俱乐部五十路六十路av | 中国女人内谢69xxxx | 97夜夜澡人人爽人人喊中国片 | 白嫩日本少妇做爰 | 久久综合九色综合欧美狠狠 | 国产精品第一区揄拍无码 | 成人欧美一区二区三区黑人 | 水蜜桃色314在线观看 | 欧美zoozzooz性欧美 | 色一情一乱一伦 | 国产av一区二区三区最新精品 | 欧美丰满熟妇xxxx性ppx人交 | 日日摸天天摸爽爽狠狠97 | www国产亚洲精品久久久日本 | 老太婆性杂交欧美肥老太 | 午夜免费福利小电影 | 中文字幕无码乱人伦 | 丝袜 中出 制服 人妻 美腿 | 久久国产自偷自偷免费一区调 | 中文字幕乱码亚洲无线三区 | 国产精品二区一区二区aⅴ污介绍 | 亚洲自偷自偷在线制服 | 青春草在线视频免费观看 | 国产明星裸体无码xxxx视频 | 国内老熟妇对白xxxxhd | 久久久久久av无码免费看大片 | 亚洲成av人综合在线观看 | 日本免费一区二区三区最新 | 国内精品人妻无码久久久影院蜜桃 | 亚洲精品国产品国语在线观看 | 国产色视频一区二区三区 | 久久五月精品中文字幕 | 无码吃奶揉捏奶头高潮视频 | 久久午夜无码鲁丝片秋霞 | 在教室伦流澡到高潮hnp视频 | 亚洲人成人无码网www国产 | 亚洲欧美日韩国产精品一区二区 | 亚洲热妇无码av在线播放 | 欧美xxxx黑人又粗又长 |