SpringMVC 源代码深度解析 IOC容器(Bean 解析、注册)

      SpringMVC通过一个配置文件描述Bean以及Bean之间的依赖关系,利用Java的反射机制实例化Bean并建立Bean之间的依赖关系。IOC容器在完成这些底层工作的基础还提供了Bean的实例缓、生命周期、Bean实例代理等。BeanFacorySpringMVC框架最核心的接口,他提供了最高级IOC的配置机制。ApplicationContextBeanFactory派生而来,这也说明了 springMVC容器中运行的主体对象是 Bean,另外 ApplicationContext 继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源,提供了更多面向实际应用的功能。SpringMVC初始化时在什么时候读取配置我们配置好的bean的文件,怎么解析和注册Bean接下来我们带着问题来分析。 

   上一篇,我们在介绍DispatcherServlet初始化的介绍父类FrameworkServlet在创建上下文时,调用了一个重启上下文时,并初始化Bean。如图所示:

   


  

ConfigurableWebApplicationContext wac;调用了wac.refresh();方法,完成了对ApplicationContext的初始化,注册啊,封装bean的工作,ConfigurableWebApplicationContext 的子类AbstractApplicationContext实现了bean初始化。我们来了解一下上下文(容器)整个继承关系。这边的箭头表示继承或者实现的,如图所示:


    从上图我们可以看出,ApplicationContext也是继承BeanFactoryApplicationContext 也继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源,提供了更多面向实际应用的功能,我们通过refresh为入口点,分析Bean整个初始化过程,并了解ApplicationContext的子类AbstractApplicationContext做了哪些工作并了解父类实现了哪些功能?每个接口都有他使用的场合,它主要是为了区分在 SpringMVC内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。我们现在先头脑中有这个继承的关系图,以后深度的了解,比较不会那么晕,这样思路就比较清晰,我们以refresh为入口点,带着这些问题进行解析。

  refresh方法的源代码如下:

  

  1. public void refresh() throws BeansException, IllegalStateException {  
  2.         synchronized (this.startupShutdownMonitor) {  
  3.             // 准备对上下文进行刷新  
  4.             prepareRefresh();  
  5.             // 初始化beanFactory   
  6.             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  7.             // 为BeanFactory配置容器特性,例如类加载器、事件处理器等  
  8.             prepareBeanFactory(beanFactory);  
  9.             try {  
  10.                 // 为容器的某些子类指定特殊的BeanPost事件处理器  
  11.                 postProcessBeanFactory(beanFactory);  
  12.                 // 调用beanFactory处理器  
  13.                 invokeBeanFactoryPostProcessors(beanFactory);  
  14.                 // 注册beanFactory处理器  
  15.                 registerBeanPostProcessors(beanFactory);  
  16.                 // 初始化消息源  
  17.                 initMessageSource();  
  18.                 // 初始化上下文事件广播.  
  19.                 initApplicationEventMulticaster();  
  20.                 // 初始化其它特殊bean,由子类来实现  
  21.                 onRefresh();  
  22.                 // 注册事件监听器.  
  23.                 registerListeners();  
  24.                 // 初始化所有单实例的bean,使用懒加载除外  
  25.                 finishBeanFactoryInitialization(beanFactory);  
  26.                 // 完成刷新并发布容器刷新事件  
  27.                 finishRefresh();  
  28.             }catch (BeansException ex) {  
  29.                 // Destroy already created singletons to avoid dangling resources.  
  30.                 destroyBeans();  
  31.                 // Reset 'active' flag.  
  32.                 cancelRefresh(ex);  
  33.                 // Propagate exception to caller.  
  34.                 throw ex;  
  35.             }  
  36.         }  
  37.     }  
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 准备对上下文进行刷新
			prepareRefresh();
			// 初始化beanFactory 
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// 为BeanFactory配置容器特性,例如类加载器、事件处理器等
			prepareBeanFactory(beanFactory);
			try {
				// 为容器的某些子类指定特殊的BeanPost事件处理器
				postProcessBeanFactory(beanFactory);
				// 调用beanFactory处理器
				invokeBeanFactoryPostProcessors(beanFactory);
				// 注册beanFactory处理器
				registerBeanPostProcessors(beanFactory);
				// 初始化消息源
				initMessageSource();
				// 初始化上下文事件广播.
				initApplicationEventMulticaster();
				// 初始化其它特殊bean,由子类来实现
				onRefresh();
				// 注册事件监听器.
				registerListeners();
				// 初始化所有单实例的bean,使用懒加载除外
				finishBeanFactoryInitialization(beanFactory);
				// 完成刷新并发布容器刷新事件
				finishRefresh();
			}catch (BeansException ex) {
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();
				// Reset 'active' flag.
				cancelRefresh(ex);
				// Propagate exception to caller.
				throw ex;
			}
		}
	}
  这个方法做了很多事情,主要有初始化 beanFactory 、注册 BeanFactory处理器等工作。我们接下来对里面重要的功能进行解析,它怎么读取我们配置文件并解析文件和注册配置文件里的bean。

     初始化beanFactory工厂时,就对读取我们配置文件并解析文件和注册配置文件里的Bean,是由AbstractApplicationContext.obtainFreshBeanFactory方法实现的,源代码如下:

     

  1. **  
  2.      * Tell the subclass to refresh the internal bean factory.  
  3.      * @return the fresh BeanFactory instance  
  4.      * @see #refreshBeanFactory()  
  5.      * @see #getBeanFactory()  
  6.      */  
  7.     protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {  
  8.         refreshBeanFactory();  
  9.         ConfigurableListableBeanFactory beanFactory = getBeanFactory();  
  10.         if (logger.isDebugEnabled()) {  
  11.             logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);  
  12.         }  
  13.         return beanFactory;  
  14.     }  
