Spring源码解读 Spring初始化Bean时扩展

前言

在此之前写了2篇博客,分析了springIOC容器初始化的过程和spring Bean依赖注入的过程,spring在初始化时还做了很多事情,比如注册执行BeanFactoryPostProcessor,注册

BeanPostPorcessor。本篇文章主要就是为了分析这俩点,还有bean依赖注入过程中Bean的初始化。

正文

用过spring的都知道,在定义bean的时候可以指定init-method属性,让bean实例化之后,根据此方法进行初始化。其实除了指定init-method方法,还有其它的方式在bean初始化前

后进行处理。个人知道的有实现InitializingBean,BeanFactoryPostProcessor,BeanPostProcessor有这三种。当然实现Aware接口也可以,但是官方文档中说的

Spring提供了一系列的Aware接口使得bean可以从容器获取需要的资源,个人觉得不适合来控制bean的初始化。

InitializingBean:bean实现此接口后bean在实例化之后会调用afterPropertiesSet()方法。

BeanFactoryPostProcessor:在spring容器加载了bean的定义文件,将其解析成BeanDefinition之后,在bean实例化之前执行的。我们在通过spring配置数据源时,

一般把数据源的具体信息写在一个配置文件中,然后用${url}的形式注入进去。将${url}替换成我们配置里的值就是通过Spring内置的一个BeanFactoryPost,

PropertyPlaceholderConfigurer来实现的。

BeanPostProcessor:定义了俩个方法。postProcessBeforeInitialization()方法在初始化之前执行,postProcessAfterInitialization在初始化之后执行。

下面通过一个例子来加深理解

1.javaBean

package vo;

import org.springframework.beans.factory.InitializingBean;

public class User implements InitializingBean {
	private String name;

	public void init() {
		System.out.println("调用了init---" + "原来的name为" + name + "name设置为init");
		name = "init";
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("调用了afterPropertiesSet---" + "原来的name为" + name + "name设置为afterPropertiesSet");
		name = "afterPropertiesSet";
	}

	@Override
	public String toString() {
		return "User [name=" + name + "]";
	}

}

2.自定义一个BeanFactoryPostProcessor

package vo;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		BeanDefinition bd = beanFactory.getBeanDefinition("user");
		MutablePropertyValues mpv = bd.getPropertyValues();
		if (mpv.contains("name")) {
			String oldName=((TypedStringValue) mpv.getPropertyValue("name").getValue()).getValue();
			System.out.println("调用了MyBeanFactoryPostProcessor---"+"原来的name为"+oldName+"将name设置为:MyBeanFactoryPostProcessor");
			mpv.addPropertyValue("name", "MyBeanFactoryPostProcessor");
		}
	}

}

3.自定义一个BeanPostProcessor

package vo;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor  implements BeanPostProcessor{

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("调用了postProcessBeforeInitialization---------beanName:"+beanName+"---bean:"+bean+"将name设置为MyBeanPostProcessor-Befor");
		if("user".equals(beanName)){
			User user = (User) bean;
			user.setName("MyBeanPostProcessor-Before");
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("调用了postProcessAfterInitialization----------beanName:"+beanName+"---bean:"+bean+"将name设置为MyBeanPostProcessor-After");
		if("user".equals(beanName)){
			User user = (User) bean;
			user.setName("MyBeanPostProcessor-After");
		}
		return bean;
	}
	
}

4.配置文件

<?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:aop="http://www.springframework.org/schema/aop"
	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 -->
	<bean id="user" class="vo.User"  lazy-init="true" init-method="init">
		<property name="name" value="czy" />
	</bean>
	<bean id="myBeanFactoryPostProcessor" class="vo.MyBeanFactoryPostProcessor" />
	<bean id="myBeanPostProcessor" class="vo.MyBeanPostProcessor"/>
</beans>

5.测试代码

package vo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Services {
	public static void main(String[] args) {
		
		@SuppressWarnings("resource")
		ApplicationContext  ctx = new ClassPathXmlApplicationContext("spring.xml");
		User u = (User) ctx.getBean("user");
		System.out.println("用户拿到的name:"+u.getName());
	}
}

6.输出结果

调用了MyBeanFactoryPostProcessor---原来的name为czy将name设置为:MyBeanFactoryPostProcessor
调用了postProcessBeforeInitialization---------beanName:user---bean:User [name=MyBeanFactoryPostProcessor]将name设置为MyBeanPostProcessor-Befor
调用了afterPropertiesSet---原来的name为MyBeanPostProcessor-Beforename设置为afterPropertiesSet
调用了init---原来的name为afterPropertiesSetname设置为init
调用了postProcessAfterInitialization----------beanName:user---bean:User [name=init]将name设置为MyBeanPostProcessor-After
用户拿到的name:MyBeanPostProcessor-After

