Spring深入学习_IOC浅析上

Spring Framework

  • 发布于2002年10月1日
  • 强大的基于 JavaBeans 的采用控制反转(Inversion of Control,IoC)原则的配置管理,使得应用程序的组建更加简易快捷。

  • 一个可用于 Java EE 等运行环境的核心 Bean工厂

  • 数据库事务的一般化抽象层,允许声明式(Declarative)事务管理器,简化事务的划分使之与底层无关。

  • 内建的针对 JTA 和单个 JDBC 数据源的一般化策略,使Spring的事务支持不要求 Java EE 环境,这与一般的 JTA 或者 EJB CMT 相反。

  • JDBC 抽象层提供了有针对性的异常等级(不再从 SQL 异常中提取原始代码),简化了错误处理,大大减少了程序员的编码量。再次利用 JDBC 时,你无需再写出另一个'终止'(finally)模块。并且面向 JDBC 的异常与 Spring 通用数据访问对象(Data Access Object)异常等级相一致。

  • 以资源容器,DAO 实现和事务策略等形式与 HibernateJDOMyBatisSQL Maps 集成。利用控制反转机制全面解决了许多典型的 Hibernate 集成问题。所有这些全部遵从 Spring 通用事务处理和通用数据访问对象异常等级规范。

  • 灵活的基于核心 Spring 功能的 MVC 网页应用程序框架。开发者通过策略接口将拥有对该框架的高度控制,因而该框架将适应于多种呈现(View)技术,例如 JSPFreeMarkerVelocityThymeleaf 等。值得注意的是,Spring 中间层可以轻易地结合于任何基于 MVC 框架的网页层,例如 StrutsWebWorkTapestry

  • 提供诸如事务管理等服务的AOP框架。

在设计应用程序 Model 时,MVC模式(例如 Struts)通常难于给出一个简洁明了的框架结构。Spring 却具有能够让这部分工作变得简单的能力。程序开发员们可以使用Spring的JDBC抽象层重新设计那些复杂的框架结构。

上述是维基百科给出的Spring核心功能模块定义,我们可以看出Spring做了什么有哪些用处。Spring是一个BeanFactory,负责加载Bean和管理Bean的生命周期与依赖关系这也就是IOC控制反转,它还支持AOP以便支持切面编程,Spring MVC给出了一个简单的MVC模式范例很方便的支持日常用到的贫血模型,以及Spring Boot可以支持简化配置。

BeanFactory

既然知道了Spring中IOC的核心组件时BeanFactory,那么我们来了解一下什么是BeanFactory。见词晓意一个组件工厂,用来生产组件和获得组件。

public interface BeanFactory {
  //用&MyFactory获得工厂,&为表示工厂的前缀
  String FACTORY_BEAN_PREFIX = "&";
  //通过名字或别名获取Bean
  Object getBean(String name) throws BeansException;
  //获取时要匹配类型requiredType需要是超类或者继承的接口
  <T> T getBean(String name, Class<T> requiredType) throws BeansException;
  //args使用显示参数创建Bean时需要用到的参数
  Object getBean(String name, Object... args) throws BeansException;
  //通过类型获取Bean
  <T> T getBean(Class<T> requiredType) throws BeansException;
  //类型和参数
  <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
  //返回一个Bean提供程序允许检测可用性和唯一性支持懒加载,就是一个ObjectFactory
  <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
  //根据name查找是否含有Bean定义和单例Bean,如果是分层的工厂则查找超类
  boolean containsBean(String name);
  //是否是单例的们也会递归查找 PS:单例是每次调用都可以返回相同实例
  boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
  //Spring支持的原型类型也就是每次调用都返回一个新的实例
  boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
  //检查是否可以返回一个给定意义的对象,ResolvableType中定义了类型
  boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
  //Class版本的type
  boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
  //拿到类型,对于FactoryBean返回工厂生产的Bean实例类型,默认会初始化工厂
  Class<?> getType(String name) throws NoSuchBeanDefinitionException;
  //allow询问要不要初始化工厂
  Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
  //返回别名,原始名称为数组中的第一个元素
  String[] getAliases(String name);

上述就是BeanFactory全部方法,先是一些重载的getBean各种类型如用name获取,或者用requiredType获取再是获取ObjectProvider再者是检测有没有这个Bean,Bean的类型与生成规则,最后是拿到这个Bean的Type与别名。可以看出这个工厂是Spring最核心的组件。
什么是耦合呢?为什么使用new会造成耦合?

