亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

AOP從靜態代理到動態代理(Emit實現)詳解

2019-10-29 19:59:09
字體:
來源:轉載
供稿:網友

【前言】

AOP(Aspect Orient Programming),我們一般稱為面向方面(切面)編程,作為面向對象的一種補充,用于處理系統中分布于各個模塊的橫切關注點,比如事務管理、日志、緩存等等。AOP實現的關鍵在于AOP框架自動創建的AOP代理,AOP代理主要分為靜態代理動態代理,靜態代理的代表為AspectJ;而動態代理則以Spring AOP為代表。

何為切面?

一個和業務沒有任何耦合相關的代碼段,諸如:調用日志,發送郵件,甚至路由分發。一切能為代碼所有且能和代碼充分解耦的代碼都可以作為一個業務代碼的切面。

我們為什么要AOP?

那我們從一個場景舉例說起:

如果想要采集用戶操作行為,我們需要掌握用戶調用的每一個接口的信息。這時候的我們要怎么做?

如果不采用AOP技術,也是最簡單的,所有方法體第一句話先調用一個日志接口將方法信息傳遞記錄。

有何問題?

實現業務沒有任何問題,但是隨之而來的是代碼臃腫不堪,難以調整維護的諸多問題(可自行腦補)。

如果我們采用了AOP技術,我們就可以在系統啟動的地方將所有將要采集日志的類注入,每一次調用方法前,AOP框架會自動調用我們的日志代碼。

是不是省去了很多重復無用的勞動?代碼也將變得非常好維護(有朝一日不需要了,只需將切面代碼注釋掉即可)

接下來我們看看AOP框架的工作原理以及實過程。

【實現思路】

AOP框架呢,一般通過靜態代理和動態代理兩種實現方式。

  AOP,靜態代理,動態代理,Emit

何為靜態代理?

靜態代理,又叫編譯時代理,就是在編譯的時候,已經存在代理類,運行時直接調用的方式。說的通俗一點,就是自己手動寫代碼實現代理類的方式。

我們通過一個例子來展現一下靜態代理的實現過程:

我們這里有一個業務類,里面有方法Test(),我們要在Test調用前和調用后分別輸出日志。

AOP,靜態代理,動態代理,Emit

我們既然要將Log當作一個切面,我們肯定不能去動原有的業務代碼,那樣也違反了面向對象設計之開閉原則。

那么我們要怎么做呢?我們定義一個新類 BusinessProxy 去包裝一下這個類。為了便于在多個方法的時候區分和辨認,方法也叫 Test()

AOP,靜態代理,動態代理,Emit

這樣,我們如果要在所有的Business類中的方法都添加Log,我們就在BusinessProxy代理類中添加對應的方法去包裝。既不破壞原有邏輯,又可以實現前后日志的功能。

當然,我們可以有更優雅的實現方式:

AOP,靜態代理,動態代理,Emit

我們可以定義代理類,繼承自業務類。將業務類中的方法定義為虛方法。那么我們可以重寫父類的方法并且在加入日志以后再調用父類的原方法。

當然,我們還有更加優雅的實現方式:

AOP,靜態代理,動態代理,Emit

我們可以使用發射的技術,寫一個通用的Invoke方法,所有的方法都可以通過該方法調用。

我們這樣便實現了一個靜態代理。

那我們既然有了靜態代理,為什么又要有動態代理呢?

我們仔細回顧靜態代理的實現過程。我們要在所有的方法中添加切面,我們就要在代理類中重寫所有的業務方法。更有甚者,我們有N個業務類,就要定義N個代理類。這是很龐大的工作量。

AOP,靜態代理,動態代理,Emit

這就是動態代理出現的背景,相比都可以猜得到,動態代理就是將這一系列繁瑣的步驟自動化,讓程序自動為我們生成代理類。

何為動態代理?

動態代理,又成為運行時代理。在程序運行的過程中,調用了生成代理類的代碼,將自動生成業務類的代理類。不需要我們手共編寫,極高的提高了工作效率和調整了程序員的心態。

原理不必多說,就是動態生成靜態代理的代碼。我們要做的,就是選用一種生成代碼的方式去生成。

