上一篇随笔说到Spring对于默认标签和自定义标签的解析方法是不同的,这里详细看一下Spring对于默认标签的解析。

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if(delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if(delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if(delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
} else if(delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
} }

在这里对根节点的子节点的四种不同类型分别做了不同的处理。这四种解析中,对bean的解析最为复杂。所以我们进入函数processBeanDefinition。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if(bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name \'" + bdHolder.getBeanName() + "\'", ele, var5);
} this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
} }

这段代码中:

1、首先委托了BeanDefinitionParseDelegate对节点做了解析,并返回了一个BeanDefinitionHolder的实例,在这个实例中已经包含了配置文件中配置的各种属性了

2、如果在当前子节点中存在自定义属性,则还需要对自定义标签进行解析

3、解析完成后,需要对解析后的bdHolder进行注册,同样注册操作委托给了BeanDefinitionReaderUtils

4、最后发出响应事件,通知相关的监听器

我们先看解析节点的部分

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute("id");
String nameAttr = ele.getAttribute("name");
ArrayList aliases = new ArrayList();
if(StringUtils.hasLength(nameAttr)) {
String[] beanName = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
aliases.addAll(Arrays.asList(beanName));
} String beanName1 = id;
if(!StringUtils.hasText(id) && !aliases.isEmpty()) {
beanName1 = (String)aliases.remove(0);
if(this.logger.isDebugEnabled()) {
this.logger.debug("No XML \'id\' specified - using \'" + beanName1 + "\' as bean name and " + aliases + " as aliases");
}
} if(containingBean == null) {
this.checkNameUniqueness(beanName1, aliases, ele);
} AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName1, containingBean);
if(beanDefinition != null) {
if(!StringUtils.hasText(beanName1)) {
try {
if(containingBean != null) {
beanName1 = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
beanName1 = this.readerContext.generateBeanName(beanDefinition);
String aliasesArray = beanDefinition.getBeanClassName();
if(aliasesArray != null && beanName1.startsWith(aliasesArray) && beanName1.length() > aliasesArray.length() && !this.readerContext.getRegistry().isBeanNameInUse(aliasesArray)) {
aliases.add(aliasesArray);
}
} if(this.logger.isDebugEnabled()) {
this.logger.debug("Neither XML \'id\' nor \'name\' specified - using generated bean name [" + beanName1 + "]");
}
} catch (Exception var9) {
this.error(var9.getMessage(), ele);
return null;
}
} String[] aliasesArray1 = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName1, aliasesArray1);
} else {
return null;
}
}

这段代码中首先提取了当前节点的id和name,beanName默认为id,如果id为空,则spring会去别名中的第一个作为beanName。

然后spring会检查当前beanName是否唯一,在BeanDefinitionParseDelegate中维护了一套已用过的beanName以及alias的集合,如果bean的beanName和alias和已存在的名字重复,则会抛错。

进一步解析节点的所有属性,并统一封装到GenericBeanDefinition中。

如果发现当前bean的beanName为空,则使用默认规则为其自动生成beanName。

最后将所有信息封装到BeanDefinitionHolder中返回。

进入到parseBeanDefinitionElement方法中

public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if(ele.hasAttribute("class")) {
className = ele.getAttribute("class").trim();
} try {
String ex = null;
if(ele.hasAttribute("parent")) {
ex = ele.getAttribute("parent");
} AbstractBeanDefinition bd = this.createBeanDefinition(className, ex);
this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
this.parseMetaElements(ele, bd);
this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
this.parseConstructorArgElements(ele, bd);
this.parsePropertyElements(ele, bd);
this.parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(this.extractSource(ele));
AbstractBeanDefinition var7 = bd;
return var7;
} catch (ClassNotFoundException var13) {
this.error("Bean class [" + className + "] not found", ele, var13);
} catch (NoClassDefFoundError var14) {
this.error("Class that bean class [" + className + "] depends on not found", ele, var14);
} catch (Throwable var15) {
this.error("Unexpected failure during bean definition parsing", ele, var15);
} finally {
this.parseState.pop();
} return null;
}

在this.createBeanDefinition方法中,Spring会生产一个GenericBeanDefinition实例,在其属性里设置了当前节点的父类parent,以及className类名的信息。