7.分析结果
3)执行InitializingBean接口的afterPropertiesSet()
5)执BeanPostProcessor的postProcessAfterInitialization方法。

可以把断点打在getBean()方法上,会发现控制台已经输出了上面的第一句化,说明BeanFactoryPostProcessor是在Bean实例化之前执行,BeanFactoryPostProcessor在spring加载了bean的定义文件在调试的时候可以。从最终的输出可以看出整个执行顺序如下:

1)执行BeanFactoryPostProcessor

2)执行BeanPostProcessor的postProcessBeforeInitialization方法。

4)执行init-method属性指定的方法

通过源码,深入分析

在spring容器启动时,会通过refresh方法进行初始化,初始化refresh()方法如下:
public void refresh() throws BeansException, IllegalStateException { 
    synchronized (this.startupShutdownMonitor) { 
        //1. 刷新前的准备工作 
        prepareRefresh(); 
        //2. 关闭释放旧的beanFactory创建新的beanFactory,读取配置文件等 
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 
        //3. 对beanFactory进行一些基本的初始化 
        prepareBeanFactory(beanFactory); 
  
        try { 
            //4. 下面两行主要用户扩展,处理所有已注册的BeanFactoryPostProcessor,实现在已经加载配置但未初始化bean时对配置进行修改 
            postProcessBeanFactory(beanFactory); 
            invokeBeanFactoryPostProcessors(beanFactory); 
            //5. 处理所有已注册的BeanPostProcessor,主要用于扩展,实现bean初始化前后的一些定制操作 
            registerBeanPostProcessors(beanFactory); 
  
            //6. 初始化消息源bean 
            initMessageSource(); 
            //7. 初始化事件监听器集,也有人叫事件监听器的注册表,所有的事件监听器都在这个bean里进行管理 
            initApplicationEventMulticaster(); 
            //8. 主要用于扩展,实现一些特殊bean的初始化,时间点是类似消息源事件监听器集等特殊bean初始化后,普通的bean初始化前 
            onRefresh(); 
            //9. 注册监听器 
            registerListeners(); 
            //10. 初始化其余的非延迟加载的单例bean 
            finishBeanFactoryInitialization(beanFactory); 
  
            //11. 刷新完成调用LifecycleProcessor的onRefresh方法,并且发布ContextRefreshedEvent事件 
            finishRefresh(); 
        } catch (BeansException ex) { 
            // 销毁已经创建的单例bean 
            destroyBeans(); 
            // 重新设置active标记 
        cancelRefresh(ex); 
            throw ex; 
        } 
    } 
}

其中第2步就是spring容器加载了bean的定义文件,将其解析成BeanDefinition,可以参考我之前的博文Spring源码解读 IOC容器初始化,第4步就是执行BeanFactoryPostProcessor,第5步是向BeanFactory注入BeanPostProcessor,BeanPostProcessor方法是在getBean()触发的,如果不是延时初始化,在第11步就执行了,如果是在用户手工调用getBean()时触发。这也是为什么我在配置文件中将lazy-init设置成true的原因。
我们先看一下第4步的源码,我在调试的时候发现postProcessBeanFactory()是个空实现,在此不追究,主要看一下invokeBeanFactoryPostProcessors()方法

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	}

发现是通过PostProcessorRegistrationDelegate来执行的,我们在看一下PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors()方法:

public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

		Set<String> processedBeans = new HashSet<String>();

		if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>();
			List<BeanDefinitionRegistryPostProcessor> registryPostProcessors =
					new LinkedList<BeanDefinitionRegistryPostProcessor>();

			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					BeanDefinitionRegistryPostProcessor registryPostProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					registryPostProcessor.postProcessBeanDefinitionRegistry(registry);
					registryPostProcessors.add(registryPostProcessor);
				}
				else {
					regularPostProcessors.add(postProcessor);
				}
			}

			// Do not initialize FactoryBeans here: We need to leave all regular beans
			// uninitialized to let the bean factory post-processors apply to them!
			// Separate between BeanDefinitionRegistryPostProcessors that implement
			// PriorityOrdered, Ordered, and the rest.
			//获取BeanFactoryPostProcessor
			String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

			// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
			List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
			for (String ppName : postProcessorNames) {
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			//根据优先级排序
			OrderComparator.sort(priorityOrderedPostProcessors);
			registryPostProcessors.addAll(priorityOrderedPostProcessors);
			//按照排序顺序循环执行
			invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);

			// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			List<BeanDefinitionRegistryPostProcessor> orderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
			for (String ppName : postProcessorNames) {
				if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
					orderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			OrderComparator.sort(orderedPostProcessors);
			registryPostProcessors.addAll(orderedPostProcessors);
			invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors, registry);

			// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
			boolean reiterate = true;
			while (reiterate) {
				reiterate = false;
				postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
				for (String ppName : postProcessorNames) {
					if (!processedBeans.contains(ppName)) {
						BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);
						registryPostProcessors.add(pp);
						processedBeans.add(ppName);
						pp.postProcessBeanDefinitionRegistry(registry);
						reiterate = true;
					}
				}
			}

			// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
			invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
		}

		else {
			// Invoke factory processors registered with the context instance.
			invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
		}
		
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

		// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
		List<String> orderedPostProcessorNames = new ArrayList<String>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
		for (String ppName : postProcessorNames) {
			if (processedBeans.contains(ppName)) {
				// skip - already processed in first phase above
			}
			else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
		OrderComparator.sort(priorityOrderedPostProcessors);
		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

		// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
		List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
		for (String postProcessorName : orderedPostProcessorNames) {
			orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		OrderComparator.sort(orderedPostProcessors);
		invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);


		List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
		for (String postProcessorName : nonOrderedPostProcessorNames) {
			nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
	}

整个方法很长很长,其实主要就是三步,第一获取实现了BeanFactoryPostProcessor的类,第二根据优先级进行排序,第三根据排好的顺序循环执行。当然里面有Spring内置的BeanFactoryPostProcess,还有一些无需排序的BeanFactoryPostProcessor。


我们在看一下registerBeanPostProcessors()方法
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
	}
发现是通过 PostProcessorRegistrationDelegate来执行。这就是代理模式啊,将接受请求的对象委托给另一个对象来处理,spring源码中大量的用到了委托模式,BeanDefition的解析委托给了BeanDefintionParseDelegate,属性值的解析委托给了BeanDefitionValueResolve。然后一下PostProcessorRegistrationDelegate里的 registerBeanPostProcessors方法。
public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

		// Register BeanPostProcessorChecker that logs an info message when
		// a bean is created during BeanPostProcessor instantiation, i.e. when
		// a bean is not eligible for getting processed by all BeanPostProcessors.
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

		// Separate between BeanPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
		List<String> orderedPostProcessorNames = new ArrayList<String>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				if (pp instanceof MergedBeanDefinitionPostProcessor) {
					internalPostProcessors.add(pp);
				}
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// First, register the BeanPostProcessors that implement PriorityOrdered.
		OrderComparator.sort(priorityOrderedPostProcessors);
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

		// Next, register the BeanPostProcessors that implement Ordered.
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		OrderComparator.sort(orderedPostProcessors);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);

		// Now, register all regular BeanPostProcessors.
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

		// Finally, re-register all internal BeanPostProcessors.
		OrderComparator.sort(internalPostProcessors);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);

		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}

也是很长很长,其实就是获取BeanPostProcessor然后注入给BeanFactory,具体的执行通过getBean()触发,getBean()的具体过程就不分析了,可以参考我之前的博文,这里主要看一下getBean()的初始化过程。代码如下

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged(new PrivilegedAction<Object>() {
				@Override
				//执行Aware方法
				public Object run() {
					invokeAwareMethods(beanName, bean);
					return null;
				}
			}, getAccessControlContext());
		}
		else {
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			//执行BeanPostProcessor里的postProcessBeforeInitialization方法
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			//执行初始化方法
			//先执行InitializingBean的afterPropertiesSet方法,再执行init-method属性指定的方法
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}

		if (mbd == null || !mbd.isSynthetic()) {
			//执行BeanPostProcessor里的postProcessAftrerInitialization方法
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		return wrappedBean;
	}

在看一下invokeInitMethod()方法

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
						@Override
						public Object run() throws Exception {
							((InitializingBean) bean).afterPropertiesSet();
							return null;
						}
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				//执行实现了InitializingBean的afterPropertiesSet方法
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		if (mbd != null) {
			String initMethodName = mbd.getInitMethodName();
			if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
				//执行init-method属性执定的方法
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		

在这俩个方法中除了之前我们测试中实现的初始化方法,还有一个执行Aware的方法,再说一下官方文档中说Spring提供了一系列的Aware接口使得bean可以从容器获取需要的资源。个人觉得不适合来控制Bean的初始化。

至此整个分析结束。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值