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

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

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

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

概述

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
国产精品免费久久久久久| 久久香蕉频线观| 亚洲另类激情图| 欧美日韩亚洲精品一区二区三区| 国产精品wwwwww| 国产精品免费视频久久久| 欧美精品制服第一页| 欧美极品少妇xxxxⅹ免费视频| 亚洲国产精品一区二区三区| 日本a级片电影一区二区| 欧美日韩亚洲成人| 日本一区二区三区四区视频| 久久久久久免费精品| 青青草一区二区| 九九久久久久久久久激情| 久国内精品在线| 亚洲午夜未满十八勿入免费观看全集| 成人综合国产精品| 成人黄色av播放免费| 欧美成人久久久| 中文字幕av日韩| 久久久999精品视频| 国产ts人妖一区二区三区| 久久久最新网址| 久久中文精品视频| 久久精品男人天堂| 亚洲日本中文字幕| 日韩美女在线看| 97色在线视频| 久久久久久综合网天天| 亚洲黄色av网站| 中文字幕亚洲无线码在线一区| 久久久人成影片一区二区三区| 欧美日韩一区二区精品| 欧美精品video| 亚洲男人的天堂网站| 国产成人精品一区二区在线| 国产精品电影久久久久电影网| 亚洲人成电影网站| 日韩久久免费电影| 国产精品国语对白| 亚洲女人初尝黑人巨大| 色悠悠国产精品| 亚洲精品久久久久久下一站| 欧美日韩福利在线观看| 成人亚洲欧美一区二区三区| 亚洲视频一区二区| 精品无人区太爽高潮在线播放| 66m—66摸成人免费视频| 91超碰中文字幕久久精品| 高清日韩电视剧大全免费播放在线观看| 国产精品观看在线亚洲人成网| 亚洲国产小视频| 人人澡人人澡人人看欧美| 国产精品旅馆在线| 国产精品va在线| 欧美成人剧情片在线观看| 91精品久久久久久久久不口人| 久久精品2019中文字幕| 欧美影院成年免费版| 精品福利樱桃av导航| 中文字幕在线看视频国产欧美| 日韩电影中文 亚洲精品乱码| 欧美精品午夜视频| 欧美香蕉大胸在线视频观看| 精品偷拍各种wc美女嘘嘘| 国产精品午夜国产小视频| 成人性生交大片免费看小说| 九九久久精品一区| 欧美大片大片在线播放| 色妞色视频一区二区三区四区| 亚洲精品国产精品久久清纯直播| 久久理论片午夜琪琪电影网| 亚洲一区二区少妇| 欧美另类老女人| 欧美成人四级hd版| 国产精品一区二区三区免费视频| 日韩成人在线视频网站| 国产亚洲美女久久| 欧美日韩国产丝袜美女| 国产精品久久久久久av福利| 欧美成人自拍视频| 欧美裸体男粗大视频在线观看| 4444欧美成人kkkk| 97精品视频在线观看| 狠狠躁夜夜躁人人爽超碰91| 欲色天天网综合久久| 国产精品久久一区主播| 国产99久久久欧美黑人| 91日韩在线视频| 亚洲黄一区二区| 亚洲国产成人精品久久久国产成人一区| 亚洲美女av在线| 国产欧美精品一区二区三区介绍| 精品综合久久久久久97| 久久深夜福利免费观看| 欧美日韩国产精品一区| 日本伊人精品一区二区三区介绍| 亚洲成人久久网| 影音先锋日韩有码| 国产中文欧美精品| 中文字幕成人精品久久不卡| 尤物yw午夜国产精品视频明星| 91精品视频观看| 亚洲一区二区福利| 精品久久久久久亚洲国产300| 国模精品视频一区二区三区| 欧美激情久久久久| 国产亚洲美女精品久久久| 国产精品久久久久久久久久久不卡| 成人精品久久一区二区三区| 久久人人爽人人爽人人片av高清| 国产精品黄色影片导航在线观看| 中文字幕亚洲一区二区三区| 亚洲视频综合网| 国产精品日韩欧美综合| 成人av.网址在线网站| 日韩一区二区三区xxxx| 亚洲国产精品电影在线观看| 日韩精品在线私人| 91在线精品播放| 久久亚洲精品视频| 国产精品96久久久久久又黄又硬| 成人久久18免费网站图片| 欧美精品在线免费观看| 国产精选久久久久久| 亚洲国产精品va在线看黑人| 久久视频国产精品免费视频在线| 欧美性猛交xxxx乱大交蜜桃| 91色中文字幕| 国内精久久久久久久久久人| 久久99精品久久久久久噜噜| 国产成人精品久久亚洲高清不卡| 欧美福利在线观看| 亚洲美女动态图120秒| 久久久999国产| 亚洲曰本av电影| 97精品视频在线观看| 热久久免费国产视频| 欧美乱大交做爰xxxⅹ性3| 日韩欧美亚洲国产一区| 亚洲国产精品人人爽夜夜爽| 欧洲美女7788成人免费视频| 亚洲久久久久久久久久| 国产亚洲日本欧美韩国| 欧美性xxxxxx| 国内伊人久久久久久网站视频| 欧美激情中文字幕乱码免费| 亚洲在线观看视频| 欧美色videos| 亚洲成色999久久网站| 欧美综合激情网| 最近2019中文字幕mv免费看| 精品成人乱色一区二区| 91精品在线影院| 搡老女人一区二区三区视频tv| 国产69精品久久久久9| 热re99久久精品国产66热| 91亚洲国产精品| 国产福利成人在线| 国产精品国产三级国产aⅴ浪潮| 国产精品亚洲美女av网站| 欧美成人手机在线| 日韩三级成人av网|