久久福利_99r_国产日韩在线视频_直接看av的网站_中文欧美日韩_久久一

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

SpringBoot java-jar命令行啟動原理解析

瀏覽:6日期:2022-08-30 08:24:37

在spring boot里,很吸引人的一個特性是可以直接把應(yīng)用打包成為一個jar/war,然后這個jar/war是可以直接啟動的,而不需要另外配置一個Web Server。那么spring boot如何啟動的呢?今天我們就來一起探究一下它的原理。首先我們來創(chuàng)建一個基本的spring boot工程來幫助我們分析,本次spring boot版本為 2.2.5.RELEASE。

// SpringBootDemo.java@SpringBootApplicationpublic class SpringBootDemo { public static void main(String[] args) { SpringApplication.run(SpringBootDemo.class); }}

下面是pom依賴:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency></dependencies><build> <finalName>springboot-demo</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build>

創(chuàng)建完工程后,執(zhí)行maven的打包命令,會生成兩個jar文件:

springboot-demo.jarspringboot-demo.jar.original

其中springboot-demo.jar.original是默認(rèn)的maven-jar-plugin生成的包。springboot-demo.jar是spring boot maven插件生成的jar包,里面包含了應(yīng)用的依賴,以及spring boot相關(guān)的類。下面稱之為executable jar或者fat jar。后者僅包含應(yīng)用編譯后的本地資源,而前者引入了相關(guān)的第三方依賴,這點(diǎn)從文件大小也能看出。

SpringBoot java-jar命令行啟動原理解析

圖1

關(guān)于executable jar,spring boot官方文檔中是這樣解釋的。

Executable jars (sometimes called “fat jars”) are archives containing your compiled classes along with all of the jar dependencies that your code needs to run.

Executable jar(有時稱為“fat jars”)是包含您的已編譯類以及代碼需要運(yùn)行的所有jar依賴項(xiàng)的歸檔文件。

Java does not provide any standard way to load nested jar files (that is, jar files that are themselves contained within a jar). This can be problematic if you need to distribute a self-contained application that can be run from the command line without unpacking.

Java沒有提供任何標(biāo)準(zhǔn)的方式來加載嵌套的jar文件(即,它們本身包含在jar中的jar文件)。如果您需要分發(fā)一個自包含的應(yīng)用程序,而該應(yīng)用程序可以從命令行運(yùn)行而無需解壓縮,則可能會出現(xiàn)問題。

To solve this problem, many developers use “shaded” jars. A shaded jar packages all classes, from all jars, into a single “uber jar”. The problem with shaded jars is that it becomes hard to see which libraries are actually in your application. It can also be problematic if the same filename is used (but with different content) in multiple jars.

為了解決這個問題,許多開發(fā)人員使用 shaded jars。 一個 shaded jar 將來自所有jar的所有類打包到一個 uber(超級)jar 中。 shaded jars的問題在于,很難查看應(yīng)用程序中實(shí)際包含哪些庫。 如果在多個jar中使用相同的文件名(但具有不同的內(nèi)容),也可能會產(chǎn)生問題。

Spring Boot takes a different approach and lets you actually nest jars directly.

Spring Boot采用了另一種方法,實(shí)際上允許您直接嵌套jar。

簡單來說,Java標(biāo)準(zhǔn)中是沒有來加載嵌套的jar文件,就是jar中的jar的方式的,為了解決這一問題,很多開發(fā)人員采用shaded jars,但是這種方式會有一些問題,而spring boot采用了不同于shaded jars的另一種方式。

Executable Jar 文件結(jié)構(gòu)

那么spring boot具體是如何實(shí)現(xiàn)的呢?帶著這個疑問,先來查看spring boot打好的包的目錄結(jié)構(gòu)(不重要的省略掉):

SpringBoot java-jar命令行啟動原理解析

圖6

可以發(fā)現(xiàn),文件目錄遵循了下面的規(guī)范:

Application classes should be placed in a nestedBOOT-INF/classesdirectory. Dependencies should be placed in a nested BOOT-INF/libdirectory.

應(yīng)用程序類應(yīng)該放在嵌套的BOOT-INF/classes目錄中。依賴項(xiàng)應(yīng)該放在嵌套的BOOT-INF/lib目錄中。

我們通常在服務(wù)器中使用java -jar命令啟動我們的應(yīng)用程序,在Java官方文檔是這樣描述的:

Executes a program encapsulated in a JAR file. The filename argument is the name of a JAR file with a manifest that contains a line in the form Main-Class:classname that defines the class with the public static void main(String[] args) method that serves as your application’s starting point.

執(zhí)行封裝在JAR文件中的程序。filename參數(shù)是具有清單的JAR文件的名稱,該清單包含Main-Class:classname形式的行,該行使用公共靜態(tài)void main(String [] args)方法定義該類,該方法充當(dāng)應(yīng)用程序的起點(diǎn)。

When you use the -jar option, the specified JAR file is the source of all user classes, and other class path settings are ignored.

使用-jar選項(xiàng)時,指定的JAR文件是所有用戶類的源,而其他類路徑設(shè)置將被忽略。

