黄a在线观看-黄a在线-黄a大片-黄色片在线看-黄色毛片免费-黄色大片网站

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

這一次搞懂Spring的XML解析原理說(shuō)明

瀏覽:134日期:2023-08-18 16:32:49

前言

Spring已經(jīng)是我們Java Web開(kāi)發(fā)必不可少的一個(gè)框架,其大大簡(jiǎn)化了我們的開(kāi)發(fā),提高了開(kāi)發(fā)者的效率。同時(shí),其源碼對(duì)于開(kāi)發(fā)者來(lái)說(shuō)也是寶藏,從中我們可以學(xué)習(xí)到非常優(yōu)秀的設(shè)計(jì)思想以及優(yōu)雅的命名規(guī)范,但因其體系龐大、設(shè)計(jì)復(fù)雜對(duì)于剛開(kāi)始閱讀源碼的人來(lái)說(shuō)是非常困難的。所以在此之前首先你得下定決心,不管有多困難都得堅(jiān)持下去;其次,最好先把設(shè)計(jì)模式掌握熟練;然后在開(kāi)始閱讀源碼時(shí)一定要多畫(huà)UML類圖和時(shí)序圖,多問(wèn)自己為什么要這么設(shè)計(jì)?這樣設(shè)計(jì)的好處是什么?還有沒(méi)有更好的設(shè)計(jì)?當(dāng)然,暈車是難免的,但還是那句話,一定要持之以恒(PS:源碼版本5.1.3.RELEASE)。

正文

熟悉IOC體系結(jié)構(gòu)

要學(xué)習(xí)Spring源碼,我們首先得要找準(zhǔn)入口,那這個(gè)入口怎么找呢?我們不妨先思考一下,在Spring項(xiàng)目啟動(dòng)時(shí),Spring做了哪些事情。這里我以最原始的xml配置方式來(lái)分析,那么在項(xiàng)目啟動(dòng)時(shí),首先肯定要先定位——找到xml配置文件,定位之后肯定是加載——將我們的配置加載到內(nèi)存,最后才是根據(jù)我們的配置實(shí)例化(本篇文章只講前兩個(gè)過(guò)程)。那么Spring是如何定位和加載xml文件的呢?涉及到哪些類呢?我們先來(lái)看張類圖:

這一次搞懂Spring的XML解析原理說(shuō)明

該圖是IOC的體系圖,整體上你需要有一個(gè)大概的印象,可以看到所有的IOC都是有繼承關(guān)系的,這樣設(shè)計(jì)的好處就是任何一個(gè)子類IOC可以直接使用父類IOC加載的Bean,有點(diǎn)像JVM類加載的雙親委派機(jī)制;而紅色方框圈起來(lái)的是本篇涉及到的重要類,需要著重記憶它們的關(guān)系。

圖中最重要的兩個(gè)類是BeanFactory和ApplicationContext,這是所有IOC的父接口。其中BeanFactory提供了最基本的對(duì)bean的操作:

這一次搞懂Spring的XML解析原理說(shuō)明

而ApplicationContex繼承了BeanFactory,同時(shí)還繼承了MessageSource、ResourceLoader、ApplicationEventPublisher等接口以提供國(guó)際化、資源加載、事件發(fā)布等高級(jí)功能。我們應(yīng)該想到平時(shí)Spring加載xml文件應(yīng)該是ApplicationContext的子類,從圖中我們可以看到一個(gè)叫ClassPathXmlApplicationContext的類,聯(lián)想到我們平時(shí)都會(huì) 將xml放到classPath下,所以我們直接從這個(gè)類開(kāi)始就行,這就是優(yōu)秀命名的好處。

探究配置加載的過(guò)程

在ClassPathXmlApplicationContext中有很多構(gòu)造方法,其中有一個(gè)是傳入一個(gè)字符串的(即配置文件的相對(duì)路徑),但最終是調(diào)用的下面這個(gè)構(gòu)造:

public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); //創(chuàng)建解析器,解析configLocations setConfigLocations(configLocations); if (refresh) { refresh(); } }

首先調(diào)用父類構(gòu)造器設(shè)置環(huán)境:

public AbstractApplicationContext(@Nullable ApplicationContext parent) { this(); setParent(parent); } public void setParent(@Nullable ApplicationContext parent) { this.parent = parent; if (parent != null) { Environment parentEnvironment = parent.getEnvironment(); if (parentEnvironment instanceof ConfigurableEnvironment) { getEnvironment().merge((ConfigurableEnvironment) parentEnvironment); } } }

然后解析傳入的相對(duì)路徑保存到configLocations變量中,最后再調(diào)用父類AbstractApplicationContext的refresh方法刷新容器(啟動(dòng)容器都會(huì)調(diào)用該方法),我們著重來(lái)看這個(gè)方法:

public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //為容器初始化做準(zhǔn)備 prepareRefresh(); // 解析xml ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn('Exception encountered during context initialization - ' + 'cancelling refresh attempt: ' + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset ’active’ flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring’s core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }

這個(gè)方法是一個(gè)典型的模板方法模式的實(shí)現(xiàn),第一步是準(zhǔn)備初始化容器環(huán)境,這一步不重要,重點(diǎn)是第二步,創(chuàng)建BeanFactory對(duì)象、加載解析xml并封裝成BeanDefinition對(duì)象都是在這一步完成的。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); return getBeanFactory(); }

點(diǎn)進(jìn)去看是調(diào)用了refreshBeanFactory方法,但這里有兩個(gè)實(shí)現(xiàn),應(yīng)該進(jìn)哪一個(gè)類里面呢?

這一次搞懂Spring的XML解析原理說(shuō)明

如果你還記得前面的繼承體系,那你就會(huì)毫不猶豫的進(jìn)入AbstractRefreshableApplicationContext類中,所以在閱讀源碼的過(guò)程中一定要記住類的繼承體系。

