国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Lombok為啥這么牛逼?SpringBoot和IDEA官方都要支持它

瀏覽:81日期:2023-09-23 08:08:16

最近 IDEA 2020最后一個(gè)版本發(fā)布了 ,已經(jīng)內(nèi)置了Lombok插件,SpringBoot 2.1.x之后的版本也在Starter中內(nèi)置了Lombok依賴(lài)。為什么他們都要支持Lombok呢?今天我來(lái)講講Lombok的使用,看看它有何神奇之處!

Lombok簡(jiǎn)介

Lombok是一款Java代碼功能增強(qiáng)庫(kù),在Github上已有9.8k+Star。它會(huì)自動(dòng)集成到你的編輯器和構(gòu)建工具中,從而使你的Java代碼更加生動(dòng)有趣。通過(guò)Lombok的注解,你可以不用再寫(xiě)getter、setter、equals等方法,Lombok將在編譯時(shí)為你自動(dòng)生成。

Lombok集成

首先我們需要在IDEA中安裝好Lombok插件,如果你使用的是最新版IDEA 2020.3,則Lombok插件已經(jīng)內(nèi)置,無(wú)需安裝。

Lombok為啥這么牛逼?SpringBoot和IDEA官方都要支持它

之后在項(xiàng)目的pom.xml文件中添加Lombok依賴(lài),SpringBoot 2.1.x版本后無(wú)需指定Lombok版本,SpringBoot在 spring-boot-dependencies 中已經(jīng)內(nèi)置。

<!--lombok依賴(lài)--><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional></dependency>Lombok使用

Lombok中有很多注解,這些注解使得我們可以更加方便的編寫(xiě)Java代碼,下面介紹下這些注解的使用。

val

使用val注解可以取代任意類(lèi)型作為局部變量,這樣我們就不用寫(xiě)復(fù)雜的ArrayList和Map.Entry類(lèi)型了,具體例子如下。