**
	 * Tell the subclass to refresh the internal bean factory.
	 * @return the fresh BeanFactory instance
	 * @see #refreshBeanFactory()
	 * @see #getBeanFactory()
	 */
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}
  1. @Override  
  2.     protected final void refreshBeanFactory() throws BeansException {  
  3.         if (hasBeanFactory()) {  
  4.             destroyBeans();  
  5.             closeBeanFactory();  
  6.         }  
  7.         try {  
  8.             DefaultListableBeanFactory beanFactory = createBeanFactory();  
  9.             beanFactory.setSerializationId(getId());  
  10.             customizeBeanFactory(beanFactory);  
  11.             loadBeanDefinitions(beanFactory);  
  12.             synchronized (this.beanFactoryMonitor) {  
  13.                 this.beanFactory = beanFactory;  
  14.             }  
  15.         }  
  16.         catch (IOException ex) {  
  17.             throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);  
  18.         }  
  19.     }  
@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
   第一步:

     refreshBeanFactory 先销毁所有Bean,关闭BeanFactory,然后重新创建一个BeanFactory,并将其赋给BeanFactory实例变量。我们先看一下它怎么创建BeanFactory? 

    createBeanFactory这个方法是创建新的BeanBeanFactory ,BeanFactory 的原始对象是 DefaultListableBeanFactory,这个非常关键,因为他设计到后面对这个对象的多种操作,DefaultListableBeanFactory是整个Bean加载的核心部分,是SpringMVC注册及加载Bean的默认实现。

    

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

   初始化BeanFactory时,判断是否有父BeanFactory,如果有设置,如果没有就为null

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

 获取父BeanFactory

  

  1. public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {  
  2. super(parentBeanFactory);  
  3. }  
  4. public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {  
  5. this();  
  6. setParentBeanFactory(parentBeanFactory);  
  7. }  
public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
super(parentBeanFactory);
}
public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {
this();
setParentBeanFactory(parentBeanFactory);
}

setParentBeanFactory设置父BeanFactory

我们对BeanFactory继承关系了解一下,为后面的解析提供比较清晰的路线,如图所示:

  

  现在BeanFactory创建好了, ApplicationContext 继承了BeanFactory,还 继承了  ResourceLoader  接口,使得  ApplicationContext  可以访问到任何外部资源, 提供了更多面向实际应用的功能,接下来我们进行第二步,分析解析配置文件和注册Bean。

 第二步:先转码在读取配置文件

     loadBeanDefinitions(beanFactory) 将找到答案,这个方法将开始加载、解析 Bean 的定义,也就是把用户定义的数据结构转化为 Ioc 容器中的特定数据结构。我们要分析一下这个方法。

       

  1. @Override  
  2.     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {  
  3.         // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
  4.         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
  5.   
  6.         // Configure the bean definition reader with this context's  
  7.         // resource loading environment.  
  8.         beanDefinitionReader.setEnvironment(this.getEnvironment());  
  9.         beanDefinitionReader.setResourceLoader(this);  
  10.         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
  11.   
  12.         // Allow a subclass to provide custom initialization of the reader,  
  13.         // then proceed with actually loading the bean definitions.  
  14.         initBeanDefinitionReader(beanDefinitionReader);  
  15.         loadBeanDefinitions(beanDefinitionReader);  
  16.     }  
