Spring MVC学习之二 DispatcherServlet如何处理请求

Spring MVC实战教程:从零开始构建项目 Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过分离模型(Model)、视图(View)和控制器(Controller)来简化Web开发。异常处理是Web开发中不可或缺的环节,Spring MVC提供了灵活的异常处理机制。开发者可以通过定义专门的异常处理器来集中处理整个应用的异常。异常处理器可以是一个方法,也可以是一个类。当应用抛出异常时,Spring会查找并调用匹配的异常处理器方法。 阅读详情

上一节我们知道了DispatcherServlet的初始化方法怎么调用后,接下来我们先看看下初始化时在initStrategies()都做了什么事情。


其内部调用了9个init*()方法,这些方法基本上就是从context中取出对应的bean并赋值给DispatcherServlet对应的属性,例如


此处只是简单的看下初始化,后续会进行详细的学习,请期待。

Servlet的请求处理都是在doService()方法中,下面是DispatcherServlet的doService()方法:

       /**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}


// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}


// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());


FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);


try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}

}

该方法做了三项工作:1.将request中的属性保存了一个快照;

                                  2.将框架中的对象保存到request中;

                                  3.调用doDispatch(request, response)分发请求;

处理请求的主要工作是在doDispatch(request, response),下面我们重点分析doDispatch(request, response)。

       /**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
1 processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

局部变量asyncManager是一个异步的web管理器,我们暂且不考虑它,只分析业务相关的代码。

1)processedRequest = checkMultipart(request);这行代码是与上传文件相关的处理,解析给定的请求为文件和参数并将其包装到一个MultipartHttpServletRequest中,MultipartHttpServletRequest提供了在标准的servletRequest方法中可以访问文件和容器参数。

2)multipartRequestParsed = (processedRequest != request);根据上一步返回processedRequest与入参对比,设置是否为文件上传的请求

3)mappedHandler = getHandler(processedRequest);从当前的handlerMappings获取第一个HandlerExecutionChain,HandlerExecutionChain中有处理器和拦截器。后续我们会详细学习此对象。

4)if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;

}

    没有找到handler则处理异常并返回结束方法的执行

5)HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    获取给定handler的HandlerAdapter

6)boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}

}

    此处代码做了修改性校验,判断内容是否被修改,不做过多就学习。HEAD请求,是HTTP1.0协议中的请求方法,用来测试超链接的有效性,可用性和最近修改。

7)if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;

}

    对请求做拦截器的前置拦截处理,如果被拦截则返回

8)// Actually invoke the handler.

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    handlerAdapter调用handler处理请求返回modelAndView

9)applyDefaultViewName(processedRequest, mv);如果当前返回的view为空则根据配置设置默认的view

10)mappedHandler.applyPostHandle(processedRequest, response, mv); 调用拦截器做后置处理

11)processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);处理分发后的请求结果,如果上步处理有异常则生成异常view,接着对存在并没有被清空的view进行渲染,渲染结束后如果存在拦截器则进行完成后拦截处理。

方法如下:

/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {


boolean errorView = false;


if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}


// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}


if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}


if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}

}

经过以上主要的11步骤,基本完成了请求的处理。流程图如下



Spring MVC与Hibernate框架整合实战源码包 Spring MVC 是 Spring 框架中用于构建现代化 Web 应用的核心模块之一,它基于经典的 MVC(Model-View-Controller)架构模式,实现了请求驱动的 Web 应用开发模型。通过解耦请求处理、业务逻辑与视图渲染,Spring MVC 提供了高度可扩展、可维护的 Web 开发方式。其整体架构围绕核心组件构建,包括前端控制器、请求映射器、控制器Controller、适配器、视图解析器等。 阅读详情

相关推荐

基于RTL8367S与XS2184的工业级POE交换机硬件设计全解析

本文深入解析基于RTL8367S与XS2184芯片的工业级POE交换机硬件设计,涵盖核心价值、芯片选型、原理图设计、PCB布局及可靠性验证等关键环节。重点探讨双芯片协同工作优势,如RTL8367S的自适应调度算法和XS2184的智能功率分配功能,为工业环境下的稳定供电与数据传输提供专业解决方案。

weixin_42614269的博客 458

spring mvc 的 intercept 和 DispatcherServlet ,出现异常后转 400,500的逻辑

  问题和背景: 遇到问题,一开始死活找不到原因,也找不到error 堆栈. 找不到原因的原因,        1. 这个堆栈是spring mvc本身打印的.        2.更关键的是这个堆栈竟然是用debug打印出来的         3. 但是代码里把非biz 打印的error,都统一打印到了 系统_other.log中    具体细节: 1. 失败后自动转成 400....

fei33423的专栏 980

MATLAB实战:手把手教你搭建构网逆变器同步稳定性仿真模型(附避坑指南)

本文详细介绍了如何在MATLAB/Simulink平台上搭建构网逆变器同步稳定性仿真模型,涵盖从基础模块配置到虚拟同步机控制实现的完整流程。针对电力电子工程师常见的控制参数整定困难、仿真结果偏差等问题,提供了LCL滤波器谐振抑制、虚拟惯量优化等关键技术解决方案,并附有避坑指南和工程实践技巧,助力研究者高效完成仿真建模。

weixin_29051193的博客 297

在springMVC的web.xml中,DispatcherServlet使用/和/*的区别

报错信息: DispatcherServlet with name 'DispatcherServlet' processing GET request for [/sBank/] (DispatcherServlet.java:843) No mapping found for HTTP request with URI [/sBank/] in DispatcherServlet with

没有脚的虫子的博客 7089

【免费】最新区块链钱包和私钥的助记词碰撞器,使用python开发

最新钱包私钥助记词碰撞器,搏一搏单车变摩托!

weixin_48529185的博客 1万+

经典点云配准算法:迭代最近点算法ICP(Iterative Closest Point)

迭代最近点算法,又名为 ICP(Iterative Closest Point)算法。 (1)ICP算法对初始值的依赖比较大,具体的配准过程中,如何选择初始值是一个问题,可以使用轮式里程计、imu预积分或其它先验的配准结果。 (2)ICP迭代次数过多,实际工程中需要考虑到其实时性的优化问题。 (3)迭代终止的阀值的设置也是需要考虑的点,可以考虑自适应阀值的方法。 (4)关于寻找最近点可以使用kdtree进行临近搜索。

qq_35102059的博客 1万+

SpringMVC处理请求(二):DispatcherServlet

DispatcherServlet里面执行处理的入口方法应该是doService,不过doService并没有直接处理,而是交给doDispatch进行具体的处理 doService主要是对request设置了一些属性,如果是include请求还会对request当前的属性做快照备份,并在处理结束后恢复,最后将请求转发给doDispatch方法. package org.springframew...

shero1604的博客 381

Excel VBA--按某一列拆分数据到多个文件

首先,将需要拆分的sheet命名为“明细”,接下来运行此代码,按提示操作即可。 在这里插入代码片 Sub chaifen() '定义变量类型 Dim sht, sh1, sh2 As Worksheet Dim k, i, j As Integer Dim irow As Integer Dim col As Integer Dim str As String '程序开始是要求输入按哪一列拆分数...

weixin_43650026的博客 5873

java7找不到uri_Spring Boot,Java Config-在名称为“ dispatcherServlet”的DispatcherServlet中找不到带有URI[/…]的HTTP请求的映...

这在stackOverflow中是一个非常普遍的问题,但是同一问题的主题都无法解决我的问题。我们有一个使用xml config的模板配置,但是现在我们试图摆脱该配置并开始使用Java config。所以我有一个使用Java配置和Spring Boot的新项目。我们还使用了JSP和Tiles 3。问题是:无法呈现我们的管理员登录页面。这是代码:主要配置类:@SpringBootApplication...

weixin_36337389的博客 591

基于转录逆转的药物重定向 GPS 模型

GPS是一个基于分子结构预测基因表达扰动的开源平台,由三个模块组成:RCL(在LINCS扰动数据上训练抗噪MLP)、GPS4Drugs(预测新化合物的基因表达谱并进行逆转评分)、MolSearch(基于MCTS的分子优化)。平台使用Morgan指纹和Gene Ontology注释作为特征输入,将基因表达扰动离散为三分类标签。训练数据来自LINCS Phase I的18,746个化合物和978个landmark基因,支持四细胞系独立训练,可用于药物重定向与化合物筛选。

听雨草堂 83

数据分析:蛋白质组的GO term的富集分析详解

在获取差异表达蛋白质的数据之后,依据蛋白质与基因的对应映射关系,开展GO(Gene Ontology)term的富集分析。通过这一分析流程,能够精准地识别出不同实验组中过表达基因所关联的显著富集的GO term通路。这些富集的GO term通路为后续的生物学研究提供了关键线索,有助于深入探究基因功能、揭示潜在的生物学机制,从而推动生物学发现的进程。

专注生信领域 1367

java uri servlet,Spring Boot,Java Config - 在DispatcherServlet中找不到带有URI [/ ...]的HTTP请求的映射,名称为“dispat...

This has been a quite common problem here in stackOverflow, but none of the topics of the same problem solves mine.We have a template configuration that uses xml config, but now we're trying to move a...

weixin_30454067的博客 265

如何使用VNC对openEuler系统进行远程图形化操作

本文详细介绍了在openEuler系统中配置VNC远程图形化操作的方法。主要内容包括:1)服务端配置:安装GNOME图形界面和TigerVNC服务,创建VNC用户并设置密码,配置VNC服务启动脚本;2)客户端连接:使用VNC Viewer等工具连接服务器;3)常见问题解决方法。通过5901端口建立连接后,用户可远程操作openEuler图形界面,适用于需要图形化管理的场景。

2501_93651177的博客 313

Spring MVC 源码学习之 DispatcherServlet处理Http请求流程

DisPatcherServlet是Spring MVC框架中最重要的一个类。它不但建立了自己持有的IOC容器,还肩负起所有的请求分发处理的重任。在MVC框架初始化后,对其他的HTTP请求的处理在doService()中完成。并且通过Handler 中的handler函数实现业务逻辑的处理。调用过程如图所示: Spring MVC 中DispatcherServlet中的doDispat...

zippo_zu的博客 390

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

java 请求渲染,Spring MVC学习之DispatcherServlet请求处理详析

前言要深入理解spring mvc的工作流程,就需要先了解spring mvc的架构:从上图可以看到 前端控制器DispatcherServlet在其中起着主导作用,理解了DispatcherServlet 就完全可以说弄清楚了spring mvc。DispatcherServlet作为Spring用于处理web请求注册的唯一一个Servlet,所有的请求都是经由DispatcherServlet...

weixin_31751887的博客 470

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

带你轻松学习Spring MVC

SpringMVC是基于MVC模式的Web框架,核心组件DispatcherServlet负责请求分发。文章详细解析了SpringMVC架构:从请求流程到三层模型(Controller/Model/View)配合,再到核心组件(HandlerMapping/HandlerAdapter等)的工作原理。重点讲解了参数绑定、RESTful设计、异常处理、过滤器/拦截器、CORS等核心功能,并对比了传统MVC与响应式WebFlux的差异。

Yilena的博客 1153

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Spring MVC系统学习(二)——Spring MVC的核心类和注解

Spring MVC(Model-View-Controller)是Spring框架的一个模块,用于构建基于Web的应用程序。它使用模型、视图和控制器分离的设计模式,使得Web开发更加模块化和灵活。在学习Spring MVC时,有几个核心类和注解是非常关键的,尤其是等。这些知识点是构建Spring MVC应用的基础。我们将逐步讲解这些概念,确保详细且易于理解。

2301_79858914的博客 1382

Spring 4 学习笔记7:MVC 配置(JAVA方式)

Spring MVC Java 配置方式介绍,使用 Servlet 3.0 支持的 Java 配置来配置 web 应用程序。

acherie的博客 1万+

史上最清晰:SpringBoot 与 Spring MVC 关系彻底讲透

它不抢 Spring MVC 的活儿,而是帮你把 Spring MVC 运行需要的所有环境、配置都搞定:自动配 Tomcat、自动配 Spring MVC 核心组件、自动扫描控制器,你只需要写业务代码,不用管任何配置。SpringBoot 能封装的远不止 MVC,还能封装 Spring Data JPA、MyBatis、Redis 等所有 Spring 生态组件,核心都是“自动配置”,MVC 只是它最常用的一个场景。没有 Spring MVC,SpringBoot 的 Web 功能就是空壳;

。。。 1101

请收下这一份Spring MVC源码学习大纲

通过以上大纲系统学习,可逐步掌握Spring MVC的底层原理与设计思想。建议结合等网页中的案例与调试指南,深化对请求分发、数据绑定等核心机制的理解。

程序员笔记 746

spring boot 和 spring MVC 有什么区别

摘要: 2026年企业现状显示,SpringBoot已成为Java后端开发主流,79%企业采用,而纯SpringMVC仅存于6.6%的遗留系统。SpringBoot并非替代SpringMVC,而是通过封装简化配置,集成SpringMVC、Tomcat等组件,提供开箱即用的微服务支持。传统SpringMVC需手动配置依赖、XML文件和外部服务器,开发效率低;SpringBoot则通过starter依赖、自动配置和内嵌容器,实现快速搭建和部署。学习建议以SpringBoot为主,但需掌握SpringMVC底层原

马优晨 537

Spring MVC 面试题集

Spring MVC是Spring框架的一个模块,用于构建基于Java的Web应用程序的MVC(Model-View-Controller)架构。它提供了一种结构化的方式来组织和管理Web应用程序,将应用程序分为模型(Model)、视图(View)和控制器(Controller)三个主要组件。模型(Model):模型代表应用程序的业务逻辑和数据。在Spring MVC中,模型通常由Java对象组成,这些对象包含应用程序的核心数据和业务逻辑。模型通常用于存储和处理数据,以便在视图和控制器之间进行交互。

smart_an的专栏 791

Spring MVC 原理

Spring MVC 原理

傍晚的猫和鱼的博客 383

springmvc5.x-mvc实现原理及源码实现

上文:spring5.x-声明式事务原理及源码实现系列文章:spring5.x-声明式事务原理及源码实现spring5.x-AOP实现原理及源码分析spring5.x-监听器原理及源码实现spring5.x-解决循环依赖分析spring5.x-IOC模块源码学习spring5.x介绍及搭配spring源码阅读环境基础知识请看原来写的文章:springmvc源...

逍遥壮士 1110

JavaWeb02-Java Web框架对比:Spring MVC vs Struts

本文对比了Java Web开发中两大主流框架Spring MVC和Struts2。Struts2基于拦截器架构,提供强大表单处理但面临严重安全漏洞问题;Spring MVC则依托Spring生态系统,以注解驱动和灵活配置为特点,成为企业级应用首选。文章详细介绍了两种框架的开发环境搭建(Maven配置、web.xml设置),并分析了它们的历史演变、核心特点及当前市场地位(Struts2份额下降,Spring MVC主导)。通过架构原理、代码示例和优缺点的系统比较,为开发者技术选型提供参考依据。

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

【 Spring MVC】二、什么是 Spring MVC?

二、什么是 Spring MVC? 2.1 概述: Spring MVC 是Spring Framework 的一部分,是基于 Java 实现的 MVC 轻量级的 Web 框架。 官方生肉: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#spring-web 翻译版本:(翻译版本是4.2.4的)(https://www.w3cschool.cn/spring_mvc_documentation_lin

你是我の唯一博客 137
上一篇: SpringMVC 学习之一 DispatcherServlet初始化方法initStrategies(context)从何而起
下一篇: MyBatis源码学习之一 工作的基本流程
tony_code_2017
博客等级 码龄9年 34粉丝 58原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值