/** * Created by macro on 2020/12/16. */public class ValExample { public static void example() { //val代替ArrayList<String>和String類(lèi)型 val example = new ArrayList<String>(); example.add('Hello World!'); val foo = example.get(0); System.out.println(foo.toLowerCase()); } public static void example2() { //val代替Map.Entry<Integer,String>類(lèi)型 val map = new HashMap<Integer, String>(); map.put(0, 'zero'); map.put(5, 'five'); for (val entry : map.entrySet()) { System.out.printf('%d: %sn', entry.getKey(), entry.getValue()); } } public static void main(String[] args) { example(); example2(); }}

當(dāng)我們使用了val注解后,Lombok會(huì)從局部變量的初始化表達(dá)式推斷出具體類(lèi)型,編譯后會(huì)生成如下代碼。

public class ValExample { public ValExample() { } public static void example() { ArrayList<String> example = new ArrayList(); example.add('Hello World!'); String foo = (String)example.get(0); System.out.println(foo.toLowerCase()); } public static void example2() { HashMap<Integer, String> map = new HashMap(); map.put(0, 'zero'); map.put(5, 'five'); Iterator var1 = map.entrySet().iterator(); while(var1.hasNext()) { Entry<Integer, String> entry = (Entry)var1.next(); System.out.printf('%d: %sn', entry.getKey(), entry.getValue()); } }}

@NonNull

在方法上使用@NonNull注解可以做非空判斷,如果傳入空值的話(huà)會(huì)直接拋出NullPointerException。

/** * Created by macro on 2020/12/16. */public class NonNullExample { private String name; public NonNullExample(@NonNull String name){ this.name = name; } public static void main(String[] args) { new NonNullExample('test'); //會(huì)拋出NullPointerException new NonNullExample(null); }}

編譯后會(huì)在構(gòu)造器中添加非空判斷,具體代碼如下。

public class NonNullExample { private String name; public NonNullExample(@NonNull String name) { if (name == null) { throw new NullPointerException('name is marked non-null but is null'); } else { this.name = name; } } public static void main(String[] args) { new NonNullExample('test'); new NonNullExample((String)null); }}

@Cleanup

當(dāng)我們?cè)贘ava中使用資源時(shí),不可避免地需要在使用后關(guān)閉資源。使用@Cleanup注解可以自動(dòng)關(guān)閉資源。

/** * Created by macro on 2020/12/16. */public class CleanupExample { public static void main(String[] args) throws IOException { String inStr = 'Hello World!'; //使用輸入輸出流自動(dòng)關(guān)閉,無(wú)需編寫(xiě)try catch和調(diào)用close()方法 @Cleanup ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes('UTF-8')); @Cleanup ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] b = new byte[1024]; while (true) { int r = in.read(b); if (r == -1) break; out.write(b, 0, r); } String outStr = out.toString('UTF-8'); System.out.println(outStr); }}

編譯后Lombok會(huì)生成如下代碼。

public class CleanupExample { public CleanupExample() { } public static void main(String[] args) throws IOException { String inStr = 'Hello World!'; ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes('UTF-8')); try { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { byte[] b = new byte[1024]; while(true) { int r = in.read(b); if (r == -1) { String outStr = out.toString('UTF-8'); System.out.println(outStr); return; } out.write(b, 0, r); } } finally { if (Collections.singletonList(out).get(0) != null) { out.close(); } } } finally { if (Collections.singletonList(in).get(0) != null) { in.close(); } } }}

@Getter/@Setter

有了@Getter/@Setter注解,我們?cè)僖膊挥镁帉?xiě)getter/setter方法了。試想下之前即使我們使用IDEA自動(dòng)生成getter/setter方法,如果類(lèi)屬性的類(lèi)型和名稱(chēng)改了,又要重新生成getter/setter方法也是一件很麻煩的事情。

/** * Created by macro on 2020/12/17. */public class GetterSetterExample { @Getter @Setter private String name; @Getter @Setter(AccessLevel.PROTECTED) private Integer age; public static void main(String[] args) { GetterSetterExample example = new GetterSetterExample(); example.setName('test'); example.setAge(20); System.out.printf('name:%s age:%d',example.getName(),example.getAge()); }}

編譯后Lombok會(huì)生成如下代碼。

public class GetterSetterExample { private String name; private Integer age; public GetterSetterExample() { } public String getName() { return this.name; } public void setName(final String name) { this.name = name; } public Integer getAge() { return this.age; } protected void setAge(final Integer age) { this.age = age; }}

@ToString

把所有類(lèi)屬性都編寫(xiě)到toString方法中方便打印日志,是一件多么枯燥無(wú)味的事情。使用@ToString注解可以自動(dòng)生成toString方法,默認(rèn)會(huì)包含所有類(lèi)屬性,使用@ToString.Exclude注解可以排除屬性的生成。

/** * Created by macro on 2020/12/17. */@ToStringpublic class ToStringExample { @ToString.Exclude private Long id; private String name; private Integer age; public ToStringExample(Long id,String name,Integer age){ this.id =id; this.name = name; this.age = age; } public static void main(String[] args) { ToStringExample example = new ToStringExample(1L,'test',20); //自動(dòng)實(shí)現(xiàn)toString方法,輸出ToStringExample(name=test, age=20) System.out.println(example); }}

編譯后Lombok會(huì)生成如下代碼。

public class ToStringExample { private Long id; private String name; private Integer age; public ToStringExample(Long id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } public String toString() { return 'ToStringExample(name=' + this.name + ', age=' + this.age + ')'; }}

@EqualsAndHashCode

使用@EqualsAndHashCode注解可以自動(dòng)生成hashCode和equals方法,默認(rèn)包含所有類(lèi)屬性,使用@EqualsAndHashCode.Exclude可以排除屬性的生成。

/** * Created by macro on 2020/12/17. */@Getter@Setter@EqualsAndHashCodepublic class EqualsAndHashCodeExample { private Long id; @EqualsAndHashCode.Exclude private String name; @EqualsAndHashCode.Exclude private Integer age; public static void main(String[] args) { EqualsAndHashCodeExample example1 = new EqualsAndHashCodeExample(); example1.setId(1L); example1.setName('test'); example1.setAge(20); EqualsAndHashCodeExample example2 = new EqualsAndHashCodeExample(); example2.setId(1L); //equals方法只對(duì)比id,返回true System.out.println(example1.equals(example2)); }}

編譯后Lombok會(huì)生成如下代碼。

public class EqualsAndHashCodeExample { private Long id; private String name; private Integer age; public EqualsAndHashCodeExample() { } public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof EqualsAndHashCodeExample)) { return false; } else { EqualsAndHashCodeExample other = (EqualsAndHashCodeExample)o; if (!other.canEqual(this)) { return false; } else { Object this$id = this.getId(); Object other$id = other.getId(); if (this$id == null) { if (other$id != null) { return false; } } else if (!this$id.equals(other$id)) { return false; } return true; } } } protected boolean canEqual(final Object other) { return other instanceof EqualsAndHashCodeExample; } public int hashCode() { int PRIME = true; int result = 1; Object $id = this.getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); return result; }}

@XxConstructor

使用@XxConstructor注解可以自動(dòng)生成構(gòu)造方法,有@NoArgsConstructor、@RequiredArgsConstructor和@AllArgsConstructor三個(gè)注解可以使用。

@NoArgsConstructor:生成無(wú)參構(gòu)造函數(shù)。 @RequiredArgsConstructor:生成包含必須參數(shù)的構(gòu)造函數(shù),使用@NonNull注解的類(lèi)屬性為必須參數(shù)。 @AllArgsConstructor:生成包含所有參數(shù)的構(gòu)造函數(shù)。

/** * Created by macro on 2020/12/17. */@NoArgsConstructor@RequiredArgsConstructor(staticName = 'of')@AllArgsConstructorpublic class ConstructorExample { @NonNull private Long id; private String name; private Integer age; public static void main(String[] args) { //無(wú)參構(gòu)造器 ConstructorExample example1 = new ConstructorExample(); //全部參數(shù)構(gòu)造器 ConstructorExample example2 = new ConstructorExample(1L,'test',20); //@NonNull注解的必須參數(shù)構(gòu)造器 ConstructorExample example3 = ConstructorExample.of(1L); }}

編譯后Lombok會(huì)生成如下代碼。

public class ConstructorExample { @NonNull private Long id; private String name; private Integer age; public ConstructorExample() { } private ConstructorExample(@NonNull final Long id) { if (id == null) { throw new NullPointerException('id is marked non-null but is null'); } else { this.id = id; } } public static ConstructorExample of(@NonNull final Long id) { return new ConstructorExample(id); } public ConstructorExample(@NonNull final Long id, final String name, final Integer age) { if (id == null) { throw new NullPointerException('id is marked non-null but is null'); } else { this.id = id; this.name = name; this.age = age; } }}

@Data

@Data是一個(gè)方便使用的組合注解,是@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstructor的組合體。

/** * Created by macro on 2020/12/17. */@Datapublic class DataExample { @NonNull private Long id; @EqualsAndHashCode.Exclude private String name; @EqualsAndHashCode.Exclude private Integer age; public static void main(String[] args) { //@RequiredArgsConstructor已生效 DataExample example1 = new DataExample(1L); //@Getter @Setter已生效 example1.setName('test'); example1.setAge(20); //@ToString已生效 System.out.println(example1); DataExample example2 = new DataExample(1L); //@EqualsAndHashCode已生效 System.out.println(example1.equals(example2)); }}

編譯后Lombok會(huì)生成如下代碼。

public class DataExample { @NonNull private Long id; private String name; private Integer age; public DataExample(@NonNull final Long id) { if (id == null) { throw new NullPointerException('id is marked non-null but is null'); } else { this.id = id; } } @NonNull public Long getId() { return this.id; } public String getName() { return this.name; } public Integer getAge() { return this.age; } public void setId(@NonNull final Long id) { if (id == null) { throw new NullPointerException('id is marked non-null but is null'); } else { this.id = id; } } public void setName(final String name) { this.name = name; } public void setAge(final Integer age) { this.age = age; } public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof DataExample)) { return false; } else { DataExample other = (DataExample)o; if (!other.canEqual(this)) { return false; } else { Object this$id = this.getId(); Object other$id = other.getId(); if (this$id == null) { if (other$id != null) { return false; } } else if (!this$id.equals(other$id)) { return false; } return true; } } } protected boolean canEqual(final Object other) { return other instanceof DataExample; } public int hashCode() { int PRIME = true; int result = 1; Object $id = this.getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); return result; } public String toString() { return 'DataExample(id=' + this.getId() + ', name=' + this.getName() + ', age=' + this.getAge() + ')'; }}

@Value

使用@Value注解可以把類(lèi)聲明為不可變的,聲明后此類(lèi)相當(dāng)于final類(lèi),無(wú)法被繼承,其屬性也會(huì)變成final屬性。

/** * Created by macro on 2020/12/17. */@Valuepublic class ValueExample { private Long id; private String name; private Integer age; public static void main(String[] args) { //只能使用全參構(gòu)造器 ValueExample example = new ValueExample(1L,'test',20); // example.setName('andy') //沒(méi)有生成setter方法,會(huì)報(bào)錯(cuò) // example.name='andy' //字段被設(shè)置為final類(lèi)型,會(huì)報(bào)錯(cuò) }}

編譯后Lombok會(huì)生成如下代碼。

public final class ValueExample { private final Long id; private final String name; private final Integer age; public static void main(String[] args) { new ValueExample(1L, 'test', 20); } public ValueExample(final Long id, final String name, final Integer age) { this.id = id; this.name = name; this.age = age; } public Long getId() { return this.id; } public String getName() { return this.name; } public Integer getAge() { return this.age; }}

@Builder

使用@Builder注解可以通過(guò)建造者模式來(lái)創(chuàng)建對(duì)象,建造者模式加鏈?zhǔn)秸{(diào)用,創(chuàng)建對(duì)象太方便了!

/** * Created by macro on 2020/12/17. */@Builder@ToStringpublic class BuilderExample { private Long id; private String name; private Integer age; public static void main(String[] args) { BuilderExample example = BuilderExample.builder() .id(1L) .name('test') .age(20) .build(); System.out.println(example); }}

編譯后Lombok會(huì)生成如下代碼。

public class BuilderExample { private Long id; private String name; private Integer age; BuilderExample(final Long id, final String name, final Integer age) { this.id = id; this.name = name; this.age = age; } public static BuilderExample.BuilderExampleBuilder builder() { return new BuilderExample.BuilderExampleBuilder(); } public String toString() { return 'BuilderExample(id=' + this.id + ', name=' + this.name + ', age=' + this.age + ')'; } public static class BuilderExampleBuilder { private Long id; private String name; private Integer age; BuilderExampleBuilder() { } public BuilderExample.BuilderExampleBuilder id(final Long id) { this.id = id; return this; } public BuilderExample.BuilderExampleBuilder name(final String name) { this.name = name; return this; } public BuilderExample.BuilderExampleBuilder age(final Integer age) { this.age = age; return this; } public BuilderExample build() { return new BuilderExample(this.id, this.name, this.age); } public String toString() { return 'BuilderExample.BuilderExampleBuilder(id=' + this.id + ', name=' + this.name + ', age=' + this.age + ')'; } }}

@SneakyThrows

還在手動(dòng)捕獲并拋出異常?使用@SneakyThrows注解自動(dòng)實(shí)現(xiàn)試試!

/** * Created by macro on 2020/12/17. */public class SneakyThrowsExample { //自動(dòng)拋出異常,無(wú)需處理 @SneakyThrows(UnsupportedEncodingException.class) public static byte[] str2byte(String str){ return str.getBytes('UTF-8'); } public static void main(String[] args) { String str = 'Hello World!'; System.out.println(str2byte(str).length); }}

編譯后Lombok會(huì)生成如下代碼。

public class SneakyThrowsExample { public SneakyThrowsExample() { } public static byte[] str2byte(String str) { try { return str.getBytes('UTF-8'); } catch (UnsupportedEncodingException var2) { throw var2; } }}

@Synchronized

當(dāng)我們?cè)诙鄠€(gè)線(xiàn)程中訪(fǎng)問(wèn)同一資源時(shí),往往會(huì)出現(xiàn)線(xiàn)程安全問(wèn)題,以前我們往往使用synchronized關(guān)鍵字修飾方法來(lái)實(shí)現(xiàn)同步訪(fǎng)問(wèn)。使用@Synchronized注解同樣可以實(shí)現(xiàn)同步訪(fǎng)問(wèn)。

package com.macro.mall.tiny.example;import lombok.*;/** * Created by macro on 2020/12/17. */@Datapublic class SynchronizedExample { @NonNull private Integer count; @Synchronized @SneakyThrows public void reduceCount(Integer id) { if (count > 0) { Thread.sleep(500); count--; System.out.println(String.format('thread-%d count:%d', id, count)); } } public static void main(String[] args) { //添加@Synchronized三個(gè)線(xiàn)程可以同步調(diào)用reduceCount方法 SynchronizedExample example = new SynchronizedExample(20); new ReduceThread(1, example).start(); new ReduceThread(2, example).start(); new ReduceThread(3, example).start(); } @RequiredArgsConstructor static class ReduceThread extends Thread { @NonNull private Integer id; @NonNull private SynchronizedExample example; @Override public void run() { while (example.getCount() > 0) { example.reduceCount(id); } } }}

編譯后Lombok會(huì)生成如下代碼。

public class SynchronizedExample { private final Object $lock = new Object[0]; @NonNull private Integer count; public void reduceCount(Integer id) { try { synchronized(this.$lock) { if (this.count > 0) { Thread.sleep(500L); Integer var3 = this.count; Integer var4 = this.count = this.count - 1; System.out.println(String.format('thread-%d count:%d', id, this.count)); } } } catch (Throwable var7) { throw var7; } }}

@With

使用@With注解可以實(shí)現(xiàn)對(duì)原對(duì)象進(jìn)行克隆,并改變其一個(gè)屬性,使用時(shí)需要指定全參構(gòu)造方法。

@With@AllArgsConstructorpublic class WithExample { private Long id; private String name; private Integer age; public static void main(String[] args) { WithExample example1 = new WithExample(1L, 'test', 20); WithExample example2 = example1.withAge(22); //將原對(duì)象進(jìn)行clone并設(shè)置age,返回false System.out.println(example1.equals(example2)); }}

編譯后Lombok會(huì)生成如下代碼。

public class WithExample { private Long id; private String name; private Integer age; public WithExample withId(final Long id) { return this.id == id ? this : new WithExample(id, this.name, this.age); } public WithExample withName(final String name) { return this.name == name ? this : new WithExample(this.id, name, this.age); } public WithExample withAge(final Integer age) { return this.age == age ? this : new WithExample(this.id, this.name, age); } public WithExample(final Long id, final String name, final Integer age) { this.id = id; this.name = name; this.age = age; }}

@Getter(lazy=true)

當(dāng)我們獲取某一個(gè)屬性比較消耗資源時(shí),可以給@Getter添加 lazy=true 屬性實(shí)現(xiàn)懶加載,會(huì)生成Double Check Lock 樣板代碼對(duì)屬性進(jìn)行懶加載。

/** * Created by macro on 2020/12/17. */public class GetterLazyExample { @Getter(lazy = true) private final double[] cached = expensive(); private double[] expensive() { double[] result = new double[1000000]; for (int i = 0; i < result.length; i++) { result[i] = Math.asin(i); } return result; } public static void main(String[] args) { //使用Double Check Lock 樣板代碼對(duì)屬性進(jìn)行懶加載 GetterLazyExample example = new GetterLazyExample(); System.out.println(example.getCached().length); }}

編譯后Lombok會(huì)生成如下代碼。

public class GetterLazyExample { private final AtomicReference<Object> cached = new AtomicReference(); public GetterLazyExample() { } private double[] expensive() { double[] result = new double[1000000]; for(int i = 0; i < result.length; ++i) { result[i] = Math.asin((double)i); } return result; } public double[] getCached() { Object value = this.cached.get(); if (value == null) { synchronized(this.cached) { value = this.cached.get(); if (value == null) { double[] actualValue = this.expensive(); value = actualValue == null ? this.cached : actualValue; this.cached.set(value); } } } return (double[])((double[])(value == this.cached ? null : value)); }}

@Log

使用@Log注解,可以直接生成日志對(duì)象log,通過(guò)log對(duì)象可以直接打印日志。

/** * Created by macro on 2020/12/17. */@Logpublic class LogExample { public static void main(String[] args) { log.info('level info'); log.warning('level warning'); log.severe('level severe'); }}

編譯后Lombok會(huì)生成如下代碼。

public class LogExample { private static final Logger log = Logger.getLogger(LogExample.class.getName()); public LogExample() { } public static void main(String[] args) { log.info('level info'); log.warning('level warning'); log.severe('level severe'); }}

@Slf4j

使用Lombok生成日志對(duì)象時(shí),根據(jù)使用日志實(shí)現(xiàn)的不同,有多種注解可以使用。比如@Log、@Log4j、@Log4j2、@Slf4j等。

/** * Created by macro on 2020/12/17. */@Slf4jpublic class LogSlf4jExample { public static void main(String[] args) { log.info('level:{}','info'); log.warn('level:{}','warn'); log.error('level:{}', 'error'); }}

編譯后Lombok會(huì)生成如下代碼。

public class LogSlf4jExample { private static final Logger log = LoggerFactory.getLogger(LogSlf4jExample.class); public LogSlf4jExample() { } public static void main(String[] args) { log.info('level:{}', 'info'); log.warn('level:{}', 'warn'); log.error('level:{}', 'error'); }}Lombok原理

如果IDEA不安裝Lombok插件的話(huà),我們打開(kāi)使用Lombok的項(xiàng)目是無(wú)法通過(guò)編譯的。裝了以后IDEA才會(huì)提示我們Lombok為我們生成的方法和屬性。

使用了@Data注解以后,查看類(lèi)結(jié)構(gòu)可以發(fā)現(xiàn)getter、setter、toString等方法。

Lombok為啥這么牛逼?SpringBoot和IDEA官方都要支持它

打開(kāi)target目錄下的 .class 文件,我們可以看到Lombok為我們生成的代碼,可見(jiàn)Lombok是通過(guò)解析注解,然后在編譯時(shí)生成代碼來(lái)實(shí)現(xiàn)Java代碼的功能增強(qiáng)的。

Lombok為啥這么牛逼?SpringBoot和IDEA官方都要支持它

參考資料

官方文檔:https://projectlombok.org/features/all

項(xiàng)目源碼地址

https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-lombok

到此這篇關(guān)于Lombok為啥這么牛逼?SpringBoot和IDEA官方都要支持它的文章就介紹到這了,更多相關(guān)Lombok SpringBoot和IDEA內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 亚洲第一成年网站大全亚洲 | 欧美一级视频在线 | 亚洲理论在线观看 | 日韩中文字幕精品一区在线 | 国产成人综合亚洲一区 | 成人a免费视频播放 | 欧美另类videosbestsex高清 | 高清在线亚洲精品国产二区 | 我看毛片 | 欧美毛片在线 | 国产一区视频在线播放 | 亚洲天码中文字幕第一页 | 欧美成年黄网站色视频 | 欧美在线做爰高清视频 | 97国产大学生情侣11在线视频 | 日本三级日产三级国产三级 | 亚洲欧美日本综合一区二区三区 | a级做爰视频免费观看 | 久久国产精品二国产精品 | 91影视做在线观看免费 | 日本japanesevideo黑人 | 成人在线免费视频 | 小草青青神马影院 | 成人一级网站 | 一级aaaaaa毛片免费同男同女 | 欧美专区一区二区三区 | 毛片基地看看成人免费 | 久久婷婷影院 | 亚洲精品tv久久久久 | 国产做a爰片久久毛片 | 日韩久久久精品中文字幕 | 国产精品黄页在线播放免费 | 国产做a爰片久久毛片a | 中文字幕精品一区二区2021年 | 国产91页 | 精品无码三级在线观看视频 | 亚洲高清在线观看看片 | 日韩亚洲精品不卡在线 | 国产三级网站在线观看 | 九九亚洲精品 | 国产亚洲欧美日韩在线观看不卡 |