  //一句简单的泛型,接口持有子类引用
  ProfileService profileService = new ProfileServiceImpl();

上述代码为题在哪里呢?其实也没啥问题,如果是写个小项目完全没有任何问题有时候我们需要替换Impl时直接Ctrl+F定位Impl然后正则替换!完结撒花,Spring结束!
开个玩笑,假设项目中有很多中这样的impl很常见的,我们在替换时需要替换每一个地方的,但是有些地方有没有什么可以快速替换的方式呢?有的
(XXXImpl)BeanFactroy.getBean(XXX);
好处时什么呢?我们可以直接在Bean工厂中替换Impl实现,如我们采用注解的方式实现一个接口,在实现类Impl上标明采用这个实现类,如果下一次想换一个那么把这个注解移到另一个类就可以,这样子对代码中侵入性修改就可以降低到最小。

ApplicationContext 应用上下文

ApplicationContext

从上图中,我们可以很明显看出Application继承自BeanFactory,Listable是可以一次获取多个Bean,BeanFactory的方法中都是获取单个,Hierarchical,可以有继承关系的BeanFactory。应用程序中常见的两个Appcontext有ClassPathXmlApplicationContext,AnnotationConfigApplicationContext


appcontext继承关系

上述最要中的观察点已经标识,程序中两种最重要的上下文可以采用注解和Xml两种形式,ApplicationContext启动过程中会负责创建Bean与Bean的依赖注入。

分析ApplicationContext的启动过程

我们从ClassPathXmlApplicationContext的源码中分析context的启动过程,进入源码中发现只有200多行美滋滋。

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
  @Nullable
  private Resource[] configResources;
  public ClassPathXmlApplicationContext(ApplicationContext parent) {
    super(parent);
  }
  public ClassPathXmlApplicationContext(
        String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
        throws BeansException {
    super(parent);
    //处理配置文件数组
    setConfigLocations(configLocations);
    if (refresh) {
        //核心方法刷新
        refresh();
    }
  }

1.setConfigLocations

setConfigLocations是超类AbstractRefreshableConfigApplicationContext中的方法用于解析配置文件路径,处理成配置文件数组。也就是一个String[]configLocations,先判断下非空再对Path进行trim去除空白符再resolvePath进行解析。

2.refresh()

这个方法也是由超类实现的值得一提的是为什么叫refresh而不是init那因为可以重新调用这个方法来进行销毁重建,具体代码如下

    public void refresh() throws BeansException, IllegalStateException {
        // 加锁以防止refresh没结束又进行一个刷新
        synchronized (this.startupShutdownMonitor) {
            // 准备上下文用于刷新
            prepareRefresh();

            // 刷新内部Bean工厂,解析配置文件生成Bean定义注册到BeanFactory
            // 没有进行初始化Bean
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 设置BeanFactory的类加载器 添加几个BeanPostProcessor手动注册一些单例Bean
            prepareBeanFactory(beanFactory);

            try {
                // 扩展点,如果类实现了这个这个方法可以再容器初始化后做些什么
                postProcessBeanFactory(beanFactory);

                // 调用processors
                invokeBeanFactoryPostProcessors(beanFactory);

                // 注册BeanPostProcessors
                // 如果Bean实现了接口的两个方法postProcessBeforeInitialization和postProcessAfterInitialization
                //那么在Bean初始化的过程之前和之后会进行调用
                registerBeanPostProcessors(beanFactory);

                // 初始化当前context的信息源
                initMessageSource();

                // 初始化时间广播器
                initApplicationEventMulticaster();

                // 初始化一些特殊的Bean
                onRefresh();

                // 注册事件监听器
                registerListeners();

                // 初始化所有单例懒加载的除外
                finishBeanFactoryInitialization(beanFactory);

                // 最后一步广播初始化完成事件
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // 发生异常则销毁创建的单例
                destroyBeans();

                // 重置active标志位为false
                cancelRefresh(ex);

                // 异常抛出去
                throw ex;
            }

            finally {
                //设置内省缓存,可能不在需要单例的元数据
                resetCommonCaches();
            }
        }
    }

这个refresh内容蛮多的,具体概括一下就是刷新一下BeanFactory加载新的Bean定义再初始化一些特殊Bean与注册事件监听器初始化单例,消除单例Bean的元数据,下面我们简单分析一下每个调用语句。

创建BeanFactory之前的准备工作

    protected void prepareRefresh() {
        // 设置启动时间与标志位
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);

        if (logger.isDebugEnabled()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Refreshing " + this);
            }
            else {
                logger.debug("Refreshing " + getDisplayName());
            }
        }

        // 初始化占位符属性源
        initPropertySources();

        // 校验标记文件是否可以解析
        getEnvironment().validateRequiredProperties();
        // 存储earlyApplicationListeners到applicationListeners,并新建一个
        // Store pre-refresh ApplicationListeners...
        if (this.earlyApplicationListeners == null) {
            this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
        }
        else {
            // Reset local application listeners to pre-refresh state.
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }

        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }

创建Bean容器,加载Bean并注册

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //刷新BeanFactroy如果有旧的就关闭再创建个新的
        //加载Bean定义与注册Bean
        refreshBeanFactory();
        //返回新的beanFactory
        return getBeanFactory();
    }
    //在AbstractRefreshableApplicationContext有这个方法的实现
    protected final void refreshBeanFactory() throws BeansException {
        //如果加载过BeanFactory就销毁掉所有Bean并关闭BeanFactory
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建一个DefaultListableBeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            //设置序列化ID
            beanFactory.setSerializationId(getId());
            // 定制一下BeanFactory允许Bean的覆盖与循环引用
            customizeBeanFactory(beanFactory);
            // 加载Bean定义
            loadBeanDefinitions(beanFactory);
            //设置beanFactory
            this.beanFactory = beanFactory;
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

方法很简单主要是为什么BeanFactory要使用DefaultListableBeanFactory,这个类继承了所有实现,被设计成一个默认的BeanFactory


DefaultListableBeanFactory

loadBeanDefinitions加载BeanDefinition

什么是BeanDefinition?Bean的定义保存了Bean的元信息指向哪个类,是否是单例要不要懒加载Bean依赖于哪些Bean
BeanDefinition定义

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    //默认提供两种类型单例与原型
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;


    //Bean的角色
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;


    // Modifiable attributes

    //设置父Bean也就是继承父Bean的配置信息
    void setParentName(@Nullable String parentName);

    // 获取父Bean
    @Nullable
    String getParentName();

    // 设置Bean的类名
    void setBeanClassName(@Nullable String beanClassName);

    // 获取Bean的类名
    @Nullable
    String getBeanClassName();

    // 设置作用域
    void setScope(@Nullable String scope);

    // 获取作用域
    @Nullable
    String getScope();

    // 设置懒加载
    void setLazyInit(boolean lazyInit);

    
    boolean isLazyInit();

    // 设置依赖的Bean 重点BeanFactory可以保证这些依赖的Bean可以在他前面初始化
    void setDependsOn(@Nullable String... dependsOn);

    // 返回依赖Bean名称
    @Nullable
    String[] getDependsOn();

    // 设置是否可以注入到其他Bean只影响类型注入
    // 显示的名称注入依然有效
    void setAutowireCandidate(boolean autowireCandidate);

    boolean isAutowireCandidate();

    // 设置Primary 同一个接口多个实现不指定名称会选择主要那个
    void setPrimary(boolean primary);

    boolean isPrimary();

    // 如果是工厂模式生产的就设置一下工厂
    void setFactoryBeanName(@Nullable String factoryBeanName);

    @Nullable
    String getFactoryBeanName();

    // 用哪个工厂方法
    void setFactoryMethodName(@Nullable String factoryMethodName);

    @Nullable
    String getFactoryMethodName();

    // 构造器参数值
    ConstructorArgumentValues getConstructorArgumentValues();

    // 构造器是否是有参的
    default boolean hasConstructorArgumentValues() {
        return !getConstructorArgumentValues().isEmpty();
    }

    // Bean中的属性值
    MutablePropertyValues getPropertyValues();

    
    default boolean hasPropertyValues() {
        return !getPropertyValues().isEmpty();
    }

    // 设置初始化方法名称
    void setInitMethodName(@Nullable String initMethodName);

    
    @Nullable
    String getInitMethodName();

    // 设置销毁方法名称
    void setDestroyMethodName(@Nullable String destroyMethodName);

    
    @Nullable
    String getDestroyMethodName();

    // 设置角色
    void setRole(int role);

    
    int getRole();

    // 提供人可以读的描述
    void setDescription(@Nullable String description);

    
    @Nullable
    String getDescription();


    // 只读属性

    // 返回可解析类型
    ResolvableType getResolvableType();

    
    boolean isSingleton();

    
    boolean isPrototype();

    boolean isAbstract();

    
    @Nullable
    String getResourceDescription();


    @Nullable
    BeanDefinition getOriginatingBeanDefinition();

}

定义了Bean的作用域,构造器,Bean生成规则,初始化和销毁方法
回到refreshBeanFactory() 中剩下还有两个重要方法

customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        //是否允许Bean定义覆盖
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        //是否允许循环依赖
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

Spring的默认值是同一配置文件不允许覆盖,不同则可以。而默认情况下Spring允许循环依赖,构造方法的循环依赖不允许