簡單說就是,java -jar 命令引導(dǎo)的具體啟動類必須配置在清單文件 MANIFEST.MF 的 Main-Class 屬性中,該命令用來引導(dǎo)標(biāo)準(zhǔn)可執(zhí)行的jar文件,讀取的是 MANIFEST.MF文件的Main-Class 屬性值,Main-Class 也就是定義包含了main方法的類代表了應(yīng)用程序執(zhí)行入口類。

那么回過頭再去看下之前打包好、解壓之后的文件目錄,找到 /META-INF/MANIFEST.MF 文件,看下元數(shù)據(jù):

Manifest-Version: 1.0 Implementation-Title: spring-boot-demo Implementation-Version: 1.0-SNAPSHOT Start-Class: com.example.spring.boot.demo.SpringBootDemo Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.2.5.RELEASE Created-By: Maven Archiver 3.4.0 Main-Class: org.springframework.boot.loader.JarLauncher

可以看到Main-Class是org.springframework.boot.loader.JarLauncher,說明項(xiàng)目的啟動入口并不是我們自己定義的啟動類,而是JarLauncher。而我們自己的項(xiàng)目引導(dǎo)類com.example.spring.boot.demo.SpringBootDemo,定義在了Start-Class屬性中,這個屬性并不是Java標(biāo)準(zhǔn)的MANIFEST.MF文件屬性。

spring-boot-maven-plugin 打包過程

我們并沒有添加org.springframework.boot.loader下的這些類的依賴,那么它們是如何被打包在 FatJar 里面的呢?這就必須要提到spring-boot-maven-plugin插件的工作機(jī)制了 。對于每個新建的 spring boot工程,可以在其 pom.xml 文件中看到如下插件:

<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build>

這個是 SpringBoot 官方提供的用于打包 FatJar 的插件,org.springframework.boot.loader 下的類其實(shí)就是通過這個插件打進(jìn)去的;

當(dāng)我們執(zhí)行package命令的時候會看到下面這樣的日志:

[INFO] --- spring-boot-maven-plugin:2.2.5.RELEASE:repackage (repackage) @ spring-boot-demo ---[INFO] Replacing main artifact with repackaged archive

repackage目標(biāo)對應(yīng)的將執(zhí)行到org.springframework.boot.maven.RepackageMojo#execute,該方法的主要邏輯是調(diào)用了org.springframework.boot.maven.RepackageMojo#repackage

// RepackageMojo.javaprivate void repackage() throws MojoExecutionException { // 獲取使用maven-jar-plugin生成的jar,最終的命名將加上.orignal后綴 Artifact source = getSourceArtifact(); // 最終文件,即Fat jar File target = getTargetFile(); // 獲取重新打包器,將重新打包成可執(zhí)行jar文件 Repackager repackager = getRepackager(source.getFile()); // 查找并過濾項(xiàng)目運(yùn)行時依賴的jar Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(), getFilters(getAdditionalFilters())); // 將artifacts轉(zhuǎn)換成libraries Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack, getLog()); try { // 提供Spring Boot啟動腳本 LaunchScript launchScript = getLaunchScript(); // 執(zhí)行重新打包邏輯,生成最后fat jar repackager.repackage(target, libraries, launchScript); } catch (IOException ex) { throw new MojoExecutionException(ex.getMessage(), ex); } // 將source更新成 xxx.jar.orignal文件 updateArtifact(source, target, repackager.getBackupFile());}// 繼續(xù)跟蹤getRepackager這個方法,知道Repackager是如何生成的,也就大致能夠推測出內(nèi)在的打包邏輯。private Repackager getRepackager(File source) { Repackager repackager = new Repackager(source, this.layoutFactory); repackager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener()); // 設(shè)置main class的名稱,如果不指定的話則會查找第一個包含main方法的類, // repacke最后將會設(shè)置org.springframework.boot.loader.JarLauncher repackager.setMainClass(this.mainClass); if (this.layout != null) { getLog().info('Layout: ' + this.layout); repackager.setLayout(this.layout.layout()); } return repackager;}

repackager設(shè)置了 layout方法的返回對象,也就是org.springframework.boot.loader.tools.Layouts.Jar

/** * Executable JAR layout. */public static class Jar implements RepackagingLayout { @Override public String getLauncherClassName() { return 'org.springframework.boot.loader.JarLauncher'; } @Override public String getLibraryDestination(String libraryName, LibraryScope scope) { return 'BOOT-INF/lib/'; } @Override public String getClassesLocation() { return ''; } @Override public String getRepackagedClassesLocation() { return 'BOOT-INF/classes/'; } @Override public boolean isExecutable() { return true; }}

layout我們可以將之翻譯為文件布局,或者目錄布局,代碼一看清晰明了,同時我們又發(fā)現(xiàn)了定義在MANIFEST.MF 文件的Main-Class屬性org.springframework.boot.loader.JarLauncher了,看來我們的下面的重點(diǎn)就是研究一下這個JarLauncher了。

JarLauncher構(gòu)造過程

因?yàn)閛rg.springframework.boot.loader.JarLauncher的類是在spring-boot-loader中的,關(guān)于spring-boot-loader,spring boot的github上是這樣介紹的:

Spring Boot Loader provides the secret sauce that allows you to build a single jar file that can be launched usingjava -jar. Generally you will not need to use spring-boot-loaderdirectly, but instead work with the Gradle or Maven plugin.

Spring Boot Loader提供了秘密工具,可讓您構(gòu)建可以使用java -jar啟動的單個jar文件。通常,您不需要直接使用spring-boot-loader,而可以使用Gradle或Maven插件。

但是若想在IDEA中來看源碼,需要在pom文件中引入如下配置:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-loader</artifactId> <scope>provided</scope></dependency>

找到org.springframework.boot.loader.JarLauncher類

// JarLauncher.javapublic class JarLauncher extends ExecutableArchiveLauncher { // BOOT-INF/classes/ static final String BOOT_INF_CLASSES = 'BOOT-INF/classes/'; // BOOT-INF/lib/ static final String BOOT_INF_LIB = 'BOOT-INF/lib/'; public JarLauncher() { } protected JarLauncher(Archive archive) { super(archive); } @Override protected boolean isNestedArchive(Archive.Entry entry) { if (entry.isDirectory()) { return entry.getName().equals(BOOT_INF_CLASSES); } return entry.getName().startsWith(BOOT_INF_LIB); } // main方法 public static void main(String[] args) throws Exception { new JarLauncher().launch(args); }}

可以發(fā)現(xiàn),JarLauncher定義了BOOT_INF_CLASSES和BOOT_INF_LIB兩個常量,正好就是前面我們解壓之后的兩個文件目錄。JarLauncher包含了一個main方法,作為應(yīng)用的啟動入口。

從 main 來看,只是構(gòu)造了一個 JarLauncher對象,然后執(zhí)行其 launch 方法 。再來看一下JarLauncher的繼承結(jié)構(gòu):

SpringBoot java-jar命令行啟動原理解析

圖2

構(gòu)造JarLauncherd對象時會調(diào)用父類ExecutableArchiveLauncher的構(gòu)造方法:

// ExecutableArchiveLauncher.javapublic ExecutableArchiveLauncher() { try { // 構(gòu)造 archive 對象 this.archive = createArchive(); } catch (Exception ex) { throw new IllegalStateException(ex); }}// 構(gòu)造 archive 對象protected final Archive createArchive() throws Exception { ProtectionDomain protectionDomain = getClass().getProtectionDomain(); CodeSource codeSource = protectionDomain.getCodeSource(); URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null; // 這里就是拿到當(dāng)前的 classpath 的絕對路徑 String path = (location != null) ? location.getSchemeSpecificPart() : null; if (path == null) { throw new IllegalStateException('Unable to determine code source archive'); } File root = new File(path); if (!root.exists()) { throw new IllegalStateException('Unable to determine code source archive from ' + root); } // 將構(gòu)造的archive 對象返回 return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));}

Archive

這里又需要我們先來了解一下Archive相關(guān)的概念。

archive即歸檔文件,這個概念在linux下比較常見 通常就是一個tar/zip格式的壓縮包 jar是zip格式

public abstract class Archive { public abstract URL getUrl(); public String getMainClass(); public abstract Collection<Entry> getEntries(); public abstract List<Archive> getNestedArchives(EntryFilter filter);}

Archive是在spring boot里抽象出來的用來統(tǒng)一訪問資源的接口。該接口有兩個實(shí)現(xiàn),分別是ExplodedArchive和JarFileArchive。前者是一個文件目錄,后者是一個jar,都是用來在文件目錄和jar中尋找資源的,這里看到JarLauncher既支持jar啟動,也支持文件系統(tǒng)啟動,實(shí)際上我們在解壓后的文件目錄里執(zhí)行 java org.springframework.boot.loader.JarLauncher 命令也是可以正常啟動的。

SpringBoot java-jar命令行啟動原理解析

圖3

在FatJar中,使用的是后者。Archive都有一個自己的URL,比如

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!

Archive類還有一個getNestedArchives方法,下面還會用到這個方法,這個方法實(shí)際返回的是springboot-demo.jar/lib下面的jar的Archive列表。它們的URL是:

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-starter-web-2.2.5.RELEASE.jar!

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-starter-2.2.5.RELEASE.jar!

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-2.2.5.RELEASE.jar!

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-autoconfigure-2.2.5.RELEASE.jar!/

省略......

launch()執(zhí)行流程

archive構(gòu)造完成后就該執(zhí)行JarLauncher的launch方法了,這個方法定義在了父類的Launcher里:

// Launcher.javaprotected void launch(String[] args) throws Exception { /* * 利用 java.net.URLStreamHandler 的擴(kuò)展機(jī)制注冊了SpringBoot的自定義的可以解析嵌套jar的協(xié)議。 * 因?yàn)镾pringBoot FatJar除包含傳統(tǒng)Java Jar中的資源外還包含依賴的第三方Jar文件 * 當(dāng)SpringBoot FatJar被java -jar命令引導(dǎo)時,其內(nèi)部的Jar文件是無法被JDK的默認(rèn)實(shí)現(xiàn) * sun.net.www.protocol.jar.Handler當(dāng)做classpath的,這就是SpringBoot的自定義協(xié)議的原因。 */ JarFile.registerUrlProtocolHandler(); // 通過 classpath 來構(gòu)建一個 ClassLoader ClassLoader classLoader = createClassLoader(getClassPathArchives()); // 1 launch(args, getMainClass(), classLoader); // 2}

