SSM答辩面试题 V1.0

SSM答辩面试题 V1.0


SSM面试题

孙小白de学习

此博客为新手学习进阶用途,有错误请大家指正

1. Maven项目依赖中作用范围 scope

当一个Maven工程添加了对某个jar包的依赖后,这个被依赖的jar包可以对应下面几个可选的范围,默认 是compile。

依赖范围编译(main)测试(test)部署示例
compile log4j
provided × servlet-api   lombok
test × × junit
runtime × JDBC驱动类
system × 和provided相似,很少用
import × × × 只能与dependencyManagement配合使用;学习SpringBoot时会使用

2. Maven项目之间的三种关系及其特征

  • 依赖关系(兄弟关系、朋友关系):

    项目A依赖项目B,项目A可以访问项目B中某些依赖,避免重复提供
    缺点:但是只有范围为compile时可以访问
    
  • 继承关系(父子关系,并列关系):

     使用继承时,需要定义父项目和子项目
     继承关系是单向关系:子项目指定父项目,父项目不用指定子项目
     父项目的所有的依赖(compile、test、provided等)子项目都可以自动使用
     父项目中可以通过dependencyManagement来管理依赖,子项目如果需要必须手动声明
     缺点:彼此间是并列关系,父项目、子项目需要分别逐个手动进行clean、compile操作
    
  • 聚合关系(父子关系,包含关系):

     首先是继承关系,并且比继承关系更进一步
     在聚合关系中, 子项目明确父项目, 但是父项目明确子项目,是双向关系
     其实是一个大项目包含多个子项目,对父项目进行clear、compile等命令,是对所有子项目进行clear、compile命令。但是如果对一个子项目进行maven操作,不影响其他子项目
    

3. 如何理解框架framework

  • 生活案例: 毛坯房

  • 框架jar (什么是jar? 对于解决特定问题所提供的固定解决方案进行封装) + 配置文件 (个性化定制,配置变化的内容,比如数据库连接参数、端口号、接口的具体实现类等)

  • 作用:

    1.可以保证减少开发时间、降低开发难度,并且还保证设计质量。
    2.框架还有一个作用是约束,统一了代码流程和风格。可降低程序员之间沟通以及日后维护的成本。

  • 常用的基于JavaEE的三大开源框架,SSM:

    SpringMVC、Spring、MyBatis

  • 总结:框架是一个半成品,已经对基础的代码进行了封装并提供相应的API,开发者在使用框架是直接调用封装好的API可以省去很多代码编写,从而提高工作效率和开发速度,并统一了代码流程和风格。


4. 如何理解ORM

什么是ORM?
	ORM,Object-Relational Mapping,对象关系映射 。
	是一种在关系型数据库和对象之间作一个映射的框架类型。
	目前最主要的ORM框架为:Mybatis(半自动)、Hibernate(全自动)
  • JDBC的缺点:
    需要手动的完成面向对象的Java语言、面向关系的数据库之间数据的转换,代码繁琐无技术含量,影响了开发效率(查询时需要手动的将结果集ResultSet的列数据转换为Java对象的属性;而添加操作时需要手动将Java对象的属性转换为数据库表的列字段)。

  • 关于面向对象的Java语言、面向关系的数据库之间数据的转换必须要做,问题在于这个转换是否可以不由开发者来做。可以的。ORM框架就是专门来做这个问题的,相当于在面向对象语言和关系数据库之间搭建一个桥梁。

ORM对象关系映射

  • Hibernate是一个全自动的ORM框架。因为Hibernate创建了Java对象和数据库表之间的完整映射,可以完全以面向对象的思想来操作数据库,程序员不需要手写SQL语句。MyBatis中还需要手写SQL语句,所以是半自动化的。但是最终MyBatis却战胜了Hibernate,主要还是因为MyBatis可以更加精确的定义SQL,更加灵活,也便于优化性能。

5. MyBatis中#{}和${}的区别是什么?

  • #{}是预编译处理,${}是字符串替换。
  • Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用 PreparedStatement 的set方法来赋值。
  • Mybatis在处理${}时,就是把${}替换成变量的值。
  • 使用#{}可以有效的防止SQL注入,提高系统安全性。

6. MyBatis中当实体类中的属性名和表中的字段名不一样,怎么办 ?

  1. 方法1:
    启用驼峰命名
    前提,属性名和字段名虽然不同,但是满足驼峰命名规则。比如字段名是emp_name、house_type_id,但是属性名是empName、HouseTypeId。
