Spring源码(二)--xml配置文件

Spring的配置文件

在分析源码中,首先需要了解它的使用,再读源码时就可以了解到,功能对应的源码是怎么运行的,怎么编写的。
首先来看DefaultBeanDefinitionDocumentReader中的parseDefaultElement方法用来对配置文件中的4大默认标签做解析处理,该方法也是在创建bean的之前会调用

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)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

其中我们看一下对bean标签的处理

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.
            //通知相关的监听器,这个bean已经加载完成了
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

从上面的代码中可以看到,bean的解析是第一步,如果有子标签还要解析子标签的属性。同事我们从这段代码中可以看到一些编程思想影子,BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 该方法本身不处理解析bean标签的属性,但是却委派给了其他类去处理 用到了委派模式。Spring中使用了大量的委派模式,这里有一个方法,凡是看到delegate结尾的类名、Dispatcher结尾的类名,在Spring中都使用了委派模式。例如大名鼎鼎的Dispatcherservlet,就是使用了委派模式来完成的(后期的文章中也会仔细分析Dispatcherservlet这个类)。

往下走,可以看到如下代码:

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        //解析id属性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        //解析name属性
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
        //分割name属性
        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.isDebugEnabled()) {
                logger.debug("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }

        if (containingBean == null) {
            //检测唯一性beanname
            checkNameUniqueness(beanName, aliases, ele);
        }
        //检测其他的属性
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            //如果不存在beanName 那么根据spring中提供的命名规则为当前bean生成对应的beanName
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        //根据spring规则,自动生成beanName
                        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);
            //封装为一个BeanDefinitionHolder实例
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

从上面的代码中,我们终于看Spring在解析bean标签的属性了,但是只是解析了id和name,其他的属性则又跑到了其他的方法中去解析。我们看到它这里调用了这个方法AbstractBeanDefinition beanDefinition=parseBeanDefinitionElement(ele, beanName, containingBean); 这里需要明确一个规律,在Spring中严格采用了接口化编程,当看到Abstractxxxx的类的时候,可以点开idea的设计,看下这个的顶层接口和最底层的实现接口,如上篇文章中的Resource接口。这个是Spring的惯用手段,采用了编程思想中的模板模式,来构建一套可以面对不同情况去实现各种对应类的方法。

BeanDefinition

在这里的BeanDefinition,就是载体,承载了bean的属性,也就是我们熟知的Spring 中的bean。这里也可以看到,Spring这个框架的优越,对于我们本身需要的bean,Spring并没有去入侵我们的代码,只是适配了一个接口,包装了一下,既满足了我们的需求,又提供了Spring管理的方法。(后期注册的时候,可以看到AOP的容器中放的,就是这个)。有如下3中实现类:

  • RootBeanDefinition:最常用的实现类,对应一般的bean标签
  • GenericBeanDefinition:是2.5版本后加入的,是一站式服务类
  • ChildBeanDefinition:当存在父子关系的bean时,父的bean使用RootBeanDefinition,子bean使用ChildBeanDefinition

这里写图片描述

bean的属性

我们接下去再来看,bean的属性解析,如下面的源码:

public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

        this.parseState.push(new BeanEntry(beanName));

        String className = null;
        //解析class属性
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        String parent = null;
        //解析parent属性
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {
            //创建父类的一个bean
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            //解析默认bean的各种属性
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            //提取description
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            //解析元数据
            parseMetaElements(ele, bd);
             //解析lookup-method属性
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
             //解析replaced-method属性
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//
            parseConstructorArgElements(ele, bd);
            //解析property子元素
            parsePropertyElements(ele, bd);
            //解析qualifier子元素
            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;
    }

其中parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); 这部分代码就在文中看了,太多了,里面涉及了一些bean 的属性,大致为以下几种,介绍一下

scope属性:

可以选的值有:

  • singleton:单例,默认情况下为singleton
  • prototype:多例,每次都会创建一个新的实例。这里我个人的理解是设计模式中的原型模式,因为原型模式的最经典的例子就是深度克隆,也就是每一份对象的属性相同,但是对象本身的实例却不是同一个。有兴趣的可以考虑去看下原型模式。
  • request:每次请求会创建一个新的实例
  • session:在不同session中创建新的实例
  • global session:在整个session范围内使用同一个实例

    abstract属性:

    用法如下:

    <bean id="person" class="spring.extend.Person" abstract="true">
        <property name="name" value="张三"></property>    
    </bean>
    <bean id="student" class="spring.extend.Student" parent="person"></bean>

    或者如下

    <bean id="person" abstract="true">
        <property name="name" value="张三"></property>    
    </bean>
    <bean id="student" class="spring.extend.Student" parent="person"></bean>

    可以看到情况2中,没有定义class,仅仅作为一个模板来使用。

    lazy-init属性: 懒加载属性
    autowier属性: 自动注入属性
    depends-on属性:Spring会按照depend-on中定义的顺序来处理Bean
    autowire-candidate属性:

    如下例子:

    <!-- 
        两个对象继承自同一接口
        autowire-candidate="false" 表示该对象不参与自动注入
     -->
    <bean class="shangbo.spring.example38.MessageServiceDBImpl" autowire-candidate="false" />
    <bean class="shangbo.spring.example38.MessageServiceFileImpl"/>
primary属性:

当设置为true时,那该bean在autowired是byType时 就是首选。spring根据primary的信息就会把这个bean拿去注入

init-method属性:指定对象的初始化方法
destroy-method属性:指定对象的销毁方法
factory-method属性:本身就是一个工厂类,使用工厂方法来创建一个实例.(在Spring中什么都是bean,所以Spring中存在着BeanFactory和FactoryBean,Spring中采用了&来区分是不是一个工厂类的,后期的文章中会有详细介绍)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值