加载BeanDefinition

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws       BeansException, IOException {
        // 实例化一个XmlBeanDefinitionReader
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // 允许子类提供BeanDefinitionReader的自定义初始化
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

loadBeanDefinitions 很重要的方法用是初始化的Reader开始加载Bean定义

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        // 使用其他重载的方法以进行加载
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int count = 0;
        // 简单的遍历加载然后返回加载个数
        for (Resource resource : resources) {
            count += loadBeanDefinitions(resource);
        }
        return count;
    }

    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.isTraceEnabled()) {
            logger.trace("Loading XML bean definitions from " + encodedResource);
        }
        // 用ThreadLocal来存放配置文件资源
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }

        try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            //核心加载方法
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            //XML转DOM
            Document doc = doLoadDocument(inputSource, resource);
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }
    // 具体的parse解析方法有很多种实现,这里不展开了如果学了编译原理应该无压力
    public void parse(XMLInputSource source) throws XNIException, IOException {

        if (fParseInProgress) {
            // REVISIT - need to add new error message
            throw new XNIException("FWK005 parse may not be called while parsing.");
        }
        fParseInProgress = true;

        try {
            setInputSource(source);
            parse(true);
        } catch (XNIException ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw ex;
        } catch (IOException ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw ex;
        } catch (RuntimeException ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw ex;
        } catch (Exception ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw new XNIException(ex);
        } finally {
            fParseInProgress = false;
            // close all streams opened by xerces
            this.cleanup();
        }

    }
    public boolean parse(boolean complete) throws XNIException, IOException {
        //
        // reset and configure pipeline and set InputSource.
        if (fInputSource != null) {
            try {
                fValidationManager.reset();
                fVersionDetector.reset(this);
                fConfigUpdated = true;
                resetSymbolTable();
                resetCommon();

                short version = fVersionDetector.determineDocVersion(fInputSource);
                if (version == Constants.XML_VERSION_1_1) {
                    initXML11Components();
                    configureXML11Pipeline();
                    resetXML11();
                } else {
                    configurePipeline();
                    reset();
                }

                // mark configuration as fixed
                fConfigUpdated = false;

                // resets and sets the pipeline.
                fVersionDetector.startDocumentParsing((XMLEntityHandler) fCurrentScanner, version);
                fInputSource = null;
            } catch (IOException | RuntimeException ex) {
                if (PRINT_EXCEPTION_STACK_TRACE)
                    ex.printStackTrace();
                throw ex;
            } catch (Exception ex) {
                if (PRINT_EXCEPTION_STACK_TRACE)
                    ex.printStackTrace();
                throw new XNIException(ex);
            }
        }

        try {
            return fCurrentScanner.scanDocument(complete);
        } catch (IOException | RuntimeException ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw ex;
        } catch (Exception ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw new XNIException(ex);
        }

    }

经过如上的漫长方法,可以把配置文件转换成DOM树上面仅仅是转换成DOM树下面介绍下解析DOM树

DefaultBeanDefinitionDocumentReader

    protected void doRegisterBeanDefinitions(Element root) {
        // 方法可以递归调用 在parseDefaultElement中调用了doRegisterBeanDefinitions
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
        //判断一下是否是根节点
        //URL里是否含有BEANS_NAMESPACE_URI
        if (this.delegate.isDefaultNamespace(root)) {
            // 读取beans的profile 
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                //按照 ,;对profileSpec差分
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
        //钩子函数
        preProcessXml(root);

        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }
    // 解析文档中 import alias bean等元素
    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)) {
                        //解析Default
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //解析定制的
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
    public boolean isDefaultNamespace(@Nullable String namespaceUri) {
        return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
    }

    /**
     * Determine whether the given node indicates the default namespace.
     */
    public boolean isDefaultNamespace(Node node) {
        return isDefaultNamespace(getNamespaceURI(node));
    }
    public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";

可以看出默认的Namespace也就是要测试namespaceUri是否是BEANS_NAMESPACE_URI

    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName" >
    

上述为一段典型的xml定义,上面有就使用到BEANS_NAMESPACE_URI,也就是解析XML中的各个节点,常见的有import,bean,aop,context,mvc,区别在于BEANS_NAMESPACE_URI中定义的是默认的其他的是定制的。

    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);
        }
        //解析<nested/>
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

对importBeanDefinitionResource进行一个浅析,读取一下元素再查看一下import里面有没有resuouce然后对引入资源进行加载

    <import resource = "xxxxx.xml">
    public static final String RESOURCE_ATTRIBUTE = "resource";
    //简述
    protected void importBeanDefinitionResource(Element ele) {
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }
        -----省略
    }

总结

对今天的BeanFactory解析进行一个总结,我们深入探讨了BeanFactory的模型与ApplicationContext,知道了上下文的refresh方法用于生成Bean定义与销毁重加载BeanFactory,对BeanDefinition进行了深入了解和解析XML文件,最后分析了一下XML文件生成的DOM树如何解析并对一些简单的如<import resuource = "">进行了浅显的解析已推出XML文件的解析大致流程。

今天时间有限先对Spring框架做一个大致了解,知道什么是BeanFactory和ApplicationContext即可。朋友们走过路过不要忘记点赞,你的点赞是我更新的动力。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343