<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聊聊Spring事務(wù)失效的12種場景,太坑了!

          共 21562字,需瀏覽 44分鐘

           ·

          2021-09-17 13:10

          前言

          對于從事java開發(fā)工作的同學(xué)來說,spring的事務(wù)肯定再熟悉不過了。

          在某些業(yè)務(wù)場景下,如果一個請求中,需要同時寫入多張表的數(shù)據(jù)。為了保證操作的原子性(要么同時成功,要么同時失敗),避免數(shù)據(jù)不一致的情況,我們一般都會用到spring事務(wù)。

          確實,spring事務(wù)用起來賊爽,就用一個簡單的注解:@Transactional,就能輕松搞定事務(wù)。我猜大部分小伙伴也是這樣用的,而且一直用一直爽。

          但如果你使用不當,它也會坑你于無形。

          今天我們就一起聊聊,事務(wù)失效的一些場景,說不定你已經(jīng)中招了。不信,讓我們一起看看。

          一 事務(wù)不生效

          1.訪問權(quán)限問題

          眾所周知,java的訪問權(quán)限主要有四種:private、default、protected、public,它們的權(quán)限從左到右,依次變大。

          但如果我們在開發(fā)過程中,把有某些事務(wù)方法,定義了錯誤的訪問權(quán)限,就會導(dǎo)致事務(wù)功能出問題,例如:

          @Service
          public class UserService {
              
              @Transactional
              private void add(UserModel userModel) {
                   saveData(userModel);
                   updateData(userModel);
              }
          }

          我們可以看到add方法的訪問權(quán)限被定義成了private,這樣會導(dǎo)致事務(wù)失效,spring要求被代理方法必須是public的。

          說白了,在AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法中有個判斷,如果目標方法不是public,則TransactionAttribute返回null,即不支持事務(wù)。

          protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
              // Don't allow no-public methods as required.
              if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
                return null;
              }

              // The method may be on an interface, but we need attributes from the target class.
              // If the target class is null, the method will be unchanged.
              Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

              // First try is the method in the target class.
              TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
              if (txAttr != null) {
                return txAttr;
              }

              // Second try is the transaction attribute on the target class.
              txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
              if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
                return txAttr;
              }

              if (specificMethod != method) {
                // Fallback is to look at the original method.
                txAttr = findTransactionAttribute(method);
                if (txAttr != null) {
                  return txAttr;
                }
                // Last fallback is the class of the original method.
                txAttr = findTransactionAttribute(method.getDeclaringClass());
                if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
                  return txAttr;
                }
              }
              return null;
            }

          也就是說,如果我們自定義的事務(wù)方法(即目標方法),它的訪問權(quán)限不是public,而是private、default或protected的話,spring則不會提供事務(wù)功能。

          2. 方法用final修飾

          有時候,某個方法不想被子類重新,這時可以將該方法定義成final的。普通方法這樣定義是沒問題的,但如果將事務(wù)方法定義成final,例如:

          @Service
          public class UserService {

              @Transactional
              public final void add(UserModel userModel){
                  saveData(userModel);
                  updateData(userModel);
              }
          }

          我們可以看到add方法被定義成了final的,這樣會導(dǎo)致事務(wù)失效。

          為什么?

          如果你看過spring事務(wù)的源碼,可能會知道spring事務(wù)底層使用了aop,也就是通過jdk動態(tài)代理或者cglib,幫我們生成了代理類,在代理類中實現(xiàn)的事務(wù)功能。

          但如果某個方法用final修飾了,那么在它的代理類中,就無法重寫該方法,而添加事務(wù)功能。

          注意:如果某個方法是static的,同樣無法通過動態(tài)代理,變成事務(wù)方法。

          3.方法內(nèi)部調(diào)用

          有時候我們需要在某個Service類的某個方法中,調(diào)用另外一個事務(wù)方法,比如:

          @Service
          public class UserService {

              @Autowired
              private UserMapper userMapper;

              @Transactional
              public void add(UserModel userModel) {
                  userMapper.insertUser(userModel);
                  updateStatus(userModel);
              }

              @Transactional
              public void updateStatus(UserModel userModel) {
                  doSameThing();
              }
          }

          我們看到在事務(wù)方法add中,直接調(diào)用事務(wù)方法updateStatus。從前面介紹的內(nèi)容可以知道,updateStatus方法擁有事務(wù)的能力是因為spring aop生成代理了對象,但是這種方法直接調(diào)用了this對象的方法,所以updateStatus方法不會生成事務(wù)。

          由此可見,在同一個類中的方法直接內(nèi)部調(diào)用,會導(dǎo)致事務(wù)失效。

          那么問題來了,如果有些場景,確實想在同一個類的某個方法中,調(diào)用它自己的另外一個方法,該怎么辦呢?

          3.1 新加一個Service方法

          這個方法非常簡單,只需要新加一個Service方法,把@Transactional注解加到新Service方法上,把需要事務(wù)執(zhí)行的代碼移到新方法中。具體代碼如下:

          @Servcie
          public class ServiceA {
             @Autowired
             prvate ServiceB serviceB;

             public void save(User user) {
                   queryData1();
                   queryData2();
                   serviceB.doSave(user);
             }
           }

           @Servcie
           public class ServiceB {

              @Transactional(rollbackFor=Exception.class)
              public void doSave(User user
          {
                 addData1();
                 updateData2();
              }

           }

          3.2 在該Service類中注入自己

          如果不想再新加一個Service類,在該Service類中注入自己也是一種選擇。具體代碼如下:

          @Servcie
          public class ServiceA {
             @Autowired
             prvate ServiceA serviceA;

             public void save(User user) {
                   queryData1();
                   queryData2();
                   serviceA.doSave(user);
             }

             @Transactional(rollbackFor=Exception.class)
             public void doSave(User user
          {
                 addData1();
                 updateData2();
              }
           }

          可能有些人可能會有這樣的疑問:這種做法會不會出現(xiàn)循環(huán)依賴問題?

          答案:不會。

          其實spring ioc內(nèi)部的三級緩存保證了它,不會出現(xiàn)循環(huán)依賴問題。但有些坑,如果你想進一步了解循環(huán)依賴問題,可以看看我之前文章《spring:我是如何解決循環(huán)依賴的?》。

          3.3 通過AopContent類

          在該Service類中使用AopContext.currentProxy()獲取代理對象

          上面的方法2確實可以解決問題,但是代碼看起來并不直觀,還可以通過在該Service類中使用AOPProxy獲取代理對象,實現(xiàn)相同的功能。具體代碼如下:

          @Servcie
          public class ServiceA {

             public void save(User user) {
                   queryData1();
                   queryData2();
                   ((ServiceA)AopContext.currentProxy()).doSave(user);
             }

             @Transactional(rollbackFor=Exception.class)
             public void doSave(User user
          {
                 addData1();
                 updateData2();
              }
           }

          4.未被spring管理

          在我們平時開發(fā)過程中,有個細節(jié)很容易被忽略。即使用spring事務(wù)的前提是:對象要被spring管理,需要創(chuàng)建bean實例。

          通常情況下,我們通過@Controller、@Service、@Component、@Repository等注解,可以自動實現(xiàn)bean實例化和依賴注入的功能。

          當然創(chuàng)建bean實例的方法還有很多,有興趣的小伙伴可以看看我之前寫的另一篇文章《@Autowired的這些騷操作,你都知道嗎?》

          如果有一天,你匆匆忙忙的開發(fā)了一個Service類,但忘了加@Service注解,比如:

          //@Service
          public class UserService {

              @Transactional
              public void add(UserModel userModel) {
                   saveData(userModel);
                   updateData(userModel);
              }    
          }

          從上面的例子,我們可以看到UserService類沒有加@Service注解,那么該類不會交給spring管理,所以它的add方法也不會生成事務(wù)。

          5.多線程調(diào)用

          在實際項目開發(fā)中,多線程的使用場景還是挺多的。如果spring事務(wù)用在多線程場景中,會有問題嗎?

          @Slf4j
          @Service
          public class UserService {

              @Autowired
              private UserMapper userMapper;
              @Autowired
              private RoleService roleService;

              @Transactional
              public void add(UserModel userModel) throws Exception {
                  userMapper.insertUser(userModel);
                  new Thread(() -> {
                      roleService.doOtherThing();
                  }).start();
              }
          }

          @Service
          public class RoleService {

              @Transactional
              public void doOtherThing() {
                  System.out.println("保存role表數(shù)據(jù)");
              }
          }

          從上面的例子中,我們可以看到事務(wù)方法add中,調(diào)用了事務(wù)方法doOtherThing,但是事務(wù)方法doOtherThing是在另外一個線程中調(diào)用的。

          這樣會導(dǎo)致兩個方法不在同一個線程中,獲取到的數(shù)據(jù)庫連接不一樣,從而是兩個不同的事務(wù)。如果想doOtherThing方法中拋了異常,add方法也回滾是不可能的。

          如果看過spring事務(wù)源碼的朋友,可能會知道spring的事務(wù)是通過數(shù)據(jù)庫連接來實現(xiàn)的。當前線程中保存了一個map,key是數(shù)據(jù)源,value是數(shù)據(jù)庫連接。

          private static final ThreadLocal<Map<Object, Object>> resources =

            new NamedThreadLocal<>("Transactional resources");

          我們說的同一個事務(wù),其實是指同一個數(shù)據(jù)庫連接,只有擁有同一個數(shù)據(jù)庫連接才能同時提交和回滾。如果在不同的線程,拿到的數(shù)據(jù)庫連接肯定是不一樣的,所以是不同的事務(wù)。

          6.表不支持事務(wù)

          周所周知,在mysql5之前,默認的數(shù)據(jù)庫引擎是myisam

          它的好處就不用多說了:索引文件和數(shù)據(jù)文件是分開存儲的,對于查多寫少的單表操作,性能比innodb更好。

          有些老項目中,可能還在用它。

          在創(chuàng)建表的時候,只需要把ENGINE參數(shù)設(shè)置成MyISAM即可:

          CREATE TABLE `category` (
            `id` bigint NOT NULL AUTO_INCREMENT,
            `one_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
            `two_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
            `three_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
            `four_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
            PRIMARY KEY (`id`)
          ) ENGINE
          =MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin

          myisam好用,但有個很致命的問題是:不支持事務(wù)

          如果只是單表操作還好,不會出現(xiàn)太大的問題。但如果需要跨多張表操作,由于其不支持事務(wù),數(shù)據(jù)極有可能會出現(xiàn)不完整的情況。

          此外,myisam還不支持行鎖和外鍵。

          所以在實際業(yè)務(wù)場景中,myisam使用的并不多。在mysql5以后,myisam已經(jīng)逐漸退出了歷史的舞臺,取而代之的是innodb。

          有時候我們在開發(fā)的過程中,發(fā)現(xiàn)某張表的事務(wù)一直都沒有生效,那不一定是spring事務(wù)的鍋,最好確認一下你使用的那張表,是否支持事務(wù)。

          7.未開啟事務(wù)

          有時候,事務(wù)沒有生效的根本原因是沒有開啟事務(wù)。

          你看到這句話可能會覺得好笑。

          開啟事務(wù)不是一個項目中,最最最基本的功能嗎?

          為什么還會沒有開啟事務(wù)?

          沒錯,如果項目已經(jīng)搭建好了,事務(wù)功能肯定是有的。

          但如果你是在搭建項目demo的時候,只有一張表,而這張表的事務(wù)沒有生效。那么會是什么原因造成的呢?

          當然原因有很多,但沒有開啟事務(wù),這個原因極其容易被忽略。

          如果你使用的是springboot項目,那么你很幸運。因為springboot通過DataSourceTransactionManagerAutoConfiguration類,已經(jīng)默默的幫你開啟了事務(wù)。

          你所要做的事情很簡單,只需要配置spring.datasource相關(guān)參數(shù)即可。

          但如果你使用的還是傳統(tǒng)的spring項目,則需要在applicationContext.xml文件中,手動配置事務(wù)相關(guān)參數(shù)。如果忘了配置,事務(wù)肯定是不會生效的。

          具體配置如下信息:

             
          <!-- 配置事務(wù)管理器 --> 
          <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"
              <property name="dataSource" ref="dataSource"></property> 
          </bean> 
          <tx:advice id="advice" transaction-manager="transactionManager"
              <tx:attributes> 
                  <tx:method name="*" propagation="REQUIRED"/>
              </tx:attributes> 
          </tx:advice> 
          <!-- 用切點把事務(wù)切進去 --> 
          <aop:config> 
              <aop:pointcut expression="execution(* com.susan.*.*(..))" id="pointcut"/> 
              <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/> 
          </aop:config> 

          默默的說一句,如果在pointcut標簽中的切入點匹配規(guī)則,配錯了的話,有些類的事務(wù)也不會生效。

          二 事務(wù)不回滾

          1.錯誤的傳播特性

          其實,我們在使用@Transactional注解時,是可以指定propagation參數(shù)的。

          該參數(shù)的作用是指定事務(wù)的傳播特性,spring目前支持7種傳播特性:

          • REQUIRED 如果當前上下文中存在事務(wù),那么加入該事務(wù),如果不存在事務(wù),創(chuàng)建一個事務(wù),這是默認的傳播屬性值。
          • SUPPORTS 如果當前上下文存在事務(wù),則支持事務(wù)加入事務(wù),如果不存在事務(wù),則使用非事務(wù)的方式執(zhí)行。
          • MANDATORY 如果當前上下文中存在事務(wù),否則拋出異常。
          • REQUIRES_NEW 每次都會新建一個事務(wù),并且同時將上下文中的事務(wù)掛起,執(zhí)行當前新建事務(wù)完成以后,上下文事務(wù)恢復(fù)再執(zhí)行。
          • NOT_SUPPORTED 如果當前上下文中存在事務(wù),則掛起當前事務(wù),然后新的方法在沒有事務(wù)的環(huán)境中執(zhí)行。
          • NEVER 如果當前上下文中存在事務(wù),則拋出異常,否則在無事務(wù)環(huán)境上執(zhí)行代碼。
          • NESTED 如果當前上下文中存在事務(wù),則嵌套事務(wù)執(zhí)行,如果不存在事務(wù),則新建事務(wù)。

          如果我們在手動設(shè)置propagation參數(shù)的時候,把傳播特性設(shè)置錯了,比如:

          @Service
          public class UserService {

              @Transactional(propagation = Propagation.NEVER)
              public void add(UserModel userModel) {
                  saveData(userModel);
                  updateData(userModel);
              }
          }

          我們可以看到add方法的事務(wù)傳播特性定義成了Propagation.NEVER,這種類型的傳播特性不支持事務(wù),如果有事務(wù)則會拋異常。

          目前只有這三種傳播特性才會創(chuàng)建新事務(wù):REQUIRED,REQUIRES_NEW,NESTED。

          2.自己吞了異常

          事務(wù)不會回滾,最常見的問題是:開發(fā)者在代碼中手動try...catch了異常。比如:

          @Slf4j
          @Service
          public class UserService {
              
              @Transactional
              public void add(UserModel userModel) {
                  try {
                      saveData(userModel);
                      updateData(userModel);
                  } catch (Exception e) {
                      log.error(e.getMessage(), e);
                  }
              }
          }

          這種情況下spring事務(wù)當然不會回滾,因為開發(fā)者自己捕獲了異常,又沒有手動拋出,換句話說就是把異常吞掉了。

          如果想要spring事務(wù)能夠正常回滾,必須拋出它能夠處理的異常。如果沒有拋異常,則spring認為程序是正常的。

          3.手動拋了別的異常

          即使開發(fā)者沒有手動捕獲異常,但如果拋的異常不正確,spring事務(wù)也不會回滾。

          @Slf4j
          @Service
          public class UserService {
              
              @Transactional
              public void add(UserModel userModel) throws Exception {
                  try {
                       saveData(userModel);
                       updateData(userModel);
                  } catch (Exception e) {
                      log.error(e.getMessage(), e);
                      throw new Exception(e);
                  }
              }
          }

          上面的這種情況,開發(fā)人員自己捕獲了異常,又手動拋出了異常:Exception,事務(wù)同樣不會回滾。

          因為spring事務(wù),默認情況下只會回滾RuntimeException(運行時異常)和Error(錯誤),對于普通的Exception(非運行時異常),它不會回滾。

          4.自定義了回滾異常

          在使用@Transactional注解聲明事務(wù)時,有時我們想自定義回滾的異常,spring也是支持的。可以通過設(shè)置rollbackFor參數(shù),來完成這個功能。

          但如果這個參數(shù)的值設(shè)置錯了,就會引出一些莫名其妙的問題,例如:

          @Slf4j
          @Service
          public class UserService {
              
              @Transactional(rollbackFor = BusinessException.class)
              public void add(UserModel userModelthrows Exception 
          {
                 saveData(userModel);
                 updateData(userModel);
              }
          }

          如果在執(zhí)行上面這段代碼,保存和更新數(shù)據(jù)時,程序報錯了,拋了SqlException、DuplicateKeyException等異常。而BusinessException是我們自定義的異常,報錯的異常不屬于BusinessException,所以事務(wù)也不會回滾。

          即使rollbackFor有默認值,但阿里巴巴開發(fā)者規(guī)范中,還是要求開發(fā)者重新指定該參數(shù)。

          這是為什么呢?

          因為如果使用默認值,一旦程序拋出了Exception,事務(wù)不會回滾,這會出現(xiàn)很大的bug。所以,建議一般情況下,將該參數(shù)設(shè)置成:Exception或Throwable。

          5.嵌套事務(wù)回滾多了

          public class UserService {

              @Autowired
              private UserMapper userMapper;

              @Autowired
              private RoleService roleService;

              @Transactional
              public void add(UserModel userModel) throws Exception {
                  userMapper.insertUser(userModel);
                  roleService.doOtherThing();
              }
          }

          @Service
          public class RoleService {

              @Transactional(propagation = Propagation.NESTED)
              public void doOtherThing() {
                  System.out.println("保存role表數(shù)據(jù)");
              }
          }

          這種情況使用了嵌套的內(nèi)部事務(wù),原本是希望調(diào)用roleService.doOtherThing方法時,如果出現(xiàn)了異常,只回滾doOtherThing方法里的內(nèi)容,不回滾 userMapper.insertUser里的內(nèi)容,即回滾保存點。。但事實是,insertUser也回滾了。

          why?

          因為doOtherThing方法出現(xiàn)了異常,沒有手動捕獲,會繼續(xù)往上拋,到外層add方法的代理方法中捕獲了異常。所以,這種情況是直接回滾了整個事務(wù),不只回滾單個保存點。

          怎么樣才能只回滾保存點呢?

          @Slf4j
          @Service
          public class UserService {

              @Autowired
              private UserMapper userMapper;

              @Autowired
              private RoleService roleService;

              @Transactional
              public void add(UserModel userModel) throws Exception {

                  userMapper.insertUser(userModel);
                  try {
                      roleService.doOtherThing();
                  } catch (Exception e) {
                      log.error(e.getMessage(), e);
                  }
              }
          }

          可以將內(nèi)部嵌套事務(wù)放在try/catch中,并且不繼續(xù)往上拋異常。這樣就能保證,如果內(nèi)部嵌套事務(wù)中出現(xiàn)異常,只回滾內(nèi)部事務(wù),而不影響外部事務(wù)。

          三 其他

          1 大事務(wù)問題

          在使用spring事務(wù)時,有個讓人非常頭疼的問題,就是大事務(wù)問題。

          通常情況下,我們會在方法上@Transactional注解,填加事務(wù)功能,比如:

          @Service
          public class UserService {
              
              @Autowired 
              private RoleService roleService;
              
              @Transactional
              public void add(UserModel userModel) throws Exception {
                 query1();
                 query2();
                 query3();
                 roleService.save(userModel);
                 update(userModel);
              }
          }


          @Service
          public class RoleService {
              
              @Autowired 
              private RoleService roleService;
              
              @Transactional
              public void save(UserModel userModel) throws Exception {
                 query4();
                 query5();
                 query6();
                 saveData(userModel);
              }
          }

          @Transactional注解,如果被加到方法上,有個缺點就是整個方法都包含在事務(wù)當中了。

          上面的這個例子中,在UserService類中,其實只有這兩行才需要事務(wù):

          roleService.save(userModel);
          update(userModel);

          在RoleService類中,只有這一行需要事務(wù):

          saveData(userModel);

          現(xiàn)在的這種寫法,會導(dǎo)致所有的query方法也被包含在同一個事務(wù)當中。

          如果query方法非常多,調(diào)用層級很深,而且有部分查詢方法比較耗時的話,會造成整個事務(wù)非常耗時,而從造成大事務(wù)問題。

          關(guān)于大事務(wù)問題的危害,可以閱讀一下我的另一篇文章《讓人頭痛的大事務(wù)問題到底要如何解決?》,上面有詳細的講解。

          2.編程式事務(wù)

          上面聊的這些內(nèi)容都是基于@Transactional注解的,主要說的是它的事務(wù)問題,我們把這種事務(wù)叫做:聲明式事務(wù)

          其實,spring還提供了另外一種創(chuàng)建事務(wù)的方式,即通過手動編寫代碼實現(xiàn)的事務(wù),我們把這種事務(wù)叫做編程式事務(wù)。例如:


             @Autowired
             private TransactionTemplate transactionTemplate;
             
             ...
             
             public void save(final User user) {
                   queryData1();
                   queryData2();
                   transactionTemplate.execute((status) => {
                      addData1();
                      updateData2();
                      return Boolean.TRUE;
                   })
             }

          在spring中為了支持編程式事務(wù),專門提供了一個類:TransactionTemplate,在它的execute方法中,就實現(xiàn)了事務(wù)的功能。

          相較于@Transactional注解聲明式事務(wù),我更建議大家使用,基于TransactionTemplate的編程式事務(wù)。主要原因如下:

          1. 避免由于spring aop問題,導(dǎo)致事務(wù)失效的問題。
          2. 能夠更小粒度的控制事務(wù)的范圍,更直觀。

          建議在項目中少使用@Transactional注解開啟事務(wù)。但并不是說一定不能用它,如果項目中有些業(yè)務(wù)邏輯比較簡單,而且不經(jīng)常變動,使用@Transactional注解開啟事務(wù)開啟事務(wù)也無妨,因為它更簡單,開發(fā)效率更高,但是千萬要小心事務(wù)失效的問題。

          程序汪資料鏈接

          程序汪接的7個私活都在這里,經(jīng)驗整理

          Java項目分享  最新整理全集,找項目不累啦 04版

          堪稱神級的Spring Boot手冊,從基礎(chǔ)入門到實戰(zhàn)進階

          臥槽!字節(jié)跳動《算法中文手冊》火了,完整版 PDF 開放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開放下載!

          字節(jié)跳動總結(jié)的設(shè)計模式 PDF 火了,完整版開放下載!

          歡迎添加程序汪個人微信 itwang008  進粉絲群或圍觀朋友圈

          瀏覽 56
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  男女啪啪18秘 免费网站 | 韩国TS『人妖av | 日韩在线视频第一页 | 亚洲无码毛片基地 | 91在线成人免费视频 |