在this.parseBeanDefinitionAttributes方法中对于一些属性进行了解析

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) {
if(ele.hasAttribute("singleton")) {
this.error("Old 1.x \'singleton\' attribute in use - upgrade to \'scope\' declaration", ele);
} else if(ele.hasAttribute("scope")) {
bd.setScope(ele.getAttribute("scope"));
} else if(containingBean != null) {
bd.setScope(containingBean.getScope());
} if(ele.hasAttribute("abstract")) {
bd.setAbstract("true".equals(ele.getAttribute("abstract")));
} String lazyInit = ele.getAttribute("lazy-init");
if("default".equals(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
} bd.setLazyInit("true".equals(lazyInit));
String autowire = ele.getAttribute("autowire");
bd.setAutowireMode(this.getAutowireMode(autowire));
String dependencyCheck = ele.getAttribute("dependency-check");
bd.setDependencyCheck(this.getDependencyCheck(dependencyCheck));
String autowireCandidate;
if(ele.hasAttribute("depends-on")) {
autowireCandidate = ele.getAttribute("depends-on");
bd.setDependsOn(StringUtils.tokenizeToStringArray(autowireCandidate, ",; "));
} autowireCandidate = ele.getAttribute("autowire-candidate");
String destroyMethodName;
if(!"".equals(autowireCandidate) && !"default".equals(autowireCandidate)) {
bd.setAutowireCandidate("true".equals(autowireCandidate));
} else {
destroyMethodName = this.defaults.getAutowireCandidates();
if(destroyMethodName != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(destroyMethodName);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
} if(ele.hasAttribute("primary")) {
bd.setPrimary("true".equals(ele.getAttribute("primary")));
} if(ele.hasAttribute("init-method")) {
destroyMethodName = ele.getAttribute("init-method");
if(!"".equals(destroyMethodName)) {
bd.setInitMethodName(destroyMethodName);
}
} else if(this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
} if(ele.hasAttribute("destroy-method")) {
destroyMethodName = ele.getAttribute("destroy-method");
if(!"".equals(destroyMethodName)) {
bd.setDestroyMethodName(destroyMethodName);
}
} else if(this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
} if(ele.hasAttribute("factory-method")) {
bd.setFactoryMethodName(ele.getAttribute("factory-method"));
} if(ele.hasAttribute("factory-bean")) {
bd.setFactoryBeanName(ele.getAttribute("factory-bean"));
} return bd;
}

在这里我们可以看到其中不少属性是我们经常使用的,当然也有一些属性不太熟悉。这里大致查了一下资料,简单介绍这些属性的意义:

scope:

对象在spring容器(IOC容器)中的生命周期。共有五中取值。singleton和protoType大家比较熟悉。另外三种request,session和global session只适用于web应用。

abstract:

当前bean是否为抽象类,默认为false

lazy-init:

延迟初始化,在ApplicationContext中默认配置时,spring会在应用启动时就对容器中的bean进行实例化,如果设置了延迟加载,则spring会在第一次使用该bean时初始化该bean。

autowire:

设置bean自动装配模式。可选5种模式   no:不使用自动装配。Bean的引用必须通过ref元素定义。byName:通过名字进行装配  byType:根据类型进行装配 constructor:和byType类似,不过是应用于通过构造函数注入的方式。autoDetect:通过对bean检查类的内部自动选择constructor还是byType。

dependency-check:

依赖检查的模式。

depends-on:

实例化时,依赖的bean,spring会在实例化当前bean之前先实例化依赖的bean。一般不需特别设置,spring会提供一套默认对依赖的检查和实例化的逻辑。

autowire-candidate:

当自动注入的类的实现类有多个时,有两种方案,一种是设置某一个实现类的autowire-candidate属性为false,则该类不参与自动注入。或者设置依赖注入的对象的autowire-candidate为指定实现类。

primary:

primary属性为true时,自动装配时当出现多个Bean的候选者时,属性primary=true的Bean被作为首选者。

init-method:

定义spring 容器在初始化bean之前的所做的操作。

destroy-method:

定义spring 容器在容器销毁之前的所做的操作。

factory-method 和 factory-bean:

spring IoC注入方式除了我们常见的set注入和构造器注入,还可以通过工厂方式注入。工厂分静态工厂和普通工厂。

如果想用某一个静态工厂来实例化bean,可以使用如下配置:

<bean name="production" class="com.wuzhe.factory.StaticFactory" factory-method="getProduction"/>

如果想通过普通工厂来实例化bean,可以使用如下配置:

<bean name="factory" class="com.wuzhe.factory.Factory"/>
<bean name="production" factory-bean="factory" factory-method="getProduction"/>

接下来是解析子节点的meta属性:  this.parseMetaElements(ele, bd)

meta属性是在定义bean时额外添加的说明。例如:

    <bean id="myBean" class="com.wuzhe.bean.MyBean">
<meta key="author" value="wuzhe"></meta>
</bean>

这里的meta属性并不会体现在myBean中,当需要meta信息的时候可以通过BeanDefinition.getAttribute(key)方法获取

接下来是解析节点的lookup-method:

this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

先来看一下这个属性的用法,我们把lookup-method称之为获取器注入。这是一种特殊的方法注入,它是把一个方法声明为返回某个类型的bean,这样可用在设计有些可插拔的功能上,解除程序依赖。

举例说明,假设GetFood类中有一个Food getFood()方法。然后Bread和Cake分别继承Food类。当我们希望getFood()返回Bread类型的bean,我们可以这样配置:

    <bean id="getFoodTest" class="com.wuzhe.lookup.GetFood">
<lookup-method name="getFood" bean="bread"/>
</bean>
<bean id="bread" class="com.wuzhe.food.Bread"/>
<bean id="cake" class="com.wuzhe.food.Cake"/>

如果我们想让getFood()方法返回cake时,只要修改bean属性就可以了

进入到解析lookup-method方法里面

    public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if(this.isCandidateElement(node) && this.nodeNameEquals(node, "lookup-method")) {
Element ele = (Element)node;
String methodName = ele.getAttribute("name");
String beanRef = ele.getAttribute("bean");
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(this.extractSource(ele));
overrides.addOverride(override);
}
} }