今天我分享一個簡單的AOP框架,代碼使用Emit生成。當然,Emit 代碼的寫法不是今天要講的主要內容,需要提前去學習。

先說效果:

定義一個Action特性類 ActionAttribute繼承自 ActionBaseAttribute,里面在Before和After方法中輸出兩條日志;

AOP,靜態代理,動態代理,Emit

定義一個Action特性類InterceptorAttribute 繼承自InterceptorBaseAttribute,里面捕獲了方法調用異常,以及執行前后分別輸出日志;

AOP,靜態代理,動態代理,Emit

然后定義一個業務類BusinessClass 實現了IBusinessClass 接口,定義了各種類型的方法

AOP,靜態代理,動態代理,Emit

AOP,靜態代理,動態代理,Emit

多余的方法不貼圖了。

我們把上面定義的方法調用切面標簽放在業務類上,表示該類下所有的方法都執行異常過濾;

我們把Action特性放在Test方法上,表明要在 Test() 方法的 Before 和 After 調用時記錄日志;

我們定義測試類:

AOP,靜態代理,動態代理,Emit

調用一下試試:

AOP,靜態代理,動態代理,Emit

可見,全類方法標簽 Interceptor 在 Test 和 GetInt 方法調用前后都打出了對應的日志;

Action方法標簽只在 Test 方法上做了標記,那么Test方法 Before 和 After 執行時打出了日志;

【實現過程】

實現的思路在上面已經有詳細的講解,可以參考靜態代理的實現思路。

我們定義一個動態代理生成類 DynamicProxy,用于原業務代碼的掃描和代理類代碼的生成;

定義兩個過濾器標簽,ActionBaseAttribute,提供Before和After切面方法;InterceptorBaseAttribute,提供 Invoke “全調用”包裝的切面方法;

Before可以獲取到當前調用的方法和參數列表,After可以獲取到當前方法調用以后的結果。

Invoke 可以拿到當前調用的對象和方法名,參數列表。在這里進行反射動態調用。

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public class ActionBaseAttribute : Attribute { public virtual void Before(string @method, object[] parameters) { } public virtual object After(string @method, object result) { return result; } }
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public class InterceptorBaseAttribute : Attribute { public virtual object Invoke(object @object, string @method, object[] parameters) {  return @object.GetType().GetMethod(@method).Invoke(@object, parameters); } }

代理生成類采用Emit的方式生成運行時IL代碼。

先把代碼放在這里:

public class DynamicProxy { public static TInterface CreateProxyOfRealize<TInterface, TImp>() where TImp : class, new() where TInterface : class {  return Invoke<TInterface, TImp>(); } public static TProxyClass CreateProxyOfInherit<TProxyClass>() where TProxyClass : class, new() {  return Invoke<TProxyClass, TProxyClass>(true); } private static TInterface Invoke<TInterface, TImp>(bool inheritMode = false) where TImp : class, new() where TInterface : class {  var impType = typeof(TImp);  string nameOfAssembly = impType.Name + "ProxyAssembly";  string nameOfModule = impType.Name + "ProxyModule";  string nameOfType = impType.Name + "Proxy";  var assemblyName = new AssemblyName(nameOfAssembly);  var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);  var moduleBuilder = assembly.DefineDynamicModule(nameOfModule);  //var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);  //var moduleBuilder = assembly.DefineDynamicModule(nameOfModule, nameOfAssembly + ".dll");  TypeBuilder typeBuilder;  if (inheritMode)  typeBuilder = moduleBuilder.DefineType(nameOfType, TypeAttributes.Public, impType);  else  typeBuilder = moduleBuilder.DefineType(nameOfType, TypeAttributes.Public, null, new[] { typeof(TInterface) });  InjectInterceptor<TImp>(typeBuilder, impType.GetCustomAttribute(typeof(InterceptorBaseAttribute))?.GetType(), inheritMode);  var t = typeBuilder.CreateType();  //assembly.Save(nameOfAssembly + ".dll");  return Activator.CreateInstance(t) as TInterface; } private static void InjectInterceptor<TImp>(TypeBuilder typeBuilder, Type interceptorAttributeType, bool inheritMode = false) {  var impType = typeof(TImp);  // ---- define fields ----  FieldBuilder fieldInterceptor = null;  if (interceptorAttributeType != null)  {  fieldInterceptor = typeBuilder.DefineField("_interceptor", interceptorAttributeType, FieldAttributes.Private);  }  // ---- define costructors ----  if (interceptorAttributeType != null)  {  var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);  var ilOfCtor = constructorBuilder.GetILGenerator();  ilOfCtor.Emit(OpCodes.Ldarg_0);  ilOfCtor.Emit(OpCodes.Newobj, interceptorAttributeType.GetConstructor(new Type[0]));  ilOfCtor.Emit(OpCodes.Stfld, fieldInterceptor);  ilOfCtor.Emit(OpCodes.Ret);  }  // ---- define methods ----  var methodsOfType = impType.GetMethods(BindingFlags.Public | BindingFlags.Instance);  string[] ignoreMethodName = new[] { "GetType", "ToString", "GetHashCode", "Equals" };  foreach (var method in methodsOfType)  {  //ignore method  if (ignoreMethodName.Contains(method.Name))   return;  var methodParameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();  MethodAttributes methodAttributes;  if (inheritMode)   methodAttributes = MethodAttributes.Public | MethodAttributes.Virtual;  else   methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final;  var methodBuilder = typeBuilder.DefineMethod(method.Name, methodAttributes, CallingConventions.Standard, method.ReturnType, methodParameterTypes);  var ilMethod = methodBuilder.GetILGenerator();  // set local field  var impObj = ilMethod.DeclareLocal(impType);  //instance of imp object  var methodName = ilMethod.DeclareLocal(typeof(string)); //instance of method name  var parameters = ilMethod.DeclareLocal(typeof(object[])); //instance of parameters  var result = ilMethod.DeclareLocal(typeof(object));  //instance of result  LocalBuilder actionAttributeObj = null;  //attribute init  Type actionAttributeType = null;  if (method.GetCustomAttribute(typeof(ActionBaseAttribute)) != null || impType.GetCustomAttribute(typeof(ActionBaseAttribute)) != null)  {   //method can override class attrubute   if (method.GetCustomAttribute(typeof(ActionBaseAttribute)) != null)   {   actionAttributeType = method.GetCustomAttribute(typeof(ActionBaseAttribute)).GetType();   }   else if (impType.GetCustomAttribute(typeof(ActionBaseAttribute)) != null)   {   actionAttributeType = impType.GetCustomAttribute(typeof(ActionBaseAttribute)).GetType();   }   actionAttributeObj = ilMethod.DeclareLocal(actionAttributeType);   ilMethod.Emit(OpCodes.Newobj, actionAttributeType.GetConstructor(new Type[0]));   ilMethod.Emit(OpCodes.Stloc, actionAttributeObj);  }  //instance imp  ilMethod.Emit(OpCodes.Newobj, impType.GetConstructor(new Type[0]));  ilMethod.Emit(OpCodes.Stloc, impObj);  //if no attribute  if (fieldInterceptor != null || actionAttributeObj != null)  {   ilMethod.Emit(OpCodes.Ldstr, method.Name);   ilMethod.Emit(OpCodes.Stloc, methodName);   ilMethod.Emit(OpCodes.Ldc_I4, methodParameterTypes.Length);   ilMethod.Emit(OpCodes.Newarr, typeof(object));   ilMethod.Emit(OpCodes.Stloc, parameters);   // build the method parameters   for (var j = 0; j < methodParameterTypes.Length; j++)   {   ilMethod.Emit(OpCodes.Ldloc, parameters);   ilMethod.Emit(OpCodes.Ldc_I4, j);   ilMethod.Emit(OpCodes.Ldarg, j + 1);   //box   ilMethod.Emit(OpCodes.Box, methodParameterTypes[j]);   ilMethod.Emit(OpCodes.Stelem_Ref);   }  }  //dynamic proxy action before  if (actionAttributeType != null)  {   //load arguments   ilMethod.Emit(OpCodes.Ldloc, actionAttributeObj);   ilMethod.Emit(OpCodes.Ldloc, methodName);   ilMethod.Emit(OpCodes.Ldloc, parameters);   ilMethod.Emit(OpCodes.Call, actionAttributeType.GetMethod("Before"));  }  if (interceptorAttributeType != null)  {   //load arguments   ilMethod.Emit(OpCodes.Ldarg_0);//this   ilMethod.Emit(OpCodes.Ldfld, fieldInterceptor);   ilMethod.Emit(OpCodes.Ldloc, impObj);   ilMethod.Emit(OpCodes.Ldloc, methodName);   ilMethod.Emit(OpCodes.Ldloc, parameters);   // call Invoke() method of Interceptor   ilMethod.Emit(OpCodes.Callvirt, interceptorAttributeType.GetMethod("Invoke"));  }  else  {   //direct call method   if (method.ReturnType == typeof(void) && actionAttributeType == null)   {   ilMethod.Emit(OpCodes.Ldnull);   }   ilMethod.Emit(OpCodes.Ldloc, impObj);   for (var j = 0; j < methodParameterTypes.Length; j++)   {   ilMethod.Emit(OpCodes.Ldarg, j + 1);   }   ilMethod.Emit(OpCodes.Callvirt, impType.GetMethod(method.Name));   //box   if (actionAttributeType != null)   {   if (method.ReturnType != typeof(void))    ilMethod.Emit(OpCodes.Box, method.ReturnType);   else    ilMethod.Emit(OpCodes.Ldnull);   }  }  //dynamic proxy action after  if (actionAttributeType != null)  {   ilMethod.Emit(OpCodes.Stloc, result);   //load arguments   ilMethod.Emit(OpCodes.Ldloc, actionAttributeObj);   ilMethod.Emit(OpCodes.Ldloc, methodName);   ilMethod.Emit(OpCodes.Ldloc, result);   ilMethod.Emit(OpCodes.Call, actionAttributeType.GetMethod("After"));  }  // pop the stack if return void  if (method.ReturnType == typeof(void))  {   ilMethod.Emit(OpCodes.Pop);  }  else  {   //unbox,if direct invoke,no box   if (fieldInterceptor != null || actionAttributeObj != null)   {   if (method.ReturnType.IsValueType)    ilMethod.Emit(OpCodes.Unbox_Any, method.ReturnType);   else    ilMethod.Emit(OpCodes.Castclass, method.ReturnType);   }  }  // complete  ilMethod.Emit(OpCodes.Ret);  } } }

里面實現了兩種代理方式,一種是 面向接口實現 的方式,另一種是 繼承重寫 的方式。

但是繼承重寫的方式需要把業務類的所有方法寫成virtual虛方法,動態類會重寫該方法。

我們從上一節的Demo中獲取到運行時生成的代理類dll,用ILSpy反編譯查看源代碼:

AOP,靜態代理,動態代理,Emit

可以看到,我們的代理類分別調用了我們特性標簽中的各項方法。

核心代碼分析(源代碼在上面折疊部位已經貼出):

AOP,靜態代理,動態代理,Emit

解釋:如果該方法存在Action標簽,那么加載 action 標簽實例化對象,加載參數,執行Before方法;如果該方法存在Interceptor標簽,那么使用類字段this._interceptor調用該標簽的Invoke方法。

AOP,靜態代理,動態代理,Emit

解釋:如果面的Interceptor特性標簽不存在,那么會加載當前掃描的方法對應的參數,直接調用方法;如果Action標簽存在,則將剛才調用的結果包裝成object對象傳遞到After方法中。

這里如果目標參數是object類型,而實際參數是直接調用返回的明確的值類型,需要進行裝箱操作,否則運行時報調用內存錯誤異常。

AOP,靜態代理,動態代理,Emit

解釋:如果返回值是void類型,則直接結束并返回結果;如果返回值是值類型,則需要手動拆箱操作,如果是引用類型,那么需要類型轉換操作。

IL實現的細節,這里不做重點討論。

【系統測試】  

1.接口實現方式,Api測試(各種標簽使用方式對應的不同類型的方法調用):

AOP,靜態代理,動態代理,Emit

結論:對于上述窮舉的類型,各種標簽使用方式皆成功打出了日志;

2.繼承方式,Api測試(各種標簽使用方式對應的不同類型的方法調用):

AOP,靜態代理,動態代理,Emit

結論:繼承方式和接口實現方式的效果是一樣的,只是方法上需要不同的實現調整;

3.直接調用三個方法百萬次性能結果:

AOP,靜態代理,動態代理,Emit

結論:直接調用三個方法百萬次調用耗時 58ms

4.使用實現接口方式三個方法百萬次調用結果

AOP,靜態代理,動態代理,Emit

結論:結果見上圖,需要注意是三個方法百萬次調用,也就是300w次的方法調用

5.使用繼承方式三個方法百萬次調用結果

AOP,靜態代理,動態代理,Emit

結論:結果見上圖,需要注意是三個方法百萬次調用,也就是300w次的方法調用

事實證明,IL Emit的實現方式性能還是很高的。

綜合分析:

通過各種的調用分析,可以看出使用代理以后和原生方法調用相比性能損耗在哪里。性能差距最大的,也是耗時最多的實現方式就是添加了全類方法代理而且是使用Invoke進行全方法切面方式。該方式耗時的原因是使用了反射Invoke的方法。

直接添加Action代理類實現 Before和After的方式和原生差距不大,主要損耗在After觸發時的拆裝箱上。

綜上分析,我們使用的時候,盡量針對性地對某一個方法進行AOP注入,而盡量不要全類方法進行AOP注入。

【總結】

通過自己實現一個AOP的動態注入框架,對Emit有了更加深入的了解,最重要的是,對CLR IL代碼的執行過程有了一定的認知,受益匪淺。

該方法在使用的過程中也發現了問題,比如有ref和out類型的參數時,會出現問題,需要后續繼續改進

本文的源代碼已托管在GitHub上,又需要可以自行拿?。樖諷tar哦~):https://github.com/sevenTiny/CodeArts

