【Spring Boot】(19)、Spring Boot嵌入式Servlet容器自动配置原理

SpringBoot(五)--内置Servlet源码分析 文章目录Servlet容器的使用默认servlet容器切换Servlet容器Servlet容器自动配置原理EmbeddedServletContainerAutoConfigurationEmbeddedServletContainerFactoryTomcatEmbeddedServletContainerFactoryServlet容器启动原理 SpringBoot默认使用Tomcat作为嵌入式的Servlet容器,只要引入了spring-boot-start-web依赖,则默认是使用Tomcat作为 阅读详情

    其中EmbeddedServletContainerAutoConfiguration是嵌入式Servlet容器的自动配置类,该类在spring-boot-autoconfigure-xxx.jar中的web模块可以找到。

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {

    @Configuration
	@ConditionalOnClass({ Servlet.class, Tomcat.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {

		@Bean
		public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
			return new TomcatEmbeddedServletContainerFactory();
		}
	}
    
    @Configuration
	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
			WebAppContext.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedJetty {

		@Bean
		public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
			return new JettyEmbeddedServletContainerFactory();
		}

	}
    
    @Configuration
	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedUndertow {

		@Bean
		public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
			return new UndertowEmbeddedServletContainerFactory();
		}

	}
    
   	//other code...
}

在这个自动配置类中配置了三个容器工厂的Bean,分别是:

  • TomcatEmbeddedServletContainerFactory

  • JettyEmbeddedServletContainerFactory

  • UndertowEmbeddedServletContainerFactory

    这里以大家熟悉的Tomcat为例,首先Spring Boot会判断当前环境中是否引入了Servlet和Tomcat依赖,并且当前容器中没有自定义的EmbeddedServletContainerFactory的情况下,则创建Tomcat容器工厂。其他Servlet容器工厂也是同样的道理。

1)、EmbeddedServletContainerFactory:嵌入式Servlet容器工厂

public interface EmbeddedServletContainerFactory {

	EmbeddedServletContainer getEmbeddedServletContainer(
			ServletContextInitializer... initializers);
}

内部只有一个方法,用于获取嵌入式的Servlet容器。

该工厂接口主要有三个实现类,分别对应三种嵌入式Servlet容器的工厂类,如图所示:


2)、EmbeddedServletContainer:嵌入式Servlet容器

同样道理,对应三种嵌入式Servlet容器,如图所示:


3)、以Tomcat容器工厂TomcatEmbeddedServletContainerFactory类为例:

public class TomcatEmbeddedServletContainerFactory
		extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
    
    //other code...
    
    @Override
	public EmbeddedServletContainer getEmbeddedServletContainer(
			ServletContextInitializer... initializers) {
        //创建一个Tomcat
		Tomcat tomcat = new Tomcat();
        
        //配置Tomcat的基本环节
		File baseDir = (this.baseDirectory != null ? this.baseDirectory
				: createTempDir("tomcat"));
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		tomcat.getService().addConnector(connector);
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
		prepareContext(tomcat.getHost(), initializers);
        
        //包装tomcat对象,返回一个嵌入式Tomcat容器,内部会启动该tomcat容器
		return getTomcatEmbeddedServletContainer(tomcat);
	}
}

看看TomcatEmbeddedServletContainerFactory#getTomcatEmbeddedServletContainer函数:

protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
    Tomcat tomcat) {
    return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
}

该函数很简单,就是来创建Tomcat容器并返回。

看看TomcatEmbeddedServletContainer类定义:

public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {

	public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        
        //初始化嵌入式Tomcat容器,并启动Tomcat
        initialize();
    }
    
    private void initialize() throws EmbeddedServletContainerException {
		TomcatEmbeddedServletContainer.logger
				.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
				addInstanceIdToEngineName();
				try {
					final Context context = findContext();
					context.addLifecycleListener(new LifecycleListener() {

						@Override
						public void lifecycleEvent(LifecycleEvent event) {
							if (context.equals(event.getSource())
									&& Lifecycle.START_EVENT.equals(event.getType())) {
								// Remove service connectors so that protocol
								// binding doesn't happen when the service is
								// started.
								removeServiceConnectors();
							}
						}

					});

					// Start the server to trigger initialization listeners
                      //启动tomcat
					this.tomcat.start();

					// We can re-throw failure exception directly in the main thread
					rethrowDeferredStartupExceptions();

					try {
						ContextBindings.bindClassLoader(context, getNamingToken(context),
								getClass().getClassLoader());
					}
					catch (NamingException ex) {
						// Naming is not enabled. Continue
					}

					// Unlike Jetty, all Tomcat threads are daemon threads. We create a
					// blocking non-daemon to stop immediate shutdown
					startDaemonAwaitThread();
				}
				catch (Exception ex) {
					containerCounter.decrementAndGet();
					throw ex;
				}
			}
			catch (Exception ex) {
				stopSilently();
				throw new EmbeddedServletContainerException(
						"Unable to start embedded Tomcat", ex);
			}
		}
	}
}

到这里就启动了嵌入式的Servlet容器,其他容器类似。

那么问题来了,我们对嵌入式容器的修改配置是如何生效的?

​ 之前讲过可以通过修改ServerProperties相关配置或者自定义EmbeddedServletContainerCustomizer定制器两种方式来修改默认配置。而ServerProperties其实就是EmbeddedServletContainerCustomizer的子类,所以说到底还是EmbeddedServletContainerCustomizer起了修改的作用。

​ 其实在EmbeddedServletContainerAutoConfiguration类上导入了一个BeanPostProcessorsRegistrar类:

@Import(BeanPostProcessorsRegistrar.class)

该类主要用于给容器导入组件,看定义:

public static class BeanPostProcessorsRegistrar
			implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (beanFactory instanceof ConfigurableListableBeanFactory) {
            this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
        }
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        if (this.beanFactory == null) {
            return;
        }
        //注册了一个EmbeddedServletContainerCustomizerBeanPostProcessor后置处理器的Bean
        registerSyntheticBeanIfMissing(registry,
                                       "embeddedServletContainerCustomizerBeanPostProcessor",
                                       EmbeddedServletContainerCustomizerBeanPostProcessor.class);
        registerSyntheticBeanIfMissing(registry,
                                       "errorPageRegistrarBeanPostProcessor",
                                       ErrorPageRegistrarBeanPostProcessor.class);
    }

    private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
                                                String name, Class<?> beanClass) {
        if (ObjectUtils.isEmpty(
            this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
            beanDefinition.setSynthetic(true);
            registry.registerBeanDefinition(name, beanDefinition);
        }
    }

}

后置处理器:在bean初始化前(创建完成,还未属性赋值),会执行初始化工作。

所以重点是在registerBeanDefinitions方法中向容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor类型的Bean:

public class EmbeddedServletContainerCustomizerBeanPostProcessor
		implements BeanPostProcessor, BeanFactoryAware {

    //初始化之前
    @Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof ConfigurableEmbeddedServletContainer) {
			postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
		}
		return bean;
	}
    
    private void postProcessBeforeInitialization(
			ConfigurableEmbeddedServletContainer bean) {
        //获取所有的定制器,调用每个定制器的customize方法,给Servlet容器进行属性赋值
		for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
			customizer.customize(bean);
		}
	}
    
    //从IOC容器中获取所有类型为EmbeddedServletContainerCustomizer的定制器
    private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
		if (this.customizers == null) {
			// Look up does not include the parent context
			this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
                //类型为EmbeddedServletContainerCustomizer的Bean
					this.beanFactory
							.getBeansOfType(EmbeddedServletContainerCustomizer.class,
									false, false)
							.values());
			Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
			this.customizers = Collections.unmodifiableList(this.customizers);
		}
		return this.customizers;
	}
    
    //other code...
}

所以之前介绍可以向容器中添加一个自定义的EmbeddedServletContainerCustomizer类型的组件,用于自定义属性配置,然后在导入的后置处理器中获取到该组件,并调用该自定义组件的customize方法,来修改默认的属性配置。

总结:

​ (1)、Spring Boot根据导入容器类型的依赖情况,会给容器中添加相应的EmbeddedServletContainerFactory嵌入式Servlet容器工厂;

        (2)、应用程序一旦导入了嵌入式Servlet容器依赖,就会触动后置处理器EmbeddedServletContainerCustomizerBeanPostProcessor;

        (3)、后置处理器从IOC容器中获取所有的EmbeddedServletContainerCustomizer类型的嵌入式Servlet容器定制器,并调用每个定制器的定制方法customize,从而修改默认属性配置。

====================打个广告,欢迎关注====================

QQ:
412425870
微信公众号:Cay课堂

csdn博客:
http://blog.youkuaiyun.com/caychen
码云:
https://gitee.com/caychen/
github:
https://github.com/caychen

点击群号或者扫描二维码即可加入QQ群:

328243383(1群)




点击群号或者扫描二维码即可加入QQ群:

180479701(2群)




Tomcat源码分析-spring boot集成tomcat 在分析源码前,我们先来了解下 spring 的 SPI 机制。我们知道,jdk 为了方便应用程序进行扩展,提供了默认的 SPI 实现(ServiceLoader),dubbo 也有自己的 SPI。spring 也是如此,他为我们提供了,允许开发人员通过文件进行扩展,下面举一个例子方便理解假如,我想要往 spring 容器中添加一个做一些初始化工作,我们可以借助 spring 提供的这个 SPI 功能完成这个需求。首先,在项目中创建我们再写个 test case,便可以通过 SPI 的方式获取我们定义的。 阅读详情

相关推荐

SpringBoot——嵌入式 Servlet容器_servlet 容器怎么配置

【3】通过注册自定义的Listener。

2401_87555332的博客 1526

Spring Boot嵌入式Servlet容器自动配置原理

https://blog.youkuaiyun.com/caychen/article/details/80346331 其中EmbeddedServletContainerAutoConfiguration是嵌入式Servlet容器的自动配置类,该类在spring-boot-autoconfigure-xxx.jar中的web模块可以找到。 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) ​ ​ ​ @Configuration ​ ​ ​ @Conditio

yygr的博客 690

【Spring Boot】配置嵌入式Servlet容器及启动原理

SpringBoot配置嵌入式Servlet容器定制和修改Servlet容器相关配置全局配置文件编写WebServerFactoryCustomizer注册Servlet三大组件注册Servlet注册Filter注册ListenerSpringBoot注册DispatcherServlet切换嵌入式Servlet容器 SpringBoot默认使用Tomcat作为嵌入式的Servlet容器 定制和修改Servlet容器相关配置 可以通过全局配置文件或者自己编写WebServerFactoryCustomiz

flora 579

SpringBoot_配置嵌入式Servlet容器

SpringBoot_配置嵌入式Servlet容器

底层码农 6643

基于docker进行安装部署seata,解决Seata的版本与Nacos的版本不匹配

本文分享了在Docker环境下部署Seata时遇到Nacos 3.x API不兼容问题的解决方案。由于Seata 2.0.0内置的nacos-client 1.4.2无法调用Nacos 3.x重构后的接口,导致注册失败。通过下载Seata 2.6.0源码自行构建Docker镜像,并调整启动方式(直接执行Java命令绕过Shell脚本),最终成功实现与Nacos 3.x的集成。关键步骤包括:创建自定义Dockerfile、配置application.yml、处理启动脚本兼容性问题,最终采用java直接运行方案

Dd52698的博客 405

Unipush2.0 App消息推送(在线+离线推送)

使用unipush2.0进行消息推送,包含在线跟离线

weixin_50251467的博客 3693

springboot-嵌入式Servlet容器自动配置原理

一、在springboot-autoconfigure的jar包下查找嵌入式的Servlet容器自动配置类 org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration.class   @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @C...

Javap 指令集 1027

Fusion 360嵌入式外壳建模实战:从草图到可装配结构