@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}
 

   XmlBeanDefinitionReader 来读取并解析 xml 文件,XmlBeanDefinitionReader 是 BeanDefinitionReader 接口的实现。我们来看一下继承关系,如图所示:

      

 

   BeanDefinitionReader 读取Resource所指向的配置文件资源,然后解析配置文件,配置文件中每一个Bean解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中。我们这边先了解一下。先实现通过BeanFactory创建XmlBeanDefinitionReader对象,然后XmlBeanDefinitionReader 配置ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用,ResourceLoader对资源的加载。 如图所示:

  



我们现在获取外面的资源也设置好了,我们进行深入解析,loadBeanDefinitions方法源代码如下:

  

  1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {  
  2.         String[] configLocations = getConfigLocations();  
  3.         if (configLocations != null) {  
  4.             for (String configLocation : configLocations) {  
  5.                 reader.loadBeanDefinitions(configLocation);  
  6.             }  
  7.         }  
  8.     }  
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				reader.loadBeanDefinitions(configLocation);
			}
		}
	}
  1. @Override  
  2.     public String[] getConfigLocations() {  
  3.         return super.getConfigLocations();  
  4.     }  
@Override
	public String[] getConfigLocations() {
		return super.getConfigLocations();
	}
  1. protected String[] getConfigLocations() {  
  2.         return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());  
  3.     }  
protected String[] getConfigLocations() {
		return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
	}

   由 AbstractRefreshableWebApplicationContext父类AbstractRefreshableConfigApplicationContext实现getConfigLocations方法,获取了web.xml配置的param-value的值,然后getConfigLocations保存在数组里面。如图所示:



现在获取了param-value的值,接下去就开始读取配置文件,具体实现时由XmlBeanDefinitionReader父类AbstractBeanDefinitionReader实现的,

