Spring源码跟踪,bean加载流程分析。

这周末加班砍了一天假,利用周日的时间宅家撸了一次Spring加载流程,把加载步骤列了出来。
不才,还望大佬指教。

一.基于注解进入容器

@Test
   public void test03(){
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
      UserService userService = context.getBean("userService", UserService.class);
      System.out.println(userService);
      userService.add();
   }

二.核心的三个方法

 public AnnotationConfigApplicationContext(Class... componentClasses) {
        this();
        this.register(componentClasses);
        this.refresh();
    }

三.this()方法

public AnnotationConfigApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);//读取器
        this.scanner = new ClassPathBeanDefinitionScanner(this);//扫包器
    }

三- 一读取器的最重要的的方法:registerAnnotationConfigProcessors方法

3.1.创建工厂 DefaultListableBeanFactory

DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry)

作用:DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册机加载bean的默认实现

它继承了AbstractAutowireCapableBeanFactory类,并且实现了ConfigurableListableBeanFactory、Serializable、BeanDefinitionRegistry接口。

实现主要功能就是以list的方式操作bean,Spring拆分成这么多接口的原因对功能进行分工,各司其职,具体结构图如下所示,小伙伴们可以自己去看看源码,快捷键(选中类然后按ctrl+alt+u)。

在这里插入图片描述

3.2 设置比较器 AnnotationAwareOrderComparator

AnnotationAwareOrderComparator是OrderComparator的子类,用来支持Spring的Ordered类、@Order注解和@Priority注解。

3.3 设置解析器ContextAnnotationAutowireCandidateResolver

从下图继承关系图我们可以看到,ContextAnnotationAutowireCandidateResolver是子类,因此它的功能是最全最强大的。
SimpleAutowireCandidateResolver最简单的实现,适配器形式的存在,不可直接使用,
GenericTypeAwareAutowireCandidateResolver注入泛型依赖,
QualifierAnnotationAutowireCandidateResolver不仅仅能够处理@Qualifier注解,也能够处理通过@Value注解解析表达式得到的suggested value,ContextAnnotationAutowireCandidateResolver支持上述描述的功能,还支持@Lazy懒处理。

beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());//解析器

在这里插入图片描述

3.4:添加四个后置处理器:

ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、
CommonAnnotationBeanPostProcessor 、EventListenerMethodProcessor。

ConfigurationClassPostProcessor:配置类后置处理器,负责扫描有@Configuration的注解

AutowiredAnnotationBeanPostProcessor:自动注入后置处理器。

CommonAnnotationBeanPostProcessor :负责解析@Resource、@WebServiceRef、@EJB三个注解。
EventListenerMethodProcessor:对 @EventListener 提供支持.主要是标注了 @EventListener 的方法进行解析, 然后转换为一个 ApplicationListener.

至此读取器AnnotatedBeanDefinitionReader执行完毕,
其底层方法registerAnnotationConfigProcessors对应的代码提供如下。

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
        DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);//1.创建工厂对象
        if (beanFactory != null) {
            if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
                beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);//2.设置比较器
            }
            if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
                beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());//
            }
        }
        Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet(8);
        RootBeanDefinition def;
        if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor")) {
            def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);//配置类后置处理器
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"));
        }
        if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalAutowiredAnnotationProcessor")) {
            def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);//自动注入后置处理器
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"));
        }
        if (jsr250Present && !registry.containsBeanDefinition("org.springframework.context.annotation.internalCommonAnnotationProcessor")) {
            def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);//负责解析@Resource、@WebServiceRef、@EJB三个注解。
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalCommonAnnotationProcessor"));
        }
        if (jpaPresent && !registry.containsBeanDefinition("org.springframework.context.annotation.internalPersistenceAnnotationProcessor")) {
            def = new RootBeanDefinition();
            try {
                def.setBeanClass(ClassUtils.forName("org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor", AnnotationConfigUtils.class.getClassLoader()));
            } catch (ClassNotFoundException var6) {
                throw new IllegalStateException("Cannot load optional framework class: org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor", var6);
            }
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalPersistenceAnnotationProcessor"));
        }
        if (!registry.containsBeanDefinition("org.springframework.context.event.internalEventListenerProcessor")) {
            def = new RootBeanDefinition(EventListenerMethodProcessor.class);//对 @EventListener 提供支持.
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.event.internalEventListenerProcessor"));
        }
        if (!registry.containsBeanDefinition("org.springframework.context.event.internalEventListenerFactory")) {
            def = new RootBeanDefinition(DefaultEventListenerFactory.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.event.internalEventListenerFactory"));
        }
        return beanDefs;
    }

三-二: ClassPathBeanDefinitionScanner底层方法:ClassPathBeanDefinitionScanner。

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) {
        this.beanDefinitionDefaults = new BeanDefinitionDefaults();
        this.beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
        this.scopeMetadataResolver = new AnnotationScopeMetadataResolver();
        this.includeAnnotationConfig = true;
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        this.registry = registry;
        if (useDefaultFilters) {
            this.registerDefaultFilters();
        }
        this.setEnvironment(environment);
        this.setResourceLoader(resourceLoader);
    }

