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

首頁 > 學院 > 開發設計 > 正文

Spring4.3.x 容器在web應用中的初始化過程

2019-11-10 20:16:00
字體:
來源:轉載
供稿:網友

概述

SPRing在web應用中的默認容器類為xmlWebapplicationContext,這個容器類通過xml文件獲取所有的配置信息。它的繼承結構如下圖,(點此查看大圖) XmlWebApplicationContext繼承結構

在web應用中,不管是ContextLoaderListener,還是DispatcherServlet初始化的時候,都是以XmlWebApplicationContext為默認容器。在下面的研究中,我將以ContextLoaderListener的初始化過程介紹spring容器在web應用的初始化。

ContextLoaderListener的初始化過程中最主要的任務時加載spring容器,并把此容器加入到ServletContext中作為整個web應用的跟容器。ContextLoaderListener加載spring容器大致分為兩個階段,第一個階段是解析web.xml文件中的初始化參數以對spring容器做定制化操作,簡單的說就是定制spring容器;第二階段是spring容器的刷新過程。下面分別對這兩個階段進行探討。

第一階段 定制spring容器

ContextLoaderListener實現了ServletContextListener,因此web容器啟動的時候就會執行它的contextInitialized方法,此方法的代碼如下。

public void contextInitialized(ServletContextEvent event) { // 獲取ContextLoader對象 this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } this.contextLoader.initWebApplicationContext(event.getServletContext()); } @Override public void contextInitialized(ServletContextEvent event) { // 執行父類ContextLoader的initWebApplicationContext方法 initWebApplicationContext(event.getServletContext()); }

這段代碼主要是調用父類ContextLoader的initWebApplicationContext(ServletContext servletContext)方法,下面是initWebApplicationContext方法在ContextLoader類中的代碼。

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { // 檢查ServletContext是否已經有了根容器 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { if (this.context == null) { // 創建容器,據contextClass初始化參數指定或者使用默認的XmlWebApplicationContext類 this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { // 如果容器沒有被刷新,執行以下操作 // 設置父容器 if (cwac.getParent() == null) { // 加載父容器。 // 通過locatorFactorySelector上下文初始化參數指定父容器所在的配置文件路徑 // 通過parentContextKey上下文初始化參數指定父容器的名稱 ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } // 配置并執行容器刷新操作 configureAndRefreshWebApplicationContext(cwac, servletContext); } } // 把spring容器加入到ServletContext中作為根容器 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }

這段代碼分成4步,首先通過調用ContextLoader的createWebApplicationContext(ServletContext sc)方法來創建spring容器,然后設置spring容器的父容器,接著調用ContextLoader的configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)方法來配置并刷新spring容器,最后把容器保存到servlet容器中。最后一步的代碼已經在上面體現了,下面我們來解析前三步的代碼。

1. 創建spring容器。

調用ContextLoader的createWebApplicationContext(ServletContext sc)方法,這個方法的代碼如下。

protected WebApplicationContext createWebApplicationContext(ServletContext sc) { // 獲取容器類對象 Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } // 使用容器類對象來實例化容器 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }

上面主要是通過ContextLoader的determineContextClass(ServletContext servletContext)方法獲取容器類對象,然后實例化容器。下面是determineContextClass方法的代碼。

/** * 獲取容器類對象 **/ protected Class<?> determineContextClass(ServletContext servletContext) { // 聲明:public static final String CONTEXT_CLASS_PARAM = "contextClass"; String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }

determineContextClass從ServletContext對象中獲取contextClass初始化參數的值,如果這個參數有值,則使用這個參數指定的容器類,否則使用默認的容器類XmlWebApplicationContext。如果不使用spring的默認容器,可以在web.xml中通過配置contextClass指定其他容器類,比如。

<!-- 定義contextClass參數 --> <context-param> <param-name>contextClass</param-name> <param-value> com.chyohn.context.XmlWebApplicationContext </param-value> </context-param>

2. 指定父容器。

創建完spring容器后,initWebApplicationContext中會調用ContextLoader的 loadParentContext(ServletContext servletContext)方法來獲取父容器,并把這個父容器與剛創建的容器關聯上。loadParentContext方法的代碼如下。

protected ApplicationContext loadParentContext(ServletContext servletContext) { ApplicationContext parentContext = null; // 聲明:public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector"; String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM); // 聲明:public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey"; String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM); if (parentContextKey != null) { // locatorFactorySelector可能會為null, 則會使用默認的 "classpath*:beanRefContext.xml" BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector); Log logger = LogFactory.getLog(ContextLoader.class); if (logger.isDebugEnabled()) { logger.debug("Getting parent context definition: using parent context key of '" + parentContextKey + "' with BeanFactoryLocator"); } this.parentContextRef = locator.useBeanFactory(parentContextKey); parentContext = (ApplicationContext) this.parentContextRef.getFactory(); } return parentContext; }

這里通過ServletContext 獲取初始化參數locatorFactorySelector指定的定義父容器的xml文件的地址,同時獲取初始化參數parentContextKey指定的父容器在前面xml文件中設置的bean名稱。下面是一個列子。

第一步在classes路徑下創建名為parentBeanRefContext.xml的xml文件(名稱可以隨便取),我這里的內容如下。

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <!-- 指定容器 --> <bean id="parentContext" class="org.springframework.context.support.ClassPathXmlApplicationContext"> <constructor-arg> <list> <value>parentOfRootContext.xml</value> </list> </constructor-arg> </bean></beans>

其中parentOfRootContext.xml文件為一個普通的spring配置文件,這里就不舉例了。

第二步,在web.xml文件中做如下配置。

<!-- 定義locatorFactorySelector參數 --> <context-param> <param-name>locatorFactorySelector</param-name> <param-value> classpath:parentBeanRefContext.xml </param-value> </context-param> <!-- 定義parentContextKey參數 --> <context-param> <param-name>parentContextKey</param-name> <param-value>parentContext</param-value> </context-param>

這樣就向容器中指定了一個父容器。在這里如果在第一步中創建的xml文件的名稱為beanRefContext.xml,那么在web.xml文件中就不用配置locatorFactorySelector參數。

3. 配置并刷新spring容器

設置了父容器后,執行ContextLoader的configureAndRefreshWebApplicationContext方法,在容器刷新前對容器進行初始化配置,代碼如下

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // 為容器設置一個有用的ID // 聲明:public static final String CONTEXT_ID_PARAM = "contextId"; String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // 生成一個默認ID if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { // servlet 2.5以前的版本 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getServletContextName())); } else { // servlet 2.5及其以上的版本wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } } // 把ServletContext保存到容器中 wac.setServletContext(sc); // 設置容器要加載的配置文件所在的路徑 // 通過contextConfigLocation上下文參數指定配置文件路徑 // 聲明:public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation"; String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (initParameter != null) { wac.setConfigLocation(initParameter); } // 在容器刷新前,自定義容器。 // 執行用戶通過contextInitializerClasses上下文參數指定的容器初始化器 customizeContext(sc, wac); // 刷新容器 wac.refresh(); } protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // 為容器設置一個有用的ID // 聲明:public static final String CONTEXT_ID_PARAM = "contextId"; String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // 創建一個默認的id wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } // 把ServletContext保存到容器中 wac.setServletContext(sc); // 設置容器要加載的配置文件所在的路徑 // 通過contextConfigLocation上下文參數指定配置文件路徑 // 聲明:public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation"; String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } // 提前執行容器環境對象的initPropertySources方法,以確保servlet屬性資源可應用于容器刷新前的任何初始化操作。 ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); } // 在容器刷新前,自定義容器。 // 執行用戶通過contextInitializerClasses上下文參數指定的容器初始化器 customizeContext(sc, wac); // 刷新容器 wac.refresh(); }

這段代碼處理配置在web.xml中的2個初始化參數,第一個是用于標志容器id的contextId初始化參數,第二是用于指定配置文件地址的contextConfigLocation初始化參數。對于contextId參數沒有過多的探討,至于contextConfigLocation參數,可以配置,也可以不配置。如果需要通過contextConfigLocation參數指定多個配置文件,配置文件地址之間可以通過英文逗號、分號、空格、制表符、換行符隔開。如果沒有配置contextConfigLocation參數,XmlWebApplicationContext將使用WEB-INF目錄下的默認配置文件地址,代碼如下。

/** Default config location for the root context */ public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; /** Default prefix for building a config location for a namespace */ public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; /** Default suffix for building a config location for a namespace */ public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml"; @Override protected String[] getDefaultConfigLocations() { if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; } else { // 返回配置地址/WEB-INF/applicationContext.xml return new String[] {DEFAULT_CONFIG_LOCATION}; } }

根據這段代碼,可以獲得兩個信息。 其一:如果spring容器有命名空間,則開發者可以在WEB-INF目錄下創建以命名空間為名稱的xml配置文件。 其二:如果spring容器沒有命名空間,則開發者可以在WEB-INF目錄下創建以applicationContext為名稱的xml配置文件。

在configureAndRefreshWebApplicationContext方法中還調用ContextLoader的customizeContext方法來執行用戶指定ApplicationContextInitializer對象,下面是customizeContext方法的代碼。

protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) { // 獲取ApplicationContextInitializer類對象列表 List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = determineContextInitializerClasses(servletContext); if (initializerClasses.size() == 0) { // 沒有指定任何 ApplicationContextInitializers對象,則什么都不做,直接返回 return; } Class<?> contextClass = applicationContext.getClass(); ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances = new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>(); for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) { // 從initializerClass對象獲取ApplicationContextInitializer的泛型類對象 Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); // 檢查contextClass是否是initializerContextClass類對象指定的類的實現 Assert.isAssignable(initializerContextClass, contextClass, String.format( "Could not add context initializer [%s] as its generic parameter [%s] " + "is not assignable from the type of application context used by this " + "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(), contextClass.getName())); initializerInstances.add(BeanUtils.instantiateClass(initializerClass)); } ConfigurableEnvironment env = applicationContext.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { // 初始化Property資源 ((ConfigurableWebEnvironment)env).initPropertySources(servletContext, null); } // 一個一個的執行ApplicationContextInitializer對象 Collections.sort(initializerInstances, new AnnotationAwareOrderComparator()); for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) { initializer.initialize(applicationContext); } }

這段代碼主要是調用ContextLoader對象的determineContextInitializerClasses方法來獲取ApplicationContextInitializer類對象列表,并使用每個ApplicationContextInitializer對象來對spring容器做更多的初始化操作。下面是determineContextInitializerClasses的代碼。

protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> determineContextInitializerClasses(ServletContext servletContext) { List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes = new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>(); // 從ServletContext中獲取應用于所有spring web應用容器的ApplicationContextInitializer實現類全名稱 // 聲明有:public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses"; String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM); if (globalClassNames != null) { for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) { classes.add(loadInitializerClass(className)); } } // 從ServletContext中獲取專為為此容器指定的ApplicationContextInitializer實現類全名稱 // 聲明:public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses"; String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM); if (localClassNames != null) { for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) { classes.add(loadInitializerClass(className)); } } return classes; } private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) { try { Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader()); Assert.isAssignable(ApplicationContextInitializer.class, clazz); return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz; } catch (ClassNotFoundException ex) { throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex); } }

determineContextInitializerClasses方法從ServletContext中獲取初始化參數contextInitializerClasses和globalInitializerClasses的值,這個值指定了用戶自定義的ApplicationContextInitializer實現類全名稱。然后根據類的全名稱創建Class對象。其中如果需要指定多個ApplicationContextInitializer實現類,那么實現類的全名稱之間使用英文逗號隔開,比如下面的配置。

<!-- 定義globalInitializerClasses參數 --> <context-param> <param-name>globalInitializerClasses</param-name> <param-value> com.damuzee.web.app.GlobalApplicationContextInitializer1, com.damuzee.web.app.GlobalApplicationContextInitializer2, com.damuzee.web.app.GlobalApplicationContextInitializer3 </param-value> </context-param> <!-- 定義contextInitializerClasses參數 --> <context-param> <param-name>contextInitializerClasses</param-name> <param-value> com.damuzee.web.app.XmlApplicationContextInitializer1, com.damuzee.web.app.XmlApplicationContextInitializer2, com.damuzee.web.app.XmlApplicationContextInitializer3 </param-value> </context-param>

到此,在web應用中spring容器初始化的第一個階段就完成了。configureAndRefreshWebApplicationContext方法通過調用容器的refresh()方法進入容器初始化的第二階段——容器的刷新過程

第二階段: 容器的刷新過程

關于spring容器的刷新過程已經在另一篇文章中描述了,詳見 Spring ApplicationContext的刷新過程

總結

在spring容器初始化的第一個階段,我們可以通過web.xml文件的配置來定制spring容器。通過web.xml文件,我們可以指定其他容器類、父容器、容器的id、需要加載的配置文件地址、以及自定義容器初始化器ApplicationContextInitializer對象。具體例子如下。

通過設置contextClass參數指定容器,例如 <context-param> <param-name>contextClass</param-name> <param-value> com.chyohn.context.XmlWebApplicationContext </param-value> </context-param>設置locatorFactorySelector和parentContextKey參數指定父容器,例如 <!-- 定義locatorFactorySelector參數 --> <context-param> <param-name>locatorFactorySelector</param-name> <param-value> classpath:parentBeanRefContext.xml </param-value> </context-param> <!-- 定義parentContextKey參數 --> <context-param> <param-name>parentContextKey</param-name> <param-value>parentContext</param-value> </context-param>

上面的parentBeanRefContext.xml配置如下

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <!-- 指定容器 --> <bean id="parentContext" class="org.springframework.context.support.ClassPathXmlApplicationContext"> <constructor-arg> <list> <value>parentOfRootContext.xml</value> </list> </constructor-arg> </bean></beans> 設置contextId參數指定容器的Id,配置如下。 <context-param> <param-name>contextId</param-name> <param-value>myContextId</param-value> </context-param>設置contextConfigLocation參數指定加載的配置文件地址,配置如下。 <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:webApplicationContent.xml classpath:application-service.xml </param-value> </context-param>設置contextInitializerClasses參數指定容器初始化器,該初始化器將在容器刷新前執行,如果有多個初始化器,使用英文逗號“,”隔開,例如 <!-- 定義contextInitializerClasses參數 --> <context-param> <param-name>contextInitializerClasses</param-name> <param-value> com.damuzee.web.app.XmlApplicationContextInitializer1, com.damuzee.web.app.XmlApplicationContextInitializer2, com.damuzee.web.app.XmlApplicationContextInitializer3 </param-value> </context-param>設置globalInitializerClasses參數指定所有容器公共的初始化器,該初始化器將在容器刷新前執行,如果有多個初始化器,使用英文逗號“,”隔開,例如 <!-- 定義globalInitializerClasses參數 --> <context-param> <param-name>globalInitializerClasses</param-name> <param-value> com.damuzee.web.app.GlobalApplicationContextInitializer1, com.damuzee.web.app.GlobalApplicationContextInitializer2, com.damuzee.web.app.GlobalApplicationContextInitializer3 </param-value> </context-param>

有了ApplicationContextInitializer對象,可以對容器的初始化做更多操作,比如設置容器id、設置父容器、設置加載的配置文件地址、添加容器級的bean工廠后處理器、監聽器等等。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲精品少妇网址| 欧洲精品久久久| 日日摸夜夜添一区| 国产精品毛片a∨一区二区三区|国| 欧美视频在线观看 亚洲欧| 国产精品久久999| 欧美激情视频在线观看| 国产精品91一区| 欧美—级a级欧美特级ar全黄| 8x海外华人永久免费日韩内陆视频| 久久久国产影院| 国产精品一区二区久久| 人九九综合九九宗合| 亚洲精品欧美日韩| 欧美黑人性猛交| 精品动漫一区二区三区| 日本精品久久电影| 国产v综合v亚洲欧美久久| 国产精品久久久久久久电影| 亚洲性69xxxbbb| 成人h猎奇视频网站| www.亚洲一二| 欧美视频不卡中文| 色视频www在线播放国产成人| 国产精品va在线播放我和闺蜜| 精品国产精品自拍| 国产99久久精品一区二区| 欧美日韩国产色| 国产精品视频大全| 国产一区二区三区视频| 日韩hd视频在线观看| 成人xxxxx| 欧美极品少妇与黑人| 欧美怡春院一区二区三区| 热久久免费视频精品| 国产美女高潮久久白浆| 91精品国产高清自在线看超| 国产精品老女人精品视频| 成人黄色免费在线观看| 国产欧美日韩视频| 国产精品久久久久久久久久新婚| 日韩在线激情视频| 亚洲一区二区三区成人在线视频精品| 51精品在线观看| 精品国产户外野外| 亚洲电影免费观看高清| 日韩高清电影好看的电视剧电影| 国产ts人妖一区二区三区| 亚洲另类激情图| 久久成人18免费网站| 91在线无精精品一区二区| 久久这里只有精品视频首页| 97在线观看视频国产| 国产一区二区三区18| 日本精品一区二区三区在线| 亚洲国产欧美日韩精品| 日韩精品视频在线| 久久久亚洲影院你懂的| 成人午夜小视频| 久久久国产精品亚洲一区| 欧美一区二区三区……| 欧美日韩色婷婷| 欧美成人午夜剧场免费观看| 亚洲福利视频在线| 国产噜噜噜噜久久久久久久久| 丝袜美腿亚洲一区二区| 欧美日韩一区二区免费在线观看| 57pao国产成人免费| 国产精品欧美在线| 亚洲人成欧美中文字幕| 亚洲国产成人精品女人久久久| 91欧美日韩一区| 在线观看精品自拍私拍| 亚洲日本aⅴ片在线观看香蕉| 国产成人精品电影久久久| 97精品国产97久久久久久免费| 亚洲国产精品视频在线观看| 国产精品高潮呻吟久久av黑人| 色老头一区二区三区在线观看| 亚洲第一av网站| 欧美激情一二区| 亚洲日韩欧美视频一区| 精品夜色国产国偷在线| 国产ts一区二区| 欧美体内谢she精2性欧美| 日本久久久久久久久久久| 午夜精品99久久免费| 91精品久久久久久久久久久| 国产视频久久久久| 成人午夜两性视频| 亚洲深夜福利视频| 91国产精品视频在线| 日韩国产高清视频在线| 成人美女免费网站视频| 亚洲第一福利视频| 5566成人精品视频免费| 国产午夜精品一区理论片飘花| 精品久久久国产精品999| 人人做人人澡人人爽欧美| 欧美激情精品久久久久久大尺度| 国产精品视频xxx| 色偷偷av一区二区三区| 欧美亚洲伦理www| 成人黄色在线免费| 久久综合久久八八| 国产欧美日韩精品丝袜高跟鞋| 精品夜色国产国偷在线| 欧美大奶子在线| 国产精品视频xxxx| 51精品国产黑色丝袜高跟鞋| 国模视频一区二区| 国产一区玩具在线观看| 国产精品电影久久久久电影网| 亚洲少妇激情视频| 亚洲天堂网站在线观看视频| 久久久av一区| 成人在线中文字幕| 午夜欧美大片免费观看| 久久亚洲国产精品| 欧美成人精品一区二区三区| 欧美日韩激情视频| 正在播放国产一区| 日韩av网站导航| www.久久草.com| 欧美性受xxx| 亚洲网站视频福利| 亚洲国产精品va在线| 91国内精品久久| 色阁综合伊人av| 日韩黄在线观看| 欧美国产极速在线| 久久亚洲精品小早川怜子66| 色青青草原桃花久久综合| 国产精品小说在线| 亚洲九九九在线观看| 欧美大片在线影院| 2019最新中文字幕| 亚洲第一区中文字幕| 欧美最猛性xxxxx亚洲精品| 在线观看国产精品日韩av| 色综久久综合桃花网| 91精品久久久久久| 91日本视频在线| 欧美最顶级丰满的aⅴ艳星| 亲子乱一区二区三区电影| 亚洲视频欧洲视频| 亚洲国产另类久久精品| 久久视频免费在线播放| 久久99精品久久久久久青青91| 国内揄拍国内精品少妇国语| 久久久久久亚洲精品中文字幕| 国产成人精品综合久久久| 欧美精品午夜视频| 97精品在线观看| 久久人人爽人人爽人人片亚洲| 亚洲精品视频在线观看视频| 2019亚洲日韩新视频| 欧美成人四级hd版| 国产精品美乳在线观看| 久久久久久国产精品三级玉女聊斋| 中国人与牲禽动交精品| 国外成人性视频| 91久久久久久久一区二区| 久久久91精品|