reader.loadBeanDefinitions(configLocation);源代码:

  

  1. public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {  
  2.     return loadBeanDefinitions(location, null);  
  3. }  
  4. public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {  
  5.     ResourceLoader resourceLoader = getResourceLoader();  
  6.     if (resourceLoader == null) {  
  7.         throw new BeanDefinitionStoreException(  
  8.                 "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");  
  9.     }  
  10.   
  11.     if (resourceLoader instanceof ResourcePatternResolver) {  
  12.         // Resource pattern matching available.  
  13.         try {  
  14.             Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);  
  15.             int loadCount = loadBeanDefinitions(resources);  
  16.             if (actualResources != null) {  
  17.                 for (Resource resource : resources) {  
  18.                     actualResources.add(resource);  
  19.                 }  
  20.             }  
  21.             if (logger.isDebugEnabled()) {  
  22.                 logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");  
  23.             }  
  24.             return loadCount;  
  25.         }  
  26.         catch (IOException ex) {  
  27.             throw new BeanDefinitionStoreException(  
  28.                     "Could not resolve bean definition resource pattern [" + location + "]", ex);  
  29.         }  
  30.     }  
  31.     else {  
  32.         // Can only load single resources by absolute URL.  
  33.         Resource resource = resourceLoader.getResource(location);  
  34.         int loadCount = loadBeanDefinitions(resource);  
  35.         if (actualResources != null) {  
  36.             actualResources.add(resource);  
  37.         }  
  38.         if (logger.isDebugEnabled()) {  
  39.             logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");  
  40.         }  
  41.         return loadCount;  
  42.     }  
  43. }  
 public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}
 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			Resource resource = resourceLoader.getResource(location);
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}
  这个方法有一个ResourcePatternResolver加载了 web.xml配置的 param-value对应的资源,例如 classpath:xxx*.xml 加载了 xxx下为 xml为后缀的文件资源, loadBeanDefinitions进行对加载进来的资源进行解析。在解析之前先进行转码,是通过 EncodedResource 对资源文件进行转码,源代码如下: 

  1. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {  
  2.         return loadBeanDefinitions(new EncodedResource(resource));  
  3.     }  
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}
   对 资源文件转码完,就开始读进来,以流的形式,源代码如下:

    

  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
  2.         Assert.notNull(encodedResource, "EncodedResource must not be null");  
  3.         if (logger.isInfoEnabled()) {  
  4.             logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
  5.         }  
  6.   
  7.         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
  8.         if (currentResources == null) {  
  9.             currentResources = new HashSet<EncodedResource>(4);  
  10.             this.resourcesCurrentlyBeingLoaded.set(currentResources);  
  11.         }  
  12.         if (!currentResources.add(encodedResource)) {  
  13.             throw new BeanDefinitionStoreException(  
  14.                     "Detected cyclic loading of " + encodedResource + " - check your import definitions!");  
  15.         }  
  16.         try {  
  17.             InputStream inputStream = encodedResource.getResource().getInputStream();  
  18.             try {  
  19.                 InputSource inputSource = new InputSource(inputStream);  
  20.                 if (encodedResource.getEncoding() != null) {  
  21.                     inputSource.setEncoding(encodedResource.getEncoding());  
  22.                 }  
  23.                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
  24.             }  
  25.             finally {  
  26.                 inputStream.close();  
  27.             }  
  28.         }  
  29.         catch (IOException ex) {  
  30.             throw new BeanDefinitionStoreException(  
  31.                     "IOException parsing XML document from " + encodedResource.getResource(), ex);  
  32.         }  
  33.         finally {  
  34.             currentResources.remove(encodedResource);  
  35.             if (currentResources.isEmpty()) {  
  36.                 this.resourcesCurrentlyBeingLoaded.remove();  
  37.             }  
  38.         }  
  39.     }  
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());
		}

		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 cyclic 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());
				}
				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();
			}
		}
	}

  说明:

   (1getValidationModeForResource(resource);获取对XML文件的验证模式

   (2Document doc = this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());加载XML文件,并获取对应的Document。这边是把配置文件读进来,以Document 形式存储。

   (3registerBeanDefinitions(doc, resource);解析并注册bean

        第三步:解析并注册bean,解析 Bean 的定义,也就是把用户定义的数据结构转化为 Ioc 容器中的特定数据结构。

      我们继续第二步中先把文件读进来,接下来进行解析和注册是由registerBeanDefinitions(doc, resource);这个方法实现的,源代码如下:

        

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {  
  2.         BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();  
  3.         documentReader.setEnvironment(this.getEnvironment());  
  4.         int countBefore = getRegistry().getBeanDefinitionCount();//<span style="color:#ff0000;">统计前BeanDefinition加载个数</span>  
  5.         documentReader.registerBeanDefinitions(doc, createReaderContext(resource));  
  6.         return getRegistry().getBeanDefinitionCount() - countBefore;  
  7.     }  
  8. <span style="color:#ff0000;">//解析并注册bean</span>  
  9. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {  
  10.         this.readerContext = readerContext;  
  11.         logger.debug("Loading bean definitions");  
  12.         Element root = doc.getDocumentElement();  
  13.         doRegisterBeanDefinitions(root);  
  14.     }  
  15.   
  16. protected void doRegisterBeanDefinitions(Element root) {  
  17.         String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);  
  18.         if (StringUtils.hasText(profileSpec)) {  
  19.             Assert.state(this.environment != null"Environment must be set for evaluating profiles");  
  20.             String[] specifiedProfiles = StringUtils.tokenizeToStringArray(  
  21.                     profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);  
  22.             if (!this.environment.acceptsProfiles(specifiedProfiles)) {  
  23.                 return;  
  24.             }  
  25.         }  
  26.         BeanDefinitionParserDelegate parent = this.delegate;  
  27.         this.delegate = createDelegate(this.readerContext, root, parent);  
  28.         preProcessXml(root);  
  29.         parseBeanDefinitions(root, this.delegate);  
  30.         postProcessXml(root);  
  31.         this.delegate = parent;  
  32.     }  
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.setEnvironment(this.getEnvironment());
		int countBefore = getRegistry().getBeanDefinitionCount();//<span style="color:#ff0000;">统计前BeanDefinition加载个数</span>
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
<span style="color:#ff0000;">//解析并注册bean</span>
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}

