博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring源码解析bean定义五UtilNamespaceHandler
阅读量:5903 次
发布时间:2019-06-19

本文共 24881 字,大约阅读时间需要 82 分钟。

hot3.png

前言

本文转自“天河聊技术”微信公众号

这次介绍UtilNamespaceHandler

 

正文

找到这个方法

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {//    根节点是否是命名空间 http://www.springframework.org/schema/beans      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)) {                  parseDefaultElement(ele, delegate);               }               else {                  delegate.parseCustomElement(ele);               }            }         }      }      else {         delegate.parseCustomElement(root);      }   }

找到这行代码

else {   delegate.parseCustomElement(ele);}
@Nullablepublic BeanDefinition parseCustomElement(Element ele) {   return parseCustomElement(ele, null);}
@Nullable   public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {      String namespaceUri = getNamespaceURI(ele);      if (namespaceUri == null) {         return null;      }//    获取解析命名空间的handler      NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);      if (handler == null) {         error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);         return null;      }//    handler解析      return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));   }

找到这行代码

//     获取解析命名空间的handler      NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
@Override   @Nullable   public NamespaceHandler resolve(String namespaceUri) {//    获取handlerMappings,从META-INF/spring.handlers这个路径加载      Map
handlerMappings = getHandlerMappings();      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);//          handler必须是这个类型 NamespaceHandler            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");            }//          创建namespaceHandler类            NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);//          初始化namespaceHandler类            namespaceHandler.init();            handlerMappings.put(namespaceUri, namespaceHandler);            return namespaceHandler;         } catch (ClassNotFoundException ex) { throw new FatalBeanException("Could not find NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", ex);         } catch (LinkageError err) { throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", err);         } } }

进入这个方法org.springframework.beans.factory.xml.UtilNamespaceHandler#init

@Overridepublic void init() {   registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());   registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());   registerBeanDefinitionParser("list", new ListBeanDefinitionParser());   registerBeanDefinitionParser("set", new SetBeanDefinitionParser());   registerBeanDefinitionParser("map", new MapBeanDefinitionParser());   registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());}

返回到这个方法org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)这一行代码进行handler解析

//     handler解析      return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

进入这个方法