重點(diǎn)關(guān)注下createClassLoader(getClassPathArchives()) 構(gòu)建ClassLoader的邏輯,首先調(diào)用getClassPathArchives()方法返回值作為參數(shù),該方法為抽象方法,具體實(shí)現(xiàn)在子類ExecutableArchiveLauncher中:

// ExecutableArchiveLauncher.java@Overrideprotected List<Archive> getClassPathArchives() throws Exception { List<Archive> archives = new ArrayList<>(this.archive.getNestedArchives(this::isNestedArchive)); postProcessClassPathArchives(archives); return archives;}

該方法會執(zhí)行Archive接口定義的getNestedArchives方法返回的與指定過濾器匹配的條目的嵌套存檔列表。從上文可以發(fā)現(xiàn),這里的archive其實(shí)就是JarFileArchive ,傳入的過濾器是JarLauncher#isNestedArchive方法引用

// JarLauncher.java@Overrideprotected boolean isNestedArchive(Archive.Entry entry) { // entry是文件目錄時,必須是我們自己的業(yè)務(wù)類所在的目錄 BOOT-INF/classes/ if (entry.isDirectory()) { return entry.getName().equals(BOOT_INF_CLASSES); } // entry是Jar文件時,需要在依賴的文件目錄 BOOT-INF/lib/下面 return entry.getName().startsWith(BOOT_INF_LIB);}

getClassPathArchives方法通過過濾器將BOOT-INF/classes/和BOOT-INF/lib/下的嵌套存檔作為List<Archive>返回參數(shù)傳入createClassLoader方法中。

// Launcher.javaprotected ClassLoader createClassLoader(List<Archive> archives) throws Exception { List<URL> urls = new ArrayList<>(archives.size()); for (Archive archive : archives) { // 前面說到,archive有一個自己的URL的,獲得archive的URL放到list中 urls.add(archive.getUrl()); } // 調(diào)用下面的重載方法 return createClassLoader(urls.toArray(new URL[0]));}// Launcher.javaprotected ClassLoader createClassLoader(URL[] urls) throws Exception { return new LaunchedURLClassLoader(urls, getClass().getClassLoader());}

createClassLoader()方法目的是為得到的URL們創(chuàng)建一個類加載器 LaunchedURLClassLoader,構(gòu)造時傳入了當(dāng)前Launcher的類加載器作為其父加載器,通常是系統(tǒng)類加載器。下面重點(diǎn)看一下LaunchedURLClassLoader的構(gòu)造過程:

// LaunchedURLClassLoader.javapublic LaunchedURLClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent);}

LaunchedURLClassLoader是spring boot自己定義的類加載器,繼承了JDK的URLClassLoader并重寫了loadClass方法,也就是說它修改了默認(rèn)的類加載方式,定義了自己的類加載規(guī)則,可以從前面得到的 List<Archive>中加載依賴包的class文件了 。

LaunchedURLClassLoader創(chuàng)建完成后,我們回到Launcher中,下一步就是執(zhí)行l(wèi)aunch的重載方法了。

// Launcher.javalaunch(args, getMainClass(), classLoader);

在此之前,會調(diào)用getMainClass方法并將其返回值作為參數(shù)。

getMainClass的實(shí)現(xiàn)在Launcher的子類ExecutableArchiveLauncher中:

// ExecutableArchiveLauncher.java@Overrideprotected String getMainClass() throws Exception { // 從 archive 中拿到 Manifest文件 Manifest manifest = this.archive.getManifest(); String mainClass = null; if (manifest != null) { // 就是MANIFEST.MF 文件中定義的Start-Class屬性,也就是我們自己寫的com.example.spring.boot.demo.SpringBootDemo這個類 mainClass = manifest.getMainAttributes().getValue('Start-Class'); } if (mainClass == null) { throw new IllegalStateException('No ’Start-Class’ manifest entry specified in ' + this); } // 返回mainClass return mainClass;}

得到mainClass后,執(zhí)行l(wèi)aunch的重載方法:

// Launcher.javaprotected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception { // 將自定義的LaunchedURLClassLoader設(shè)置為當(dāng)前線程上下文類加載器 Thread.currentThread().setContextClassLoader(classLoader); // 構(gòu)建一個 MainMethodRunner 實(shí)例對象來啟動應(yīng)用 createMainMethodRunner(mainClass, args, classLoader).run();}// Launcher.javaprotected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) { return new MainMethodRunner(mainClass, args);}

MainMethodRunner對象構(gòu)建完成后,調(diào)用它的run方法:

// MainMethodRunner.javapublic void run() throws Exception { // 使用當(dāng)前線程上下文類加載器也就是自定義的LaunchedURLClassLoader來加載我們自己寫的com.example.spring.boot.demo.SpringBootDemo這個類 Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName); // 找到SpringBootDemo的main方法 Method mainMethod = mainClass.getDeclaredMethod('main', String[].class); // 最后,通過反射的方式調(diào)用main方法 mainMethod.invoke(null, new Object[] { this.args });}