protected void doRegisterBeanDefinitions(Element root) {
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			if (!this.environment.acceptsProfiles(specifiedProfiles)) {
				return;
			}
		}
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(this.readerContext, root, parent);
		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);
		this.delegate = parent;
	}
 

  说明:

    (1public static final String PROFILE_ATTRIBUTE = "profile";判断bean节点是否定义了profile属性,如果有,就要到环境变量中去寻找

    (2BeanDefinitionParserDelegate是对BeanDefinition的解析的,这里就不具体分析了。

   (2) parseBeanDefinitions(root, this.delegate);bean注册到BeanDefinition对象中。

  这个 parseBeanDefinitions(root, this.delegate)开始注册了,我们解析到重点了,源代码如下:

    

  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {  
  2.         if (delegate.isDefaultNamespace(root)) {  
  3.             NodeList nl = root.getChildNodes();  
  4.             for (int i = 0; i < nl.getLength(); i++) {  
  5.                 Node node = nl.item(i);  
  6.                 if (node instanceof Element) {  
  7.                     Element ele = (Element) node;  
  8.                     if (delegate.isDefaultNamespace(ele)) {  
  9.                         parseDefaultElement(ele, delegate);  
  10.                     }  
  11.                     else {  
  12.                         delegate.parseCustomElement(ele);  
  13.                     }  
  14.                 }  
  15.             }  
  16.         }  
  17.         else {  
  18.             delegate.parseCustomElement(root);  
  19.         }  
  20.     }  
  21. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {  
  22.        //对IMPORT标签进行处理的  
  23.         if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {  
  24.             importBeanDefinitionResource(ele);  
  25.         }  
  26.         //对ALIAS标签进行处理的  
  27.         else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {  
  28.             processAliasRegistration(ele);  
  29.         }  
  30.         //对BEAN标签进行处理的  
  31.         else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {  
  32.             processBeanDefinition(ele, delegate);  
  33.         }  
  34.         else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {  
  35.             // recurse  
  36.             doRegisterBeanDefinitions(ele);  
  37.         }  
  38.     }  
  39. //我们来对处理bean标签进行分析  
  40.   protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {  
  41.         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);  
  42.         if (bdHolder != null) {  
  43.             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);  
  44.             try {  
  45.                 // Register the final decorated instance.  
  46.                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  
  47.             }  
  48.             catch (BeanDefinitionStoreException ex) {  
  49.                 getReaderContext().error("Failed to register bean definition with name '" +  
  50.                         bdHolder.getBeanName() + "'", ele, ex);  
  51.             }  
  52.             // Send registration event.  
  53.             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));  
  54.         }  
  55.     }  
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);
		}
	}
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);
		}
		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.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}
 

 解析Beanidname等元素设置到BeanDefinition然后放到放到BeanDefinitionHolder中,这个是由parseBeanDefinitionElement进行处理的,源代码如下:

   
  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {  
  2.         //解析ID属性  
  3.         String id = ele.getAttribute(ID_ATTRIBUTE);  
  4.          //解析name属性  
  5.         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);  
  6.   
  7.         List<String> aliases = new ArrayList<String>();  
  8.         if (StringUtils.hasLength(nameAttr)) {  
  9.             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);  
  10.             aliases.addAll(Arrays.asList(nameArr));  
  11.         }  
  12.   
  13.         String beanName = id;  
  14.         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {  
  15.             beanName = aliases.remove(0);  
  16.             if (logger.isDebugEnabled()) {  
  17.                 logger.debug("No XML 'id' specified - using '" + beanName +  
  18.                         "' as bean name and " + aliases + " as aliases");  
  19.             }  
  20.         }  
  21.   
  22.         if (containingBean == null) {  
  23.             checkNameUniqueness(beanName, aliases, ele);  
  24.         }  
  25. //对其它标签进行解析  
  26.         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);  
  27.         if (beanDefinition != null) {  
  28.             if (!StringUtils.hasText(beanName)) {  
  29.                 try {  
  30.                     if (containingBean != null) {  
  31.                         beanName = BeanDefinitionReaderUtils.generateBeanName(  
  32.                                 beanDefinition, this.readerContext.getRegistry(), true);  
  33.                     }  
  34.                     else {  
  35.                         beanName = this.readerContext.generateBeanName(beanDefinition);  
  36.                         // Register an alias for the plain bean class name, if still possible,  
  37.                         // if the generator returned the class name plus a suffix.  
  38.                         // This is expected for Spring 1.2/2.0 backwards compatibility.  
  39.                         String beanClassName = beanDefinition.getBeanClassName();  
  40.                         if (beanClassName != null &&  
  41.                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&  
  42.                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {  
  43.                             aliases.add(beanClassName);  
  44.                         }  
  45.                     }  
  46.                     if (logger.isDebugEnabled()) {  
  47.                         logger.debug("Neither XML 'id' nor 'name' specified - " +  
  48.                                 "using generated bean name [" + beanName + "]");  
  49.                     }  
  50.                 }  
  51.                 catch (Exception ex) {  
  52.                     error(ex.getMessage(), ele);  
  53.                     return null;  
  54.                 }  
  55.             }  
  56.             String[] aliasesArray = StringUtils.toStringArray(aliases);  
  57.             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);  
  58.         }  
  59.   
  60.         return null;  
  61.     }  
  62. //对其它标签进行解析  
  63. public AbstractBeanDefinition parseBeanDefinitionElement(  
  64.             Element ele, String beanName, BeanDefinition containingBean) {  
  65.   
  66.         this.parseState.push(new BeanEntry(beanName));  
  67.         //对class属性进行解析  
  68.         String className = null;  
  69.         if (ele.hasAttribute(CLASS_ATTRIBUTE)) {  
  70.             className = ele.getAttribute(CLASS_ATTRIBUTE).trim();  
  71.         }  
  72.   
  73.         try {  
  74.             //对parent属性进行解析  
  75.             String parent = null;  
  76.             if (ele.hasAttribute(PARENT_ATTRIBUTE)) {  
  77.                 parent = ele.getAttribute(PARENT_ATTRIBUTE);  
  78.             }  
  79.             AbstractBeanDefinition bd = createBeanDefinition(className, parent);  
  80.              //解析默认bean的各种属性  
  81.             parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);  
  82.             bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));  
  83.             //解析元数据  
  84.             parseMetaElements(ele, bd);  
  85.             parseLookupOverrideSubElements(ele, bd.getMethodOverrides());  
  86.             parseReplacedMethodSubElements(ele, bd.getMethodOverrides());  
  87.             //解析构造函数  
  88.             parseConstructorArgElements(ele, bd);  
  89.             //解析Property  
  90.             parsePropertyElements(ele, bd);  
  91.             parseQualifierElements(ele, bd);  
  92.   
  93.             bd.setResource(this.readerContext.getResource());  
  94.             bd.setSource(extractSource(ele));  
  95.   
  96.             return bd;  
  97.         }  
  98.         catch (ClassNotFoundException ex) {  
  99.             error("Bean class [" + className + "] not found", ele, ex);  
  100.         }  
  101.         catch (NoClassDefFoundError err) {  
  102.             error("Class that bean class [" + className + "] depends on not found", ele, err);  
  103.         }  
  104.         catch (Throwable ex) {  
  105.             error("Unexpected failure during bean definition parsing", ele, ex);  
  106.         }  
  107.         finally {  
  108.             this.parseState.pop();  
  109.         }  
  110.   
  111.         return null;  
  112.     }  
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        //解析ID属性
		String id = ele.getAttribute(ID_ATTRIBUTE);
         //解析name属性
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<String>();
		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) {
			checkNameUniqueness(beanName, aliases, ele);
		}
