jcommander使用指南(JCommander)

      網友投稿 833 2022-05-30

      總覽

      在Java中經常會遇到需要輸入參數的情況,JCommander 是一個非常小的 Java 框架,可以輕松解析命令行參數。 下文完整解析JCommander的用法。

      例如您可以使用選項描述注釋字段:

      import com.beust.jcommander.Parameter; public class Args { @Parameter private List parameters = new ArrayList<>(); @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity") private Integer verbose = 1; @Parameter(names = "-groups", description = "Comma-separated list of group names to be run") private String groups; @Parameter(names = "-debug", description = "Debug mode") private boolean debug = false; }

      然后你只需讓 JCommander 解析:

      jcommander使用指南(JCommander)

      Args args = new Args(); String[] argv = { "-log", "2", "-groups", "unit" }; JCommander.newBuilder() .addObject(args) .build() .parse(argv); Assert.assertEquals(jct.verbose.intValue(), 2);

      其他例子:

      class Main { @Parameter(names={"--length", "-l"}) int length; @Parameter(names={"--pattern", "-p"}) int pattern; public static void main(String ... argv) { Main main = new Main(); JCommander.newBuilder() .addObject(main) .build() .parse(argv); main.run(); } public void run() { System.out.printf("%d %d", length, pattern); } }

      $ java Main -l 512 --pattern 2 512 2

      類型的選擇

      代表參數的字段可以是任何類型。 默認支持基本類型(整數、布爾值等… ),您可以編寫類型轉換器來支持任何其他類型(文件等… )。

      Boolean

      當在 boolean 或 Boolean 類型的字段上找到 Parameter 注釋時,JCommander 將其解釋為參數為0的選項:

      @Parameter(names = "-debug", description = "Debug mode") private boolean debug = false;

      這樣的參數在命令行上不需要任何額外的參數,如果在解析過程中檢測到,相應的字段將設置為 true。 如果您想定義一個默認為 true 的布爾參數,您可以將其聲明為具有 1 的元數。然后用戶必須明確指定他們想要的值:

      @Parameter(names = "-debug", description = "Debug mode", arity = 1) private boolean debug = true;

      使用以下任一方式調用:

      program -debug true program -debug false

      當在 String、Integer、int、Long 或 long 類型的字段上找到 Parameter 注釋時,JCommander 將解析以下參數并嘗試將其轉換為正確的類型:

      @Parameter(names = "-log", description = "Level of verbosity") private Integer verbose = 1;

      java Main -log 3

      將導致字段 verbose 接收值 3。但是:

      $ java Main -log test

      將導致拋出異常。

      Lists

      當在 List 類型的字段上找到 Parameter 注釋時,JCommander 會將其解釋為可以多次出現的選項:

      @Parameter(names = "-host", description = "The host") private List hosts = new ArrayList<>();

      將允許您解析以下命令行:

      $ java Main -host host1 -verbose -host host2

      當 JCommander 完成上述行的解析時,字段 hosts 將包含字符串“host1”和“host2”。

      Password

      如果您的參數之一是密碼或您不希望在歷史記錄中顯示或明確顯示的其他值,則可以將其聲明為密碼類型,然后 JCommander 將要求您在控制臺中輸入它:

      public class ArgsPassword { @Parameter(names = "-password", description = "Connection password", password = true) private String password; }

      當你運行你的程序時,你會得到如下提示:

      Value for -password (Connection password):

      在 JCommander 恢復之前,您需要在此時鍵入值。

      顯示輸入

      在 Java 6 中,默認情況下,您將無法看到您在提示符下輸入的密碼(Java 5 和更低版本將始終顯示密碼)。 但是,您可以通過將 echoInput 設置為 true 來覆蓋它(默認為 false,此設置僅在密碼為 true 時有效):

      public class ArgsPassword { @Parameter(names = "-password", description = "Connection password", password = true, echoInput = true) private String password; }

      自定義類型(轉換器和拆分器)

      將參數綁定到自定義類型或更改 JCommander 拆分參數的方式(默認為逗號拆分),JCommander 提供了兩個接口 IStringConverter 和 IParameterSplitter。

      自定義類型 - 單值

      使用@Parameter 的converter= 屬性或實現IStringConverterFactory。

      默認情況下,JCommander 僅將命令行解析為基本類型(字符串、布爾值、整數和長整數)。 很多時候,您的應用程序實際上需要更復雜的類型(例如文件、主機名、列表等)。 為此,您可以通過實現以下接口來編寫類型轉換器:

      public interface IStringConverter { T convert(String value); }

      例如,這是一個將字符串轉換為文件的轉換器:

      public class FileConverter implements IStringConverter { @Override public File convert(String value) { return new File(value); } }

      然后,您需要做的就是使用正確的類型聲明您的字段并將轉換器指定為屬性:

      @Parameter(names = "-file", converter = FileConverter.class) File file;

      JCommander 附帶了一些常見的轉換器(有關更多信息,請參閱 IStringConverter 的實現)。

      提示:

      如果轉換器用于列表字段:

      @Parameter(names = "-files", converter = FileConverter.class) List files;

      應用程序調用如下:

      $ java App -files file1,file2,file3

      JCommander 會將字符串 file1,file2,file3 拆分為 file1,file2,file3 并將其一一提供給轉換器。

      有關解析值列表的替代解決方案,請參閱自定義類型 - 列表值。

      如果您使用的自定義類型在您的應用程序中出現多次,則必須在每個注釋中指定轉換器可能會變得乏味。 為了解決這個問題,您可以使用 IStringConverterFactory:

      public interface IStringConverterFactory { Class> getConverter(Class forType); }

      例如,假設您需要解析表示主機和端口的字符串:

      $ java App -target example.com:8080

      定義持有類

      public class HostPort { public HostPort(String host, String port) { this.host = host; this.port = port; } final String host; final Integer port; }

      以及創建此類實例的字符串轉換器:

      class HostPortConverter implements IStringConverter { @Override public HostPort convert(String value) { String[] s = value.split(":"); return new HostPort(s[0], Integer.parseInt(s[1])); } }

      工廠很簡單:

      public class Factory implements IStringConverterFactory { public Class> getConverter(Class forType) { if (forType.equals(HostPort.class)) return HostPortConverter.class; else return null; }

      您現在可以使用 HostPort 類型作為不帶任何 converterClass 屬性的參數:

      public class ArgsConverterFactory { @Parameter(names = "-hostport") private HostPort hostPort; }

      您需要做的就是將工廠添加到您的 JCommander 對象中:

      ArgsConverterFactory a = new ArgsConverterFactory(); JCommander jc = JCommander.newBuilder() .addObject(a) .addConverterFactory(new Factory()) .build() .parse("-hostport", "example.com:8080"); Assert.assertEquals(a.hostPort.host, "example.com"); Assert.assertEquals(a.hostPort.port.intValue(), 8080);

      使用字符串轉換器工廠的另一個優點是您的工廠可以來自依賴注入框架。

      自定義類型 - 列表值

      使用 @Parameter 注釋的 listConverter= 屬性并分配自定義 IStringConverter 實現以將字符串轉換為值列表。

      如果您的應用程序需要復雜類型的列表,請通過實現與以前相同的接口來編寫列表類型轉換器:

      public interface IStringConverter { T convert(String value); }

      其中 T 是一個列表。

      例如,這是一個將字符串轉換為 List 的列表轉換器:

      public class FileListConverter implements IStringConverter> { @Override public List convert(String files) { String [] paths = files.split(","); List fileList = new ArrayList<>(); for(String path : paths){ fileList.add(new File(path)); } return fileList; } }

      然后,您需要做的就是使用正確的類型聲明您的字段并將列表轉換器指定為屬性:

      @Parameter(names = "-files", listConverter = FileListConverter.class) List file;

      現在,如果您像以下示例一樣調用應用程序:

      $ java App -files file1,file2,file3

      拆分

      使用 @Parameter 注釋的 splitter= 屬性并分配自定義 IParameterSplitter 實現來處理參數在子部分中的拆分方式。

      默認情況下,JCommander 會嘗試以逗號分隔 List 字段類型的參數。

      要將參數拆分到其他字符上,您可以通過實現以下接口編寫自定義拆分器:

      public interface IParameterSplitter { List split(String value); }

      例如,這是一個拆分器,它使用分號拆分字符串:

      public static class SemiColonSplitter implements IParameterSplitter { public List split(String value) { return Arrays.asList(value.split(";")); } }

      然后,您需要做的就是使用正確的類型聲明您的字段并將拆分器指定為屬性:

      @Parameter(names = "-files", converter = FileConverter.class, splitter = SemiColonSplitter.class) List files;

      JCommander 會將字符串 file1;file2;file3 拆分為 file1、file2、file3 并將其一一提供給轉換器。

      參數驗證

      參數驗證可以通過兩種不同的方式執行:在單個參數級別或全局。

      單個參數驗證

      您可以通過提供一個實現以下接口的類來要求 JCommander 對您的參數執行早期驗證:

      public interface IParameterValidator { /** * Validate the parameter. * * @param name The name of the parameter (e.g. "-host"). * @param value The value of the parameter that we need to validate * * @throws ParameterException Thrown if the value of the parameter is invalid. */ void validate(String name, String value) throws ParameterException; }

      這是一個示例實現,它將確保參數是一個正整數:

      public class PositiveInteger implements IParameterValidator { public void validate(String name, String value) throws ParameterException { int n = Integer.parseInt(value); if (n < 0) { throw new ParameterException("Parameter " + name + " should be positive (found " + value +")"); } } }

      在 @Parameter 注釋的 validateWith 屬性中指定實現此接口的類的名稱:

      @Parameter(names = "-age", validateWith = PositiveInteger.class) private Integer age;

      嘗試將負整數傳遞給此選項將導致拋出 ParameterException。

      可以指定多個驗證器:

      @Parameter(names = "-count", validateWith = { PositiveInteger.class, CustomOddNumberValidator.class }) private Integer value;

      全局參數驗證

      使用 JCommander 解析參數后,您可能希望對這些參數執行額外的驗證,例如確保兩個互斥參數未同時指定。 由于此類驗證涉及所有潛在的組合,JCommander 不提供任何基于注釋的解決方案來執行此驗證,因為這種方法必然會受到 Java 注釋的本質的限制。 相反,您應該簡單地在 Java 中對 JCommander 剛剛解析的所有參數執行此驗證。

      主要參數

      到目前為止,我們看到的所有@Parameter 注釋都定義了一個名為names 的屬性。 您可以定義一個(最多一個)參數而不使用任何此類屬性。 此參數可以是 List 或單個字段(例如 String 或具有轉換器的類型,例如 File),在這種情況下,只需要一個主要參數。

      @Parameter(description = "Files") private List files = new ArrayList<>(); @Parameter(names = "-debug", description = "Debugging level") private Integer debug = 1;

      將允許您解析:

      $ java Main -debug file1 file2

      私有參數

      參數也可以是私有的:

      public class ArgsPrivate { @Parameter(names = "-verbose") private Integer verbose = 1; public Integer getVerbose() { return verbose; } } ArgsPrivate args = new ArgsPrivate(); JCommander.newBuilder() .addObject(args) .build() .parse("-verbose", "3"); Assert.assertEquals(args.getVerbose().intValue(), 3);

      參數分隔符

      默認情況下,參數由空格分隔,但您可以更改此設置以允許使用不同的分隔符:

      $ java Main -log:3

      或者

      $ java Main -level=42

      您使用 @Parameters 注釋定義分隔符:

      @Parameters(separators = "=") public class SeparatorEqual { @Parameter(names = "-level") private Integer level = 2; }

      多重描述

      您可以將參數描述分布在多個類上。 例如,您可以定義以下兩個類:

      public class ArgsMaster { @Parameter(names = "-master") private String master; } public class ArgsSlave { @Parameter(names = "-slave") private String slave; }

      并將這兩個對象傳遞給 JCommander:

      ArgsMaster m = new ArgsMaster(); ArgsSlave s = new ArgsSlave(); String[] argv = { "-master", "master", "-slave", "slave" }; JCommander.newBuilder() .addObject(new Object[] { m , s }) .build() .parse(argv); Assert.assertEquals(m.master, "master"); Assert.assertEquals(s.slave, "slave");

      @ 語法

      JCommander 支持 @ 語法,它允許您將所有選項放入文件中并將此文件作為參數傳遞:

      /tmp/parameters

      -verbose file1 file2 file3

      $ java Main @/tmp/parameters

      參數的多個值

      固定參數數量

      如果您的某些參數需要多個值,例如以下示例,在 -pairs 之后需要兩個值:

      $ java Main -pairs slave master foo.xml

      那么您需要使用 arity 屬性定義您的參數并將該參數設為 List

      @Parameter(names = "-pairs", arity = 2, description = "Pairs") private List pairs;

      您不需要為 boolean 或 Boolean 類型的參數(默認 arity 為 0)以及 String、Integer、int、Long 和 long 類型(默認 arity 為 1)的參數指定 arity。

      另外,請注意,對于定義元數的參數,只允許使用 List。 如果您需要的參數是 Integer 或其他類型(此限制是由于 Java 的擦除),您將不得不自己轉換這些值。

      可變參數

      您可以指定一個參數可以接收不定數量的參數,直到下一個選項。 例如:

      program -foo a1 a2 a3 -bar program -foo a1 -bar

      這樣的參數可以用兩種不同的方式解析。

      如果以下參數的數量未知,則您的參數必須是 List 類型,并且您需要將布爾變量 Arity 設置為 true:

      @Parameter(names = "-foo", variableArity = true) public List foo = new ArrayList<>();

      或者,您可以根據出現的順序定義一個將存儲以下參數的類:

      static class MvParameters { @SubParameter(order = 0) String from; @SubParameter(order = 1) String to; } @Test public void arity() { class Parameters { @Parameter(names = {"--mv"}, arity = 2) private MvParameters mvParameters; } Parameters args = new Parameters(); JCommander.newBuilder() .addObject(args) .args(new String[]{"--mv", "from", "to"}) .build(); Assert.assertNotNull(args.mvParameters); Assert.assertEquals(args.mvParameters.from, "from"); Assert.assertEquals(args.mvParameters.to, "to"); }

      多個選項名稱

      您可以指定多個選項名稱:

      @Parameter(names = { "-d", "--outputDirectory" }, description = "Directory") private String outputDirectory;

      將允許以下兩種語法:

      $ java Main -d /tmp $ java Main --outputDirectory /tmp

      其他選項配置

      您可以通過幾種不同的方式配置如何查找選項:

      JCommander#setCaseSensitiveOptions(boolean):指定選項是否區分大小寫。 如果使用 false 調用此方法,則“-param”和“-PARAM”被視為相等。

      JCommander#setAllowAbbreviatedOptions(boolean):指定用戶是否可以傳遞縮寫選項。 如果使用 true 調用此方法,則用戶可以通過“-par”來指定名為 -param 的選項。 如果縮寫名稱不明確,JCommander 將拋出 ParameterException。

      必選和可選參數

      如果您的某些參數是強制性的,您可以使用 required 屬性(默認為 false):

      @Parameter(names = "-host", required = true) private String host;

      如果未指定此參數,JCommander 將拋出異常,告訴您缺少哪些選項。

      默認值

      為參數指定默認值的最常見方法是在聲明時初始化字段:

      private Integer logLevel = 3;

      對于更復雜的情況,您可能希望能夠在多個主要類中重用相同的默認值,或者能夠在一個集中的位置(例如 .properties 或 XML 文件)中指定這些默認值。 在這種情況下,您可以使用 IDefaultProvider:

      public interface IDefaultProvider { /** * @param optionName The name of the option as specified in the names() attribute * of the @Parameter option (e.g. "-file"). * * @return the default value for this option. */ String getDefaultValueFor(String optionName); }

      通過將此接口的實現傳遞給您的 JCommander 對象,您現在可以控制將哪個默認值用于您的選項。 請注意,此方法返回的值隨后將傳遞給字符串轉換器(如果有),從而允許您為所需的任何類型指定默認值。

      例如,這是一個默認提供程序,它將為除“-debug”之外的所有參數分配默認值 42:

      private static final IDefaultProvider DEFAULT_PROVIDER = new IDefaultProvider() { @Override public String getDefaultValueFor(String optionName) { return "-debug".equals(optionName) ? "false" : "42"; } }; // ... JCommander jc = JCommander.newBuilder() .addObject(new Args()) .defaultProvider(DEFAULT_PROVIDER) .build()

      Help參數

      如果您的參數之一用于顯示一些幫助或用法,則需要使用幫助屬性:

      @Parameter(names = "--help", help = true) private boolean help;

      如果您省略此布爾值,JCommander 將在嘗試驗證您的命令并發現您未指定某些必需參數時發出錯誤消息。

      更復雜的語法(命令)

      諸如 git 或 svn 之類的復雜工具可以理解一整套命令,每個命令都有自己特定的語法:

      $ git commit --amend -m "Bug fix"

      上面的“commit”等詞在 JCommander 中稱為“commands”,您可以通過為每個命令創建一個 arg 對象來指定它們:

      @Parameters(separators = "=", commandDescription = "Record changes to the repository") private class CommandCommit { @Parameter(description = "The list of files to commit") private List files; @Parameter(names = "--amend", description = "Amend") private Boolean amend = false; @Parameter(names = "--author") private String author; } @Parameters(commandDescription = "Add file contents to the index") public class CommandAdd { @Parameter(description = "File patterns to add to the index") private List patterns; @Parameter(names = "-i") private Boolean interactive = false; }

      然后你用你的 JCommander 對象注冊這些命令。

      在解析階段之后,您在 JCommander 對象上調用 getParsedCommand(),并根據返回的命令,您知道要檢查哪個 arg 對象(如果您想在命令行上出現第一個命令之前支持選項,您仍然可以使用主 arg 對象):

      CommandMain cm = new CommandMain(); CommandAdd add = new CommandAdd(); CommandCommit commit = new CommandCommit(); JCommander jc = JCommander.newBuilder() .addObject(cm) .addCommand("add", add); .addCommand("commit", commit); .build(); jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java"); Assert.assertTrue(cm.verbose); Assert.assertEquals(jc.getParsedCommand(), "commit"); Assert.assertTrue(commit.amend); Assert.assertEquals(commit.author, "cbeust"); Assert.assertEquals(commit.files, Arrays.asList("A.java", "B.java"));

      異常

      每當 JCommander 檢測到錯誤時,它都會拋出 ParameterException。 請注意,這是一個運行時異常,因為此時您的應用程序可能未正確初始化。 此外,ParameterException 包含 JCommander 實例,如果您需要顯示一些幫助,也可以在其上調用 usage()。

      使用

      您可以在用于解析命令行的 JCommander 實例上調用 usage() 以生成程序理解的所有選項的摘要:

      Usage:

      [options] Options: -debug Debug mode (default: false) -groups Comma-separated list of group names to be run * -log, -verbose Level of verbosity (default: 1) -long A long number (default: 0)

      您可以通過在 JCommander 對象上調用 setProgramName() 來自定義程序的名稱。 前面有星號的選項是必需的。

      您還可以通過設置@Parameter 注解的 order 屬性來指定調用 usage() 時每個選項的顯示順序:

      class Parameters { @Parameter(names = "--importantOption", order = 0) private boolean a; @Parameter(names = "--lessImportantOption", order = 3) private boolean b;

      隱藏參數

      如果您不希望某些參數出現在用法中,可以將它們標記為“隱藏”:

      @Parameter(names = "-debug", description = "Debug mode", hidden = true) private boolean debug = false;

      國際化

      您可以將參數的描述國際化。 首先使用類頂部的@Parameters 注釋來定義消息包的名稱,然后在所有需要翻譯的@Parameters 上使用descriptionKey 屬性而不是description。 此 descriptionKey 是消息包中字符串的鍵:

      @Parameters(resourceBundle = "MessageBundle") private class ArgsI18N2 { @Parameter(names = "-host", description = "Host", descriptionKey = "host") String hostName; }

      你的包需要定義這個鍵:

      host: H?te

      然后,JCommander 將使用默認語言環境來解析您的描述。

      參數委托

      如果您在同一個項目中編寫許多不同的工具,您可能會發現這些工具中的大多數都可以共享配置。 雖然您可以對對象使用繼承來避免重復此代碼,但對實現的單一繼承的限制可能會限制您的靈活性。 為了解決這個問題,JCommander 支持參數委托。

      當 JCommander 在您的一個對象中遇到使用 @ParameterDelegate 注釋的對象時,它的行為就好像該對象已添加為描述對象本身一樣:

      class Delegate { @Parameter(names = "-port") private int port; } class MainParams { @Parameter(names = "-v") private boolean verbose; @ParametersDelegate private Delegate delegate = new Delegate(); }

      上面的示例指定了一個委托參數 Delegate,然后在 MainParams 中引用該參數。 您只需將 MainParams 對象添加到您的 JCommander 配置中即可使用委托:

      MainParams p = new MainParams(); JCommander.newBuilder().addObject(p).build() .parse("-v", "-port", "1234"); Assert.assertTrue(p.isVerbose); Assert.assertEquals(p.delegate.port, 1234);

      動態參數

      JCommander 允許您指定編譯時未知的參數,例如“-Da=b -Dc=d”。 此類參數使用 @DynamicParameter 注釋指定,并且必須是 Map 類型。 動態參數允許在命令行中出現多次:

      @DynamicParameter(names = "-D", description = "Dynamic parameters go here") private Map params = new HashMap<>();

      您可以使用屬性 assignment 指定不同于 = 的分配字符串。

      自定義使用格式

      JCommander 允許您自定義 JCommander#usage() 方法的輸出。 您可以通過繼承 IUsageFormatter 然后調用 JCommander#setUsageFormatter(IUsageFormatter) 來做到這一點。

      僅打印參數名稱的用法格式化程序示例,由新行分隔,如下所示:

      class ParameterNamesUsageFormatter implements IUsageFormatter { // Extend other required methods as seen in DefaultUsageFormatter // This is the method which does the actual output formatting public void usage(StringBuilder out, String indent) { if (commander.getDescriptions() == null) { commander.createDescriptions(); } // Create a list of the parameters List params = Lists.newArrayList(); params.addAll(commander.getFields().values()); // Append all the parameter names if (params.size() > 0) { out.append("Options:\n"); for (ParameterDescription pd : params) { out.append(pd.getNames()).append("\n"); } } } }

      JCommander 在其他語言中的使用

      Kotlin

      class Args { @Parameter var targets: List = arrayListOf() @Parameter(names = arrayOf("-bf", "--buildFile"), description = "The build file") var buildFile: String? = null @Parameter(names = arrayOf("--checkVersions"), description = "Check if there are any newer versions of the dependencies") var checkVersions = false }

      Groovy

      import com.beust.jcommander.* class Args { @Parameter(names = ["-f", "--file"], description = "File to load. Can be specified multiple times.") List file } new Args().with { JCommander.newBuilder().addObject(it).build().parse(argv) file.each { println "file: ${new File(it).name}" } }

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:excel表格內繪畫實線的操作教程全解(excel表格如何畫實線)
      下一篇:是時候使用Kotlin編程了(kotlin可以做什么)
      相關文章
      亚洲精品成人片在线观看| 亚洲国产精品无码AAA片| 亚洲人成人网站在线观看| 亚洲天然素人无码专区| 亚洲天天在线日亚洲洲精| 亚洲精品乱码久久久久久中文字幕| 亚洲AV无码国产一区二区三区| 亚洲人成在线中文字幕| 亚洲一区在线视频| 亚洲色图综合网站| 亚洲精品国产手机| 亚洲成在人线中文字幕| 亚洲免费在线观看视频| 亚洲国产精品线观看不卡| 78成人精品电影在线播放日韩精品电影一区亚洲 | 亚洲VA成无码人在线观看天堂| 精品亚洲综合在线第一区| 亚洲成AV人片在线观看ww| 亚洲AV无码成人网站久久精品大 | 亚洲国产成人久久综合| 国产成人人综合亚洲欧美丁香花| 亚洲AV香蕉一区区二区三区| 四虎亚洲国产成人久久精品| 亚洲精品国产高清不卡在线| 久久久久亚洲?V成人无码| 亚洲人成人网站色www| 亚洲产国偷V产偷V自拍色戒| 亚洲国产一区二区a毛片| 亚洲神级电影国语版| 国产精品亚洲片在线va| 亚洲欧美日韩自偷自拍| 五月天婷亚洲天综合网精品偷| 亚洲欧洲自拍拍偷精品 美利坚| 亚洲综合区小说区激情区| 国产亚洲av片在线观看16女人| 亚洲成人在线网站| 亚洲婷婷综合色高清在线| 亚洲熟妇AV日韩熟妇在线| 日本亚洲中午字幕乱码| 国产亚洲精品成人AA片新蒲金| 亚洲不卡中文字幕无码|