<settings>
    <!-- 将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
  1. 方法2:
    起别名
    数据库表的字段的别名就起为实体类中对应属性名。
	select emp_id as empId,emp_name as empName,emp_sal as salary from t_emp
  1. 方法3:
    进行ResultMap映射
    一次性定义,重复使用,避免重复起别名。
<resultMap id="employeeMap" type="com.atguigu.mybatis.entity.Employee">
    <!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
    <!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
    <id column="emp_id" property="empId"/>    
    <!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
    <result column="emp_name" property="empName"/>
    <result column="emp_salary" property="empSalary"/>
</resultMap>

<resultMap id="employeeMap" type="com.atguigu.mybatis.entity.Employee">
    <!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
    <!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
    <id column="emp_id" property="empId"/>    
    <!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
    <result column="emp_name" property="empName"/>
    <result column="emp_salary" property="empSalary"/>
</resultMap>

<select id="selectEmployeeByRM" resultMap="employeeMap">
    select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>

7. MyBatis如何获取自动生成的(主)键值?如何完成MySQL的批量操作?

  1. 在<insert>标签中使用 useGeneratedKeys 和 keyProperty 两个属性来获取自动生成的主键值。
    例如:
<insert id="insertname"  usegeneratedkeys="true" keyproperty="id">  
    insert into names (name) values (#{name})  
</insert> 
  1. MyBatis完成MySQL的批量操作主要是通过<foreache>标签来拼装相应的SQL语句。
    例如:
<insert id="insertBatch" >  
    insert into tbl_employee(last_name,email,gender,d_id) values  
    <foreach collection="emps" item="curr_emp" separator=",">  
        (#{curr_emp.lastName},#{curr_emp.email},#{curr_emp.gender},#{curr_emp.dept.id})  
    </foreach>  
</insert>  

8. 讲述下Mybatis的一级、二级缓存

  1. 缓存作用
    使用缓存可以减少对硬盘(数据库)的访问次数,提高访问速度。
  2. 缓存分类
    • 一级缓存:自动开启,默认开启。针对同一个SqlSession(不同的SqlSession有不同的一级缓存)
    • 二级缓存:手动开启,默认关闭。针对同一个SqlSessionFactory(由这一个SqlSessionFactory创建所有的SqlSession可以共享二级缓存中的数据)
  3. 缓存访问步骤
    1. 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
    2. 如果二级缓存没有命中,再查询一级缓存。
    3. 如果一级缓存也没有命中,则查询数据库。

9. 简述Mybatis的动态SQL,列出常用的6个标签及作用。

动态SQL是MyBatis的强大特性之一 基于功能强大的OGNL表达式。
动态SQL主要是来解决查询条件不确定的情况,在程序运行期间,根据提交的条件动态的完成查询。
常用的标签:

  • <if> : 进行条件的判断
  • <where>:在判断后的SQL语句前面添加WHERE关键字,并处理SQL语句开始位置的AND 或者OR的问题
  • <trim>:可以在SQL语句前后进行添加指定字符 或者去掉指定字符.
  • <set>: 主要用于修改操作时出现的逗号问题
  • <choose> <when> <otherwise>:类似于java中的switch语句.在所有的条件中选择其一
  • <foreach>:迭代操作

10. 描述下IoC依赖注入,有哪些方式

控制反转(IoC)

  • 生活案例:点外卖
  • 传统 Java 开发模式中,当需要一个对象时,我们会自己使用 new 或者 getInstance 等直接或者间接调用构造方法创建一个对象。而在 Spring开发模式中Springspring 容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接获取并调用Spring提供的对象就可以了
  • IoC作用:对象之间解耦,利于后期维护和修改。(修改xml文件不同于修改Java源代码,不涉及重新编译、打包、部署等操作)。
  • 依赖注入(DI),DI 是 IOC 的另一种表述方式。Spring使用 JavaBean 对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。

注入方式

  • 通过setter方法注入
  • 通过构造方法注入

11. 解释Spring自动装配的各种模式

  1. byType:首先根据所需要的组件类型到IoC容器中查找,能够找到唯一的bean,直接执行装配。如果完全找不到匹配这个类型的bean,装配失败。
  2. byName:如果和所需类型匹配的bean不止一个,没有@Qualifier注解:根据==@Autowired==标记位置成员变量的变量名作为bean的id进行匹配,能够找到则执行装配。找不到则装配失败。
  3. 使用==@Qualifier==注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配,能够找到则执行装配,找不到则装配失败。

Spring自动装配


12. 说一下Spring中支持的bean作用域

在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参加下表:

取值含义创建对象的时机
singleton在IoC容器中,这个bean的对象始终为单实例Ioc容器初始化时
prototype这个bean在IoC容器中有多个实例获取bean时

如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):

取值含义
request在一个请求范围内有效
session在一个会话范围内有效

13. 解释Spring框架中bean的生命周期

  • Bean 的生命周期主要阶段包括
    1. bean对象创建
    2. 给bean对象属性注入
    3. 执行Bean后置处理器中的postProcessBeforeInitialization
    4. 调用初始化方法,进行初始化,初始化方法是通过init-method来指定的
    5. 执行Bean的后置处理器中postProcessAfterInitialization
    6. 使用Bean
    7. IOC容器关闭时,销毁Bean对象

14. Spring中常用的设计模式

  • 代理模式——Spring 中两种代理方式,若目标对象实现了若干接口,Spring 使用JDK的java.lang.reflect.Proxy类代理。若目标没有实现任何接口,Spring 使用 CGLIB 库生成目标类的子类。
  • 单例模式——在 Spring 的配置文件中设置 bean 默认为单例模式。
  • 模板方式模式——用来解决代码重复的问题。比如:RestTemplate、JmsTemplate、JpaTemplate。
  • 工厂模式——在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用同一个接口来指向新创建的对象。Spring 中使用== beanFactory== 来创建对象的实例。

15. 说下对Spring面向切面编程(AOP)的理解

  • 面向切面编程AOP可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。可以和过滤器进行类比来理解AOP,有很多相似点。
  • OOP解决纵向的业务问题,AOP解决横向的问题,比如日志、安全验证、事务、异常等。相同的代码重复的出现在项目的不同位置,不利于维护。可以将这些功能分别提取出来,由多份变成一份,然后在编译或者运行时织入到指定的多个位置。
  • Spring底层使用了动态代理模式实现AOP。若目标对象实现了若干接口,Spring 使用JDK的动态代理。若目标没有实现任何接口,Spring 使用 CGLIB 库生成目标类的子类。还可以配置不管是否实现接口,都是要CGLIB。

AOP


16. 说下Spring面向切面编程(AOP)的常用术语

  • 横切关注点
    纵向关注点(核心关注点 业务关注点):add()、sub()、findAll()、insert(),OOP解决纵向关注点。
    横切关注点:日志、安全验证、事务、异常等非业务功能,代码相似却分散才项目的不同位置。AOP 解决横切关注点。
  • 通知Advice(称为增强更容易理解)
    就是AOP的要做的事情,比如写日志、进行事务处理。在Spring中,通知表现为一个方法。
    通知的分类:前置通知返回通知异常通知后置通知环绕通知
  • 连接点 Joinpoint
    在什么地方加入通知呢?比如目标类的add()、sub()、findAll()方法,在Spring中,连接点是一个方法。
  • 切入点 Pointcut
    一个表达式,能涵盖所有的连接点。比如:execution(* com.atguigu.service.CalculatorImpl.*(…))。
  • 切面 Aspect
    切面是一个类,包括两部分:通知(干什么)+切入点(在哪里干)
    类比过滤器:定义一个类(EncodingFilter)+过滤路径(*.html)。
  • 目标 target:被代理的目标对象。
  • 代理 proxy:向目标对象应用通知之后创建的代理对象。
  • 织入 weave:指把通知应用到目标上,生成代理对象的过程。

17. 请描述一下Spring 的声明式事务管理

  • 编程式事务管理:指事务功能的相关操作全部通过开发者编写代码来实现,事务代码的结构基本是确定的,暴露细节,代码重复,不利于后期维护。
  • 声明式事务管理:既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装。封装起来后,只需要在配置文件中进行简单的配置即可完成操作。好处在于,可以消除冗余的代码,提高开发效率,并且框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各个方面的优化。
  • Spring5.2后声明式事务的顶级接口是TransactionManager,它是一个空接口,真正的接口是PlatformTransactionManager。整合JDBCTemplate和MyBatis使用实现类DataSourceTransactionManager。

  • 声明式事务管理包括基于XML配置和基于注解的事务管理两种,目前推荐基于注解方式。实现步骤:
    • 添加依赖:spring-orm.jar,依赖于spring-tx.jar和spring-jdbc.jar
    • 配置事务管理器:DataSourceTransactionManager
    • 启用事务的注解驱动:tx:annotation-driven</tx:annotation-driven>
    • 使用@Transactional注解标注方法或者类

18. SpringMVC中如何实现请求域request中传递数据

  • 使用原生API
  • 使用Model
  • 使用Map
  • 使用ModelMap
  • 使用ModelAndView
    注意:传入的Model、ModelMap、Map类型的数的三种方式其实本质上都是 BindingAwareModelMap 类型的,而四种非原生方式底层都调用了原生的request.setAttribute(name,value)。

19. SpringMVC怎么样设定重定向和转发的

  • 方式1:默认使用转发,并添加前缀和后缀,将逻辑路径转换为物理路径
return "admin/index";  // 转发到WEB-INF/templates/index.html
  • 方式2:显示指定forword来使用转发,不再添加前缀后缀,后面的路径就是要跳转的物理路径。
return "forward:/add.html"; 
return "forward:/admin/findAll"; //跳转到Controller的方法
return "forward:/WEB-INF/templates/index.html"; 
  • 方式3:显示指定redirect来使用重定向,可以重定向到互联网任意可以访问的位置。
return "redirect:/add.html"; 
return "redirect:/admin/findAll"; //跳转到Controller的方法
return "redirect:http://www.atguigu.com"; 

20.说出Spring/SpringMVC常用的6个注解,并简单描述作用

注解作用
@Component基本注解,标识一个受Spring管理的组件
@Controller标识为一个表示层的组件
@Service标识为一个业务层的组件
@Repository标识为一个持久层的组件
@Configuration标识为一个配置类
@Bean标记一个方法,将方法返回的对象交给Spring容器管理,bean id为方法名
@Autowired自动装配
@Qualifier(“xxx” )具体指定要装配的组件的id值
@RequestMapping()完成请求映射
@PathVariableRestful风格映射请求URL中占位符到请求处理方法的形参
@RequestBody标识从请求体中获取请求数据
@ResponseBody标识返回结果直接写入HTTP响应体中,一般在Ajax异步获取数据时使用

21. SpringMVC中如何实现Restful风格的数据传输和接收

  1. web.xml中添加过滤器org.springframework.web.filter.HiddenHttpMethodFilter,负责根据表单项的取值情况将POST请求转换为PUT请求或者DELETE。
  2. 视图中页面上使用Restful的地址提交请求(不管是Ajax还是非Ajax请求均可)。
  3. Controller中方法使用@GetMapping/@PostMapping/@PutMapping/@DeleteMapping指定映射路径,如果包含路径变量使用{}来表示,方法参数前使用@PathVariable注解将路径变量和参数绑定。
<filter>
   <filter-name>hiddenHttpMethodFilter</filter-name>
   <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>hiddenHttpMethodFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>
<a th:href="@{/emp/zh/300}">按照姓名和工资查询员工1</a><br>
<form th:action="@{/emp}" method="post">
    <input type="hidden" name="_method" value="put">
    员工姓名:<input type="text" name="empId" value="2" readonly><br>
    员工姓名:<input type="text" name="empName" value="张三"><br>
    员工薪水:<input type="text" name="empSalary" value="123.45"><br>
    <input type="submit" value="提交">
</form>
@Controller
@RequestMapping("/emp")
@Slf4j
public class EmployeeController {
    @GetMapping("/{empName}/{salary}")
    public String findEmp(@PathVariable("empName") String ename,@PathVariable Double salary){
        log.debug("--EmployeeController findEmp empName="+ename+",salary="+salary+"--");
        return "result";
    }
    @PutMapping
    public String updateEmp(Employee employee, HttpServletRequest request){
         log.debug("method:"+request.getMethod()); //PUT
         log.debug("-----EmployeeController updateEmp:"+employee+"-------");
        return "result";
    }
}


22.简述SpringMVC中如何通过Ajax请求返回JSON数据

  1. 在项目中加入JSON转换的依赖,例如Jackson,Fastjson,Gson等。默认使用Jackson。如何使用其他JSON转换组件,需要在配置文件中说明,比如:
<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <!-- 配置Fastjson支持 -->
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <value>text/html;charset=UTF-8</value>
                    <value>application/json</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>


23.SpringMVC中拦截器和JavaEE中Filter的异同

SpringMVC 拦截器

相似点:三要素相同

  • 拦住:必须先把请求拦住,才能执行后续操作。
  • 处理:拦截器或过滤器存在的意义就是对请求进行统一处理
  • 放行:对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源;也可能不放行,比如安全验证中没有登录。

不同点

  • 工作平台不同
    过滤器工作在 Servlet 容器中,在DispatcherServlet前执行(Filter执行顺序早于所有的Servlet)。
    拦截器工作在SpringMVC 的基础上,在DispatcherServlet后执行,只有经过了DispatcherServlet的请求才有可能过拦截器。
  • 拦截的范围不同
    过滤器:能够拦截到的最大范围是整个 Web 应用
    拦截器:能够拦截到的最大范围是整个SpringMVC 负责的请求(看DispatcherServlet的的设置是/还是*.action)。
  • IOC 容器支持不同
    过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的。
    拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件

24. 简单的谈一下SpringMVC的工作流程

执行流程图
SpringMVC工作流程
执行流程

(1) 浏览器发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他组件进行处理,作为统一访问点,进行全局的流程控制。
(2) DispatcherServlet——>HandlerMapping,处理器映射器将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象。
(3) DispatcherServlet——>HandlerAdapter,处理器适配器将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器。
(4) HandlerAdapter——>调用处理器相应功能处理方法,并返回一个ModelAndView对象(Model部分是业务对象返回的模型数据,View部分为逻辑视图名)。
(5) DispatcherServlet——> ViewResolver,视图解析器将把逻辑视图名解析为物理视图,返回View对象。
(6) DispatcherServlet——>View,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构。
(7) DispatcherServlet——>响应,返回控制权给DispatcherServlet,由它响应用户,到此一个流程结束。

总结强调

(1) 总控制器DispatcherServlet(不是撒手掌柜)每次调用其他组件要求返回结果,并根据结果调用下个组件。
(2) 分控制器Handler的返回值:ModelAndView。Model中是数据,View是要跳转到的视图。
(3) 以上流程针对非Ajax请求。如果是Ajax请求,就不返回ModelAndView了,而是直接返回数据片段。也就不涉及视图解析和渲染,但是涉及拦截器的执行。
(4) SpringMVC流程固定,开发者的主要任务是:开发控制器类和视图文件,部分是否还要开发拦截器(图中红框标记部分)。


25. 简单的谈一下SpringMVC的核心API

  • DispatcherServlet:总控制器
  • HandlerMapping:处理器映射器,建立了请求路径和分控制器方法之间的映射
  • HandlerExecutionChain:总控制器调用HandlerMapping组件的返回值,是一个执行链,不仅有要执行的分控制器方法,还有相应的多个拦截器,组成一个执行链
  • HandlerAdapter:处理器适配器,调用Handler,不是由总控制直接调用的,而是由HandlerAdapter来调用
  • ViewResolver:逻辑视图(result)----->物理视图(/WEB-INF/templates/result.html)

26.说下ContextLoaderListener的作用

常见使用场景:

  • 场景1:使用了Spring但是没有使用SpringMVC的web项目(比如Dubbo服务提供者),如何加载Spring配置文件?可以使用ContextLoaderListener来加载。
  • 场景2:使用了SpringMVC但是项目规模大,有多个配置文件,除了使用DispatcherServlet一次性加载,是否有其他方法?可以使用ContextLoaderListener和DispatcherServlet分别加载。
    使用及其注意事项:
  • public class ContextLoaderListener extends ContextLoader implements ServletContextListener,该类是一个ServletContextListener,在项目启动的时候加载。注意:Servlet、Filter、Listener的加载顺序:Listener、Filter、Servlet。所以会先加载ContextLoaderListener ,再加载DispatcherServlet。
  • 使用ContextLoaderListener加载非SpringMVC配置文件创建的IoC容器是父容器,DispatcherServlet加载SpringMVC的的配置文件创建的IoC容器是子容器。子容器优先使用自己的Bean,如果没有,可以使用父容器的Bean。
  • 注意事项:同时使用了ContextLoaderListener和DispatcherServlet,如果<context:component-scan >的路径设置不合理,就会重复的创建Bean,甚至导致无法应用业务层事务的问题。
错误的加载方式
<!--ContextLoaderListener加载的配置文件:spring-tx.xml-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
<!-- DispatcherServlet加载的配置文件;springmvc.xml-->
<context:component-scan base-package="com.atguigu"></context:component-scan>

正确的加载方式
<!--ContextLoaderListener加载的配置文件:spring-tx.xml-->
<context:component-scan base-package="com.atguigu.service,com.atguigu.dao"></context:component-scan>
<!-- DispatcherServlet加载的配置文件;springmvc.xml-->
<context:component-scan base-package="com.atguigu.controller"></context:component-scan>



  • 3
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值