参数化三维建模是现代电子结构设计的核心能力,其本质是通过几何约束与特征驱动实现设计意图的精确表达和快速迭代。Fusion 360作为集成CAD/CAM/CAE的云端平台,以时间轴驱动的参数化工作流、基于基准面的草图约束机制和面向制造的特征操作逻辑,显著提升了嵌入式硬件的结构验证效率。该技术价值在于支持工程师在无专业机械背景前提下,独立完成PCB适配、散热布局、接口开孔与装配干涉检查等关键任务,广泛应用于3D打印原型、CNC加工外壳及IoT终端结构开发。本文以ESP32冰箱监控终端为实例,详解从基准定义、草图

weixin_35671843的博客 587

Spring Boot 自动配置servlet容器原理

以tomcat为例: 嵌入式Servlet容器自动配置: //org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration @Configuration @ConditionalOnWebApplication @EnableConfiguration...

sakuragio的博客 393

Flutter 完整学习文档 - 从入门到精通

Widget 就像乐高积木,Flutter 中所有的东西都是 Widget。一个按钮是 Widget,一段文字是 Widget,甚至布局、边距、对齐方式也是 Widget!"万物皆 Widget"

zwjapple的专栏 2313

别再手动拆解字节了!用博图这个功能指令,5分钟搞定ARRAY转BOOL数组

本文详细介绍了西门子博图中`ARRAY_TO_BOOL`功能指令的高效应用,帮助工程师快速实现ARRAY到BOOL数组的转换。通过实战案例和参数配置指南,展示了如何5分钟内完成传统方法需半小时的任务,大幅提升PLC编程效率,特别适用于通信报文解析和设备状态监控。

weixin_42653691的博客 260

Spring Boot 中嵌入式 Servlet 容器自动配置原理

