《Spring源码分析之BeanDefinition的创建过程》

一、Spring的几个类说明

1.1 BeanDefinition

Spring框架在各大框架中相当于管理员的角色,而管理员调度的单位是Bean,然后BeanDefinition就是框架内部对Bean的定义,BeanDefinition描述和定义了创建一个Bean需要的所有信息,属性,构造函数参数以及访问它们的方法。还有其他一些信息,比如这些定义来源自哪个类等等信息;

1.2 Resource

Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。
Resource一般包括这些实现类:UrlResource、ClassPathResource、FileSystemResource、ServletContextResource、InputStreamResource、ByteArrayResource

1.3 BeanFactory

BeanFactory,以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
Spring为我们提供了许多易用的BeanFactory实现,XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个完全可配置的系统或应用。

1.4 ApplicationListener

是一个接口,里面只有一个onApplicationEvent方法。如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到 ApplicationContext时,调用ApplicationContext.publishEvent()方法,这个bean得到通知。类似于Oberver设计模式。

1.5 BeanFactory的主要继承关系

在这里插入图片描述

二、BeanDefinition的创建过程

2.1 加载BeanDefiniton的入口

  // 从spring-loy-bean.xml文件中加载资源
  Resource classPathResource = new ClassPathResource("spring-loy-beans.xml");
  // 创建bean工厂,用于处理beanDefinition信息
  BeanFactory factory = new DefaultListableBeanFactory();
  BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;
  // 使用BeanDefinitonReader将信息加载到registry中
  BeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
  // 通过reader加载真正的xml文件中的Bean信息
  reader.loadBeanDefinitions(classPathResource);

这里的入口即为reader.loadBeanDefinitons(classPathResoure);

2.2 进入源码分析加载过程

2.2.1 首先会进入XmlBeanDefinitionReader类的loadBeanDefinitions方法

该实现类是reader的实现类,因为我们解析的是xml文件,所以自然实现类为XmlBeanDefinitionReader.

     /**
     * 从指定的xml文件中加载bean definitions信息
	 * Load bean definitions from the specified XML file.
	 */
	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

2.2.2 继续进入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());  
        }  
        
        // 为了线程能使用同一个 currentResources  
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
        if (currentResources == null) {  
            currentResources = new HashSet<EncodedResource>(4);  
            this.resourcesCurrentlyBeingLoaded.set(currentResources);  
        }  
        
        // 这里其实就是为了避免循环加载,如果重复加载了相同的文件就会抛出异常  
        if (!currentResources.add(encodedResource)) {  
            throw new BeanDefinitionStoreException(  
                    "Detected recursive 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());  
                }  
                // 从资源文件中加载beanDefinitons信息
                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.set(null);  
            }  
        }  
    }  

其实这个方法的关键方法就是doLoadBeanDefinitions(inputSource, encodedResource.getResource()),真正开始进入读取文件。

2.2.3 进入doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法

	/**
	 * 真正从指定的XML文件加载beanDefinition信息的方法
	 * Actually load bean definitions from the specified XML file.
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
		    // 将xml文件信息读取到document类中
			Document doc = doLoadDocument(inputSource, resource);
			// 注册DOM文档中包含的beanDefinition
			return registerBeanDefinitions(doc, resource);
		}
		// 省略异常处理代码
		...
	}

2.2.4 进入registerBeanDefinitions方法

在这个方法里创建了BeanDefinitionDocumentReader,解析、注册BeanDefinition的工作又委托给了它

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        documentReader.setEnvironment(getEnvironment());
        //获取BeanDefinition的registry对象,DefaultListableBeanFactory,初始化XmlBeanDefinitionReader时进行的设置
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        //BeanDefinition读取过程中传递的上下文,封装相关的的配置和状态
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }

继续解析

     /**
     * 根据给定的root中注册每个beanDefinition
     */
    protected void doRegisterBeanDefinitions(Element root) {
        //beans标签的profile属性
        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;
            }
        }

        // 创建一个代理类,用于解析xml文件
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(this.readerContext, root, parent);

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

        this.delegate = parent;
    }

方法开始判断是否包含profile属性,如果存在校验环境变量是进行了设置。profile的作用类似maven的profile,可以做到开发、测试、生产环境的切换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值