至此,我們自己的main方法開始被調(diào)用,所有我們自己的應(yīng)用程序類文件均可通過/BOOT-INF/classes加載,所有依賴的第三方j(luò)ar均可通過/BOOT-INF/lib加載,然后就開始了spring boot的啟動流程了。

debug技巧

以上就是spring boot通過java -jar命令啟動的原理了,了解了原理以后我們可不可以通過debug來進(jìn)一步加深一下理解呢?通常我們在IDEA里啟動時是直接運(yùn)行main方法,因?yàn)橐蕾嚨腏ar都讓IDEA放到classpath里了,所以spring boot直接啟動就完事了,并不會通過上面的方式來啟動。不過我們可以通過配置IDEA的 run/debug configurations 配置 JAR Application 來實(shí)現(xiàn)通過Jar方式啟動。

SpringBoot java-jar命令行啟動原理解析

圖4

當(dāng)我們做了以上設(shè)置后,就可以來方便的在IDEA里來dubug源碼了。

SpringBoot java-jar命令行啟動原理解析

圖5小結(jié)

本文通過JarLauncher為切入點(diǎn),介紹了spring boot的java -jar的啟動方式,闡述了JarLauncher啟動的基本工作原理,同時簡單介紹了相關(guān)的spring-boot-maven-plugin插件和Archive、LaunchedURLClassLoader等相關(guān)概念,希望能夠?qū)Υ蠹业睦斫庥兴鶐椭?/p>