上面这段代码会将lookup-method要mock返回的方法和bean封装在LookupOverride类中,并添加到bd的methodOverrides属性中。

接下来是解析replace-method属性

this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

replace-method更加强大,不但可以替换bean,还可以替换原有方法的逻辑。它可以将bean中的方法替换成另一个继承MethodReplace的类reimplement的实现

public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if(this.isCandidateElement(node) && this.nodeNameEquals(node, "replaced-method")) {
Element replacedMethodEle = (Element)node;
String name = replacedMethodEle.getAttribute("name");
String callback = replacedMethodEle.getAttribute("replacer");
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
List argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, "arg-type");
Iterator var11 = argTypeEles.iterator(); while(var11.hasNext()) {
Element argTypeEle = (Element)var11.next();
String match = argTypeEle.getAttribute("match");
match = StringUtils.hasText(match)?match:DomUtils.getTextValue(argTypeEle);
if(StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
} replaceOverride.setSource(this.extractSource(replacedMethodEle));
overrides.addOverride(replaceOverride);
}
} }

这里解析replace-method的思路和lookup-method类似,都是讲元素的属性封装一下,并添加到bd的methodOverrides属性中。不同的是replace-method属性封装在RepalceOverride中。

接下来看对构造函数的解析

 public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
String indexAttr = ele.getAttribute("index");
String typeAttr = ele.getAttribute("type");
String nameAttr = ele.getAttribute("name");
if(StringUtils.hasLength(indexAttr)) {
try {
int value = Integer.parseInt(indexAttr);
if(value < 0) {
this.error("\'index\' cannot be lower than 0", ele);
} else {
try {
this.parseState.push(new ConstructorArgumentEntry(value));
Object valueHolder = this.parsePropertyValue(ele, bd, (String)null);
ValueHolder valueHolder1 = new ValueHolder(valueHolder);
if(StringUtils.hasLength(typeAttr)) {
valueHolder1.setType(typeAttr);
} if(StringUtils.hasLength(nameAttr)) {
valueHolder1.setName(nameAttr);
} valueHolder1.setSource(this.extractSource(ele));
if(bd.getConstructorArgumentValues().hasIndexedArgumentValue(value)) {
this.error("Ambiguous constructor-arg entries for index " + value, ele);
} else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(value, valueHolder1);
}
} finally {
this.parseState.pop();
}
}
} catch (NumberFormatException var19) {
this.error("Attribute \'index\' of tag \'constructor-arg\' must be an integer", ele);
}
} else {
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value1 = this.parsePropertyValue(ele, bd, (String)null);
ValueHolder valueHolder2 = new ValueHolder(value1);
if(StringUtils.hasLength(typeAttr)) {
valueHolder2.setType(typeAttr);
} if(StringUtils.hasLength(nameAttr)) {
valueHolder2.setName(nameAttr);
} valueHolder2.setSource(this.extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder2);
} finally {
this.parseState.pop();
}
} }

