spring bean源码简单解析

来源:互联网 发布:iphone数据恢复软件 编辑:程序博客网 时间:2024/04/30 10:26

最近在看spring的源码,发现看这个还是有点早,看的很吃力,有多东西还不是很明白,像代理等,

我感觉spring用abstract模板来写主要功能,用接口来拓展功能,用的出神入化,但也让很多简单

的东西变得不那么好懂了,就是写的啰嗦了,个人感觉。下面就是下spring bean源码的学习:

private static final Resource RETURNS_NULL_CONTEXT = qualifiedResource(CLASS, "returnsNull.xml");

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();new XmlBeanDefinitionReader(factory).loadBeanDefinitions(RETURNS_NULL_CONTEXT);Object result = factory.getBean("factoryBean");

这个就是整个spring bean的源码所做的整个解析,只是总体上的,基本上是三步:

1:加载xml,封装成Resource对象。

2:解析xml文件,相关的属性放到BeanDefinition,并把BeanDefinition放到BeanFactory中,实际就是beanDefinitionMap中。

3:通过getBean("factoryBean")方法来把对象注入。

下面来个直观点的东西:

从这个目录结构不难看出整个项目的具体思想,不具体说每个类和接口的作用了,下面具体说下载入解析xml的整个流程:

/** * Load bean definitions from the specified XML file. * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource));}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {    InputStream inputStream = encodedResource.getResource().getInputStream();        try {            InputSource inputSource = new InputSource(inputStream);            if (encodedResource.getEncoding() != null) {                inputSource.setEncoding(encodedResource.getEncoding());            }            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());        }}
第二段代码太长,不直观,只贴了关键一部分,从上面不难看出,具体做了3部分:

1:封装Resource资源,

2:获取输入流

3:调用doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法,也就是解析


/**     * Actually load bean definitions from the specified XML file.     * @param inputSource the SAX InputSource to read from     * @param resource the resource descriptor for the XML file     * @return the number of bean definitions found     * @throws BeanDefinitionStoreException in case of loading or parsing errors     */protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {    try {int validationMode = getValidationModeForResource(resource);Document doc = this.documentLoader.loadDocument(    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());return registerBeanDefinitions(doc, resource);    }}
这段代码也只截取了部分关键代码,具体说了三个意思:

1:获取xml验证模模式

2:获取Document对象

3:进行注册

这里的注册主要就是把拥有所有所有属性BeanDefinition放到map中,这里也可能是大多数不太明白的地方,笔者个人认为。


/**     * Register the bean definitions contained in the given DOM document.     * Called by {@code loadBeanDefinitions}.     * <p>Creates a new instance of the parser class and invokes     * {@code registerBeanDefinitions} on it.     * @param doc the DOM document     * @param resource the resource descriptor (for context information)     * @return the number of bean definitions found     * @throws BeanDefinitionStoreException in case of parsing errors     * @see #loadBeanDefinitions     * @see #setDocumentReaderClass     * @see BeanDefinitionDocumentReader#registerBeanDefinitions     */    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();        documentReader.setEnvironment(getEnvironment());        int countBefore = getRegistry().getBeanDefinitionCount();        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));        return getRegistry().getBeanDefinitionCount() - countBefore;    }

这里主要是说用BeanDefinitionDocumentReader来注册,解析工作。


/** * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). * <p>Opens a DOM Document; then initializes the default settings * specified at the {@code <beans/>} level; then parses the contained bean definitions. */public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");Element root = doc.getDocumentElement();doRegisterBeanDefinitions(root);}

/** * Register each bean definition within the given root {@code <beans/>} element. */protected void doRegisterBeanDefinitions(Element root) {String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {return;}}// Any nested <beans> elements will cause recursion in this method. In// order to propagate and preserve <beans> default-* attributes correctly,// keep track of the current (parent) delegate, which may be null. Create// the new (child) delegate with a reference to the parent for fallback purposes,// then ultimately reset this.delegate back to its original (parent) reference.// this behavior emulates a stack of delegates without actually necessitating one.BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(this.readerContext, root, parent);preProcessXml(root);parseBeanDefinitions(root, this.delegate);postProcessXml(root);this.delegate = parent;}

/**     * Parse the elements at the root level in the document:     * "import", "alias", "bean".     * @param root the DOM root element of the document     */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 {delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}

这三段代码比较简单,最后一段代码就是逐次遍历所有元素,像<bean/>等。下面重头戏来了,下面就是对得到的

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {processBeanDefinition(ele, delegate);}else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}}

/**     * Process the given bean element, parsing the bean definition     * and registering it with the registry.     */protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// Send registration event.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}

/** * Parses the supplied {@code <bean>} element. May return {@code null} * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {String id = ele.getAttribute(ID_ATTRIBUTE);String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);List<String> aliases = new ArrayList<String>();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.isDebugEnabled()) {logger.debug("No XML 'id' specified - using '" + beanName +"' as bean name and " + aliases + " as aliases");}}if (containingBean == null) {checkNameUniqueness(beanName, aliases, ele);}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.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;}

/** * Parse the bean definition itself, without regard to name or aliases. May return * {@code null} if problems occurred during the parsing of the bean definition. */public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {this.parseState.push(new BeanEntry(beanName));String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}try {String parent = null;if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}AbstractBeanDefinition bd = createBeanDefinition(className, parent);parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));parseMetaElements(ele, bd);parseLookupOverrideSubElements(ele, bd.getMethodOverrides());parseReplacedMethodSubElements(ele, bd.getMethodOverrides());parseConstructorArgElements(ele, bd);parsePropertyElements(ele, bd);parseQualifierElements(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;}

这里主要对id,name,class,等属性的解读。也就是所说的属性注入到BeanDefinition,最后放到map中。

这只是整个流程,我没有详细解读,我感觉自己打上断点多走几遍更实际点。这些东西只是我个人的理解,

有不对的地方希望指出来,在此谢谢了。

对于对象的注入主要是AbstractAutowireCapableBeanFactory这个类来操作。这篇就不多说了,下篇再和大家探讨。











0 0