到此這篇關(guān)于SpringBoot java-jar命令行啟動原理解析的文章就介紹到這了,更多相關(guān)SpringBoot java-jar命令行啟動內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 亚洲第一精品在线 | 99国产精品久久久久久久 | 99久久精品国产毛片 | 久产久精品| 91久久爽久久爽爽久久片 | 国产在线视频a | 狠狠操夜夜爱 | 亚洲福利二区 | 国产xxxx成人精品免费视频频 | 北条麻妃99精品青青久久主播 | 日韩av一区在线 | 中文字幕日韩一区 | 俺要去97中文字幕 | 久久精品这里热有精品 | 国产超碰人人模人人爽人人添 | 色一级| 国产一区二区精品丝袜 | 中文字幕成人 | 成人在线小视频 | 日韩三级在线免费观看 | 精品一二三区 | 鲁一鲁综合 | 欧美精品在线免费观看 | 七七婷婷婷婷精品国产 | 国产免费视频 | 狠狠干av | 91精品国产综合久久久久 | 日韩午夜电影 | 精品欧美久久 | 久久精品国产91精品亚洲高清 | 国产精品99一区二区三区 | 久久视频一区 | 亚洲精品二区三区 | www.久久99 | 蜜桃av一区二区三区 | 成人h视频在线观看 | 亚洲人成人一区二区在线观看 | 亚洲一区日韩 | 欧美日韩精品一区二区在线播放 | 国产精品久久久一区二区三区 | 91社影院在线观看 | 黄片毛片在线观看 | 久久成| 一区二区视屏 | 成人欧美 | 亚洲丶国产丶欧美一区二区三区 | 亚洲天堂一区二区三区 | 色女人av| 91中文字幕在线观看 | 日本丶国产丶欧美色综合 | 天天操天天操 | 亚洲精品免费视频 | 欧美日韩在线精品 | 久久久激情视频 | 国产一级免费在线观看 | 狠狠综合 | 国产精品18久久久久久首页狼 | 91免费在线播放 | 亚洲精品视频在线播放 | 国产精品久久九九 | 国产精品中文字幕在线 | 国产精品美女视频一区二区三区 | 四影虎影www4hu23cmo | 91免费在线视频 | 狠狠狠色丁香婷婷综合久久五月 | 成人羞羞在线观看网站 | 综合久久综合 | 91精品国产91久久久久久吃药 | 亚洲免费a | www中文字幕 | 亚洲免费在线观看 | 日韩久久久久久久久久久 | 国产在线a| 亚洲日韩中文字幕一区 | 国产一区91| 欧美白人做受xxxx视频 | 91性高湖久久久久久久久_久久99 | 97久久精品| 日韩欧美一级 | 最新日韩免费 | 日韩有码在线播放 | h视频网站在线 | 自拍偷拍亚洲欧美 | 91夜夜| 欧美高清视频一区 | 伊人狠狠| 日本成人中文字幕在线观看 | 欧美色综合天天久久综合精品 | 理论片87福利理论电影 | 亚洲精选久久 | 日韩一区不卡 | 国产色在线| 亚洲午夜视频 | 青青草网 | 日韩成人一区二区 | 久免费视频 | 成人欧美一区二区三区黑人孕妇 | 午夜高清视频 | 天天草狠狠干 | 欧美韩一区二区 | 一区二区三区国产精品 | 国产98色在线 | 日韩 | 日韩高清一区二区 | 最新天堂中文在线 | 日中文字幕在线 | 禁果av一区二区三区 | 91精品国产美女在线观看 | 黄色大片网站在线观看 | 色婷婷国产精品综合在线观看 | 国产成人av免费 | 日韩1区 | 成人国产免费视频 | 久久一区二区三区四区五区 | 久久人| 久草视 | 国内精品久久久久久中文字幕 | 中文字幕乱码一区二区三区 | 色综合一区| 色站综合 | 国内精品成人 | 欧美精品成人 | 精品国产综合 | 久久精品2| 久久久男人天堂 | 青青草亚洲| 国产看片网站 | 99在线精品视频 | 美女一级a毛片免费观看97 | 日韩成人一级片 | www.av在线 | 五月婷婷丁香在线 | 狠狠的日 | 91超碰caoporn97人人 | 叶山小百合av一区二区 | 婷婷激情五月 | 国产福利视频在线观看 | 视频一区在线 | 日本三级电影天堂 | 天天射影院| 一二三四区视频在线观看 | 91国偷自产一区二区三区亲奶 | 欧美精品1区2区3区 免费亚洲婷婷 | 狠狠视频 | 国产黄色免费视频 | 伊人艹| 黄色免费av| jlzzjlzz亚洲日本少妇 | 精品在线一区二区三区 | 日韩在线免费 | 99精品视频在线观看 | 国产黄色在线免费看 | 久久最新 | 中文字幕亚洲精品 | 亚洲av毛片一级二级在线 | 久久精品一区二区三区四区 | 91免费电影 | 国产剧情一区二区 | 久久99精品国产91久久来源 | 亚洲视频在线观看一区二区三区 | 国产成人在线播放 | 国产一区 欧美 | 国产精品精品视频一区二区三区 | 欧美一区二区在线播放 | 国产一级视频免费观看 | 国产色视频在线观看免费 | 国产精品久久久久久久久大全 | 四虎影视免费在线观看 | 国产ts余喵喵和直男多体位 | 久久人 | a级毛片免费高清视频 | 亚洲综合天堂网 | 久久情趣视频 | 精品一区免费观看 | 影视一区二区 | 午夜精品一区二区三区免费视频 | 亚洲精品电影在线观看 | 亚洲免费在线视频 | 午夜一区二区三区 | 日韩三区 | 成人午夜精品 | 中文字幕亚洲精品 | 国产一区二区精品在线观看 | 麻豆视频在线 | 日韩高清中文字幕 | 欧美日韩精品一区二区三区四区 | 黄色一级免费大片 | 亚洲高清在线 | 欧美一级在线免费观看 | 91视频一区二区 | 国产日韩精品久久 | 久久亚洲一区二区三 | 色综合久久久久 | av黄色一级片| 亚洲一区二区三区国产 | 久久亚洲综合 | a级性生活片 | 久久777| 亚洲精品乱码久久久久久久久 | 国产精品69毛片高清亚洲 | 色狠狠一区 | 一区二区三区在线 | 亚洲精品一区二区三区 | 亚洲一区二区三区四区五区午夜 | 亚洲精品一区二区三区在线播放 | 99成人| 国产一区二区欧美 | 欧美成年黄网站色视频 | 精品欧美一区二区三区 | h视频免费在线 | 国产成人在线一区二区 | 亚洲欧洲tv| 91在线播 | 欧美在线一区二区 | 亚洲视频1区 | 99精品99| 夜夜草| 91 在线观看 | 国产精品毛片久久久久久久 | 黄色片毛片 | www国产xxx | 午夜免费观看网站 | 亚洲成人在线网站 | 久久永久视频 | 欧美 日韩 中文字幕 | 毛片视频免费 | 综合久久精品 | 国产成人精品a视频一区www | 国产成人精品a视频一区www | 午夜在线观看视频网站 | 特黄色一级片 | 欧美成视频| 香港三级日本三级a视频 | 综合久久久久 | 国产视频久久 | 99pao成人国产永久免费视频 | 亚洲第一福利视频 | 欧美国产日韩在线 | 91精品久久久久久久久久小网站 | 欧美二区在线观看 | 午夜影院在线观看 | 亚洲人成人一区二区在线观看 | 免费av一区 | 天天狠狠操| 色花av | 天天碰天天操 | 免费黄色录像视频 | 超碰在线99 | 免费黄色av | 五月婷婷激情 | 久久成人综合 | 欧美视频二区 | 国产精品一区av | 亚洲精品一区二区三区蜜桃久 | 日本在线不卡视频 | 在线观看中文 | 一区二区三区在线观看国产 | av大片| 视频二区| 国产精品大全 | 欧美日韩三级 | 亚洲精品三级 | 亚洲高清视频网站 | 青青草一区二区三区 | 天堂动漫 | 亚洲欧美自拍视频 | 亚洲精品视频在线观看免费 | yy6080久久伦理一区二区 | 免费成人在线观看视频 | 一本一道久久a久久精品逆3p | 黄色毛片免费看 | 6080yy午夜一二三区久久 | 久久免费视频3 | 四虎永久在线 | 天天干天天操天天爽 | 免费99精品国产自在在线 | 国内精品久久久久久影视8 91一区二区在线观看 | 777色狠狠一区二区三区 | 91小视频 | 久久精品91| 黄色片在线免费观看 | 中文字幕乱码亚洲精品一区 | 日韩一片 | av在线网址观看 | 国产美女永久免费无遮挡 | 午夜视频精品 | 欧美不卡视频 | 91精品国产日韩91久久久久久 | 亚洲免费视频网站 | 亚洲日韩中文字幕 | 亚洲毛片网站 | 欧洲一级毛片 | 国产wwwcom | 国产高清在线 | 国产在线观看 | 亚洲福利影院 | 欧美全黄| 中文久久 | 国产在线高清视频 | 亚洲二区在线观看 | 日韩欧美一区二区三区视频 | 欧美lesbianxxxxhd视频社区 | 黄色片com | 亚洲一区 | 91久久久久久久久久久久久久久久 | 精品一区在线 | 中文字幕avav | 午夜黄色av| 久久久99久久久国产自输拍 | 视频1区2区 | 亚洲成人免费观看 | 成人免费共享视频 | 午夜资源 | 精品国产一区二区三区久久久 | 亚洲欧美国产一区二区 | 欧美激情第二页 | 欧美在线a | 日韩在线小视频 | 国产精品久久久久久久久久三级 | 毛片国产 | 男女羞羞视频免费观看 | 亚洲精品福利网站 | 在线播放亚洲 | 美日韩免费视频 | 亚洲精品国产第一综合99久久 | 精品久久精品久久 | www中文字幕 | 国产精品一区在线观看 | 国产中文在线 | 日本免费一区二区三区 | 成人在线一区二区 | 亚洲人成一区 | 国产a级大片 | 天堂中文网官网 | 91视频网址 | 久久久一二三 | av大全在线 | av网址在线播放 | 免费人成电影 | 欧美激情一区二区 | 91免费视频在线 | 国产人久久人人人人爽 | 毛片视频观看 | av资源首页| 成人黄页在线观看 | 国产精品视频一区二区三区四蜜臂 | 福利视频一区 | 国产一级大片 | 久福利| 91精品国产乱码久久久久久 | 中文字幕一区二区在线观看 | 亚洲国产精品久久久久 | 久久久久久久久久国产精品 | 国产成人99久久亚洲综合精品 | 这里有精品视频 | 91亚洲成人 | 中文字幕一区二区三区四区 | 日本久久久久久 | 一a级毛片 | 国产午夜久久 | 久久久国产日韩 | 99免费观看视频 | 精品视频在线视频 | 亚洲一区影院 | 毛片天堂 | 婷婷综合五月天 | 91精品久久久久久久久中文字幕 | 婷婷综合网| 亚洲成人精品在线观看 | 久久天堂网 | 欧美 亚洲 另类 激情 另类 | 中文字幕在线免费视频 | 波多野结衣先锋影音 | 亚洲欧美aa | 国产精品一二三区 | 日韩中文一区二区三区 | 成人小视频在线观看 | 日韩成人免费av | 日本在线网 | 国产欧美精品区一区二区三区 | 天天澡天天狠天天天做 | 亚洲精品9999| 中文字幕在线免费视频 | 涩涩导航 | 亚洲一区二区三区精品视频 | 国产精品久久久久久久久久久久 | 一级在线观看 | 国产精品久久久久久久娇妻 | www.福利视频 | 在线免费观看激情视频 | 美女久久久 | 免费毛片一区二区三区久久久 | 男人天堂999 | 亚洲 欧美 综合 | 国产成人在线免费观看 | 六月综合激情 | 久草毛片| 性免费网站 | 国产小视频在线播放 | 亚洲精品乱码久久久久久蜜桃 | 国产精品久久久久久久久免费桃花 | 色官网 | 日韩中文字幕一区二区 | 亚洲一区 | 最新中文字幕在线 | 欧美日韩亚洲一区二区 | 午夜免费小视频 | 成年人视频在线免费观看 | 久久久久久影院 | 天天天综合网 | 成人欧美一区二区三区视频xxx | 免费一区| 久久人体| 成年人免费看 | 婷婷亚洲五月 | 亚洲欧美激情另类 | 亚洲激情av | 一区二区三区视频免费在线观看 | 中文一区 | 亚洲欧美国产毛片在线 | 一区二区三区国产亚洲网站 | 成人看片在线 | 精品自拍视频 | 国产在线精品一区二区三区 | 免费大片在线观看网站 | 久在线| 91福利视频导航 | 日韩精品久久 | 国产成人61精品免费看片 | 久草天堂| 一区视频在线 | 久久91精品国产 | 亚洲高清视频一区 | 亚洲国产精品久久 | 国产高清免费视频 | 1000部精品久久久久久久久 | 欧美福利在线观看 | 日韩特级 | 亚洲视频在线播放 | 精品国产一区二区三区久久久 | 国产视频久久久久 | 国产精品久久久久久久久久久久久 | 日本成人三级 | www.色.com | 久久精品久久久 | 精品在线一区二区 | 日韩av免费在线播放 | 在线成人www免费观看视频 | 成人一级黄色大片 | 精品美女在线观看视频在线观看 | 国产精品日产欧美久久久久 | 国产精品成人在线观看 | 国产一区 | 中文字幕精品一区 | 国产精品毛片一区二区在线看 | 欧美一区| 香蕉久久久久久 | 天天草天天干 | 日韩三级在线免费 | 日本高清无卡码一区二区久久 | 日韩欧美成人影院 | 成人av网站在线观看 | 亚洲精品乱码久久久久久麻豆不卡 | 国产精品视频免费 | 日韩欧美一区二区在线观看 | 欧美日韩精品久久久 | 成人激情视频 | 欧美二区三区 | 久久蜜桃视频 | 精品久久一二三区 | 1000部羞羞视频在线看视频 | 国产欧美日韩在线观看 | 亚洲成av人片一区二区三区 | 操网| 久久精品一区二区三区中文字幕 | 美国一级黄色片 | 国产视频久久久久久久 | 久久成人综合 | 久久av网址 | 日韩一区二区在线观看 | 91爱爱| 日韩欧美一级在线 | 亚洲 欧美 日韩在线 | 视频在线一区二区 | 国产三级在线免费观看 | 久久久久久亚洲精品 | 视频网站免费观看 | 综合中文字幕 | 亚洲视频一区在线 | 久久亚洲精品国产一区 | 国产精品美女久久久久久免费 | 婷婷色综合 | 国产综合精品一区二区三区 | 青青99 | 久久另类| 日韩精品久久久 | 亚洲欧美综合一区 | 在线亚洲人成电影网站色www | 成人性视频免费网站 | 欧美精品久久久久久久久 | 成人做爰www免费看视频网站 | 中文字幕精品三区 | 成人a视频 | 日本美女一区二区 | 国产精品视频一区二区三区 | 国产一区二区三区 | 欧美天堂 | 玖玖免费 | 欧美xxxx在线 | 国外成人在线视频网站 | 激情网站免费 | 欧美日韩精品一区二区三区 | 久久成人精品一区二区三区 | 伊人短视频 | 九九天堂网| 国产精品久久久久久久久久久杏吧 | 中文字幕高清 | 亚洲视频在线看 | 亚洲精品成人网 | 33eee在线视频免费观看 | 99re6在线视频精品免费 | 国产精品自产拍在线观看桃花 | 色欧美片视频在线观看 | 在线亚洲成人 | 欧美日一级片 | 午夜激情视频在线观看 | www.亚洲 | 一级毛片视频 | 国产99久久精品一区二区永久免费 | 成人国产精品久久 | 亚洲毛片| 天天人人精品 | 欧美国产免费 | 国内精品久久久久久中文字幕 | www.99精品 | 国产女人爽到高潮免费视频 | 国产精品爱久久久久久久 | 婷婷激情五月 | 91av在线视频播放 | 狠狠操狠狠干 | av片在线观看 | 色欧美片视频在线观看 | 久久99久久久久 | 欧美国产日韩在线 | 91资源在线观看 | 国产激情毛片 | 国产成人午夜高潮毛片 | 欧美视频一区二区三区在线观看 | 欧美不卡视频一区发布 | 亚洲国产精品精华液com | 日韩一级视频 | 欧美怡红院视频一区二区三区 | 久久国产婷婷国产香蕉 | 超碰高清 | 亚洲精品v日韩精品 | 国产成人精品免高潮在线观看 | 欧美午夜精品久久久久久浪潮 | 欧美视频网站 | 日韩欧美国产精品 | 日韩不卡一区二区 | 黄网站色大毛片 | 男女羞羞网站 | 免费毛片在线播放 | 成人福利在线 | 亚洲一区二区三区爽爽爽爽爽 | 亚洲免费视频在线观看 | av国产精品 | 91香蕉| 毛片免费视频 | 国产区在线 | 国产视频中文字幕 | 亚洲va中文字幕 | 91精品国产综合久久福利软件 | 亚洲视频在线观看免费 | 国产无区一区二区三麻豆 | 国产精品99久久久久久动医院 | 久久久一区二区三区 | 曰批视频在线观看 | 久久午夜影院 | 久久久精彩视频 | 亚洲免费视频一区二区 | 久久久久国产 | 欧美成a| 久久国产精品免费一区二区三区 | 午夜成人免费电影 | 午夜视频免费 | 国产欧美日韩精品一区二区三区 | 国产一区二区在线免费观看 | 久久噜噜噜精品国产亚洲综合 | 国产3区| 影音先锋亚洲资源 | 亚洲免费在线观看 | 精产国产伦理一二三区 | 色欧美片视频在线观看 | jizz在亚洲 | 在线天堂新版最新版在线8 www.国产欧美 | www久久精品 | 日韩免费在线视频 | 日韩视频精品在线观看 | 九色av| 91精品在线观看入口 | 亚洲欧美综合乱码精品成人网 | 日本视频免费高清一本18 | 亚洲精品一区二区三区樱花 | 精品国产乱码久久久久久1区2区 | 日韩av一区二区在线观看 | 爱爱视频网站 | 99re在线 | 看一级黄色大片 | 一区二区蜜桃 | 久操成人| 日本在线观看 | 欧美欧美欧美 | 国产亚洲在线 | 欧美99热| 天天操夜夜操免费视频 |