spring对constructor-arg是否存在index属性的处理流程稍有不同,不同在于如果存在index属性,spring会将type、name和index属性封装在ValueHolder并添加至bd的constructorArgumentValues的indexedArgumentValues属性中,如果不存在则添加至genericArgumentValue属性中。

解析property属性和constructor-arg的过程类似

public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute("name");
if(!StringUtils.hasLength(propertyName)) {
this.error("Tag \'property\' must have a \'name\' attribute", ele);
} else {
this.parseState.push(new PropertyEntry(propertyName)); try {
if(bd.getPropertyValues().contains(propertyName)) {
this.error("Multiple \'property\' definitions for property \'" + propertyName + "\'", ele);
return;
} Object val = this.parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
this.parseMetaElements(ele, pv);
pv.setSource(this.extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
} finally {
this.parseState.pop();
} }
}

解析qualifier

Spring在进行自动注入时,容器中候选的Bean有且只有一个,当有多个候选Bean的时候可以通过Qualifier指定注入Bean的名称。

具体解析过程和之前大同小异

public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
String typeName = ele.getAttribute("type");
if(!StringUtils.hasLength(typeName)) {
this.error("Tag \'qualifier\' must have a \'type\' attribute", ele);
} else {
this.parseState.push(new QualifierEntry(typeName)); try {
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
qualifier.setSource(this.extractSource(ele));
String value = ele.getAttribute("value");
if(StringUtils.hasLength(value)) {
qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
} NodeList nl = ele.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if(this.isCandidateElement(node) && this.nodeNameEquals(node, "attribute")) {
Element attributeEle = (Element)node;
String attributeName = attributeEle.getAttribute("key");
String attributeValue = attributeEle.getAttribute("value");
if(!StringUtils.hasLength(attributeName) || !StringUtils.hasLength(attributeValue)) {
this.error("Qualifier \'attribute\' tag must have a \'name\' and \'value\'", attributeEle);
return;
} BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
attribute.setSource(this.extractSource(attributeEle));
qualifier.addMetadataAttribute(attribute);
}
} bd.addQualifier(qualifier);
} finally {
this.parseState.pop();
}
}
}

至此,对bean节点的属性解析就完成了,相当于spring将配置文件转换成了内存中的GenericBeanDefinition对象。xml中的属性都可以在GenericBeanDefinition找到储存的位置。

解析完bean后,就需要对beanDefinition进行注册,注册我理解就是把解析的beanDefinition存放在内存中,统一管理。在XmlFactory中,所有的beanDefinition缓存都存放在了它的父类DefaultListableBeanFactory的

beanDefinitionMap中,key值为beanName。