protected final void refreshBeanFactory() throws BeansException { //如果BeanFactory不為空,則清除BeanFactory和里面的實(shí)例 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //創(chuàng)建DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); //設(shè)置是否可以循環(huán)依賴 allowCircularReferences //是否允許使用相同名稱重新注冊(cè)不同的bean實(shí)現(xiàn). customizeBeanFactory(beanFactory); //解析xml,并把xml中的標(biāo)簽封裝成BeanDefinition對(duì)象 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException('I/O error parsing bean definition source for ' + getDisplayName(), ex); } }

在這個(gè)方法中首先會(huì)清除掉上一次創(chuàng)建的BeanFactory和對(duì)象實(shí)例,然后創(chuàng)建了一個(gè)DefaultListableBeanFactory對(duì)象并傳入到了loadBeanDefinitions方法中,這也是一個(gè)模板方法,因?yàn)槲覀兊呐渲貌恢褂衳ml,還有注解等,所以這里我們應(yīng)該進(jìn)入AbstractXmlApplicationContext類中:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { //創(chuàng)建xml的解析器,這里是一個(gè)委托模式 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context’s // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); //這里傳一個(gè)this進(jìn)去,因?yàn)锳pplicationContext是實(shí)現(xiàn)了ResourceLoader接口的 beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); //主要看這個(gè)方法 loadBeanDefinitions(beanDefinitionReader); }

首先創(chuàng)建了一個(gè)XmlBeanDefinitionReader對(duì)象,見(jiàn)名知意,這個(gè)就是解析xml的類,需要注意的是該類的構(gòu)造方法接收的是BeanDefinitionRegistry對(duì)象,而這里將DefaultListableBeanFactory對(duì)象傳入了進(jìn)去(別忘記了這個(gè)對(duì)象是實(shí)現(xiàn)了BeanDefinitionRegistry類的),如果你足夠敏感,應(yīng)該可以想到后面會(huì)委托給該類去注冊(cè)。注冊(cè)什么呢?自然是注冊(cè)BeanDefintion。記住這個(gè)猜想,我們稍后來(lái)驗(yàn)證是不是這么回事。

接著進(jìn)入loadBeanDefinitions方法獲取之前保存的xml配置文件路徑,并委托給XmlBeanDefinitionReader對(duì)象解析加載:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } //獲取需要加載的xml配置文件 String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }

最后會(huì)進(jìn)入到抽象父類AbstractBeanDefinitionReader中:

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { // 這里獲取到的依然是DefaultListableBeanFactory對(duì)象 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( 'Cannot load bean definitions from location [' + location + ']: no ResourceLoader available'); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //把字符串類型的xml文件路徑,形如:classpath*:user/**/*-context.xml,轉(zhuǎn)換成Resource對(duì)象類型,其實(shí)就是用流 //的方式加載配置文件,然后封裝成Resource對(duì)象 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //主要看這個(gè)方法 int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace('Loaded ' + count + ' bean definitions from location pattern [' + location + ']'); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( 'Could not resolve bean definition resource pattern [' + location + ']', ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace('Loaded ' + count + ' bean definitions from location [' + location + ']'); } return count; } }

這個(gè)方法中主要將xml配置加載到存中并封裝成為Resource對(duì)象,這一步不重要,可以略過(guò),主要的還是loadBeanDefinitions方法,最終還是調(diào)用到子類XmlBeanDefinitionReader的方法:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { try { //獲取Resource對(duì)象中的xml文件流對(duì)象 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //InputSource是jdk中的sax xml文件解析對(duì)象 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //主要看這個(gè)方法 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //把inputSource 封裝成Document文件對(duì)象,這是jdk的API Document doc = doLoadDocument(inputSource, resource); //主要看這個(gè)方法,根據(jù)解析出來(lái)的document對(duì)象,拿到里面的標(biāo)簽元素封裝成BeanDefinition int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug('Loaded ' + count + ' bean definitions from ' + resource); } return count; } } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 創(chuàng)建DefaultBeanDefinitionDocumentReader對(duì)象,并委托其做解析注冊(cè)工作 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //主要看這個(gè)方法,需要注意createReaderContext方法中創(chuàng)建的幾個(gè)對(duì)象 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; } public XmlReaderContext createReaderContext(Resource resource) { // XmlReaderContext對(duì)象中保存了XmlBeanDefinitionReader對(duì)象和DefaultNamespaceHandlerResolver對(duì)象的引用,在后面會(huì)用到 return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver()); }

接著看看DefaultBeanDefinitionDocumentReader中是如何解析的:

protected void doRegisterBeanDefinitions(Element root) { // 創(chuàng)建了BeanDefinitionParserDelegate對(duì)象 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); // 如果是Spring原生命名空間,首先解析 profile標(biāo)簽,這里不重要 if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug('Skipped XML bean definition file due to specified profiles [' + profileSpec + '] not matching: ' + getReaderContext().getResource()); } return; } } } preProcessXml(root); //主要看這個(gè)方法,標(biāo)簽具體解析過(guò)程 parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }

在這個(gè)方法中重點(diǎn)關(guān)注preProcessXml、parseBeanDefinitions、postProcessXml三個(gè)方法,其中preProcessXml和postProcessXml都是空方法,意思是在解析標(biāo)簽前后我們自己可以擴(kuò)展需要執(zhí)行的操作,也是一個(gè)模板方法模式,體現(xiàn)了Spring的高擴(kuò)展性。然后進(jìn)入parseBeanDefinitions方法看具體是怎么解析標(biāo)簽的:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { //默認(rèn)標(biāo)簽解析 parseDefaultElement(ele, delegate); } else { //自定義標(biāo)簽解析 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }

這里有兩種標(biāo)簽的解析:Spring原生標(biāo)簽和自定義標(biāo)簽。怎么區(qū)分這兩種標(biāo)簽?zāi)兀?/p>

// 自定義標(biāo)簽<context:component-scan/>// 默認(rèn)標(biāo)簽<bean:/>

如上,帶前綴的就是自定義標(biāo)簽,否則就是Spring默認(rèn)標(biāo)簽,無(wú)論哪種標(biāo)簽在使用前都需要在Spring的xml配置文件里聲明Namespace URI,這樣在解析標(biāo)簽時(shí)才能通過(guò)Namespace URI找到對(duì)應(yīng)的NamespaceHandler。

xmlns:context='http://www.springframework.org/schema/context'

http://www.springframework.org/schema/beans

isDefaultNamespace判斷是不是默認(rèn)標(biāo)簽,點(diǎn)進(jìn)去看看是不是跟我上面說(shuō)的一致:

public boolean isDefaultNamespace(Node node) { return isDefaultNamespace(getNamespaceURI(node)); } public static final String BEANS_NAMESPACE_URI = 'http://www.springframework.org/schema/beans'; public boolean isDefaultNamespace(@Nullable String namespaceUri) { return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri)); }

可以看到http://www.springframework.org/schema/beans所對(duì)應(yīng)的就是默認(rèn)標(biāo)簽。接著,我們進(jìn)入parseDefaultElement方法:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //import標(biāo)簽解析 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //alias標(biāo)簽解析 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //bean標(biāo)簽 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }

這里面主要是對(duì)import、alias、bean標(biāo)簽的解析以及beans的字標(biāo)簽的遞歸解析,主要看看bean標(biāo)簽的解析:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 解析elment封裝為BeanDefinitionHolder對(duì)象 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 該方法功能不重要,主要理解設(shè)計(jì)思想:裝飾者設(shè)計(jì)模式以及SPI設(shè)計(jì)思想 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 完成document到BeanDefinition對(duì)象轉(zhuǎn)換后,對(duì)BeanDefinition對(duì)象進(jìn)行緩存注冊(cè) BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } } public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // 獲取id和name屬性 String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 獲取別名屬性,多個(gè)別名可用,;隔開(kāi) List<String> aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isTraceEnabled()) { logger.trace('No XML ’id’ specified - using ’' + beanName + '’ as bean name and ' + aliases + ' as aliases'); } } //檢查beanName是否重復(fù) if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // 具體的解析封裝過(guò)程還在這個(gè)方法里 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isTraceEnabled()) { logger.trace('Neither XML ’id’ nor ’name’ specified - ' + 'using generated bean name [' + beanName + ']'); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; } // bean的解析 public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); // 獲取class名稱和父類名稱 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { // 創(chuàng)建GenericBeanDefinition對(duì)象 AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 解析bean標(biāo)簽的屬性,并把解析出來(lái)的屬性設(shè)置到BeanDefinition對(duì)象中 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //解析bean中的meta標(biāo)簽 parseMetaElements(ele, bd); //解析bean中的lookup-method標(biāo)簽 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //解析bean中的replaced-method標(biāo)簽 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析bean中的constructor-arg標(biāo)簽 parseConstructorArgElements(ele, bd); //解析bean中的property標(biāo)簽 parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } return null; }