@Override   @Nullable   public BeanDefinition parse(Element element, ParserContext parserContext) {//    获得bean定义解析器      BeanDefinitionParser parser = findParserForElement(element, parserContext);      return (parser != null ? parser.parse(element, parserContext) : null);   }

进入这个方法

org.springframework.beans.factory.xml.NamespaceHandlerSupport#parse

@Override   @Nullable   public BeanDefinition parse(Element element, ParserContext parserContext) {//    获得bean定义解析器      BeanDefinitionParser parser = findParserForElement(element, parserContext);      return (parser != null ? parser.parse(element, parserContext) : null);   }

进入这个方法

org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parse

@Override   @Nullable   public final BeanDefinition parse(Element element, ParserContext parserContext) {//    解析bean定义的模板方法      AbstractBeanDefinition definition = parseInternal(element, parserContext);      if (definition != null && !parserContext.isNested()) {         try {//          解析id属性            String id = resolveId(element, definition, parserContext);            if (!StringUtils.hasText(id)) {               parserContext.getReaderContext().error(                     "Id is required for element '" + parserContext.getDelegate().getLocalName(element)                           + "' when used as a top-level tag", element);            }            String[] aliases = null;            if (shouldParseNameAsAliases()) {//             解析name属性               String name = element.getAttribute(NAME_ATTRIBUTE);               if (StringUtils.hasLength(name)) {                  aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));               }            }            BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);            registerBeanDefinition(holder, parserContext.getRegistry());            if (shouldFireEvents()) {               BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);               postProcessComponentDefinition(componentDefinition);               parserContext.registerComponent(componentDefinition);            }         }         catch (BeanDefinitionStoreException ex) {            String msg = ex.getMessage();            parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);            return null;         }      }      return definition;   }

找到这一行代码

//     解析bean定义的模板方法      AbstractBeanDefinition definition = parseInternal(element, parserContext);
@Override   protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();      String parentName = getParentName(element);      if (parentName != null) {      }         builder.getRawBeanDefinition().setParentName(parentName);      Class
beanClass = getBeanClass(element);      if (beanClass != null) { builder.getRawBeanDefinition().setBeanClass(beanClass);      } else { String beanClassName = getBeanClassName(element);         if (beanClassName != null) { builder.getRawBeanDefinition().setBeanClassName(beanClassName);         } } builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));//    获取内部bean的bean定义      BeanDefinition containingBd = parserContext.getContainingBeanDefinition();//    如果匿名bean有scope,外部bean的scope以这个为主      if (containingBd != null) { // Inner bean definition must receive same scope as containing bean.         builder.setScope(containingBd.getScope());      }//    是否延迟加载      if (parserContext.isDefaultLazyInit()) { // Default-lazy-init applies to custom bean definitions as well.         builder.setLazyInit(true);      } doParse(element, parserContext, builder);      return builder.getBeanDefinition();   }

进入这个方法

//  调用此方法时会具体调用子类重写的方法,如org.springframework.beans.factory.xml.UtilNamespaceHandler.ListBeanDefinitionParser.doParse()   protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {      doParse(element, builder);   }

找到上面的实现方法

org.springframework.beans.factory.xml.UtilNamespaceHandler.ListBeanDefinitionParser#doParse list节点解析

@Override      protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {         List parsedList = parserContext.getDelegate().parseListElement(element, builder.getRawBeanDefinition());         builder.addPropertyValue("sourceList", parsedList);         String listClass = element.getAttribute("list-class");         if (StringUtils.hasText(listClass)) {            builder.addPropertyValue("targetListClass", listClass);         }//       获取scope属性值         String scope = element.getAttribute(SCOPE_ATTRIBUTE);         if (StringUtils.hasLength(scope)) {            builder.setScope(scope);         }      }   }
public List parseListElement(Element collectionEle, @Nullable BeanDefinition bd) {//    value-type标签      String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);      NodeList nl = collectionEle.getChildNodes();      ManagedList target = new ManagedList<>(nl.getLength());      target.setSource(extractSource(collectionEle));      target.setElementTypeName(defaultElementType);      target.setMergeEnabled(parseMergeAttribute(collectionEle));      parseCollectionElements(nl, target, bd, defaultElementType);      return target;   }
public boolean parseMergeAttribute(Element collectionElement) {//    merge属性      String value = collectionElement.getAttribute(MERGE_ATTRIBUTE);//    如果是default则以beans的配置为主      if (DEFAULT_VALUE.equals(value)) {         value = this.defaults.getMerge();      }      return TRUE_VALUE.equals(value);   }
protected void parseCollectionElements(         NodeList elementNodes, Collection target, @Nullable BeanDefinition bd, String defaultElementType) {      for (int i = 0; i < elementNodes.getLength(); i++) {         Node node = elementNodes.item(i);//       description 解析属性         if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {//          递归调用            target.add(parsePropertySubElement((Element) node, bd, defaultElementType));         }      }   }

进入这个方法

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parsePropertySubElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition, java.lang.String)

@Nullable   public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {//    如果不是beans节点      if (!isDefaultNamespace(ele)) {         return parseNestedCustomElement(ele, bd);      }//    解析bean节点      else if (nodeNameEquals(ele, BEAN_ELEMENT)) {//       解析
节点的bean定义         BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);         if (nestedBd != null) {//          解析内部的required的元素            nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);         } return nestedBd;      }//    ref属性指定的节点      else if (nodeNameEquals(ele, REF_ELEMENT)) { // A generic reference to any name of any bean.//       获取bean属性值         String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);         boolean toParent = false;         if (!StringUtils.hasLength(refName)) { // A reference to the id of another bean in a parent context.parent属性值获取另一个bean的引用            refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);            toParent = true;            if (!StringUtils.hasLength(refName)) { error("'bean' or 'parent' is required for
element", ele);               return null;            } } if (!StringUtils.hasText(refName)) { error("
element contains empty target attribute", ele);            return null;         } RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);         ref.setSource(extractSource(ele));         return ref;      }//    解析idref属性,注入的是bean的id而不是bean的实例      else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele);      }//    如果是value节点      else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType);      }//    如果是null节点      else if (nodeNameEquals(ele, NULL_ELEMENT)) { // It's a distinguished null value. Let's wrap it in a TypedStringValue         // object in order to preserve the source location.         TypedStringValue nullHolder = new TypedStringValue(null);         nullHolder.setSource(extractSource(ele));         return nullHolder;      }//    如果是array节点      else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd);      }//    如果是list节点      else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd);      }//    如果是set节点      else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd);      }//    如果是map节点      else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd);      }//    如果是props节点      else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele);      } else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);         return null;      } }

找到这行代码

//     解析idref属性,注入的是bean的id而不是bean的实例      else if (nodeNameEquals(ele, IDREF_ELEMENT)) {         return parseIdRefElement(ele);      }
@Nullablepublic Object parseIdRefElement(Element ele) {   // A generic reference to any name of any bean.获取bean属性值   String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);   if (!StringUtils.hasLength(refName)) {      error("'bean' is required for 
element", ele);      return null;   } if (!StringUtils.hasText(refName)) { error("
element contains empty target attribute", ele);      return null;   } RuntimeBeanNameReference ref = new RuntimeBeanNameReference(refName);   ref.setSource(extractSource(ele));   return ref;}

找到这行代码

//     如果是value节点      else if (nodeNameEquals(ele, VALUE_ELEMENT)) {         return parseValueElement(ele, defaultValueType);      }
public Object parseValueElement(Element ele, @Nullable String defaultTypeName) {      // It's a literal value.      String value = DomUtils.getTextValue(ele);//    获取type属性值      String specifiedTypeName = ele.getAttribute(TYPE_ATTRIBUTE);      String typeName = specifiedTypeName;      if (!StringUtils.hasText(typeName)) {         typeName = defaultTypeName;      }      try {         TypedStringValue typedValue = buildTypedStringValue(value, typeName);         typedValue.setSource(extractSource(ele));         typedValue.setSpecifiedTypeName(specifiedTypeName);         return typedValue;      }      catch (ClassNotFoundException ex) {         error("Type class [" + typeName + "] not found for 
element", ele, ex);         return value;      } }

找到这行代码

//     如果是array节点      else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {         return parseArrayElement(ele, bd);      }
public Object parseArrayElement(Element arrayEle, @Nullable BeanDefinition bd) {//    获取value-type属性值      String elementType = arrayEle.getAttribute(VALUE_TYPE_ATTRIBUTE);      NodeList nl = arrayEle.getChildNodes();      ManagedArray target = new ManagedArray(elementType, nl.getLength());      target.setSource(extractSource(arrayEle));      target.setElementTypeName(elementType);      target.setMergeEnabled(parseMergeAttribute(arrayEle));      parseCollectionElements(nl, target, bd, elementType);      return target;   }
public boolean parseMergeAttribute(Element collectionElement) {//    merge属性      String value = collectionElement.getAttribute(MERGE_ATTRIBUTE);//    如果是default则以beans的配置为主      if (DEFAULT_VALUE.equals(value)) {         value = this.defaults.getMerge();      }      return TRUE_VALUE.equals(value);   }
protected void parseCollectionElements(         NodeList elementNodes, Collection target, @Nullable BeanDefinition bd, String defaultElementType) {      for (int i = 0; i < elementNodes.getLength(); i++) {         Node node = elementNodes.item(i);//       description 解析属性         if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {//          递归调用            target.add(parsePropertySubElement((Element) node, bd, defaultElementType));         }      }   }

找到这行代码

//     如果是list节点      else if (nodeNameEquals(ele, LIST_ELEMENT)) {         return parseListElement(ele, bd);      }
public List parseListElement(Element collectionEle, @Nullable BeanDefinition bd) {//    value-type标签      String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);      NodeList nl = collectionEle.getChildNodes();      ManagedList target = new ManagedList<>(nl.getLength());      target.setSource(extractSource(collectionEle));      target.setElementTypeName(defaultElementType);      target.setMergeEnabled(parseMergeAttribute(collectionEle));      parseCollectionElements(nl, target, bd, defaultElementType);      return target;   }

找到这行代码

//     如果是set节点      else if (nodeNameEquals(ele, SET_ELEMENT)) {         return parseSetElement(ele, bd);      }
public Set parseSetElement(Element collectionEle, @Nullable BeanDefinition bd) {//    获取value-type属性值      String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);      NodeList nl = collectionEle.getChildNodes();      ManagedSet target = new ManagedSet<>(nl.getLength());      target.setSource(extractSource(collectionEle));      target.setElementTypeName(defaultElementType);      target.setMergeEnabled(parseMergeAttribute(collectionEle));      parseCollectionElements(nl, target, bd, defaultElementType);      return target;   }

找到这行代码

//     如果是map节点      else if (nodeNameEquals(ele, MAP_ELEMENT)) {         return parseMapElement(ele, bd);      }
public Map
parseMapElement(Element mapEle, @Nullable BeanDefinition bd) {//    获取key-type属性值      String defaultKeyType = mapEle.getAttribute(KEY_TYPE_ATTRIBUTE);//    获取value-type属性值      String defaultValueType = mapEle.getAttribute(VALUE_TYPE_ATTRIBUTE);//    获取entry节点的子节点      List
entryEles = DomUtils.getChildElementsByTagName(mapEle, ENTRY_ELEMENT);      ManagedMap
map = new ManagedMap<>(entryEles.size());      map.setSource(extractSource(mapEle));      map.setKeyTypeName(defaultKeyType);      map.setValueTypeName(defaultValueType);      map.setMergeEnabled(parseMergeAttribute(mapEle));      for (Element entryEle : entryEles) { // Should only have one value child element: ref, value, list, etc.         // Optionally, there might be a key child element.         NodeList entrySubNodes = entryEle.getChildNodes();         Element keyEle = null;         Element valueEle = null;         for (int j = 0; j < entrySubNodes.getLength(); j++) { Node node = entrySubNodes.item(j);            if (node instanceof Element) { Element candidateEle = (Element) node;//             如果是key节点               if (nodeNameEquals(candidateEle, KEY_ELEMENT)) { if (keyEle != null) { error("
element is only allowed to contain one
sub-element", entryEle);                  } else { keyEle = candidateEle;                  } } else { // Child element is what we're looking for.如果是description节点                  if (nodeNameEquals(candidateEle, DESCRIPTION_ELEMENT)) { // the element is a
-> ignore it                  } else if (valueEle != null) { error("
element must not contain more than one value sub-element", entryEle);                  } else { valueEle = candidateEle;                  } } } } // Extract key from attribute or sub-element.         Object key = null;//       是否有key属性         boolean hasKeyAttribute = entryEle.hasAttribute(KEY_ATTRIBUTE);//       是否有key-ref属性         boolean hasKeyRefAttribute = entryEle.hasAttribute(KEY_REF_ATTRIBUTE);         if ((hasKeyAttribute && hasKeyRefAttribute) || (hasKeyAttribute || hasKeyRefAttribute) && keyEle != null) { error("
element is only allowed to contain either " + "a 'key' attribute OR a 'key-ref' attribute OR a
sub-element", entryEle);         } if (hasKeyAttribute) { key = buildTypedStringValueForMap(entryEle.getAttribute(KEY_ATTRIBUTE), defaultKeyType, entryEle);         } else if (hasKeyRefAttribute) { String refName = entryEle.getAttribute(KEY_REF_ATTRIBUTE);            if (!StringUtils.hasText(refName)) { error("
element contains empty 'key-ref' attribute", entryEle);            } RuntimeBeanReference ref = new RuntimeBeanReference(refName);            ref.setSource(extractSource(entryEle));            key = ref;         } else if (keyEle != null) { key = parseKeyElement(keyEle, bd, defaultKeyType);         } else { error("
element must specify a key", entryEle);         } // Extract value from attribute or sub-element.         Object value = null;//       是否有value属性         boolean hasValueAttribute = entryEle.hasAttribute(VALUE_ATTRIBUTE);//       是否有value-ref属性         boolean hasValueRefAttribute = entryEle.hasAttribute(VALUE_REF_ATTRIBUTE);//       是否有value-type属性         boolean hasValueTypeAttribute = entryEle.hasAttribute(VALUE_TYPE_ATTRIBUTE);         if ((hasValueAttribute && hasValueRefAttribute) || (hasValueAttribute || hasValueRefAttribute) && valueEle != null) { error("
element is only allowed to contain either " + "'value' attribute OR 'value-ref' attribute OR
sub-element", entryEle);         } if ((hasValueTypeAttribute && hasValueRefAttribute) || (hasValueTypeAttribute && !hasValueAttribute) || (hasValueTypeAttribute && valueEle != null)) { error("
element is only allowed to contain a 'value-type' " + "attribute when it has a 'value' attribute", entryEle);         } if (hasValueAttribute) { String valueType = entryEle.getAttribute(VALUE_TYPE_ATTRIBUTE);            if (!StringUtils.hasText(valueType)) { valueType = defaultValueType;            } value = buildTypedStringValueForMap(entryEle.getAttribute(VALUE_ATTRIBUTE), valueType, entryEle);         } else if (hasValueRefAttribute) { String refName = entryEle.getAttribute(VALUE_REF_ATTRIBUTE);            if (!StringUtils.hasText(refName)) { error("
element contains empty 'value-ref' attribute", entryEle);            } RuntimeBeanReference ref = new RuntimeBeanReference(refName);            ref.setSource(extractSource(entryEle));            value = ref;         } else if (valueEle != null) {//          递归调用            value = parsePropertySubElement(valueEle, bd, defaultValueType);         } else { error("
element must specify a value", entryEle);         } // Add final key and value to the Map.         map.put(key, value);      } return map;   }

找到这行代码

//     如果是props节点      else if (nodeNameEquals(ele, PROPS_ELEMENT)) {         return parsePropsElement(ele);      }
public Properties parsePropsElement(Element propsEle) {      ManagedProperties props = new ManagedProperties();      props.setSource(extractSource(propsEle));      props.setMergeEnabled(parseMergeAttribute(propsEle));//    获取prop的子节点      List
propEles = DomUtils.getChildElementsByTagName(propsEle, PROP_ELEMENT);      for (Element propEle : propEles) {//       获取key属性值         String key = propEle.getAttribute(KEY_ATTRIBUTE);         // Trim the text value to avoid unwanted whitespace         // caused by typical XML formatting.         String value = DomUtils.getTextValue(propEle).trim();         TypedStringValue keyHolder = new TypedStringValue(key);         keyHolder.setSource(extractSource(propEle));         TypedStringValue valueHolder = new TypedStringValue(value);         valueHolder.setSource(extractSource(propEle));         props.put(keyHolder, valueHolder);      } return props;   }

返回到这个方法

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)

@Nullable   public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {      String namespaceUri = getNamespaceURI(ele);      if (namespaceUri == null) {         return null;      }//    获取解析命名空间的handler      NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);      if (handler == null) {         error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);         return null;      }//    handler解析      return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));   }

以上是util的namespace的源码解析。

 

总结一下

util注入有几种类型

1、list

2、set

3、map

4、props

5、array

 

最后

本次介绍到这里,以上内容仅供参考。

转载于:https://my.oschina.net/u/3775437/blog/1810428

你可能感兴趣的文章
[LeetCode] Meeting Rooms II
查看>>
从Swift学习iOS开发的路线指引
查看>>
Scribes:小型文本编辑器,支持远程编辑
查看>>
ssh 安装笔记
查看>>
3-继承
查看>>
海归千千万 为何再无钱学森
查看>>
vue2.0 仿手机新闻站(六)详情页制作
查看>>
JSP----九大内置对象
查看>>
Java中HashMap详解
查看>>
delphi基本语法
查看>>
260. Single Number III
查看>>
Hadoop生态圈-Kafka的完全分布式部署
查看>>
[MODx] Build a CMP (Custom manager page) using MIGX in MODX 2.3 -- 1
查看>>
jQuery自动完成点击html元素
查看>>
[算法]基于分区最近点算法的二维平面
查看>>
webpack多页应用架构系列(七):开发环境、生产环境傻傻分不清楚?
查看>>
笨办法学C 练习1:启用编译器
查看>>
树的总结--树的性质(树的深度) leetcode
查看>>
nagios短信报警(飞信fetion20080522004-linrh4)
查看>>
【Android游戏开发之六】在SurfaceView中添加组件!!!!并且相互交互数据!!!!...
查看>>