• Spring IOC源码:obtainFreshBeanFactory 详解(上)


    Spring源码系列:

    Spring IOC源码:简单易懂的Spring IOC 思路介绍
    Spring IOC源码:核心流程介绍
    Spring IOC源码:ApplicationContext刷新前准备工作
    Spring IOC源码:obtainFreshBeanFactory 详解(上)
    Spring IOC源码:obtainFreshBeanFactory 详解(中)
    Spring IOC源码:obtainFreshBeanFactory 详解(下)
    Spring IOC源码:<context:component-scan>源码详解
    Spring IOC源码:invokeBeanFactoryPostProcessors 后置处理器详解
    Spring IOC源码:registerBeanPostProcessors 详解
    Spring IOC源码:实例化前的准备工作
    Spring IOC源码:finishBeanFactoryInitialization详解
    Spring IoC源码:getBean 详解
    Spring IoC源码:createBean( 上)
    Spring IoC源码:createBean( 中)
    Spring IoC源码:createBean( 下)
    Spring IoC源码:finishRefresh 完成刷新详解

    前言

    上篇文章我们讲解了refresh()方法前的准备工作,主要是初始化一些缓存容器、环境属性,还有对配置文件路径进行解析,查找和替换占位符等。Spring IOC源码:ApplicationContext刷新前准备工作
    这节文章介绍refresh方法中的prepareRefresh()跟obtainFreshBeanFactory()方法。prepareRefresh比较简单主要是设置一下上下文的状态、开始时间等。obtainFreshBeanFactory是一个比较核心的方法,主要解析配置文件,封装成BeanDefinition,并放入上下文缓存中。
    几个主要的缓存:
    beanDefinitionNames缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 集合,后面实例化遍历此集合。
    beanDefinitionMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和 BeanDefinition 映射,后续通过beanName获取。
    aliasMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和别名映射。

    正文

    方法1:prepareRefresh

    	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();
    		
    		//下面是初始化监听器和事件源集合
    		// 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<>();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    initPropertySources拓展方法案例:

        @Override
        protected void initPropertySources() {
            System.out.println("扩展initPropertySource");
            //这里添加了一个name属性到Environment里面,以方便我们在后面用到
            getEnvironment().getSystemProperties().put("name","bobo");
            //这里要求Environment中必须包含username属性,如果不包含,则抛出异常
            getEnvironment().setRequiredProperties("username");
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    方法2:obtainFreshBeanFactory

    	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    		refreshBeanFactory();
    		return getBeanFactory();
    	}
    
    • 1
    • 2
    • 3
    • 4

    重点在于refreshBeanFactory方法,见方法3

    方法3:refreshBeanFactory

    	protected final void refreshBeanFactory() throws BeansException {
    		//判断一下当前上下文是否有实例化beanFactory,存在则销毁,后面会重新构建
    		if (hasBeanFactory()) {
    			destroyBeans();
    			closeBeanFactory();
    		}
    		try {
    			//实例化一个beanFactory
    			DefaultListableBeanFactory beanFactory = createBeanFactory();
    			//设置ID
    			beanFactory.setSerializationId(getId());
    			// 定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖.属于拓展方法
    			customizeBeanFactory(beanFactory);
    			// 初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析
    			//封装BeanDefinition对象用于后续实例化初始化等工作做准备
    			loadBeanDefinitions(beanFactory);
    			synchronized (this.beanFactoryMonitor) {
    				this.beanFactory = beanFactory;
    			}
    		}
    		catch (IOException ex) {
    			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    createBeanFactory(),实例化BeanFactory工厂对象,见方法4
    customizeBeanFactory(beanFactory),设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖. 见方法
    loadBeanDefinitions(beanFactory),见方法7详解

    方法4:createBeanFactory

    	protected DefaultListableBeanFactory createBeanFactory() {
    		return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    	}
    
    • 1
    • 2
    • 3

    new DefaultListableBeanFactory:见方法5
    getInternalParentBeanFactory:获取父类内部的BeanFactory,如果该父类实现了ConfigurableApplicationContext ,则返回其BeanFactory属性,不然返回父类本身。这里因为父类也是AbstractApplicationContext的子类,而AbstractApplicationContext又实现了BeanFactoty,所以本身也是一个BeanFactoty。
    在这里插入图片描述

    	protected BeanFactory getInternalParentBeanFactory() {
    		return (getParent() instanceof ConfigurableApplicationContext ?
    				((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent());
    	}
    
    • 1
    • 2
    • 3
    • 4

    方法5:new DefaultListableBeanFactory

    实例化一个BeanFactory时,会调用多层父类的构造方法。
    调用父类AbstractAutowireCapableBeanFactory有参构造方法时,会父类的BeanFactory设置到当前属性值中

    	public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    		this();
    		setParentBeanFactory(parentBeanFactory);
    	}
    
    • 1
    • 2
    • 3
    • 4

    调用父类AbstractAutowireCapableBeanFactory无参构造方法时,会设置忽略些属性值

    	public AbstractAutowireCapableBeanFactory() {
    		super();
    		//忽略属性注入时,不设置该值。一般是通过postProcessor进行注入的
    		ignoreDependencyInterface(BeanNameAware.class);
    		ignoreDependencyInterface(BeanFactoryAware.class);
    		ignoreDependencyInterface(BeanClassLoaderAware.class);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    方法6:customizeBeanFactory

    这个方法可以在子类中进行拓展,设置其属性值,案例如下:l

        @Override
        protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
            super.setAllowBeanDefinitionOverriding(false);
            super.setAllowCircularReferences(false);
            super.customizeBeanFactory(beanFactory);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    方法7:loadBeanDefinitions

    这个方法比较复杂,主要是用来解析配置、封装等;

    	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    		// 将BeanDefinition封装成XmlBeanDefinitionReader,后续对其操作都是使用XmlBeanDefinitionReader
    		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
    		//给其设置些属性
    		beanDefinitionReader.setEnvironment(this.getEnvironment());
    		beanDefinitionReader.setResourceLoader(this);
    		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
    		//提供拓展方法,供子类进行拓展,可以对beanDefinitionReader进行一些初始化操作
    		initBeanDefinitionReader(beanDefinitionReader);
    		//解析配置
    		loadBeanDefinitions(beanDefinitionReader);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    loadBeanDefinitions(beanDefinitionReader);见方法8

    方法8:loadBeanDefinitions

    这里我们可以看都有两个配置文件格式,这些都是在进入refresh前进行占位符替换,赋值的。不管是哪种格式,最终都是以Resource格式进行的,String数组在后续步骤中也会转为Resource;接下来我们看下reader.loadBeanDefinitions(configLocations),见方法9

    	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    		//获取配置文件路径,refresh方法前做的操作,这里有两个类型一种是Resource一种是String
    		Resource[] configResources = getConfigResources();
    		if (configResources != null) {
    			reader.loadBeanDefinitions(configResources);
    		}
    		String[] configLocations = getConfigLocations();
    		if (configLocations != null) {
    			reader.loadBeanDefinitions(configLocations);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    方法9:loadBeanDefinitions

    	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    		Assert.notNull(locations, "Location array must not be null");
    		int count = 0;
    		for (String location : locations) {
    			count += loadBeanDefinitions(location);
    		}
    		return count;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    遍历执行loadBeanDefinitions(location);

    	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    		return loadBeanDefinitions(location, null);
    	}
    
    • 1
    • 2
    • 3

    再接下看loadBeanDefinitions(location, null);

    	public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    		//获取上下文的资源加载器
    		ResourceLoader resourceLoader = getResourceLoader();
    		if (resourceLoader == null) {
    			throw new BeanDefinitionStoreException(
    					"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
    		}
    		//判断加载器类型
    		if (resourceLoader instanceof ResourcePatternResolver) {
    			// Resource pattern matching available.
    			try {
    				//将传进来的配置文件路径进行解析,判断路径中是否含有classPath*: 或war:等,对其解析成多个Resource
    				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
    				//解析处理
    				int count = loadBeanDefinitions(resources);
    				if (actualResources != null) {
    					Collections.addAll(actualResources, resources);
    				}
    				if (logger.isTraceEnabled()) {
    					logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
    				}
    				return count;
    			}
    			catch (IOException ex) {
    				throw new BeanDefinitionStoreException(
    						"Could not resolve bean definition resource pattern [" + location + "]", ex);
    			}
    		}
    		else {
    			// 通过路径封装成一个Resource 
    			Resource resource = resourceLoader.getResource(location);
    			int count = loadBeanDefinitions(resource);
    			if (actualResources != null) {
    				actualResources.add(resource);
    			}
    			if (logger.isTraceEnabled()) {
    				logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
    			}
    			return count;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    封装完Resource后,继续进行配置文件的解析操作 loadBeanDefinitions(resources);

    	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);
    		}
    		//加载获取EncodedResource,如果为空则创建一个空集合
    		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    		if (currentResources == null) {
    			currentResources = new HashSet<>(4);
    			this.resourcesCurrentlyBeingLoaded.set(currentResources);
    		}
    		if (!currentResources.add(encodedResource)) {
    			throw new BeanDefinitionStoreException(
    					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    		}
    		try {
    			//获取到当前Resource的输入流
    			InputStream inputStream = encodedResource.getResource().getInputStream();
    			try {
    				//封装成InputSource,方便对其类型进行解析
    				InputSource inputSource = new InputSource(inputStream);
    				if (encodedResource.getEncoding() != null) {
    					inputSource.setEncoding(encodedResource.getEncoding());
    				}
    				//在Spring中看到do开头的,代表是真正的逻辑处理的地方了
    				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.remove();
    			}
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 见方法10

    方法10:doLoadBeanDefinitions

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    			throws BeanDefinitionStoreException {
    
    		try {
    			//根据inputSource和resource加载XML文件,并封装成Document
    			Document doc = doLoadDocument(inputSource, resource);
    			//解析并封装和注册成BeanDefinition
    			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);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    doLoadDocument(inputSource, resource);见方法11
    registerBeanDefinitions(doc, resource);见方法12

    方法11:doLoadBeanDefinitions

    
    	protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    	   // getValidationModeForResource(resource): 获取XML配置文件的验证模式
         // documentLoader.loadDocument: 加载XML文件,解析封装成 Document
    		return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
    				getValidationModeForResource(resource), isNamespaceAware());
    	}
    
    	
    	protected int getValidationModeForResource(Resource resource) {
    		//获取当前的验证模式标识
    		int validationModeToUse = getValidationMode();
    		//如果为指定则返回指定的
    		if (validationModeToUse != VALIDATION_AUTO) {
    			return validationModeToUse;
    		}
    		//解析出配置文件的验证模式
    		int detectedMode = detectValidationMode(resource);
    		//如果检测出的验证模式不为 VALIDATION_AUTO, 则返回检测出来的验证模式
    		if (detectedMode != VALIDATION_AUTO) {
    			return detectedMode;
    		}
    		// Hmm, we didn't get a clear indication... Let's assume XSD,
    		// since apparently no DTD declaration has been found up until
    		// detection stopped (before finding the document's root tag).
    		return VALIDATION_XSD;
    	}
    	
    	public int getValidationMode() {
    		return this.validationMode;
    	}
    	
    	protected int detectValidationMode(Resource resource) {
    		if (resource.isOpen()) {
    			throw new BeanDefinitionStoreException(
    					"Passed-in Resource [" + resource + "] contains an open stream: " +
    					"cannot determine validation mode automatically. Either pass in a Resource " +
    					"that is able to create fresh streams, or explicitly specify the validationMode " +
    					"on your XmlBeanDefinitionReader instance.");
    		}
    
    		InputStream inputStream;
    		try {
    			inputStream = resource.getInputStream();
    		}
    		catch (IOException ex) {
    			throw new BeanDefinitionStoreException(
    					"Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
    					"Did you attempt to load directly from a SAX InputSource without specifying the " +
    					"validationMode on your XmlBeanDefinitionReader instance?", ex);
    		}
    
    		try {
    			//解析检查出当前的验证标识
    			return this.validationModeDetector.detectValidationMode(inputStream);
    		}
    		catch (IOException ex) {
    			throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
    					resource + "]: an error occurred whilst reading from the InputStream.", ex);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    一般Spring常用的验证文件是声明方法有两种DTD和XSD,DTD在Spring中已经停止使用了

    DTD:(Document type Definition )文档类型定义,是一种xml约束模式语言,是xml文件的验证机制,属于xml文件组成的一部分。保证xml文档格式正确的有效方法。

    XSD:(XML Schemas Definition),描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查XML文档是否符合要求。
    获取各个参数完成后,进入加载Document步骤

    	@Override
    	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
    			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    		//创建DocumentBuilderFactory 工厂对象
    		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    		if (logger.isTraceEnabled()) {
    			logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
    		}
    		//创建解析器
    		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    		// 使用DocumentBuilder解析inputSource返回Document对象
    		return builder.parse(inputSource);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    方法12:registerBeanDefinitions

    	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    		//通过反射创建默认的DefaultBeanDefinitionDocumentReader,其为BeanDefinitionDocumentReader的子类
    		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    		//记录统计前BeanDefinition的加载个数
    		int countBefore = getRegistry().getBeanDefinitionCount();
    		//封装BeanDefinition对象,并加入缓存集合中
    		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    		return getRegistry().getBeanDefinitionCount() - countBefore;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));执行前需要获取参数值,先看下该方法逻辑createReaderContext(resource);

    	//创建XmlReaderContext ,用于解析配置文件
    	public XmlReaderContext createReaderContext(Resource resource) {
    		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
    				this.sourceExtractor, this, getNamespaceHandlerResolver());
    	}
    
    	//获取命名空间
    	public NamespaceHandlerResolver getNamespaceHandlerResolver() {
    		if (this.namespaceHandlerResolver == null) {
    			this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
    		}
    		return this.namespaceHandlerResolver;
    	}
    
    	//实例化默认的命名空间
    	protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
    		ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
    		return new DefaultNamespaceHandlerResolver(cl);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    命名空间记录了加载配置文件的解析器类路径,我们在解析文件的时候,不同的xml文件解析可能不一样的,那如果使用不同的处理器来解析呢,这里就需要用到命名空间了,根据配置文件的命名空间来找到命名空间文件,该文件记录了解析该配置文件的多个解析器类路径。Spring需要用这些来解析处理,所以我们需要先获取。
    在这里插入图片描述
    在这里插入图片描述接下来进入documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

    	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    		this.readerContext = readerContext;
    		doRegisterBeanDefinitions(doc.getDocumentElement());
    	}
    
    • 1
    • 2
    • 3
    • 4

    看到do开头,就证明是真正开始核心处理的地方doRegisterBeanDefinitions 见方法13

    	protected void doRegisterBeanDefinitions(Element root) {
    
    		BeanDefinitionParserDelegate parent = this.delegate;
    		this.delegate = createDelegate(getReaderContext(), root, parent);
    		//校验root节点的命名空间是否为默认的命名空间(默认命名空间http://www.springframework.org/schema/beans)
    		if (this.delegate.isDefaultNamespace(root)) {
    			//获取profile属性
    			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    			if (StringUtils.hasText(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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    profile属性主要用于环境开发,比如xml文件中不同环境dev、sit、uat、prod设置不同的配置。
    进入 parseBeanDefinitions(root, this.delegate)方法

    	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);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这里首先会判断当前配置的命名空间,如果不是默认的,则走自定义的逻辑,如 。否则是默认命令空间,则获取其子元素遍历判断,调用其默认处理逻辑,如 。
    parseDefaultElement(ele, delegate) 和 delegate.parseCustomElement(ele) 方法是解析 bean 定义的两个核心方法,在后续文章后会对其进行讲解。

    总结

    这节内容主要讲解了配置文件解析前的处理及解析处理过程:

    1. 创建一个新的 BeanFactory:DefaultListableBeanFactory。
    2. 根据 web.xml 中 contextConfigLocation 配置的路径,读取 Spring配置文件,验证配置文件的内容,并封装成 Resource。
    3. 根据 Resource 加载 XML 配置文件,并解析成 Document 对象 。
    4. 拿到 Document 中的根节点,遍历根节点和所有子节点。

    对于xml文件有默认处理方法和自定义处理方法,Spring会根据命名空间判断选择不同的对应逻辑。

  • 相关阅读:
    文件翻译-免费文件翻译-批量文件翻译软件大全
    OptaPlanner 发展方向与问题
    RocketMQ 数据持久化、高可用、高性能、读写原理、扩容总结
    Android开发知识学习——HTTP基础
    SQL基础
    【小沐学Unity3d】3ds Max 骨骼动画制作(CAT、Character Studio、Biped、骨骼对象)
    企业架构LNMP学习笔记40
    Flink总结
    elasticsearch常用接口和基本操作
    基于Python的智能视频分析之人数统计的多种实现
  • 原文地址:https://blog.csdn.net/weixin_45031612/article/details/127718650