2019独角兽企业重金招聘Python工程师标准>>>

注 :源码对应版本为 4.2.8.RELEASE

引入Spring依赖的时候,是否发现spring依赖有spring-beans和spring-context两个jar,spring容器的顶层接口BeanFactory在spring-beans,而我们常用的ApplicationContext则在spring-context中定义。目前的认知是,ApplicationContext是在BeanFactory的基础上,提供了很多容器上下文的功能。那么到底BeanFactory提供了哪些功能,ApplicationContext在这些功能的基础上又额外提供了哪些上下文功能,其中的实现原理是什么?

一、BeanFactory

首先抛开ApplicationContext,我们单独来看一下spring-beans模块中提供了哪些接口和功能:

<?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-4.2.xsd"><bean class="lujing.sample.bean.TargetBean" />
</beans>
public class XmlBeanFacotyTest {public static void main(String[] args) {//指定xml文件Resource resource = new ClassPathResource("spring/applicationContext-beanFactory.xml");//实例化一个XmlBeanFactoryXmlBeanFactory beanFactory = new XmlBeanFactory(resource);//这样就可以工作啦,可以getBean了TargetBean targetBean = beanFactory.getBean(TargetBean.class);System.out.println(targetBean.getName());}
}

查看XmlBeanFactory的源码可以看到,也比较简单:

@Deprecated
@SuppressWarnings({"serial", "all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);/*** Create a new XmlBeanFactory with the given resource,* which must be parsable using DOM.* @param resource XML resource to load bean definitions from* @throws BeansException in case of loading or parsing errors*/public XmlBeanFactory(Resource resource) throws BeansException {this(resource, null);}/*** Create a new XmlBeanFactory with the given input stream,* which must be parsable using DOM.* @param resource XML resource to load bean definitions from* @param parentBeanFactory parent bean factory* @throws BeansException in case of loading or parsing errors*/public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {super(parentBeanFactory);this.reader.loadBeanDefinitions(resource);}}

其实就是2个核心类,分别是 DefaultListableBeanFactory  和  XmlBeanDefinitionReader ,spring本身标识了XmlBeanFactory废弃,取而代之使用 DefaultListableBeanFactory 和 XmlBeanDefinitionReader,所以以上代码可以写成:

public class BeanFactoryTest {public static void main(String[] args) {Resource resource = new ClassPathResource("spring/applicationContext-beanFactory.xml");DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(null);new XmlBeanDefinitionReader(beanFactory).loadBeanDefinitions(resource);TargetBean targetBean = beanFactory.getBean(TargetBean.class);System.out.println(targetBean.getName());}
}

显而易见,spring中xml解析是由 XmlBeanDefinitionReader 来完成的。剥离了xml这一层具体的实现(也可以有基于Annotation的实现等)

二、XmlBeanDefinitionReader

从上述代码中可以看到,xml到解析工作通过 loadBeanDefinitions方法完成:

   public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource));}
 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");if (logger.isInfoEnabled()) {logger.info("Loading XML bean definitions from " + encodedResource.getResource());}//这里使用ThreadLocal一方面为了线程安全考虑,另一方面提供存储//使用//    private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =//  new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently //  being loaded");//而不用 Set<EncodedResource>  currentResources = new HashSet<EncodedResource>(4);//是因为该方法可能会被多线程调用。为了记录当前正在解析的Resource文件Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {//初始化set并设置线程上文currentResources = new HashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}//利用Set的add方法,判断重复的定义if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {InputStream inputStream = encodedResource.getResource().getInputStream();try {InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}//继续调用解析return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally {inputStream.close();}}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {//移除线程上限为变量currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}}

从上面的代码中可以看到,该方法主要做了:

利用线程上下文值,记录当前线程正在解析的xml文件。检查import标签可能引入的循环解析问题

从这个方法中我们也可以借鉴两种常用的写法:

1. 关于ThreadLocal的用法,在使用get(),set()使用完成以后,必须要在finally代码块中 remove()掉

