<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Spring系列__04AOP

      AOP簡介

      今天來介紹一下AOP。AOP,中文常被翻譯為“面向切面編程”,其作為OOP的擴展,其思想除了在Spring中得到了應用,也是不錯的設計方法。通常情況下,一個軟件系統(tǒng),除了正常的業(yè)務邏輯代碼,往往還有一些功能性的代碼,比如:記錄日志、數(shù)據(jù)校驗等等。最原始的辦法就是直接在你的業(yè)務邏輯代碼中編寫這些功能性代碼,但是,這樣除了當時開發(fā)的時候比較方便以外;代碼的閱讀性、可維護性都會大大降低。而且,當你需要頻繁使用一個功能的時候(比如記錄日志),你還需要重復編寫。而使用AOP的好處,簡單來說就是,它能把這種重復性的功能代碼抽離出來,在需要的時候,通過動態(tài)代理技術,在不修改源代碼的情況下提供增強性功能。
      優(yōu)勢:

      • 減少重復代碼
      • 提高開發(fā)效率
      • 代碼更加整潔,提高了可維護性
        說了這么多,簡單演示一下,我們假定現(xiàn)在要實現(xiàn)一個賬戶轉(zhuǎn)賬的功能,這里面會涉及到一些事務的控制,從代碼的合理性角度出發(fā),我們將其放在service層。
      @Getter
      @Setter
      @NoArgsConstructor
      @AllArgsConstructor
      @ToString
      @EqualsAndHashCode
      public class Account implements Serializable {
          private Integer id;
          private String name;
          private Float money;
      }
      
      public interface AccountDao {
          /**
           * 查詢所有
           * @return
           */
          List<Account> findAllAccount();
      
          /**
           * 查詢一個
           * @return
           */
          Account findAccountById(Integer accountId);
      
          /**
           * 保存
           * @param account
           */
          void saveAccount(Account account);
      
          /**
           * 更新
           * @param account
           */
          void updateAccount(Account account);
      
          /**
           * 刪除
           * @param acccountId
           */
          void deleteAccount(Integer acccountId);
      
          /**
           * 根據(jù)名稱查詢賬戶
           * @param accountName
           * @return  如果有唯一的一個結(jié)果就返回,如果沒有結(jié)果就返回null
           *          如果結(jié)果集超過一個就拋異常
           */
          Account findAccountByName(String accountName);
      }
      
      public class AccountServiceImpl_OLD implements AccountService {
      
          private AccountDao accountDao;
          private TransactionManager txManager;
      
          public void setTxManager(TransactionManager txManager) {
              this.txManager = txManager;
          }
      
          public void setAccountDao(AccountDao accountDao) {
              this.accountDao = accountDao;
          }
      
          @Override
          public List<Account> findAllAccount() {
              try {
                  //1.開啟事務
                  txManager.beginTransaction();
                  //2.執(zhí)行操作
                  List<Account> accounts = accountDao.findAllAccount();
                  //3.提交事務
                  txManager.commit();
                  //4.返回結(jié)果
                  return accounts;
              }catch (Exception e){
                  //5.回滾操作
                  txManager.rollback();
                  throw new RuntimeException(e);
              }finally {
                  //6.釋放連接
                  txManager.release();
              }
      
          }
      
          @Override
          public Account findAccountById(Integer accountId) {
              try {
                  //1.開啟事務
                  txManager.beginTransaction();
                  //2.執(zhí)行操作
                  Account account = accountDao.findAccountById(accountId);
                  //3.提交事務
                  txManager.commit();
                  //4.返回結(jié)果
                  return account;
              }catch (Exception e){
                  //5.回滾操作
                  txManager.rollback();
                  throw new RuntimeException(e);
              }finally {
                  //6.釋放連接
                  txManager.release();
              }
          }
      
          @Override
          public void saveAccount(Account account) {
              try {
                  //1.開啟事務
                  txManager.beginTransaction();
                  //2.執(zhí)行操作
                  accountDao.saveAccount(account);
                  //3.提交事務
                  txManager.commit();
              }catch (Exception e){
                  //4.回滾操作
                  txManager.rollback();
              }finally {
                  //5.釋放連接
                  txManager.release();
              }
      
          }
      
          @Override
          public void updateAccount(Account account) {
              try {
                  //1.開啟事務
                  txManager.beginTransaction();
                  //2.執(zhí)行操作
                  accountDao.updateAccount(account);
                  //3.提交事務
                  txManager.commit();
              }catch (Exception e){
                  //4.回滾操作
                  txManager.rollback();
              }finally {
                  //5.釋放連接
                  txManager.release();
              }
      
          }
      
          @Override
          public void deleteAccount(Integer acccountId) {
              try {
                  //1.開啟事務
                  txManager.beginTransaction();
                  //2.執(zhí)行操作
                  accountDao.deleteAccount(acccountId);
                  //3.提交事務
                  txManager.commit();
              }catch (Exception e){
                  //4.回滾操作
                  txManager.rollback();
              }finally {
                  //5.釋放連接
                  txManager.release();
              }
      
          }
      
          @Override
          public void transfer(String sourceName, String targetName, Float money) {
              try {
                  //1.開啟事務
                  txManager.beginTransaction();
                  //2.執(zhí)行操作
      
                  //2.1根據(jù)名稱查詢轉(zhuǎn)出賬戶
                  Account source = accountDao.findAccountByName(sourceName);
                  //2.2根據(jù)名稱查詢轉(zhuǎn)入賬戶
                  Account target = accountDao.findAccountByName(targetName);
                  //2.3轉(zhuǎn)出賬戶減錢
                  source.setMoney(source.getMoney()-money);
                  //2.4轉(zhuǎn)入賬戶加錢
                  target.setMoney(target.getMoney()+money);
                  //2.5更新轉(zhuǎn)出賬戶
                  accountDao.updateAccount(source);
      
                  int i=1/0;
      
                  //2.6更新轉(zhuǎn)入賬戶
                  accountDao.updateAccount(target);
                  //3.提交事務
                  txManager.commit();
      
              }catch (Exception e){
                  //4.回滾操作
                  txManager.rollback();
                  e.printStackTrace();
              }finally {
                  //5.釋放連接
                  txManager.release();
              }
      
      
          }
      

      在這里,我們看見了很惡心的代碼:大量重復性的記錄日志的代碼,而且,當你更改的時候,你發(fā)現(xiàn)并不方便。后續(xù)我們會對這個代碼進行改寫。

      AOP的實現(xiàn)方式

      AOP通過動態(tài)代理的來實現(xiàn)。

      動態(tài)代理簡介

      在這里先簡單介紹一下動態(tài)代理:使用一個代理將對象包裝起來, 然后用該代理對象取代原始對象.。任何對原始對象的調(diào)用都要通過代理. 代理對象決定是否以及何時將方法調(diào)用轉(zhuǎn)到原始對象上。其調(diào)用過程如下圖所示:

      特點:

      • 字節(jié)碼隨用隨創(chuàng)建,隨用隨加載。
      • 它與靜態(tài)代理的區(qū)別也在于此。因為靜態(tài)代理是字節(jié)碼一上來就創(chuàng)建好,并完成加載。
      • 裝飾者模式就是靜態(tài)代理的一種體現(xiàn)。

      動態(tài)代理有兩種形式

      • 基于接口的動態(tài)代理
        提供者: JDK 官方的 Proxy 類。
        要求:被代理類最少實現(xiàn)一個接口
      • 基于子類的動態(tài)代理
        提供者:第三方的 CGLib,如果報 asmxxxx 異常,需要導入 asm.jar。
        要求:被代理類不能用 final 修飾的類(最終類)。
        下面結(jié)合示例來解釋一下這兩種動態(tài)代理的實現(xiàn)方式:
        筆者最近更換了一臺新的電腦,就以買電腦來舉個例子吧。現(xiàn)在大家買電腦,已經(jīng)很少去實體店了,多半是通過電商渠道。不管是什么,都是從中間商來買,這一行為,在無形中就體現(xiàn)了代理模式的思想。
        電腦生產(chǎn)商最開始的時候,除了生產(chǎn)和組裝電腦,同時還可以將電腦出售給消費者或者經(jīng)銷商(代理商),而他對顧客來說,需要完成兩種服務:銷售商品和售后服務。當行業(yè)發(fā)展到一定階段,電腦生產(chǎn)商不斷增多,人們就會制定一些行業(yè)規(guī)范來讓大家共同遵守(也就是抽象出來的接口)。而且,電腦生產(chǎn)商為了節(jié)約成本,不再提供直接和消費者銷售的服務,我們消費者也因此只能從代理商那里購買新的電腦。這便是典型的代理模式。

      使用 JDK 官方的 Proxy 類創(chuàng)建代理對象

      public interface IProducer {
       
          public void saleProduct(float money);
      
          public void afterService(float money);
      }
      
      public class Producer implements IProducer {
       
          @Override
          public void saleProduct(float money) {
              System.out.println("銷售產(chǎn)品,并拿到錢:" + money);
          }
      
        
          @Override
          public void afterService(float money) {
              System.out.println("提供售后服務,并拿到錢:" + money);
          }
      }
      
      //消費者
      public class Client {
          public static void main(String[] args) {
              final Producer producer = new Producer();
              /**
               *  如何創(chuàng)建代理對象:
               *  使用Proxy類中的newProxyInstance方法
               *  創(chuàng)建代理對象的要求:
               *      被代理類最少實現(xiàn)一個接口,如果沒有則不能使用
               *  newProxyInstance方法的參數(shù):
               *      ClassLoader:類加載器
               *          它是用于加載代理對象字節(jié)碼的。和被代理對象使用相同的類加載器。固定寫法。
               *      Class[]:字節(jié)碼數(shù)組
               *          它是用于讓代理對象和被代理對象有相同方法。固定寫法。
               *     InvocationHandler:用于提供增強的代碼
               *          它是讓我們寫如何代理。我們一般都是些一個該接口的實現(xiàn)類,通常情況下都是匿名內(nèi)部類,但不是必須的。
               *          此接口的實現(xiàn)類都是誰用誰寫。
               */
      
              IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                      producer.getClass().getInterfaces(), new InvocationHandler() {
                          /**
                           * 作用:執(zhí)行被代理對象的任何接口方法都會經(jīng)過該方法
                           * 方法參數(shù)的含義
                           * @param proxy   代理對象的引用
                           * @param method  當前執(zhí)行的方法
                           * @param args    當前執(zhí)行方法所需的參數(shù)
                           * @return 和被代理對象方法有相同的返回值
                           * @throws Throwable
                           */
                          @Override
                          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                              //提供增強的代碼
                              Object returnValue = null;
      
                              //1.獲取方法執(zhí)行的參數(shù)
                              Float money = (Float)args[0];
                              //2.判斷當前方法是不是銷售,如果是的話,打八折
                              if("saleProduct".equals(method.getName())) {
                                  returnValue = method.invoke(producer, money*0.8f);
                              }
                              return returnValue;
                          }
                      });
              proxyProducer.saleProduct(10000f);
      
      
          }
      }
      
      

      使用jdk提供的Proxy來創(chuàng)建代理對象的時候,要求別代理對象至少要實現(xiàn)一個接口,代理類需要實現(xiàn)同樣的接口并由同一類加載器加載。如果沒有這樣,就不能使用這種方式了。其他具體內(nèi)容,請參考官方文檔。

      cglib方式來實現(xiàn)動態(tài)代理

      其實,說AOP是OOP的延伸,還是很容易證明的:jdk提供動態(tài)代理的方式是實現(xiàn)接口,而cglib的實現(xiàn)方式就是利用了OOP的繼承。原理大同小異,主要區(qū)別就是不用實現(xiàn)接口而是改用繼承,也因此具備繼承的限制:被代理的類不能是被final修飾。

      public class Client {
          public static void main(String[] args) {
              final Producer producer = new Producer();
              /**
               *  create方法的參數(shù):
               *      Class:字節(jié)碼
               *          它是用于指定被代理對象的字節(jié)碼。
               *
               *      Callback:用于提供增強的代碼
               *          它是讓我們寫如何代理。我們一般都是些一個該接口的實現(xiàn)類,通常情況下都是匿名內(nèi)部類,但不是必須的。
               *          此接口的實現(xiàn)類都是誰用誰寫。
               *          我們一般寫的都是該接口的子接口實現(xiàn)類:MethodInterceptor
               */
              Producer proxyProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
                  @Override
                  public Object intercept(Object o, Method method, Object[] objects,
                                          MethodProxy methodProxy) throws Throwable {
                      Object result = null;
                      Float price = (Float) objects[0];
                      if ("saleProduct".equals(method.getName())) {
                          result = method.invoke(o, price * 0.8f);
                      }
                      return result;
                  }
              });
      
              proxyProducer.saleProduct(10000f);
          }
      }
      
      ublic class Client {
          public static void main(String[] args) {
              final Producer producer = new Producer();
              /**
               *  create方法的參數(shù):
               *      Class:字節(jié)碼
               *          它是用于指定被代理對象的字節(jié)碼。
               *
               *      Callback:用于提供增強的代碼
               *          它是讓我們寫如何代理。我們一般都是些一個該接口的實現(xiàn)類,通常情況下都是匿名內(nèi)部類,但不是必須的。
               *          此接口的實現(xiàn)類都是誰用誰寫。
               *          我們一般寫的都是該接口的子接口實現(xiàn)類:MethodInterceptor
               */
              Producer proxyProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
                  @Override
                  public Object intercept(Object o, Method method, Object[] objects,
                                          MethodProxy methodProxy) throws Throwable {
                      Object result = null;
                      Float price = (Float) objects[0];
                      if ("saleProduct".equals(method.getName())) {
                          result = method.invoke(o, price * 0.8f);
                      }
                      return result;
                  }
              });
      
              proxyProducer.saleProduct(10000f);
          }
      }
      

      Spring中的AOP

      以上所述的兩種生成代理對象的方法,在Spring中都會應用:默認優(yōu)先使用jdk自帶的方式,當發(fā)現(xiàn)別代理類沒有實現(xiàn)接口時改用cglib方式。

      專業(yè)術語直白翻譯

      • Joinpoint(連接點):
        所謂的連擊點,就是你的業(yè)務邏輯中的每一個方法,都被稱作連接點。而且,和AspectJ和JBoss不同,Spring不支持字段和構造器連接點,只支持方法級別的連接點。
      • Pointcut(切入點):
        當你要對一個連接點進行額外的功能添加時,這個連接點就是切入點。
      • Advice(通知/增強):
        通知就是你攔截了切點后要做的事情。根據(jù)你要做的時機,分為:前置通知、后置通知、返回通知、異常通知和環(huán)繞通知。
      • Introduction(引介):
        引介是一種特殊的通知在不修改類代碼的前提下, Introduction 可以在運行期為類動態(tài)地添加一些方法或 Field。
      • Target(目標對象):
        代理的目標對象。
      • Weaving(織入):
        是指把增強應用到目標對象來創(chuàng)建新的代理對象的過程。spring 采用動態(tài)代理織入,而 AspectJ 采用編譯期織入和類裝載期織入。
      • Proxy(代理) :
        一個類被 AOP 織入增強后,就產(chǎn)生一個結(jié)果代理類。
      • Aspect(切面):
        是切入點和通知(引介)的結(jié)合。

      實戰(zhàn)演練

      這次我們打算做一個簡單一點的功能:實現(xiàn)一個能夠進行加減乘除運算的計算器,并進行相應的日志記錄
      過程主要是以下幾步:
      1.開發(fā)業(yè)務邏輯代碼
      2.開發(fā)切面代碼
      3.配置ioc,將計算器和切面配置到Spring容器中
      4.切面配置,開啟AOP
      對于配置的方式,主要是還是兩種方式:

      Java配置:

      public interface ArithmeticCalculator {
      
      	int add(int i, int j);
      	int sub(int i, int j);
      	
      	int mul(int i, int j);
      	int div(int i, int j);
      	
      }
      
      @Component("arithmeticCalculator")
      public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
      
      	@Override
      	public int add(int i, int j) {
      		int result = i + j;
      		return result;
      	}
      
      	@Override
      	public int sub(int i, int j) {
      		int result = i - j;
      		return result;
      	}
      
      	@Override
      	public int mul(int i, int j) {
      		int result = i * j;
      		return result;
      	}
      
      	@Override
      	public int div(int i, int j) {
      		int result = i / j;
      		return result;
      	}
      
      }
      
      
      package com.spring.demo.springaop;
      
      import org.aspectj.lang.JoinPoint;
      import org.aspectj.lang.annotation.*;
      import org.springframework.stereotype.Component;
      
      import java.util.Arrays;
      
      /**
       * 可以使用 @Order 注解指定切面的優(yōu)先級, 值越小優(yōu)先級越高
       */
      @Aspect
      @Component
      public class LoggingAspect {
          @Pointcut("execution(public int com.spring.demo.springaop.ArithmeticCalculator.*(..))")
          public void declareJoinPoint() {}
      
          @Before("declareJoinPoint()")
          public void beforeMehtod(JoinPoint joinPoint) {
              String methodName = joinPoint.getSignature().getName();
              Object[] args = joinPoint.getArgs();
              System.out.println("the " + methodName + " begins with " + Arrays.asList(args));
          }
      
      
          @AfterReturning(value = "declareJoinPoint()", returning = "result")
          public void afterMethod(JoinPoint joinPoint, Object result) {
              String methodName = joinPoint.getSignature().getName();
              System.out.println("the " + methodName + " ends successfully with result is " + result);
          }
      
          @AfterThrowing(value = "declareJoinPoint()", throwing = "e")
          public void afterException(JoinPoint joinPoint, Exception e) {
              String methodName = joinPoint.getSignature().getName();
              System.out.println("the " + methodName + "occurs a Exception by" + e.getMessage());
          }
      
          /**
           * 環(huán)繞通知需要攜帶 ProceedingJoinPoint 類型的參數(shù).
           * 環(huán)繞通知類似于動態(tài)代理的全過程: ProceedingJoinPoint 類型的參數(shù)可以決定是否執(zhí)行目標方法.
           * 且環(huán)繞通知必須有返回值, 返回值即為目標方法的返回值
           */
      	/*
      	@Around("execution(public int com.spring.demo.springaop.ArithmeticCalculator.*(..))")
      	public Object aroundMethod(ProceedingJoinPoint pjd){
      
      		Object result = null;
      		String methodName = pjd.getSignature().getName();
      
      		try {
      			//前置通知
      			System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
      			//執(zhí)行目標方法
      			result = pjd.proceed();
      			//返回通知
      			System.out.println("The method " + methodName + " ends with " + result);
      		} catch (Throwable e) {
      			//異常通知
      			System.out.println("The method " + methodName + " occurs exception:" + e);
      			throw new RuntimeException(e);
      		}
      		//后置通知
      		System.out.println("The method " + methodName + " ends");
      
      		return result;
      	}
      	*/
      }
      
      
      
      @Order(1)
      @Aspect
      @Component
      public class VlidationAspect {
      
      	@Before("com.spring.demo.springaop.LoggingAspect.declareJoinPoint()")
      	public void validateArgs(JoinPoint joinPoint){
      		System.out.println("-->validate:" + Arrays.asList(joinPoint.getArgs()));
      	}
      	
      }
      
      @EnableAspectJAutoProxy
      @Configuration
      @ComponentScan
      public class MainConcig {
      
      }
      
      
      public class Main {
          public static void main(String[] args) {
              AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.spring.demo" +
                      ".springaop");
              ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) context.getBean("arithmeticCalculator");
              int add = arithmeticCalculator.add(100, 200);
          }
      }
      
      

      xml文件配置

      JavaBean還是這些,只是將各個注解刪除即可,而bean的配置和aop功能的開啟,由配置文件來聲明。要引入aop命名空間。

      <!-- 配置 bean -->
      	<bean id="arithmeticCalculator" 
      		class="com.spring.demo.springaop.xml.ArithmeticCalculatorImpl"></bean>
      
      	<!-- 配置切面的 bean. -->
      	<bean id="loggingAspect"
      		class="com.spring.demo.springaop.xml.LoggingAspect"></bean>
      
      	<bean id="vlidationAspect"
      		class="com.spring.demo.springaop.xml.VlidationAspect"></bean>
      
      	<!-- 配置 AOP -->
      	<aop:config>
      		<!-- 配置切點表達式 -->
      		<aop:pointcut expression="execution(* com.spring.demo.springaop.ArithmeticCalculator.*(int, int))"
      			id="pointcut"/>
      		<!-- 配置切面及通知 -->
      		<aop:aspect ref="loggingAspect" order="2">
      			<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
      			<aop:after method="afterMethod" pointcut-ref="pointcut"/>
      			<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
      			<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
      			<!--  
      			<aop:around method="aroundMethod" pointcut-ref="pointcut"/>
      			-->
      		</aop:aspect>	
      		<aop:aspect ref="vlidationAspect" order="1">
      			<aop:before method="validateArgs" pointcut-ref="pointcut"/>
      		</aop:aspect>
      	</aop:config>
      

      切入點表達式說明(引用別人的,懶得寫了)

      execution:匹配方法的執(zhí)行(常用)
      execution(表達式)
      表達式語法: execution([修飾符] 返回值類型 包名.類名.方法名(參數(shù)))
      寫法說明:
      全匹配方式:
      public void com.itheima.service.impl.AccountServiceImpl.saveAccount(com.itheima.domain.Account)
      訪問修飾符可以省略
      void com.itheima.service.impl.AccountServiceImpl.saveAccount(com.itheima.domain.Account)
      返回值可以使用號,表示任意返回值
      *
      com.itheima.service.impl.AccountServiceImpl.saveAccount(com.itheima.domain.Account)
      包名可以使用
      號,表示任意包,但是有幾級包,需要寫幾個*

      • ....AccountServiceImpl.saveAccount(com.itheima.domain.Account)
        使用..來表示當前包,及其子包
      • com..AccountServiceImpl.saveAccount(com.itheima.domain.Account)
        類名可以使用*號,表示任意類
      • com...saveAccount(com.itheima.domain.Account)
        方法名可以使用
        號,表示任意方法
      • com...( com.itheima.domain.Account)
        參數(shù)列表可以使用*,表示參數(shù)可以是任意數(shù)據(jù)類型,但是必須有參數(shù)
      • com...(*)
        參數(shù)列表可以使用..表示有無參數(shù)均可,有參數(shù)可以是任意類型
      • com...(..)
        全通配方式:
      • ...(..)
        注:
        通常情況下,我們都是對業(yè)務層的方法進行增強,所以切入點表達式都是切到業(yè)務層實現(xiàn)類。
        execution(
        com.itheima.service.impl..(..))

      補充說明: 引入通知
      引入通知是一種特殊的通知類型. 它通過為接口提供實現(xiàn)類, 允許對象動態(tài)地實現(xiàn)接口, 就像對象已經(jīng)在運行時擴展了實現(xiàn)類一樣。

      引入通知可以使用兩個實現(xiàn)類 MaxCalculatorImpl 和 MinCalculatorImpl, 讓 ArithmeticCalculatorImpl 動態(tài)地實現(xiàn) MaxCalculator 和 MinCalculator 接口. 而這與從 MaxCalculatorImpl 和 MinCalculatorImpl 中實現(xiàn)多繼承的效果相同. 但卻不需要修改 ArithmeticCalculatorImpl 的源代碼。
      引入通知也必須在切面中聲明。

      代碼演示

      posted @ 2019-09-16 23:17  本墨  閱讀(313)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 亚洲av成人网在线观看| 国产三级精品三级| 黑人巨茎大战欧美白妇| 国产伦子沙发午休系列资源曝光| 国产亚洲情侣一区二区无| 小鲜肉自慰网站xnxx| a级免费视频| 精品国产中文字幕第一页| 久久久久久久久18禁秘| 亚洲国产一区二区三区最新| 色综合色综合综合综合综合| 97久久久亚洲综合久久| 蜜桃av亚洲精品一区二区| 成人无码潮喷在线观看| 精品无码一区二区三区水蜜桃| 亚洲av熟女国产一二三| 少妇熟女久久综合网色欲| 欧美最猛黑人xxxx| 欧美国产亚洲日韩在线二区| 中文字幕国产精品日韩| 泾川县| 激情四射激情五月综合网| 亚洲蜜臀av乱码久久| 亚洲一卡2卡三卡四卡精品| 狠狠综合久久av一区二| 92国产精品午夜福利免费| 99热精品国产三级在线观看| 精品久久亚洲中文无码| 欧美大胆老熟妇乱子伦视频| 石门县| 国产jizzjizz视频| 久久久精品国产精品久久| 人妻少妇偷人无码视频| 91久久天天躁狠狠躁夜夜| 国产精品久久久一区二区三区| 久久精品女人天堂av免费观看| 溆浦县| 热99久久这里只有精品| 激情97综合亚洲色婷婷五| 日韩精品人妻中文字幕| 国产国语毛片在线看国产|