Spring原理解析-BeanFactory---容器加载过程解析

Spring的IOC容器---BeanFactory---容器加载过程解析

容器加载

DefaultListableBeanFactory类介绍

  DefaultListableBeanFactory类是BeanFactory的默认实现类,其间接实现BeanFactory接口、BeanDefinitionRegistry接口,同时依赖于BeanDefinition接口。其类图如下:

Spring原理解析-BeanFactory---容器加载过程解析
如上图所示,DefaultListableBeanFactory类间接实现BeanFactory接口和BeanDefinitionRegistry接口,其中BeanDefinitionRegistry接口又依赖于BeanDefinition接口。接下来将围绕着张图来讲解BeanFactory容器的启动过程。

BeanFactory接口介绍

  BeanFactory接口主要是用于创建bean的工厂接口,其定义的接口方法分为以下几个部分:获取bean实例、判断容器中是否包含某个bean、判断实例是否为单列模式或则原型模式、类型匹配、实例类型、获取别名。具体接口内容如下:

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
	//获取bean实例
    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
	//是否包含指定实例
    boolean containsBean(String var1);
	//是否为单例模式
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
	//是否为原型模式
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
	//类型匹配
    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
	//返回实例类型
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
	//返回别名
    String[] getAliases(String var1);
}

BeanDefinition接口介绍

  根据接口名称便可以猜出,该接口是跟bean定义相关。的确,该接口是用来管理每个bean的定义信息。在容器启动的时候,spring会将配置文件中定以的信息或则使用注解(如@Componet @Service @Controller等)定义的bean封装成一个个BeanDefinition对象,每个bean对应一个BeanDefinition对象,在对象实例化期间,使用bean对应的BeanDenifition对象通过反射生成实例对象。BeanDefinition接口方法用来管理bean的属性信息
,如父类名、类名、作用域、懒加载等。以下代码为BeanDefinition接口源码:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = "singleton";
    String SCOPE_PROTOTYPE = "prototype";
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;
	//父类名
    void setParentName(String var1);

    String getParentName();
	//类名
    void setBeanClassName(String var1);

    String getBeanClassName();
	//作用域
    void setScope(String var1);

    String getScope();
	//懒加载
    void setLazyInit(boolean var1);

    boolean isLazyInit();
	//依赖
    void setDependsOn(String... var1);

    String[] getDependsOn();

    void setAutowireCandidate(boolean var1);

    boolean isAutowireCandidate();

    void setPrimary(boolean var1);

    boolean isPrimary();
	//工厂类名
    void setFactoryBeanName(String var1);

    String getFactoryBeanName();
	//工厂方法名
    void setFactoryMethodName(String var1);

    String getFactoryMethodName();

    ConstructorArgumentValues getConstructorArgumentValues();

    MutablePropertyValues getPropertyValues();

    boolean isSingleton();

    boolean isPrototype();

    boolean isAbstract();

    int getRole();

    String getDescription();

    String getResourceDescription();

    BeanDefinition getOriginatingBeanDefinition();
}

  可以发现,BeanDefinition接口中的属性信息跟xml配置文件中的标签中的属性一一对应,也就是说一个BeanDefinition对应一个标签信息。在容器启动的时候,就会先将标签信息解析封装成BeanDefinition接口实现类对象。
  BeanDefinition有三个间接实现类,分别是RootBeanFactory、ChildBeanFactory和GenericBeanDefinition。其中RootBeanFactory是用来封装父类定义信息(没有显示继承其他类的类)、ChildBeanDefinition是用来封装子类定义信息。

BeanDefinitionRegistry接口介绍

  同样,根据接口名就可以看出,这个接口跟BeanDefinition对象的注册有关,上一部分将了,在容器启动阶段会将标签定义的bean信息解析封装成BeanDefinition对象,以备后续实例化使用,但是这些BeanDefinition归谁保存维护呢。没错,BeanDefinitionRegistry就是用来保存维护BeanDefinition对象的。

public interface BeanDefinitionRegistry extends AliasRegistry {
	//注册BeanDefinition
    void registerBeanDefinition(String var1, BeanDefinition var2) throws BeanDefinitionStoreException;
	//移除BeanDefinition
    void removeBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
	//获取BeanDefinition
    BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
	//是否包含指定BeanDefinition对象
    boolean containsBeanDefinition(String var1);
	//获取所有BeanDefinition对象名称
    String[] getBeanDefinitionNames();
	//获取BeanDefinition对象数量
    int getBeanDefinitionCount();
	//BeanDefinition是否在使用
    boolean isBeanNameInUse(String var1);
}

如何解析xml文件

  到这里,大家想必对BeanDefinition管理和BeanFactory工厂有一定 的了解了吧,但是大家是否会疑惑,spring是如何将x在这里插入代码片ml中的字符定义转换成BeanDefinition对象的呢,那么接下来我将带着大家继续探讨(其实看到这里,各位可以返现,我明没有深入的去分析源码,而是从原理层面上分析,当大家能够掌握基本原理再去看源码就应该简单了)
  将解析之前我先上一段代码,如下:

public class ClassA {
    
    public void say(){
        System.out.println("我是ClassA对象,我在说话");
    }
}

  这是一个普通的class类

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
    <bean id="classA" class="springBeanTest.ClassA"/>
</beans>

  这是spring的配置文件spring.xml