2. loadBeanDefinitions 和 doLoadBeanDefinitions的方法命名,我们发现spring中这类命名特别多。do开头的方法往往是正真的业务实现,一些外围处理都环绕在外面。这样的写法看起来真的特别舒服,一层一层的,极大的减少了代码的理解复杂度。同时也为扩展提供了很好的架构。一般spring中do开头的方法都是protected的,显然告诉spring的使用者,这是一个扩展点。这也是设计模式中 “模板模式”很好的体现。

 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {//生成XML Document 文档对象Document doc = doLoadDocument(inputSource, resource);//注册Bean定义return registerBeanDefinitions(doc, resource);}//catch 各种异常,统一包装为 XmlBeanDefinitionStoreException//这里就不贴代码了}

生成XML文档对象的时候,使用了DocumentLoader实现类来完成,底层主要是利用JAXP完成从xml解析问Document对象:

  protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {//private DocumentLoader documentLoader = new DefaultDocumentLoader();return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());}

Bean的解析使用BeanDefinitionDocumentReader

  public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();int countBefore = getRegistry().getBeanDefinitionCount();documentReader.registerBeanDefinitions(doc, createReaderContext(resource));return getRegistry().getBeanDefinitionCount() - countBefore;}

三、DocumentLoader

spring底层通过JAXP技术,完成从xml到Document对象的解析。

在xml解析中有一点比较重要,就是xml的验证,它主要分为DTD验证和XSD验证两种。无论哪一种验证,java都需要加载相应的DTD或XSD文件,默认是直接从xml中对应的地址下载,这里是 http://www.springframework.org/schema/beans/spring-beans-4.2.xsd

<?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-4.2.xsd"><bean class="lujing.sample.bean.TargetBean" />
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd">
<beans><bean class="lujing.sample.bean.TargetBean" />
</beans>

为了避免这样的网络请求,jaxp提供了EntityResolver方法,spring就是通过实现该接口,实现了从classpath中加载对应的文件。

  protected EntityResolver getEntityResolver() {if (this.entityResolver == null) {// Determine default EntityResolver to use.ResourceLoader resourceLoader = getResourceLoader();if (resourceLoader != null) {this.entityResolver = new ResourceEntityResolver(resourceLoader);}else {this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());}}return this.entityResolver;}

四、BeanDefinitionDocumentReader

 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {//创建 BeanDefinitionDocumentReader 对象BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();//记录调用BeanDefinitionDocumentReader之前已经解析完成的 Bean 数量//getRegistry返回的是我们创建的 DefaultListableBeanFactoryint countBefore = getRegistry().getBeanDefinitionCount();//解析documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//现在的Bean数量减去之前的数量,就是本次加载的Bean数量return getRegistry().getBeanDefinitionCount() - countBefore;}

创建BeanDefinitionDocumentReader就是根据设定的class实例化,默认实现是org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader

 protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));}

具体的BeanDefinitionDocumentReader通过registerBeanDefinitions方法完成。

创建XmlReaderContext主要是把XmlBeanDefinitionReader中设置的NamespaceHandlerResolver传递过去,在解析的时候需要用到。这也是设计模式中“门面模式”的体现。真正的逻辑都是委托给别的核心组件来完成。

    public XmlReaderContext createReaderContext(Resource resource) {return new XmlReaderContext(resource, this.problemReporter, this.eventListener,this.sourceExtractor, this, getNamespaceHandlerResolver());}
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {//设置解析上下文this.readerContext = readerContext;logger.debug("Loading bean definitions");//获取根节点Element root = doc.getDocumentElement();//调用真正的解析方法doRegisterBeanDefinitions(root);}

下面是真正的解析核心方法了,方法仍旧是一个模版方法,对最外层的beans节点,做了过滤

解析代码的时候都用到了 delegate.isDefaultNamespace(element)方法,该方法的用处就是判断元素节点是不是 beans 命名空间下的 节点,或者是 自定义节点如 <tx:annotation-driver> 。spring判断的依据就是节点的namespace是不是固定的 http://www.springframework.org/schema/beans,spring在xml解析的时候,设置了 namespaceAware参数为true,每个节点都可以取到 namespace。

protected void doRegisterBeanDefinitions(Element root) {// 解析一个<beans/>节点,任何嵌套的beans标签都会重新调用一次这个方法//每个beans节点都有一个BeanDefinitionParserDelegate,同时再实例化的时候指定 parent//这里主要考虑 传播和保存 benas节点中 default-*(default-init-method等)值BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);//profile机制只对标准beans定义的生效,对于自定义标签定义的beans不生效if (this.delegate.isDefaultNamespace(root)) {//读取profile属性值,用于区分如生产、测试等String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);//未指定profile默认bean都是生效的if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);//如果指定了 profiles 且 不符合 environment定义的 激活的 profiles ,那么直接忽略if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {return;}}}//扩展点方法,默认实现为空preProcessXml(root);//利用BeanDefinitionParserDelegate真正的解析节点parseBeanDefinitions(root, this.delegate);//扩展点方法,默认实现为空postProcessXml(root);this.delegate = parent;}

4.1  BeanDefinitionParserDelegate

里面使用了BeanDefinitionParserDelegate委派处理:

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)) {//默认节点parseDefaultElement(ele, delegate);}else {//自定义标签解析,如常见的 <tx:annotation-driven/>delegate.parseCustomElement(ele);}}}}//是自定义节点,调用自定义节点的解析方式,这里的考虑点还是 自定义的 beans 标签,比如 <coustom:beans/>else {delegate.parseCustomElement(root);}}
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {//import节点解析if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}//alias节点解析else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}//最复杂的 bean 节点解析else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {processBeanDefinition(ele, delegate);}//嵌套的beans标签解析,又回到了顶层解析方法else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {doRegisterBeanDefinitions(ele);}}

import标签其实就是循环解析xml文件,这里就不贴代码了;alias标签其实就是注册别名,也非常简单。

五、自定义标签解析

spring 的 自定义标签机制是 spring的扩展的基础,众多功能如context,webmvc,transaction都使用自定义标签,方便用户再极简配置就能享受强大的功能。如 bean扫描<context:component-scan base-package=""/>,事务的<tx:annotation-driven/>等

  public BeanDefinition parseCustomElement(Element ele) {return parseCustomElement(ele, null);}public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {//获取命名空间String namespaceUri = getNamespaceURI(ele);//利用命名空间得到一个自定义解析器 NamespaceHandler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}//自定义解析器的 parse 方法return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}

从上面的代码可以看到,自定义解析大概分为3步:

1. 获取自定义标签的 namespaceUri ,用的是  node.getNamespaceURI(); ,以<tx:annotation-driven/>为例就是: http://www.springframework.org/schema/tx

xmlns:tx="http://www.springframework.org/schema/tx"

2. 利用 NamespaceHandlerResolver 的 resolve 方法实例化一个 NamespaceHandler

public interface NamespaceHandlerResolver {/*** 根据namespaceUri实例化一个NamespaceHandler对象*/NamespaceHandler resolve(String namespaceUri);}

这里使用的是 DefaultNamespaceHandlerResolver 实现类:

public NamespaceHandler resolve(String namespaceUri) {//加载 namespaceUri=namespaceHandler 对应表Map<String, Object> handlerMappings = getHandlerMappings();Object handlerOrClassName = handlerMappings.get(namespaceUri);if (handlerOrClassName == null) {return null;}// 如果是NamespaceHandler对象就直接返回。这里对象的判断是,前面如果有该标签出现过,这里直接缓存对象了else if (handlerOrClassName instanceof NamespaceHandler) {return (NamespaceHandler) handlerOrClassName;}//NamespaceHandler的完整类路径处理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);//init方法,注意这个方法很重要,是用户扩展点中很重要的一点namespaceHandler.init();//覆盖原来的,避免多次实例化handlerMappings.put(namespaceUri, namespaceHandler);return namespaceHandler;}catch (ClassNotFoundException ex) {throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +namespaceUri + "] not found", ex);}catch (LinkageError err) {throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +namespaceUri + "]: problem with handler class file or dependent class", err);}}}

可以看到 getHandlerMappings()方法读取了所有的 META-INF/spring.handlers" 文件。还是以 tx为例,在spring-tx.jar 可以看到 META-INF/spring.handlers 文件的内容:

http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler

在上面的代码中我们可以看到,init()方法是生命周期方法,用于初始化。

public class TxNamespaceHandler extends NamespaceHandlerSupport {static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";static String getTransactionManagerName(Element element) {return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);}@Overridepublic void init() {registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());}}

init()方法基本上是固定的写法,就是为每个标签定义一个 BeanDefinitionParser 

六、Bean标签的解析

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.java
    /*** Process the given bean element, parsing the bean definition* and registering it with the registry.*/protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {//利用delegate获取一个BeanDefinitionHolder对象,包含了beanName,alias, BeanDefinitionBeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {//装饰BeanDefinitionHolder bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// 注册到容器中,这里就是 DefaultListableBeanFactoryBeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// 发送事件通知getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.java
    /***  解析<bean>节点,如果解析错误返回null.错误被通知到 *  org.springframework.beans.factory.parsing.ProblemReporter*/public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {return parseBeanDefinitionElement(ele, null);}

简单的做了一个适配:

    /***  解析<bean>节点,如果解析错误返回null.错误被通知到 *  org.springframework.beans.factory.parsing.ProblemReporter*/public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {//读取id属性String id = ele.getAttribute(ID_ATTRIBUTE);//读取name属性String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//处理name属性为别名List<String> aliases = new ArrayList<String>();if (StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}//默认是beanName为id属性,没有id属性使用第一个beanName作为beanNameString beanName = id;if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {beanName = aliases.remove(0);if (logger.isDebugEnabled()) {logger.debug("No XML 'id' specified - using '" + beanName +"' as bean name and " + aliases + " as aliases");}}if (containingBean == null) {//校验beanName的唯一性,在同一个Beans标签下面不能重复checkNameUniqueness(beanName, aliases, ele);}AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);if (beanDefinition != null) {//如果没有指定beanName使用,使用spring规则生成beanNameif (!StringUtils.hasText(beanName)) {try {if (containingBean != null) {beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);}else {beanName = this.readerContext.generateBeanName(beanDefinition);String beanClassName = beanDefinition.getBeanClassName();if (beanClassName != null &&beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {aliases.add(beanClassName);}}if (logger.isDebugEnabled()) {logger.debug("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;}
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {//出错的时候 error 要用到this.parseState.push(new BeanEntry(beanName));//class属性String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}try {//parent属性String parent = null;if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}//自定义的bean标签返回的是 GenericBeanDefinition AbstractBeanDefinition bd = createBeanDefinition(className, parent);//解析各种属性 lazy-init,init-method 等parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);//description节点直接读bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));//解析metaparseMetaElements(ele, bd);//解析lookup-methodparseLookupOverrideSubElements(ele, bd.getMethodOverrides());//解析replaced-methodparseReplacedMethodSubElements(ele, bd.getMethodOverrides());//解析构造函数参数parseConstructorArgElements(ele, bd);//解析propertyparsePropertyElements(ele, bd);//解析qualifierparseQualifierElements(ele, bd);bd.setResource(this.readerContext.getResource());bd.setSource(extractSource(ele));return bd;}catch (ClassNotFoundException ex) {error("Bean class [" + className + "] not found", ele, ex);}catch (NoClassDefFoundError err) {error("Class that bean class [" + className + "] depends on not found", ele, err);}catch (Throwable ex) {error("Unexpected failure during bean definition parsing", ele, ex);}finally {this.parseState.pop();}return null;}

终于看到了对所有属性元素的解析,其他都是一些相对简单的解析,最难的就是property节点的解析:

public void parsePropertyElements(Element beanEle, BeanDefinition bd) {NodeList nl = beanEle.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {//遍历子节点//isCandidateElement: //(node instanceof Element && (isDefaultNamespace(node) || !isDefaultNamespace(node.getParentNode())));//标签名称是property//调用parsePropertyElement解析Node node = nl.item(i);if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {parsePropertyElement((Element) node, bd);}}}

转载于:https://my.oschina.net/u/206123/blog/1549747

Spring Ioc 源码分析(一)- XML 解析相关推荐

  1. Spring Ioc 源码分析(一)--Spring Ioc容器的加载

    1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:Intellj idea debug 模式 ...

  2. Spring Ioc源码分析 之 Bean的加载(6):属性填充(populateBean())

    "属性填充",也是在populateBean()方法中. 首先回顾下CreateBean的主流程: 如果是单例模式,从factoryBeanInstanceCache 缓存中获取B ...

  3. Spring Ioc源码分析 之 Bean的加载(4):实例化Bean(createBeanInstance()方法)

    实例化 Bean 在doCreateBean()代码 <2> 处,有一行代码instanceWrapper = createBeanInstance(beanName, mbd, args ...

  4. java获取当前周一_Java互联网架构-Spring IOC源码分析

    欢迎关注头条号:java小马哥 周一至周日下午三点半!精品技术文章准时送上!!! 精品学习资料获取通道,参见文末 源码介绍之前,看几个问题: Bean的承载对象是什么? Bean的定义如何存储的? B ...

  5. Spring Security3源码分析-http标签解析(转)

    为什么80%的码农都做不了架构师?>>>    在FilterChainProxy初始化的过程中,大概描述了标签解析的一些步骤,但不够详细   <http auto-confi ...

  6. Spring Ioc源码分析 之 Bean的加载(7):初始化

    接着分析doCreateBean()的第6步--初始化 bean 实例对象 首先回顾下CreateBean的主流程: 如果是单例模式,从factoryBeanInstanceCache 缓存中获取Be ...

  7. Spring Ioc源码分析 之 Bean的加载(5):循环依赖处理(populateBean())

    首先回顾下Bean加载的主流程: 1.如果是单例模式,从factoryBeanInstanceCache 缓存中获取BeanWrapper 实例对象并删除缓存 2.调用 createBeanInsta ...

  8. Spring Ioc源码分析系列--容器实例化Bean的四种方法

  9. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

最新文章

  1. java程序通过命令行运行之Path和ClassPath的注意点
  2. Entity Framework 6 Recipes 2nd Edition(10-5)译 - 在存储模型中使用自定义函数
  3. List list= new ArrayList为什么这样写 接口引用指向实现类的对象
  4. 释疑の采购订单的批量修改
  5. 数据库oracle文件怎么打开,Oracle某个数据文件损坏,如何打开数据库-数据库专栏,ORACLE...
  6. oracle spm使用1
  7. Bootstrap FileInput(文件上传)中文API整理
  8. 【Python基础知识-pycharm版】第十节_异常
  9. 2000坐标系高程与85高程转换_【科普】测量人必须知道的几大坐标系
  10. C语言 vprintf 函数 - C语言零基础入门教程
  11. GraphX实现N度关系
  12. Oracle 单实例数据库安装和real application clusters数据库安装的区别
  13. Python Window10 环境安装流程
  14. 分布式系统的Raft算法 1
  15. atitit.文件上传带进度条的实现原理and组件选型and最佳实践总结O7
  16. 【C++】字体文件解析(FreeType)
  17. python——algorithms模块
  18. 华师大学 计算机 调剂,2020华南师范大学调剂公告
  19. 莱诺和柯南Excel甘特图
  20. 拉卡拉支付开启生态化移动支付新趋势

热门文章

  1. Linux OpenSSL获取证书指纹值(443、MD5、SHA1、SHA256)
  2. Celery 框架学习笔记(分布式框架)
  3. C/C++:Windows编程—调用DLL程序的2种方法
  4. python版:装饰器模式
  5. 05 Python字符串的通用操作
  6. UVA 11039.Building designing
  7. 存储系统又慢又杂太难用?我们推荐这几本书帮你解决问题
  8. 谷歌、微软、亚马逊6个惊人的A/B测试实例
  9. 什么是机器阅读理解?跟自然语言处理有什么关系?
  10. 重磅盘点!过去一年里最受欢迎的技术干货,全在这里了