bean標(biāo)簽的解析步驟仔細(xì)理解并不復(fù)雜,就是將一個(gè)個(gè)標(biāo)簽屬性的值裝入到了BeanDefinition對(duì)象中,這里需要注意parseConstructorArgElements和parsePropertyElements方法,分別是對(duì)constructor-arg和property標(biāo)簽的解析,解析完成后分別裝入了BeanDefinition對(duì)象的constructorArgumentValues和propertyValues中,而這兩個(gè)屬性在接下來(lái)c和p標(biāo)簽的解析中還會(huì)用到,而且還涉及一個(gè)很重要的設(shè)計(jì)思想——裝飾器模式。

Bean標(biāo)簽解析完成后將生成的BeanDefinition對(duì)象、bean的名稱以及別名一起封裝到了BeanDefinitionHolder對(duì)象并返回,然后調(diào)用了decorateBeanDefinitionIfRequired進(jìn)行裝飾:

public BeanDefinitionHolder decorateBeanDefinitionIfRequired( Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) { BeanDefinitionHolder finalDefinition = definitionHolder; //根據(jù)bean標(biāo)簽屬性裝飾BeanDefinitionHolder,比如<bean p:username='dark'/> NamedNodeMap attributes = ele.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Node node = attributes.item(i); finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); } //根據(jù)bean標(biāo)簽子元素裝飾BeanDefinitionHolder NodeList children = ele.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); } } return finalDefinition; }

在這個(gè)方法中分別對(duì)Bean標(biāo)簽的屬性和子標(biāo)簽迭代,獲取其中的自定義標(biāo)簽進(jìn)行解析,并裝飾之前創(chuàng)建的BeanDefinition對(duì)象,如同下面的c和p:

// c:和p:表示通過(guò)構(gòu)造器和屬性的setter方法給屬性賦值,是constructor-arg和property的簡(jiǎn)化寫(xiě)法

<bean p:username='Dark' p:password='111' c:age='12' c:sex='1'/>

兩個(gè)步驟是一樣的,我們點(diǎn)進(jìn)decorateIfRequired方法中:

public BeanDefinitionHolder decorateIfRequired( Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { //根據(jù)node獲取到node的命名空間,形如:http://www.springframework.org/schema/p String namespaceUri = getNamespaceURI(node); if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) { // 根據(jù)配置文件獲取namespaceUri對(duì)應(yīng)的處理類,SPI思想 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler != null) { //調(diào)用NamespaceHandler處理類的decorate方法,開(kāi)始具體裝飾過(guò)程,并返回裝飾完的對(duì)象 BeanDefinitionHolder decorated = handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd)); if (decorated != null) { return decorated; } } else if (namespaceUri.startsWith('http://www.springframework.org/')) { error('Unable to locate Spring NamespaceHandler for XML schema namespace [' + namespaceUri + ']', node); } else { // A custom namespace, not to be handled by Spring - maybe 'xml:...'. if (logger.isDebugEnabled()) { logger.debug('No Spring NamespaceHandler found for XML schema namespace [' + namespaceUri + ']'); } } } return originalDef; }

這里也和我們之前說(shuō)的一樣,首先獲取到標(biāo)簽對(duì)應(yīng)的namespaceUri,然后通過(guò)這個(gè)Uri去獲取到對(duì)應(yīng)的NamespceHandler,最后再調(diào)用NamespceHandler的decorate方法進(jìn)行裝飾。我們先來(lái)看看獲取NamespceHandler的過(guò)程,這涉及到一個(gè)非常重要的高擴(kuò)展性的思想——SPI(有關(guān)SPI,在我之前的文章Dubbo——SPI及自適應(yīng)擴(kuò)展原理中已經(jīng)詳細(xì)講解過(guò),這里不再贅述):

public NamespaceHandler resolve(String namespaceUri) { // 獲取spring中所有jar包里面的 'META-INF/spring.handlers'文件,并且建立映射關(guān)系 Map<String, Object> handlerMappings = getHandlerMappings(); //根據(jù)namespaceUri:http://www.springframework.org/schema/p,獲取到這個(gè)命名空間的處理類 Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { String className = (String) handlerOrClassName; try { Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException('Class [' + className + '] for namespace [' + namespaceUri + '] does not implement the [' + NamespaceHandler.class.getName() + '] interface'); } NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); //調(diào)用處理類的init方法,在init方法中完成標(biāo)簽元素解析類的注冊(cè) namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } } } // AOP標(biāo)簽對(duì)應(yīng)的NamespaceHandler,可以發(fā)現(xiàn)NamespaceHandler的作用就是管理和注冊(cè)與自己相關(guān)的標(biāo)簽解析器 public void init() { // In 2.0 XSD as well as in 2.1 XSD. registerBeanDefinitionParser('config', new ConfigBeanDefinitionParser()); registerBeanDefinitionParser('aspectj-autoproxy', new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator('scoped-proxy', new ScopedProxyBeanDefinitionDecorator()); // Only in 2.0 XSD: moved to context namespace as of 2.1 registerBeanDefinitionParser('spring-configured', new SpringConfiguredBeanDefinitionParser()); }

看到這里我們應(yīng)該就清楚了Spring是如何解析xml里的標(biāo)簽了以及我們?nèi)绻獢U(kuò)展自己的標(biāo)簽該怎么做。只需要?jiǎng)?chuàng)建一個(gè)我們的自定義標(biāo)簽和解析類,并指定它的命名空間以及NamespaceHandler,最后在META-INF/spring.handlers文件中指定命名空間和NamespaceHandler的映射關(guān)系即可,就像Spring的c和p標(biāo)簽一樣:

http://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler

http://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler

像這樣使用SPI的思想設(shè)計(jì)我們的項(xiàng)目的話,當(dāng)需要擴(kuò)展時(shí),不需要改動(dòng)任何的代碼,非常的方便優(yōu)雅。

接著,我們回到handler的decorate方法,這里有三個(gè)默認(rèn)的實(shí)現(xiàn)類:NamespaceHandlerSupport、SimpleConstructorNamespaceHandler、SimplePropertyNamespaceHandler。第一個(gè)是一個(gè)抽象類,與我們這里的流程無(wú)關(guān),感興趣的可自行了解,第二個(gè)和第三個(gè)則分別是c和p標(biāo)簽對(duì)應(yīng)的NamespaceHandler,兩個(gè)裝飾的處理邏輯基本上是一樣的,我這里進(jìn)入的是SimpleConstructorNamespaceHandler類:

public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) { if (node instanceof Attr) { Attr attr = (Attr) node; String argName = StringUtils.trimWhitespace(parserContext.getDelegate().getLocalName(attr)); String argValue = StringUtils.trimWhitespace(attr.getValue()); ConstructorArgumentValues cvs = definition.getBeanDefinition().getConstructorArgumentValues(); boolean ref = false; // handle -ref arguments if (argName.endsWith(REF_SUFFIX)) { ref = true; argName = argName.substring(0, argName.length() - REF_SUFFIX.length()); } ValueHolder valueHolder = new ValueHolder(ref ? new RuntimeBeanReference(argValue) : argValue); valueHolder.setSource(parserContext.getReaderContext().extractSource(attr)); // handle 'escaped'/'_' arguments if (argName.startsWith(DELIMITER_PREFIX)) { String arg = argName.substring(1).trim(); // fast default check if (!StringUtils.hasText(arg)) { cvs.addGenericArgumentValue(valueHolder); } // assume an index otherwise else { int index = -1; try { index = Integer.parseInt(arg); } catch (NumberFormatException ex) { parserContext.getReaderContext().error( 'Constructor argument ’' + argName + '’ specifies an invalid integer', attr); } if (index < 0) { parserContext.getReaderContext().error( 'Constructor argument ’' + argName + '’ specifies a negative index', attr); } if (cvs.hasIndexedArgumentValue(index)) { parserContext.getReaderContext().error( 'Constructor argument ’' + argName + '’ with index '+ index+' already defined using <constructor-arg>.' + ' Only one approach may be used per argument.', attr); } cvs.addIndexedArgumentValue(index, valueHolder); } } // no escaping -> ctr name else { String name = Conventions.attributeNameToPropertyName(argName); if (containsArgWithName(name, cvs)) { parserContext.getReaderContext().error( 'Constructor argument ’' + argName + '’ already defined using <constructor-arg>.' + ' Only one approach may be used per argument.', attr); } valueHolder.setName(Conventions.attributeNameToPropertyName(argName)); cvs.addGenericArgumentValue(valueHolder); } } return definition; }

很簡(jiǎn)單,拿到c標(biāo)簽對(duì)應(yīng)的值,封裝成ValueHolder,再添加到BeanDefinition的ConstructorArgumentValues屬性中去,這樣就裝飾完成了。

講到這里你可能會(huì)覺(jué)得,這和平時(shí)看到裝飾器模式不太一樣。其實(shí),設(shè)計(jì)模式真正想要表達(dá)的是各種模式所代表的思想,而不是死搬硬套的實(shí)現(xiàn),只有靈活的運(yùn)用其思想才算是真正的掌握了設(shè)計(jì)模式,而裝飾器模式的精髓就是動(dòng)態(tài)的將屬性、功能、責(zé)任附加到對(duì)象上,這樣你再看這里是否是運(yùn)用了裝飾器的思想呢?

裝飾完成后返回BeanDefinitionHolder對(duì)象并調(diào)用BeanDefinitionReaderUtils.registerBeanDefinition方法將該對(duì)象緩存起來(lái),等待容器去實(shí)例化。這里就是將其緩存到DefaultListableBeanFactory的beanDefinitionMap屬性中,自己看看代碼也就明白了,我就不貼代碼了。至此,Spring的XML解析原理分析完畢,下面是我畫(huà)的時(shí)序圖,可以對(duì)照看看:

這一次搞懂Spring的XML解析原理說(shuō)明

總結(jié)

本篇是Spring源碼分析的第一篇,只是分析了refresh中的obtainFreshBeanFactory方法,我們可以看到僅僅是對(duì)XML的解析和bean定義的注冊(cè)緩存,Spring就做了這么多事,并考慮到了各個(gè)可能會(huì)擴(kuò)展的地方,那我們平時(shí)做的項(xiàng)目呢?看似簡(jiǎn)單的背后是否有深入思考過(guò)呢?

以上這篇這一次搞懂Spring的XML解析原理說(shuō)明就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 天堂欧美城网站网址 | 黄网站在线播放 | 在线 丝袜 欧美 日韩 制服 | 日韩午夜无码精品试看 | 亚洲欧洲综合 | 亚洲国产黄 | 日韩伊人 | 黄色av网址在线 | 国产7777777 | 国产精品igao视频网网址 | 亚洲无色 | 国语自产少妇精品视频 | 亚洲日韩av片在线观看 | eeuss一区二区 | 亚洲国产一区二区三区四区 | 久久久无码中文字幕久... | 天天艹天天 | 三女同志亚洲人狂欢 | 久操色 | 国产日韩在线观看一区 | 亚洲人成无码网www 国产精品第一区揄拍无码 丰满少妇高潮惨叫视频 | 欧美涩涩涩| 亚洲乱码日产精品bd在线观看 | 亚洲欧美日韩一区在线观看 | 国产免费无遮挡 | 五月天堂婷婷 | 日日躁夜夜摸月月添添添 | 最新国产精品精品视频 | 1000部啪啪未满十八勿入下载 | 国产小仙女精品av揉 | 久久免费视频3 | 182tv在线观看免费午夜免费线路 | 欧美高清videosex极品 | 绯色av中文字幕一区三区 | 中国黄色毛片视频 | 一区二区国产精品视频 | 色美av| 国产 日韩 欧美 制服 另类 | 和漂亮岳做爰3中文字幕 | 久久精品人人做人人综合 | 国产a级精品毛片 | 亚洲天堂av网站 | 欧美精品在线观看视频 | 精品国产亚洲一区二区三区 | 性色一区 | 精品久久久久久久久久国产潘金莲 | 日批视频在线 | 国产精品美女久久久久aⅴ国产馆 | 婷婷久久久久 | 欧美精品在线播放 | 久久综合九色综合97网 | 国产精品色| 男女下面进入的视频免费午夜 | 欧美一级黄色大片 | 欧美激情成人在线 | 成人天堂入口网站 | 亚洲综合不卡 | 99久久久国产精品无码免费 | 日本毛片在线观看 | 色婷婷综合久久久中文一区二区 | 美女裸体自慰在线观看 | 美女网站免费观看视频 | 美女黄色片子 | 他也色在线 | 老司机午夜精品视频资源 | 国产在热线精品视频 | 国内少妇偷人精品视频免费 | 国产成人精品一区二区三区网站观看 | 亚洲精品网站日本xxxxxxx | 亚洲国产一区二区三区波多野结衣 | 欧美精品久久久久久久久免 | 美女国产一区 | 少妇激三级做爰在线观看 | 国产三区视频 | 九九热精品视频在线播放 | 日韩激情在线 | 剧情av在线 | 蜜桃免费在线视频 | 一级做性色a爱片久久毛片欧 | 国产精品久久久久久久毛片明星 | 美女av免费看| 后进极品白嫩翘臀在线播放 | 久久国产精品-国产精品 | 毛片久久久久久久 | 在线观看亚洲网站 | 日韩人妻无码一区二区三区久久99 | 999精彩视频| 国产精品理论片在线观看 | 中日韩高清无专码区2021 | 成人v| 麻豆理论片 | 五十路熟妇高熟无码视频 | 国产对白受不了了中文对白 | 午夜精品久久久内射近拍高清 | 国产成人精品一区二区在线小狼 | 让少妇高潮无乱码高清在线观看 | 爽插| 久久男人av资源网站 | 女人高潮抽搐喷液30分钟视频 | 电车痴汉在线观看 | 国产成a人片在线观看视频下载 | 成人av网址大全 | 水蜜桃亚洲一二三四在线 | 我要看黄色a级片 | 欧美 中文字幕 | 日本日本19xxxⅹhd乱影响 | 午夜一区二区三区四区 | 成人丝袜激情一区二区 | 麻豆视频二区 | 毛片在哪里看 | 毛片少妇| 亚州视频在线 | 露脸丨91丨九色露脸 | 伊大人香蕉综合8在线视 | 美女久久久久久久 | 香蕉久草| 国内精品免费视频 | 成人亚洲综合av天堂 | 一卡二卡三卡在线观看 | 亚洲免费成人av | 国产黄色在线免费观看 | 欧美成a | 亚洲精品视频在线播放 | 午夜日韩视频 | 韩国和日本免费不卡在线v 婷婷俺也去俺也去官网 | 国产精品成人av在线观看 | 俄罗斯av在线 | 国产精品乱码久久久久 | 91精品国产乱码久久蜜臀 | 国产 亚洲 制服 无码 中文 | 大阳蒂毛茸茸videoshd | 国产系列精品av | 性生交大片免费全视频 | 午夜视频在线观看网站 | 国产精品h片在线播放 | 97视频免费在线观看 | 88国产精品视频一区二区三区 | 久久久久无码精品国产app | 欧美一区二区在线观看视频 | 天堂网av中文字幕 | 国产免费一区二区三区免费视频 | 黄色三级网站在线观看 | 日韩久久一区 | 天堂资源在线www在线观看 | 蜜臀av夜夜澡人人爽人人 | 精品国产大片 | 亚洲青春草 | av天堂久久天堂色综合 | 欧美精品一级在线观看 | 亚洲国产免费视频 | 91九色丨porny丨朋友 | 久久一本日日摸夜夜添 | 国产色99精品9i | 黄色片久久 | 日韩久久久久 | av图片在线观看 | 最新欧美大片 | 国产超碰人人模人人爽人人添 | 日本在线免费观看 | aaa黄色大片 | 日本性视频网站 | 国语自产偷拍精品视频偷 | 狠狠色噜噜 | 日韩精品一区二区av在线 | 7777久久亚洲中文字幕 | 欧美黄色免费视频 | 中文字幕无码免费久久 | 国产又黄又猛视频 | 一区一区三区四区产品动漫 | 在线中文字幕视频 | 亚洲综合激情另类专区 | 欧美性生活网 | 黄色片a | 孕妇怀孕高潮潮喷视频孕妇 | 亚洲精品久久久久中文字幕m男 | 亚洲欧美日韩精品suv | 人妻无码久久一区二区三区免费 | 东北女人毛多水多牲交视频 | 中文字幕丰满乱孑伦无码专区 | 欧洲金发美女大战黑人 | 婷婷六月在线 | 波多野42部无码喷潮在线 | 国产毛片一区二区三区va在线 | 日本成人一级片 | 韩国三级hd中文字幕叫床 | 久久精品国产精品亚洲毛片 | jzjzjz欧美丰满少妇 | 国产日韩欧美激情 | 久久久免费毛片 | 夜夜爽av福利精品导航 | 国产精品成人一区二区艾草 | 欧美啪啪一区 | 欧美极品在线视频 | 欧洲成人综合网 | 亚洲综合无码久久精品综合 | 欧美高清一区 | 久久综合给合综合久久 | 午夜免费播放观看在线视频 | 337p大胆啪啪私拍人体 | 九九精品久久 | 91成人在线观看喷潮蘑菇 | 久久精品丝袜 | 欧美麻豆视频 | 99久久免费精品国产男女高不卡 | 超碰在线伊人 | 男人的天堂无码动漫av | 成年性生交大片免费看 | 日韩欧美高清在线 | a猛片免费播放 | 国产羞羞 | 国产精品国产三级国产普通话对白 | 国产一区二区三区免费看 | 亚洲色精品三区二区一区 | 欧美一级免费观看 | 无码日韩精品一区二区免费暖暖 | 国产91在线视频 | 精品久久久久中文字幕日本 | avhd101在线播放高清谜片 | www四虎com| 国产欧美一区二区三区在线看 | 波多野结衣一区 | 伊人久久97 | 国产成人午夜福利在线播放 | 亚洲一级二级视频 | 天天插夜夜爽 | 蜜臀久久99精品久久久久久 | 久久亚洲国产成人精品无码区 | 天天精品视频 | 少妇视频网 | blacked蜜桃精品一区 | 亚洲一区中文字幕在线观看 | 欧美一级黄色片子 | 免费欧美| 欧美日韩综合一区二区三区 | 91干视频 | 午夜天堂av天堂久久久 | 国产精品污污 | 裸体女人高潮毛片 | av不卡免费在线 | 91精品日产一二三区乱码 | 国产精品入口网站7777 | 噜噜色综合噜噜色噜噜色 | 国产1区2| www国产高清 | 日韩射| 国产欧美又粗又猛又爽 | 五月天国色天香国语版 | 免费a级片视频 | 91成人免费在线观看 | 在线免费不卡视频 | 在线免费观看黄色av | 99超碰在线观看 | 三上悠亚在线精品二区 | 国产l精品国产亚洲区在线观看 | 免费在线观看黄 | 亚洲三页| 欧美裸体女人 | 亚洲成av人片不卡无码手机版 | 成人黄色免费 | 韩日精品视频 | 一级一级特黄女人精品毛片 | 久色精品视频 | 久久精热| 中文字幕热久久久久久久 | 国产又色又爽又刺激在线播放 | 国产毛a片啊久久久久久保和丸 | 在线播放成人av | 91成年版| 国产一级啪啪 | 欧美午夜特黄aaaaaa片 | 国产精品水嫩水嫩 | 国产视频一二三四区 | av在线免费在线观看 | 日日操网站 | 91国内精品自线在拍白富美 | 一区二区三区日韩欧美 | 久久婷五月| 黄色网页免费在线观看 | 99精品国产综合久久久久五月天 | 成人午夜影院 | 丰满少妇猛烈进人免费看高潮 | 婷婷色激情 | 亚洲综合色av | 中文字日产幕乱五区 | 国内精品一区二区三区不卡 | 日韩三级久久 | 午夜美女福利视频 | 天堂网va| 国产精品一二三区在线观看 | 精品无码成人久久久久久 | 业余 自由 性别 成熟偷窥 | 亚洲一区二区三区含羞草 | 久久久久人妻一区精品性色av | 日本韩无专砖码高清 | 亚洲日本人的毛茸茸 | 久久视| 国产亚洲欧美日韩精品一区二区三区 | 欧美特黄一级视频 | √天堂在线 | 天堂在线中文在线 | 在线免费看mv的网站入口 | 337p日本大胆噜噜噜鲁 | 色综合 图片区 小说区 | 蜜臀aⅴ一区二区三区 | 麻豆一区二区三区蜜桃免费 | 女人裸体特黄做爰的视频 | 欧洲av一区二区 | 国产精品jk白丝蜜臀av小说 | 国产一区二区在线播放视频 | 狠狠色老熟妇老熟女 | 五月婷婷综合网 | 一级毛片黄色 | 国产三级韩国三级日本带黄 | 第一av在线 | 亚洲色婷婷久久精品av蜜桃久久 | 伊人网伊人网 | 国产午夜精品av一区二区麻豆 | 五月丁香综合缴情六月小说 | a毛片在线| 欧美69式互添视频在线 | 推油少妇久久99久久99久久 | 九七超碰在线 | 2021天天操| 国产伦精品一区二区三区免费观看 | 久久久久有精品国产麻豆 | 日本熟妇色xxxxx欧美老妇 | 日本黄色片段 | 国产一区二区视频在线播放 | 天天干天天搞天天射 | 国产精品爽爽爽爽爽爽在线观看 | 国产黄色一区 | 欧美日韩成人在线视频 | 日韩精品人妻系列无码专区 | 亚洲欧洲中文日韩久久av乱码 | 91字幕网 | 精品欧美一区二区三区在线观看 | 久久9精品区-无套内射无码 | 伊人久久一区二区三区 | 久久精品国产亚洲a | 超碰在线中文字幕 | 老牛嫩草一区二区三区消防 | 成人免费一区二区 | 这里只有久久精品 | 日本午夜免费福利视频 | 精品久久国产字幕高潮 | 51精品国产人成在线观看 | 亚洲综合成人网 | 日韩一区二区免费播放 | 国产一区二区三区在线看 | 精品久久a | 日本福利片在线观看 | 精品国产三级a∨在线 | 亚洲一区二区无码影院 | 欧美一区二区三区的 | 婷婷丁香狼人久久大香线蕉 | 亚洲免费一级视频 | 国产精品高清网站 | 特级黄一级播放 | 欧美性情网 | 18女下面流水不遮图 | 毛片免 | 日本高清在线一区 | 精品乱码一区二区三区 | 午夜剧场免费在线观看 | 又大又长粗又爽又黄少妇视频 | 中国凸偷窥xxxx自由视频妇科 | 亚洲自拍第三页 | 欧洲精品一卡2卡三卡4卡影视 | 天堂а√在线中文在线新版 | 国产精品久久久18成人 | 成年女人免费视频播放体验区 | 国产伦精品一区二区三区四区免费 | 精品久久久久久久人人人人传媒 | 亚洲图片自拍偷拍 | 精品无码久久久久久久动漫 | www.欧美在线 | 欧美激情一区二区三区在线 | 希岛爱理av免费一区二区 | 超高清欧美videossex4 | 免费看欧美片 | 奇米色777欧美一区二区 | 免费在线成人网 | 白嫩少妇hdxxxⅹ性大陆 | 精品二区在线观看 | 久久羞羞视频 | 久久久久久久99 | 国产啪亚洲国产精品无码 | 亚洲一级片网站 | 午夜刺激视频 | 国产日韩第一页 | 91tv国产成人福利 | 国产肉体xxxx裸体137大胆 | 亚洲黄色一区二区三区 | 激情天堂网 | 日本高清不卡aⅴ免费网站 久久精品国产av一区二区三区 | 小毛片在线观看 | 无码人妻久久久一区二区三区 | 丰满亚洲大尺度无码无码专线 | 男阳茎进女阳道视频大全 | 亚洲免费久久 | 日韩精品一区二区三区色欲av | 亚洲精品久久久乳夜夜欧美 | 中文字幕婷婷 | 寂寞的日本美妇 | 影音先锋资源av | 少妇太爽了太深了太硬了 | 丝袜一区在线观看 | 国产精品一区二区在线看 | 三级网站在线看 | 91国内精品自线在拍白富美 | 久久精品麻豆日日躁夜夜躁 | 激情欧美亚洲 | 午夜免费| 琪琪女色窝窝777777 | 新版天堂资源中文8在线 | 国产精品毛片va一区二区三区 | 日韩在线一区二区三区免费视频 | 人人草人人爱 | 免费成人高清视频 | 欧美35页视频在线观看 | 99久久精品久久久久久ai换脸 | 国产一级大片在线观看 | 94精品激情一区二区三区 | 日韩三级在线播放 | 久热只有精品 | 性做久久久久久久 | 亚洲精品www | 亚洲精品v天堂中文字幕 | 国产女主播视频一区二区 | 丁香六月久久 | 成人黄色免费网站 | 亚洲香蕉成人av网站在线观看 | 国产免费中文字幕 | 成人激情视频网站 | 大桥未久av片 | 久草中文在线观看 | 精品手机在线 | www.97超碰| 亚洲色成人www永久网站 | 国产精品夫妻视频 | 亚洲第一免费视频 | 91久久国产视频 | 免费看国产曰批40分钟 | 久久一二三区 | 日本黄色精品 | 欧美深夜在线 | 五月天丁香久久 | 亚洲精品一区二区三区中文字幕 | 超碰色人阁 | a级特黄一级一大片多人 | 国产成人精品手机在线观看 | 欧美午夜精品一区二区 | 在线无码av一区二区三区 | 国产又粗又硬又大爽黄老大爷视 | 老汉av| 亚洲精品鲁一鲁一区二区三区 | 日韩高清无线码2023 | 欧美区一区 | 超碰人人人人人人人 | 国产露双乳喂奶在线观看 | 91操碰| 久草在线综合 | 久久在线 | 香港三级午夜理伦三级 | 色天天干 | 依人在线视频 | 91久久国产成人精品 | 天天舔天天插 | 91久久精品国产91久久 | 日本裸体xx少妇18在线 | 粗大的内捧猛烈进出 | 国产成人无码一区二区在线观看 | jizzjizz黄大片 | 极品色综合| 性做久久久 | 男女又爽又黄激情免费视频大 | 日韩欧美在线观看 | 国产欧美精品日韩区二区麻豆天美 | www.日日| 在线免费观看黄色av | 成年人的毛片 | 天堂中文а√在线官网 | 美女精品一区二区 | 欧美爱爱网 | 一本久久综合亚洲鲁鲁五月天 | 成人免费视频在线播放 | 11月流出美女撒尿偷拍在线播放 | 国产高清视频一区 | 欧美日韩在线观看成人 | 91精品在线免费 | 天海翼一区二区三区 | 中文字幕大香视频蕉免费 | 无码h黄肉3d动漫在线观看 | 国产精品久久久久久无毒偷食禁果 | 视频毛片| 国产精品久久影院 | 九九免费精品视频 | 国产免费高清 | 日本动漫做毛片一区二区 | 天堂少妇 | 看全色黄大色黄大片大学生 | 无码夜色一区二区三区 | 91性高潮久久久久久久 | 精品麻豆视频 | 亚洲人高潮女人毛茸茸 | xfyy5566黑夜在线手机版 | 91成人精品国产刺激国语对白 | 超碰av男人的天堂 | 爱av免费| 男女交性全过程3d | 另类专区av | 蜜桃精品成人影片 | 九色91丨porny丨丝袜 | 伊人精品成人久久综合软件 | 91美女在线视频 | 白嫩大乳丰满美女白嫩白嫩 | 国产精品丝袜久久久久久高清 | 色avav色avav爱av亚洲 | 高清性色生活片97 | 国产黄色激情视频 | 涩涩鲁亚洲精品一区二区 | 老妇做爰xxx视频一区二区三区 | 无码少妇一区二区 | 人妻激情文学 | 国产精品久久久久久婷婷天堂 | 国产成人综合精品 | 三个男吃我奶头一边一个视频 | 国产精品成人va在线观看 | 嫩草影院污 | 五月av在线 | 黄色毛片网 | 国产激情视频在线 | 欧美伦理一区二区 | 久久午夜私人影院 | 操一操日一日 | 亚洲男人天堂2020 | 欧美国产激情18 | 九一福利视频 | 日本少妇色| 中文幕无线码中文字夫妻 | 久久综合给合久久狠狠狠97色 | 免费一二三区 | 久久久亚洲成人 | 亚洲一区二区三区无码久久 | 国产亚洲人成在线播放 | 日韩亚洲欧美中文在线 | 嫩草影院在线观看视频 | 久久午夜无码鲁丝片 | 二色av| 国产一级二级在线观看 | 亚洲精品国产精品国自产网站按摩 | 不卡的一区二区 | 色永久 | 日本a级一区 | 日本午夜影院 | 欧美激情性做爰免费视频 | 精品国产一级片 | 真人做人试看60分钟免费 | 爱啪啪影视 | 欧美精品免费在线 | 午夜男人天堂 | 天天弄天天干 | av日韩在线免费观看 | 日韩a片无码毛片免费看 | 久久久人成影片免费观看 | 黄色小说在线免费观看 | 亚洲中文字幕高清有码在线 | 日韩和欧美一区二区三区 | 韩国毛片在线 | 国产91丝袜在线播放0 | 久久精品日产第一区二区三区 | 麻花传媒在线观看免费 | а天堂中文在线官网在线 | 成人艳情一二三区 | 天堂一区二区mv在线观看 | 亚洲操片| 男女男精品视频站 | 女上男下激烈啪啪xx00免费 | 国产自偷自偷免费一区 | 国产中文字二暮区 | 一本色道88久久加勒比精品 | 国产午夜精品一区二区 | 欧美激情校园春色 | 奇米四色影视 | 亚洲欧美在线综合 | 大学生女人三级在线播放 | 亚洲视频1区 | 亚洲精品国产suv一区88 | 先锋资源在线视频 | 96精品视频在线观看 | 老司机午夜免费福利 | 午夜小视频在线免费观看 | 国产a在亚洲线播放 | 日韩一级免费 | 亚洲精品一区二区三区四区乱码 | 91人人爽人人爽人人精88v | 毛片在线网| 欧美日韩久久久久 | 精品麻豆剧传媒av国产九九九 | 色噜噜av亚洲色一区二区 | 国产在线精品一区二区三区 | 欧美精品久久久久久久监狱 | 欧美熟妇色ⅹxxx欧美妇 | 精品国产福利一区二区 | 琪琪女色窝窝777777 | 国产在线国偷精品产拍免费观看 | 成人免费在线观看网站 | 成年人的毛片 | 挺进美女教师的蜜桃肥臀视频 | 天堂a免费视频在线观看 | 在线亚洲欧美 | 悠悠色在线| 欧美日本韩国在线 | 免费观看一区二区三区 | 久久这里有精品 |