public class LifeCycleTest {
    @Test
    public void test1(){
        //创建bean的工厂类
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //创建一个xml文件的reader类,将bean工厂传入reader中
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) beanFactory);
        //加载xml未配置文件,并将配置文件解析成BeanDefinition对象保存在工厂类中
        reader.loadBeanDefinitions("classpath:/spring.xml");
        //从工厂类中获取classA类的beanDefinition对象
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("classA");
        //打印
        System.out.println(beanDefinition.getBeanClassName());
        System.out.println(beanDefinition.isSingleton());
    }
}

  运行结果如下图,可以看出,spring确实将xml配置文件解析成beanDefinition对象
Spring原理解析-BeanFactory---容器加载过程解析

  1. 以baenFactory为构造器参数,创建BeanDefinitionReader对象:

  AbstractBeanDefinitionReader抽象类构造器部分内容:

public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
		//将beanFactory传给父类构造器
        super(registry);
       
    }

  父类构造器部分代码如下:

protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        //父类构造器将beanFactory复制给自己的依赖对象,这样便为BeanDefinitionReader对象设置好了BeanFactory实例,用于注册beanDefinition。
        this.registry = registry;
        //初始化ResourceLoader
        if (this.registry instanceof ResourceLoader) {
            this.resourceLoader = (ResourceLoader)this.registry;
        } else {
        	//创建resourceLoader对象,类型为DefaultResourceLoader
            this.resourceLoader = new PathMatchingResourcePatternResolver();
        }
    }
  1. 调用AbstrcatBeanDefinitionReader方法:
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//获取上一步初始化时创建的resourceLoader对象
        ResourceLoader resourceLoader = this.getResourceLoader();
        if (resourceLoader == null) {
            //异常处理代码
        } else {
            int loadCount;
            //接下来的代码是根据loaction的格式创建不同的Resource对象。
            if (!(resourceLoader instanceof ResourcePatternResolver)) {
    		//省略部分代码
            } else {
                try {
                //根绝loaction格式,创建像一面的Resource对象
                    Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
               //从resource对象中加载beanDefinition对象
                    loadCount = this.loadBeanDefinitions(resources);
                   	//省略部分代码
                    return loadCount;
                } catch (IOException var10) {
                    //省略部分代码
                }
            }
        }
    }

  Resource接口屏蔽了不同资源的访问差别,为上层提供统一的资源访问接口。

  1. 获取到resource对象之后,spring会使用该对象创建一个EncodedResource对象,主要是方便编码处理

  以下代码是XmlBeanDefinitionReader类中的方法

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(new EncodedResource(resource));
    }
  1. 得到EncodedResource对象之后,调用以下方法进行xml文件解析,并封装成beanDefinition对象,储存在beanFactroy中。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
       //省去异常处理
        Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        if (!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;
            try {
                InputStream inputStream = encodedResource.getResource().getInputStream();

                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
					//此处开始进行解析加载
                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                } finally {
                    inputStream.close();
                }
            } catch (IOException var15) {
               //异常处理
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if (((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }
  1. doLoadBeanDefinitions方法源码如下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
        	//这里将从resource中加载资源,并封装成Document对象
            Document doc = this.doLoadDocument(inputSource, resource);
            //解析doucument对象,生成beanDefinition对象并注册到BeanDefinitionRegistry中(BeanFactory接口的默认实现类同时也实现了BeanDefinitionRegistry接口,因此可以从BeanDefinitionRegistry获取所有的beanDefinition对象)
            return this.registerBeanDefinitions(doc, resource);
        } catch (BeanDefinitionStoreException var4) {
            //连续一串的异常处理
        } 
    }
  1. registerBeanDefinitions方法,该方法的作用是使用documentReader对象读取document对象中的内容,并解析生成beanDefinition对象,然后注册到BeanDefinitionRegistry中去。详解解析过程此处不再继续深究。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//创建documentReader对象
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        int countBefore = this.getRegistry().getBeanDefinitionCount();
       	//解析document对象,生成beanDefinition对象并注册
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }
  1. 总结:从以上步骤可以发现,xml的具体解析工作并非由BeanDefinitionReader对象来做的,而是由DocumentReader对象来完成的。下面是一张XmlBeanDefinitionReader类的类图,将根据这张类图来梳理整个流程。
    Spring原理解析-BeanFactory---容器加载过程解析
      BeanDefinitionReader类更具配置文件路径名格式,生成不同的Resource接口实现类,这样做的目的是屏蔽对不同资源加载的差异性,获得resource对象之后,BeanDefinitionReader使用resource对象,创建一个EncodedResource对象,方便对资源的编码处理。然后BeanDefinitionReader在解析EncodedResource对象,生成Doucement对象,最后才是使用BeanDefinitionReader内部的DoucementReader对象来解析Doucement,从而生成BeanDefinition对象,并注册到BeanDefinitionRegistry中去。至此,完成了配置文件的解析工作。这里说明一下AbstractBeanDefinitionReader的实现类不只XmlBeanDefinitionReader一个,还有PropertiesBeanDefinitionReader,不同的配置文件使用不同的实现类,同时也可以根据自己的需求自定义一个BeanDefinitionReader实现类,来完成配置文件的机械加载工作。
上一篇:Spring常考的面试题


下一篇:Spring的ApplicationContext&BeanFactory