我们看一下注册的源码

 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if(beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition)beanDefinition).validate();
} catch (BeanDefinitionValidationException var9) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9);
}
} BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
if(oldBeanDefinition != null) {
if(!this.isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean \'" + beanName + "\': There is already [" + oldBeanDefinition + "] bound.");
} if(oldBeanDefinition.getRole() < beanDefinition.getRole()) {
if(this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean \'" + beanName + "\' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
} else if(!beanDefinition.equals(oldBeanDefinition)) {
if(this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean \'" + beanName + "\' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
} else if(this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean \'" + beanName + "\' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
} this.beanDefinitionMap.put(beanName, beanDefinition);
} else {
if(this.hasBeanCreationStarted()) {
Map var4 = this.beanDefinitionMap;
synchronized(this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
ArrayList updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if(this.manualSingletonNames.contains(beanName)) {
LinkedHashSet updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
} else {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
} this.frozenBeanDefinitionNames = null;
} if(oldBeanDefinition != null || this.containsSingleton(beanName)) {
this.resetBeanDefinition(beanName);
} }

在这段代码中

1、首先对传入的beanDefinition做了合法性的校验

2、然后根据beanName查找当前beanDefinitionMap缓存中是否已有相应的beanDefinion。如果有且当前设置了不允许bean覆盖,则会抛错,若允许覆盖,则用当前beanDefinition覆盖旧的beanDefinition,并清除缓存中旧的bean信息。

3、如果beanDefinitionMap缓存中不存在旧的beanDifinition, 则在beanDefinitionMap中存放当前beanDefinition。

这里涉及到的几个缓存:beanDefinitionMap很好理解,manualSingletonNames的含义并不清楚,这里留个疑问,后续若学习到相关知识,再来补充。

注册好了beanDefinition,接下来就是注册alias。注册的alias和beanName的对应关系存放在了aliasMap中

    public void registerAlias(String name, String alias) {
Assert.hasText(name, "\'name\' must not be empty");
Assert.hasText(alias, "\'alias\' must not be empty");
if(alias.equals(name)) {
this.aliasMap.remove(alias);
} else {
String registeredName = (String)this.aliasMap.get(alias);
if(registeredName != null) {
if(registeredName.equals(name)) {
return;
} if(!this.allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias \'" + alias + "\' for name \'" + name + "\': It is already registered for name \'" + registeredName + "\'.");
}
} this.checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
} }

注册别名的过程还是很容易理解的:

1、若beanName和alias相同,则无需处理,并在缓存中删除alias

2、若存在alias注册了其他beanName,则根据是否允许覆盖的设置,进行相应处理

3、循环检查alias

4、注册alias

至此对bean默认标签的解析和注册就完成了

Spring源码学习(2)——默认标签的解析的更多相关文章

  1. spring源码学习之默认标签的解析(一)

    继续spring源码的学习之路,现在越来越觉得这个真的很枯燥的,而且我觉得要是自己来看源码,真的看不下去,不是没有耐心,而是真的没有头绪,我觉得结合着书来看,还是很有必要的,最起码大致的流程是能够捋清 ...

  2. spring源码学习之默认标签的解析(二)

    这个是接着上一篇来写,主要是这章内容比较多,还是分开来写吧! 一.AbstractBeanDefinition属性介绍 XML中的所有的属性都可以在GenericBeanDefinition中找到对应 ...

  3. Spring 源码学习(1) —— 自定义标签

    Spring 工作流程是先加载解析xml配置文件:配置文件中存在默认的标签,也可以自定义标签.解析默认标签调用: private void parseDefaultElement(Element el ...

  4. Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

  5. Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

  6. spring源码学习之路---IOC初探(二)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...

  7. Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作

    写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...

  8. Spring 源码学习——Aop

    Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ...

  9. Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

    Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...

  10. spring源码学习之路---深入AOP(终)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章和各位一起看了一下sp ...

随机推荐

  1. python 第四阶段 学习记录之----异步

    异步: 知识情况: 1.多线程, 多线程使用场景 1.IO操作不占CPU,读写数据(网络中.系统中) 2.计算占CPU, 3.上下文切换不占CPU.它消耗资源 python多线程 不适合CPU密集型的 ...

  2. FAT32文件系统学习(上)

    2011-06-02 22:30:48 目的:需要编写SD读图片的底层驱动程序.所以要了解一个SD卡常用文件系统基本概念.累计学习用时2.5小时. 一,FAT32的保留区 1,引导扇区 :引导扇区是F ...

  3. 【python】python2.x中的除法

    在生信分析中有许多时候我们需要用到除法,在经历无数次break out 之后我终于发现原来python 2.x中只有整除,而没有浮点除法,这就是没有基础的弊病. 那么如何在python 2.x中运用除 ...

  4. 一些sql优化原则

    1.我们在设计表的时候,尽量让字段拥有默认值,尽量不要让字段的值为null. 因为,在 where 子句中对字段进行 null 值判断(is null或is not null)将导致引擎放弃使用索引而 ...

  5. PHP遍历目录和文件及子目录和文件

    正常直接使用opendir方法,就可以读到所有的目录和文件 文件可以直接记录下来,目录则需要再进一步获取里边的文件信息 也就是,如果当前读出来是目录,则需要再次调用函数本身(递归),直到没有目录 循环 ...

  6. 经典算法问题的java实现 (二)

    原文地址: http://liuqing-2010-07.iteye.com/blog/1403190   1.数值转换(System Conversion) 1.1 r进制数   数N的r进制可以表 ...

  7. 【Git】vs code+git 不使用ssh的链接remote server的方式

    git config --global user.name "dennis wu" git config --global user.email "email" ...

  8. [转载]Black-Scholes 模型中 d1,d2 是怎么得到的?如何理解 Black-Scholes 模型?

    https://www.optbbs.com/thread-253244-1-1.html

  9. Lintcode481-Binary Tree Leaf Sum-Easy

    481. Binary Tree Leaf Sum Given a binary tree, calculate the sum of leaves. Example Example 1: Input ...

  10. angular --- s3core移动端项目(二)

    product-ctrl.js angular.modules('myApp').controller('ProductCtrl',['$scope','$rootScope','$timeout', ...