this.registerDefaultFilters: 注册默认的过滤器
this.setEnvironment: 设置环境
this.setResourceLoader:设置类加载器

至此this()方法执行完毕,我们接下来看register与refresh方法。

四、register:

public void register(Class... componentClasses) {
        Class[] var2 = componentClasses;
        int var3 = componentClasses.length;
        for(int var4 = 0; var4 < var3; ++var4) {
            Class<?> componentClass = var2[var4];
            this.registerBean(componentClass);
        }
    }

遍历类然后注册,我们接下来看看核心方法registerBean下的doRegisterBean方法。

 private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) {
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            abd.setInstanceSupplier(supplier);
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
            abd.setScope(scopeMetadata.getScopeName());
            String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);
            AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
            int var10;
            int var11;
            if (qualifiers != null) {
                Class[] var9 = qualifiers;
                var10 = qualifiers.length;
                for(var11 = 0; var11 < var10; ++var11) {
                    Class<? extends Annotation> qualifier = var9[var11];
                    if (Primary.class == qualifier) {
                        abd.setPrimary(true);
                    } else if (Lazy.class == qualifier) {
                        abd.setLazyInit(true);
                    } else {
                        abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                    }
                }
            }
            if (customizers != null) {
                BeanDefinitionCustomizer[] var13 = customizers;
                var10 = customizers.length;
                for(var11 = 0; var11 < var10; ++var11) {
                    BeanDefinitionCustomizer customizer = var13[var11];
                    customizer.customize(abd);
                }
            }
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
            definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
        }
    }

解析用户传入的 Spring 配置类,解析成一个 BeanDefinition 然后注册到容器中。

五、最重要的方法refresh方法。

public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();//1、刷新前准备
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);//2、准备bean工厂
            try {
                this.postProcessBeanFactory(beanFactory);//3、空方法
                this.invokeBeanFactoryPostProcessors(beanFactory);//
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }
                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }
        }
    }

1:this.prepareRefresh()

作用记录初始化的时间,初始化资源,运行环境配置,创建监听器集合。

protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();//1.记录初始化的时间
        // 切换容器为初始化状态
        this.closed.set(false);
        this.active.set(true);
        //确保日志开启
        if (this.logger.isDebugEnabled()) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Refreshing " + this);
            } else {
                this.logger.debug("Refreshing " + this.getDisplayName());
            }
        }
        //初始化资源(为空方法,可以给子类扩展。)
        this.initPropertySources();
        //运行环境配置
        this.getEnvironment().validateRequiredProperties();
        //创建监听器集合,保存监听器。
        if (this.earlyApplicationListeners == null) {
            this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);
        } else {
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }
        this.earlyApplicationEvents = new LinkedHashSet();
    }

2:prepareBeanFactory()

作用: 设置bean的加载器,设置El表达式解析器,属性注册解析器、Bean后置处理器等。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //1.设置Bean的加载器
        beanFactory.setBeanClassLoader(this.getClassLoader());
        //2.设置EL表达式解析器
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        //3.设置属性注册解析器 
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, this.getEnvironment()));
        //4.设置Bean后置处理器
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        //5.设置自动忽略的装配接口
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
        //6.注册可以解析的自动装配
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
        //7.判断是否有loadTimeWeaver的bean,如果有则交给LoadTimeWeaverAwareProcessor。
        if (beanFactory.containsBean("loadTimeWeaver")) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }
        //8.判断是否包含enviroment,有则注册enviroment的bean
        if (!beanFactory.containsLocalBean("environment")) {
            beanFactory.registerSingleton("environment", this.getEnvironment());
        }
       //9.注册systemProperties组件bean
        if (!beanFactory.containsLocalBean("systemProperties")) {
            beanFactory.registerSingleton("systemProperties", this.getEnvironment().getSystemProperties());
        }
        //10.注册systemEnvironment组件bean
        if (!beanFactory.containsLocalBean("systemEnvironment")) {
            beanFactory.registerSingleton("systemEnvironment", this.getEnvironment().getSystemEnvironment());
        }
    }

3 postProcessBeanFactory()

作用: spring中并没有具体去实现postProcessBeanFactory方法,是提供给想要实现BeanPostProcessor的三方框架使用的。谁要使用谁就去实现

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    }

4 invokeBeanFactoryPostProcessors

 this.invokeBeanFactoryPostProcessors(beanFactory);

作用:
本方法会实例化和调用所有 BeanFactoryPostProcessor(包括其子类 BeanDefinitionRegistryPostProcessor)。

BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点。

Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。

BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor,比
BeanFactoryPostProcessor 具有更高的优先级,主要用来在常规的 BeanFactoryPostProcessor 检测开始之前注册其他 bean 定义。

特别是,你可以通过 BeanDefinitionRegistryPostProcessor 来注册一些常规的 BeanFactoryPostProcessor,因为此时所有常规的 BeanFactoryPostProcessor 都还没开始被处理。

:这边的 “常规 BeanFactoryPostProcessor” 主要用来跟 BeanDefinitionRegistryPostProcessor 区分。

5 registerBeanPostProcessors

this.registerBeanPostProcessors(beanFactory);

作用:

本方法会注册所有的 BeanPostProcessor,将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中。

BeanPostProcessor 接口是 Spring 初始化 bean 时对外暴露的扩展点,Spring IoC 容器允许 BeanPostProcessor 在容器初始化 bean 的前后,添加自己的逻辑处理。在 registerBeanPostProcessors 方法只是注册到 BeanFactory 中,具体调用是在 bean 初始化的时候。

具体的:在所有 bean 实例化时,执行初始化方法前会调用所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法,在执行初始化方法后会调用所有 BeanPostProcessor 的 postProcessAfterInitialization 方法。

6 initMessageSource()

作用:针对于国际化问题的MessageSource

假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的i18n国际化问题。

对于有国际化要求的应用系统,我们不能简单地采用硬编码的方式编写用户界面信息、报错信息等内容,而必须为这些需要国际化的信息进行特殊处理。

简单来说,就是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定的目录中,由系统自动根据客户端语言选择适合的资源文件。

protected void initMessageSource() {
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if (beanFactory.containsLocalBean("messageSource")) {
            this.messageSource = (MessageSource)beanFactory.getBean("messageSource", MessageSource.class);
            if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
                HierarchicalMessageSource hms = (HierarchicalMessageSource)this.messageSource;
                if (hms.getParentMessageSource() == null) {
                    hms.setParentMessageSource(this.getInternalParentMessageSource());
                }
            }
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Using MessageSource [" + this.messageSource + "]");
            }
        } else {
            DelegatingMessageSource dms = new DelegatingMessageSource();
            dms.setParentMessageSource(this.getInternalParentMessageSource());
            this.messageSource = dms;
            beanFactory.registerSingleton("messageSource", this.messageSource);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("No 'messageSource' bean, using [" + this.messageSource + "]");
            }
        }
    }

7 initApplicationEventMulticaster

作用:注册SimpleApplicationEventMulticaster内容非常简单,先判断有没有自定义的ApplicationEventMulticaster,没有的话就注册一个。

protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
            this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        } else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
            }
        }
    }

8 this.onRefresh()

作用: 没什么好说的,是空方法,估计是为了给后人扩展的

protected void onRefresh() throws BeansException {
    }

9 this.registerListeners()

protected void registerListeners() {
        Iterator var1 = this.getApplicationListeners().iterator();
        //获取applicationlisteners,放入监听器中。
        while(var1.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var1.next();
            this.getApplicationEventMulticaster().addApplicationListener(listener);
        }
        //从容器中获取所有实现ApplicationListener接口的bd的bdName
        String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
        String[] var7 = listenerBeanNames;
        int var3 = listenerBeanNames.length;
        for(int var4 = 0; var4 < var3; ++var4) {
            String listenerBeanName = var7[var4];
            this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }
        //发布早期的监听器。
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            Iterator var9 = earlyEventsToProcess.iterator();
            while(var9.hasNext()) {
                ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
                this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

10.finishBeanFactoryInitialization

作用:
到了spring加载流程最复杂的一步,开始实例化所有的bd。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
       //判断是否有bdName为ConversionService的bd(实现ConversionService接口),有的话注册格式转换器服务类。
        if (beanFactory.containsBean("conversionService") && beanFactory.isTypeMatch("conversionService", ConversionService.class)) {
            beanFactory.setConversionService((ConversionService)beanFactory.getBean("conversionService", ConversionService.class));
        }
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver((strVal) -> {
                return this.getEnvironment().resolvePlaceholders(strVal);
            });
        }
       //获取LoadTimeWeaverAware类型的bd,提前实例化。
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        String[] var3 = weaverAwareNames;
        int var4 = weaverAwareNames.length;
        for(int var5 = 0; var5 < var4; ++var5) {
            String weaverAwareName = var3[var5];
            this.getBean(weaverAwareName);
        }
        beanFactory.setTempClassLoader((ClassLoader)null);
        //冻结上下文,不允许在进行修改配置
        beanFactory.freezeConfiguration();
        //实例化预处理
        beanFactory.preInstantiateSingletons();
    }

11.finishRefresh()

protected void finishRefresh() {
        //1.为此上下文初始化生命周期处理器
        this.clearResourceCaches();
        this.initLifecycleProcessor();
        //2.刷新完毕事件传播到生命周期处理器
        this.getLifecycleProcessor().onRefresh();
        //3.推送上下文刷新完毕事件到相应的监听器。
        this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
        LiveBeansView.registerApplicationContext(this);
    }

到了这里,refresh方法执行完毕,当方法执行报错时会被捕捉错误,并且销毁bean,取消刷新,抛出一个异常。

catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }
                this.destroyBeans();//销毁bean
                this.cancelRefresh(var9);//取消刷新
                throw var9;//抛出异常。
            } finally {
                this.resetCommonCaches();
            }

好了,至此Spring的加载流程源码分析我们到这里就结束了,我只是把大概的步骤以及每一步的作用罗列出来,至于更深入的地方自己还没来得即去研究,大家可以根据步骤一步步去跟踪。

微信公众号/b站/知乎 搜索【迪巴哥没八哥】 一起交流技术,越努力,越幸运!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值