//对其它标签进行解析
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
						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);
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}

		return null;
	}
//对其它标签进行解析
public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {

		this.parseState.push(new BeanEntry(beanName));
        //对class属性进行解析
		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}

		try {
            //对parent属性进行解析
			String parent = null;
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);
             //解析默认bean的各种属性
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            //解析元数据
			parseMetaElements(ele, bd);
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            //解析构造函数
			parseConstructorArgElements(ele, bd);
            //解析Property
			parsePropertyElements(ele, bd);
			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;
	}

  对beanidnameclass等属性的解析并设置到BeanDefinition然后放到放到BeanDefinitionHolder中,并放入到IOC容器中建立对应的数据结构。

   现在IOC容器中已经建立对应的数据结构,接下来开始对bean进行注册,

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());源代码如下:

   

  1. public static void registerBeanDefinition(  
  2.             BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)  
  3.             throws BeanDefinitionStoreException {  
  4.   
  5.         // Register bean definition under primary name.  
  6.         String beanName = definitionHolder.getBeanName();  
  7.         registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());  
  8.   
  9.         // Register aliases for bean name, if any.  
  10.         String[] aliases = definitionHolder.getAliases();  
  11.         if (aliases != null) {  
  12.             for (String aliase : aliases) {  
  13.                 registry.registerAlias(beanName, aliase);  
  14.             }  
  15.         }  
  16.     }  
  17.    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)  
  18.             throws BeanDefinitionStoreException {  
  19.         Assert.hasText(beanName, "Bean name must not be empty");  
  20.         Assert.notNull(beanDefinition, "BeanDefinition must not be null");  
  21.   
  22.         if (beanDefinition instanceof AbstractBeanDefinition) {  
  23.             try {  
  24.                 ((AbstractBeanDefinition) beanDefinition).validate();  
  25.             }  
  26.             catch (BeanDefinitionValidationException ex) {  
  27.                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,  
  28.                         "Validation of bean definition failed", ex);  
  29.             }  
  30.         }  
  31.         synchronized (this.beanDefinitionMap) {  
  32.             Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);  
  33.             if (oldBeanDefinition != null) {  
  34.                 if (!this.allowBeanDefinitionOverriding) {  
  35.                     throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,  
  36.                             "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +  
  37.                             "': There is already [" + oldBeanDefinition + "] bound.");  
  38.                 }  
  39.                 else {  
  40.                     if (this.logger.isInfoEnabled()) {  
  41.                         this.logger.info("Overriding bean definition for bean '" + beanName +  
  42.                                 "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");  
  43.                     }  
  44.                 }  
  45.             }  
  46.             else {  
  47.                 this.beanDefinitionNames.add(beanName);  
  48.                 this.frozenBeanDefinitionNames = null;  
  49.             }  
  50.             this.beanDefinitionMap.put(beanName, beanDefinition);  
  51.         }  
  52.   
  53.         resetBeanDefinition(beanName);  
  54.     }  