1,参照 Spring Boot 自动配置包里面的web模块,EmbeddedServletContainerAutoConfiguration 嵌入式的 Servlet 容器自动配置 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication @Import(B...

Java_Glory的博客 415

3、流程引擎下载安装与部署

camunda有多种使用方式,此处先使用独立平台的方式部署引擎,这种用法适合分布式,微服务化,多语言异构系统的使用方式。

leyoliu的博客 5520

SpringBoot——嵌入式Servlet容器自动配置原理以及启动原理

0 嵌入式Servlet容器自动配置原理以及启动原理的步骤 1 嵌入式Servlet容器自动配置原理(以Tomcat为例) 1.1 简单介绍 2 嵌入式Servlet容器启动原理(以Tomcat为例) 2.1 SpringBoot启动时,启动Tomcat的原理 2.2 嵌入式的Servlet容器工厂创建tomcat容器,初始化并启动容器 2.2.1 嵌入式Servlet容器工厂的自动配置类 1> 注册后置处理器(WebServerFactoryCustomizerBeanPostPro..

weixin_44730681的博客 1082

xtop:multi_driven_net与incomplete_timing_cell fail reason 分析

man multi_driven_net”可以得知,这个问题是multi driven结构引起的,使用命令“get_failed_pins -reason multi_driven_net”,获取violation pin。“man incomplete_timing_cell”可以得知,这个fail reason的原因是缺少timing library cell,检查并补全后这个错误就显示了。这里选择的解决方法是修改lib文件,把inout改成input,重新lib2idb。通过这种方法解决了问题。

m0_61544122的博客 564

【FISCO BCOS】二十、多机部署区块链

实际开发中,为了满足高可用性和高容错性的需求,往往会采用多级部署区块链的方式,今天介绍一下FISCO BCOS的多机部署流程。

naihe_fish的博客 3646

嵌入式Servlet容器自动配置原理

嵌入式Servlet容器自动配置原理: 在spring-boot自动配置包中,有一个web模块,里边有一个嵌入式自动配置容器的配置:ServletWebServerFactoryAutoConfiguration 于此模型概念,在一个Servlet Web应用中,Spring Boot会使用上表中所说的WebServer工厂组件生成相应的WebServer实例。而这里的WebServer工厂组件又是从哪里来的呢 ? 这就是自动配置类ServletWebServerFactoryAutoConfigurati

weixin_42310074的博客 273

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Spring Boot4-2 --嵌入式Servlet容器自动配置原理(Spring Boot版本2.1.3)(理解原理)

springBoot 默认使用的是tomcat 在导入的依赖中:autoconfigure 中的web 目录下所有含有Server的类都与Servlet容器的相关 笔记的原理分析基于Spring Boot2.1.3版本;2.0 以下版本的一些类被代替 嵌入式Servlet容器自动配置原理 主要的三个类: 1.ServletWebServerfactoryConfihration 有三个:...

Stitch__的博客 511

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

【SpringBoot】26 核心功能 - Web开发原理 - Spring Boot 中定制 Servlet 容器

Spring Boot通过自动配置机制管理嵌入式Servlet容器,默认使用Tomcat。开发者可通过排除默认依赖并引入Jetty/Undertow实现容器切换,或通过实现WebServerFactoryCustomizer接口定制端口、上下文路径等参数。对于更复杂需求,可直接自定义ConfigurableServletWebServerFactory进行深度配置。这些方式提供了灵活的Web服务器定制能力,满足不同业务场景需求。

杰西笔记 1万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

SpringBoot——嵌入式Servlet容器自动配置原理

Servlet容器自动配置类 在spring-boot-autoconfigure包中有个web模块: 该模块下有个类EmbeddedWebServerFactoryCustomizerAutoConfiguration,负责嵌入式Servlet容器的自动配置,该类的源码: @Configuration @EnableConfigurationProperties(ServerPropertie...

如不來的博客 710

Spring Boot 内置与外置Servlet容器讲解(六)

1、配置嵌入式Servlet容器 SpringBoot默认使用Tomcat作为嵌入式的Servlet容器; 问题? 1)、如何定制和修改Servlet容器的相关配置; 1、修改和server有关的配置(ServerProperties【也是EmbeddedServletContainerCustomizer】); server.port=8081 server.context...

hfbin的博客 1万+

Spring boot中嵌入式Servlet容器的配置和启动原理

以下分析基于Springboot2.0以上版本,1.X版本跟2.0以上版本源码差异性较大 嵌入式Servlet容器的自动配置原理流程如下: SpringBoot 根据的依赖信息(默认是tomcat),创建对应的WebServerFactoryCustomizer(定制器,用来定制一些属性,默认为tomcat 所以就是定制tomcat的一些属性); WebServerFactoryCustomizerBeanPostProcessor(web工厂定制器的后置处理器)获取所有类型为web服务工厂定制器的组件(

湟同学你好的博客 567

idea servlet自动配置web.xml_Spring Boot学习04_嵌入式Servlet容器自动配置原理

在Spring Boot的自动配置包下,找到web模块中的servlet文件夹下的ServletWebServerFactoryConfiguration类一、嵌入式Servlet容器默认的配置原理1、该类中有三个静态内部类(都是配置类):EmbeddedUndertow、EmbeddedJetty、EmbeddedTomcat这里拿EmbeddedTomcat来举例说明@ConditionalO...

weixin_36317502的博客 252

Spring Boot 嵌入式Servlet容器启动原理

1,Spring Boot 应用启动运行run方法 2,refreshContext(context);Spring Boot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;如果是web应用创建一个web的ioc容器:AnnotationConfigEmbeddedWebApplicationContext,否则创建一个默认的容器:AnnotationConfigAp...

Java_Glory的博客 328

【SpringBoot源码】嵌入式Servlet容器自动配置原理

带着问题看源码:SpringBoot 嵌入式Servlet容器自动配置原理

梦染 * 星尘 的博客 481

SpringBoot嵌入式servlet容器Tomcat的自动配置原理,以及启动过程分析

springboot采用大量的自动配置,开发者通过少量的配置,就可开发spring应用,可以用来开发单个微服务应用,下面来介绍一下springboot的内置servlet的原理。 1)、创建一个springboot工程 导入相关依赖: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apa...

莫失莫忘的博客 1670
上一篇: 【Spring Boot】(18)、Spring Boot配置嵌入式Servlet容器
下一篇: 【Spring Boot】(20)、Spring Boot嵌入式Servlet容器启动原理
caychen
博客等级 码龄9年 110粉丝 113原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值