該代碼的位置在 CodeArts.CSharp 分區下

AOP,靜態代理,動態代理,Emit

VS打開后,可以在 EmitDynamicProxy 分區下找到;本博客所有的測試項目都在項目中可以找到。

AOP,靜態代理,動態代理,Emit

再次放上源代碼地址,供一起學習的朋友參考,希望能幫助到你:https://github.com/sevenTiny/CodeArts

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到c#教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美亚洲日本网站| 精品女同一区二区三区在线播放| 亚洲成人av片在线观看| 国产精品久久久久久久久久久久久久| 国产三级精品网站| 日本一区二区不卡| 国内成人精品一区| 国产视频丨精品|在线观看| 亚洲人成啪啪网站| 国产在线观看精品一区二区三区| 国产日韩综合一区二区性色av| 国产在线一区二区三区| 亚洲97在线观看| 国产精品视频最多的网站| www.久久久久久.com| 久久久久久久久亚洲| 亚洲全黄一级网站| 欧美天天综合色影久久精品| 久久免费视频这里只有精品| 亚洲性日韩精品一区二区| 日韩精品免费综合视频在线播放| 欧美午夜久久久| 国产精品mp4| 日韩av在线天堂网| 国产91|九色| 粗暴蹂躏中文一区二区三区| 国产精品久久久久久久久久小说| 国产精品视频免费在线| 国产精品久久久久久久电影| 51ⅴ精品国产91久久久久久| 日韩天堂在线视频| 色偷偷亚洲男人天堂| 性色av一区二区三区| 68精品国产免费久久久久久婷婷| 国产成人精彩在线视频九色| 日韩精品中文字幕有码专区| 国产精品国产三级国产aⅴ浪潮| 欧美野外wwwxxx| 黄色91在线观看| 精品亚洲一区二区三区在线观看| 国产精品久久久久久久久久新婚| 热久久免费视频精品| 亚洲性视频网站| 国产999在线观看| 欧美整片在线观看| 久久久免费高清电视剧观看| 国产精品在线看| 视频在线观看一区二区| 91亚洲精华国产精华| 日日狠狠久久偷偷四色综合免费| 久久久久久久久综合| 红桃视频成人在线观看| 精品中文字幕在线观看| 中文字幕国产亚洲2019| 日韩中文字幕久久| 91在线视频一区| 日韩一区视频在线| 欧美精品videos另类日本| 在线播放国产一区二区三区| 欧美韩日一区二区| 日韩av综合中文字幕| 欧美国产日韩一区二区三区| 亚洲无限av看| 久久久女人电视剧免费播放下载| 国产精品吹潮在线观看| 亚洲美女又黄又爽在线观看| 久久免费视频观看| 久久久免费高清电视剧观看| 日本精品视频在线观看| 欧美日韩综合视频网址| 欧美性受xxxx黑人猛交| 国产亚洲a∨片在线观看| 国产精品av电影| 成人黄色免费在线观看| 91国产视频在线播放| 欧美性xxxx极品高清hd直播| 久久久久久欧美| 亚洲国产97在线精品一区| 久久精品男人天堂| 国产一级揄自揄精品视频| 日韩av在线高清| 亚洲电影免费观看高清完整版在线观看| 欧美激情奇米色| 久久夜精品va视频免费观看| 日韩美女在线观看| 日本久久久久久| 色老头一区二区三区在线观看| 在线观看中文字幕亚洲| 欧美日韩亚洲天堂| 亚洲精品美女在线观看播放| 国产精品91免费在线| 中文字幕欧美精品日韩中文字幕| 国产午夜精品免费一区二区三区| 精品无码久久久久久国产| 欧美日韩激情网| 中文字幕不卡在线视频极品| 亚洲免费影视第一页| 深夜精品寂寞黄网站在线观看| 久久精品国产清自在天天线| 国产精品一区二区在线| 久久99精品国产99久久6尤物| 日韩有码片在线观看| 亚洲无av在线中文字幕| 久久久亚洲影院你懂的| 国产日韩在线一区| 91av在线视频观看| 亚洲欧美中文日韩在线v日本| 尤物九九久久国产精品的特点| 国产精品99免视看9| 俺去了亚洲欧美日韩| 久久久久久久影视| 亚洲精品视频中文字幕| 久久久人成影片一区二区三区观看| 精品中文字幕久久久久久| 久久精品国产亚洲一区二区| 欧美影院在线播放| 国产欧亚日韩视频| 国产99久久精品一区二区永久免费| 高清在线视频日韩欧美| 欧美视频免费在线观看| 欧美成人免费在线观看| 中文字幕亚洲综合久久筱田步美| 亚洲欧美另类国产| 国产日韩精品在线播放| 国产最新精品视频| 国产婷婷成人久久av免费高清| 日韩视频永久免费观看| 国产精品白嫩美女在线观看| 日韩精品亚洲精品| 97不卡在线视频| 91情侣偷在线精品国产| 91精品中国老女人| 午夜精品美女自拍福到在线| 欧美一区二区视频97| 国产91精品黑色丝袜高跟鞋| 欧美一区二区三区四区在线| 久久久亚洲精选| 亚洲图片制服诱惑| 亚洲欧洲视频在线| 亚洲人精选亚洲人成在线| 欧美精品在线观看91| 日韩av手机在线| 日韩欧美中文在线| 国产日本欧美一区| 热久久免费国产视频| 国产精品久久久久久久9999| 亚洲国产欧美一区| 欧美在线精品免播放器视频| 久久久久久久久久久免费| 国产深夜精品福利| 在线日韩日本国产亚洲| 亚洲无av在线中文字幕| 久久6免费高清热精品| 黄色成人av在线| 国产97色在线|日韩| 97在线精品国自产拍中文| 中文字幕亚洲国产| 高清欧美性猛交xxxx黑人猛交| 91精品国产综合久久久久久久久| 亚洲一区美女视频在线观看免费| 久久精品国产亚洲精品2020| 日本国产高清不卡| 成人国内精品久久久久一区| 中文字幕精品久久久久|