public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String aliase : aliases) {
				registry.registerAlias(beanName, aliase);
			}
		}
	}
   public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}
		synchronized (this.beanDefinitionMap) {
			Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
			if (oldBeanDefinition != null) {
				if (!this.allowBeanDefinitionOverriding) {
					throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
							"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
							"': There is already [" + oldBeanDefinition + "] bound.");
				}
				else {
					if (this.logger.isInfoEnabled()) {
						this.logger.info("Overriding bean definition for bean '" + beanName +
								"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
					}
				}
			}
			else {
				this.beanDefinitionNames.add(beanName);
				this.frozenBeanDefinitionNames = null;
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}

		resetBeanDefinition(beanName);
	}

   先检查是不是有相同名字的BeanDefinition已经在IoC容器中注册了,如果有相同名字的BeanDefinition,但又不允许覆盖,那么会抛出异常,如果可以可以覆盖,或者IOC容器中没注册,然后把Bean的名字存入到beanDefinitionNames的同时,把beanName作为Mapkey,beanDefinition作为value存入到IoC容器持有的BeanDefinitionMap中去。


总结:

   (1)SpringMVC先创建BeanFactory时销毁所有Bean,关闭BeanFactory,然后重新创建一个BeanFactory,并将其赋给BeanFactory实例变量。

   (2)创建好了BeanFactory,并设置好环境,ApplicationContext 继承了 ResourceLoader 接口,并通过ResourcePatternResolver加载了web.xml配置的param-value对应的资源,例如classpath:xxx*.xml 加载了xxx下为xml为后缀的文件资源,loadBeanDefinitions进行对加载进来的资源进行解析,在解析之前先进行转码,是通过EncodedResource对资源文件进行转码

   (3)对读取进来的配置文件进行验证,是否是XML格式的,然后配置文件中的Beanidnameclass等属性的解析并设置到BeanDefinition然后放到放到BeanDefinitionHolder中,并放入到IOC容器中建立对应的数据结构。是以Document形式的存储的。

    (4)在IOC容器中已经建立对应的数据结构,解析 Bean 的定义和注册,先检查是不是有相同名字的BeanDefinition已经在IoC容器中注册了,如果有相同名字的BeanDefinition,但又不允许覆盖,那么会抛出异常,如果可以可以覆盖,或者IOC容器中没注册,然后把Bean的名字存入到beanDefinitionNames的同时,把beanName作为Mapkey,beanDefinition作为value存入到IoC容器持有的BeanDefinitionMap中去。


 我们现在注册好,要实例化,还有Spring最核心的IOC依赖注入,怎么实例化并依赖注入,带着这些问题?下一篇我们继续解析。





   

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值