Java后端-快速职场篇:105、面试之框架部分

目录

2.5 框架部分

2.5.1什么是框架?

2.5.2 MVC模式

2.5.3 MVC框架

2.5.4 简单讲一下struts2的执行流程?

2.5.5 Struts2中的拦截器,你都用它干什么?

2.5.6 简单讲一下SpringMVC的执行流程?

2.5.7 说一下struts2和springMVC有什么不同?

2.5.8 说一下Spring中的两大核心?

2.5.9 AOP是什么?你都拿它做什么?

2.5.10讲一下Spring的事务传播特性

2.5.11 Spring事务的隔离级别

2.5.12 什么是ORM?

2.5.13 iBatis(mybatis)与Hibernate有什么不同?

2.5.14 Hibernate映射对象的状态

2.5.15 介绍一下Hibernate的缓存?

2.5.16 简单讲一下webservice使用的场景?

2.5.17 简单介绍一下activiti?


2.5 框架部分


2.5.1什么是框架?


  框架(Framework)是一个框子——指其约束性,也是一个架子——指其支撑性。

 

  IT语境中的框架,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构。在此结构上可以根据具体问题扩展、安插更多的组成部分,从而更迅速和方便地构建完整的解决问题的方案。

 

1)框架本身一般不完整到可以解决特定问题,但是可以帮助您快速解决特定问题;

   没有框架所有的工作都从零开始做,有了框架,为我们提供了一定的功能,我们就可以在框 架的基础上开发,极大的解放了生产力。

不同的框架,是为了解决不同领域的问题。一定要为了解决问题才去学习框架。

2)框架天生就是为扩展而设计的;

3)框架里面可以为后续扩展的组件提供很多辅助性、支撑性的方便易用的实用工具(utilities),也就是说框架时常配套了一些帮助解决某类问题的库(libraries)或工具(tools)。

  java中就是一系列的jar包,其本质就是对jdk功能的扩展.

 

2.5.2 MVC模式


   MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

最简单的、最经典就是Jsp(view) +Servlet(controller) + JavaBean(model)

 

当控制器收到来自用户的请求
控制器调用JavaBean完成业务
完成业务后通过控制器跳转JSP页面的方式给用户反馈信息
Jsp个 用户做出响应。
    控制器都是核心

 

2.5.3 MVC框架


什么是MVC框架?

  是为了解决传统MVC模式(Jsp + Servlet + JavaBean)的一些问题而出现的框架。

 

传统MVC模式问题

所有的Servlet和Servlet映射都要配置在web.xml中,如果项目太大,web.xml就太庞大,并且不能实现模块化管理。
Servlet的主要功能就是接受参数、调用逻辑、跳转页面,比如像其他字符编码、文件上传等功能也要写在Servlet中,不能让Servlet主要功能而需要做处理一下特例。
3、接受参数比较麻烦(String name = request.getParameter(“name”),User user=new User user.setName(name)),不能通过model接收,只能单个接收,接收完成后转换封装model.

4、跳转页面方式比较单一(forword,redirect),并且当我的页面名称发生改变时需要修改Servlet源代码.

 

现在比较常用的MVC框架有:

   struts

   webwork

   Struts2

   Spring MVC

 

2.5.4 简单讲一下struts2的执行流程?


Struts2的原理?

一个请求在Struts2框架中的处理大概分为以下几个步骤:

1、客户端浏览器发送请求

2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin);

3、接着FilterDispatcher(StrutsPrepareAndExecuteFilter)被调用,FilterDispatcher

(StrutsPrepareAndExecuteFilter)询问ActionMapper来决定这个请求是否需要调用某个Action;

如果ActionMapper决定需要调用某个Action,FilterDispatcher
    (StrutsPrepareAndExecuteFilter)把请求的处理交给ActionProxy;

5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类;

6、ActionProxy创建一个ActionInvocation的实例。

7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。

8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2框架中继承的标签。在这个过程中需要涉及到ActionMapper。

 

面试:

浏览器发送请求,经过一系列的过滤器后,到达核心过滤器(StrutsPrepareAndExecuteFilter).
StrutsPrepareAndExecuteFilter通过ActionMapper判断当前的请求是否需要某个Action处理,如果不需要,则走原来的流程。如果需要则把请求交给ActionProxy来处理
ActionProxy通过Configuration Manager询问框架的配置文件(Struts.xml),找到需要调用的Action类;
创建一个ActionInvocation实例,来调用Action的对应方法来获取结果集的name,在调用前后会执行相关拦截器。
通过结果集的Name知道对应的结果集来对浏览器进行响应。
 

拦截、判断、寻找、执行、响应

 

2.5.5 Struts2中的拦截器,你都用它干什么?


java里的拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重用部分的方式。

在AOP(Aspect-Oriented Programming)中拦截器用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。

 

面试:

    struts2中的的功能(参数处理、文件上传、字符编码等)都是通过系统拦截器实现的。

如果业务需要,当然我们也可以自定义拦截器,进行可插拔配置,在执行Action的方法前后、加入相关逻辑完成业务。

使用场景:

用户登录判断,在执行Action的前面判断是否已经登录,如果没有登录的跳转到登录页面。
用户权限判断,在执行Action的前面判断是否具有,如果没有权限就给出提示信息。
操作日志......
    4、......

 

2.5.6 简单讲一下SpringMVC的执行流程?
 

  1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;

  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

 3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)

   4.  提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

      HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

      数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

      数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

      数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

      5.  Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;

      6.  根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

      7. ViewResolver 结合Model和View,来渲染视图

      8. 将渲染结果返回给客户端。

 

面试:

用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获(捕获)
2、 DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;(查找handler)

3、 DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller), Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象(执行handler)

4、DispatcherServlet 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver) (选择ViewResolver)

5、通过ViewResolver 结合Model和View,来渲染视图,DispatcherServlet 将渲染结果返回给客户端。(渲染返回)

 

快速记忆技巧:

核心控制器捕获请求、查找Handler、执行Handler、选择ViewResolver,通过ViewResolver渲染视图并返回

 

2.5.7 说一下struts2和springMVC有什么不同?
 

目前企业中使用SpringMvc的比例已经远远超过Struts2,那么两者到底有什么区别,是很多初学者比较关注的问题,下面我们就来对SpringMvc和Struts2进行各方面的比较:

 

1. 核 心控制器(前端控制器、预处理控制器):对于使用过mvc框架的人来说这个词应该不会陌生,核心控制器的主要用途是处理所有的请求,然后对那些特殊的请求 (控制器)统一的进行处理(字符编码、文件上传、参数接受、异常处理等等),spring mvc核心控制器是Servlet,而Struts2是Filter。

 

2.控制器实例:Spring Mvc会比Struts快一些(理论上)。Spring Mvc是基于方法设计,而Sturts是基于对象,每次发一次请求都会实例一个action,每个action都会被注入 属性,而Spring更像Servlet一样,只有一个实例,每次请求执行对应的方法即可(注意:由于是单例实例,所以应当避免全局变量的修改,这样会产生线程安全问题)。

 

3. 管理方式:大部分的公司的核心架构中,就会使用到spring,而spring mvc又是spring中的一个模块,所以spring对于spring mvc的控制器管理更加简单方便,而且提供了全 注解方式进行管理,各种功能的注解都比较全面,使用简单,而struts2需要采用XML很多的配置参数来管理(虽然也可以采用注解,但是几乎没有公司那 样使用)。

 

4.参数传递:Struts2中自身提供多种参数接受,其实都是通过(ValueStack)进行传递和赋值,而SpringMvc是通过方法的参数进行接收。

 

5.学习难度:Struts更加很多新的技术点,比如拦截器、值栈及OGNL表达式,学习成本较高,springmvc 比较简单,很较少的时间都能上手。

 

6.intercepter 的实现机制:struts有以自己的interceptor机制,spring mvc用的是独立的AOP方式。这样导致struts的配置文件量还是比spring mvc大,虽然struts的配置能继承,所以我觉得论使用上来讲,spring mvc使用更加简洁,开发效率Spring MVC确实比struts2高。spring mvc是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上spring3 mvc就容易实现restful url。struts2是类级别的拦截,一个类对应一个request上下文;实现restful url要费劲,因为struts2 action的一个方法可以对应一个url;而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。spring3 mvc的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架方法之间不共享变量,而struts2搞的就比较乱,虽然方法之间 也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码,读程序时带来麻烦。

 

spring mvc处理ajax请求,直接通过返回数据,方法中使用注解@ResponseBody,spring mvc自动帮我们对象转换为JSON数据。而struts2是通过插件的方式进行处理
 

在SpringMVC流行起来之前,Struts2在MVC框架中占核心地位,随着SpringMVC的出现,SpringMVC慢慢的取代struts2,但是很多企业都是原来搭建的框架,使用Struts2较多。

 

2.5.8 说一下Spring中的两大核心?


Spring是什么?

spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架(相对于重量级的EJB),主要是针对javaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,ibatis框架等组合使用。

 

IOC(Inversion of Control )或DI(Dependency Injection)
       IOC控制权反转

          原来:我的Service需要调用DAO,Service就需要创建DAO

          Spring:Spring发现你Service依赖于dao,就给你注入.

       核心原理:就是配置文件+反射(工厂也可以)+容器(map)  

AOP:面向切面编程
       核心原理:使用动态代理的设计模式在执行方法前后或出现异常做加入相关逻辑。

      我们主要使用AOP来做:

      1、事务处理

      2、权限判断

      3、日志

      4、....

 

2.5.9 AOP是什么?你都拿它做什么?


AOP:面向切面编程
       核心原理:使用动态代理的设计模式在执行方法前后或出现异常做加入相关逻辑。

      我们主要使用AOP来做:

      1、事务处理 执行方法前,开启事务、执行完成后关闭事务、出现异常后回滚事务

      2、权限判断 在执行方法前,判断是否具有权限

      3、日志     在执行前进行日志处理

      4、....

 

2.5.10讲一下Spring的事务传播特性


多个事务存在是怎么处理的策略

PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启
2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
3. PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4. PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
5. PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
7. PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
 

 

Propagation

Required 需要 如果存在一个事务,则支持当前事务。如果没有事务则开启

Supports 支持 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行

Mandatory 必要的 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。

required_new 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。

Not_support 总是非事务地执行,并挂起任何存在的事务。

Never 绝不 总是非事务地执行,如果存在一个活动事务,则抛出异常

Nested 嵌套的 如果有就嵌套、没有就开启事务


 

2.5.11 Spring事务的隔离级别


1. ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
另外四个与JDBC的隔离级别相对应
2. ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
      这种隔离级别会产生脏读,不可重复读和幻像读。
3. ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
4. ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
      它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。

除了防止脏读,不可重复读外,还避免了幻像读。

 

其中的一些概念的说明:

  脏读: 指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据, 那么另外一 个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

不可重复读: 指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。 那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

 幻觉读: 指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及 到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

 

2.5.12 什么是ORM?


 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。那么,到底如何实现持久化呢?一种简单的方案是采用硬编码方式(jdbc操作sql方式),为每一种可能的数据库访问操作提供单独的方法。

这种方案存在以下不足:

    1.持久化层缺乏弹性。一旦出现业务需求的变更,就必须修改持久化层的接口

    2.持久化层同时与域模型与关系数据库模型绑定,不管域模型还是关系数据库模型发生变化,都要修改持久化曾的相关程序代码,增加了软件的维护难度。

 

   ORM提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。Java典型的ORM框架有:Hibernate,ibatis(mybatis),speedframework。

   ORM的方法论基于三个核心原则:

  简单:以最基本的形式建模数据。

  传达性:数据库结构被任何人都能理解的语言文档化。

  精确性:基于数据模型创建正确标准化了的结构。

 

   对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。可以简单的方案是采用硬编码方式(jdbc操作sql方式),为每一种可能的数据库访问操作提供单独的方法。这种方法存在很多缺陷,使用

使用ORM框架(为了解决解决面向对象与关系数据库存在的互不匹配的现象的框架)来解决. 

   Hibernate,ibatis(mybatis),

 

2.5.13 iBatis(mybatis)与Hibernate有什么不同?


相同点:

   都是java中orm框架、屏蔽jdbc api的底层访问细节,使用我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。jdbc api编程流程固定,还将sql语句与java代码混杂在了一起,经常需要拼凑sql语句,细节很繁琐。

   ibatis的好处:屏蔽jdbc api的底层访问细节;将sql语句与java代码进行分离;提供了将结果集自动封装称为实体对象和对象的集合的功能.queryForList返回对象集合,用queryForObject返回单个对象;提供了自动将实体对象的属性传递给sql语句的参数。

   Hibername的好处:Hibernate是一个全自动的orm映射工具,它可以自动生成sql语句,并执行并返回java结果。

 

不同点:

   1、hibernate要比ibatis功能强大很多。因为hibernate自动生成sql语句。

   2、ibatis需要我们自己在xml配置文件中写sql语句,hibernate我们无法直接控制该语句,我们就无法去写特定的高效率的sql。对于一些不太复杂的sql查询,hibernate可以很好帮我们完成,但是,对于特别复杂的查询,hibernate就很难适应了,这时候用ibatis就是不错的选择,因为ibatis还是由我们自己写sql语句。

  ibatis可以出来复杂语句,而hibernate不能。

   3、ibatis要比hibernate简单的多。ibatis是面向sql的,不同考虑对象间一些复杂的映射关系。

 

2.5.14 Hibernate映射对象的状态


临时状态/瞬时状态(transient):刚刚用new语句创建,没有被持久化

不处于session中(没有使用session的方法去操作临时对象)。该对象成为临时对象

 

持久化状态/托管状态(persistent):已经被持久化,加入到session的缓存中。session是没有关闭该状态的对象为持久化对象。

 

游离状态/脱管状态(detached):已经被持久化,但不处于session中。

该状态的对象为游离对象。

 

删除状态(removed):对象有关联的ID,并且在Session管理下,但是已经被计划(事务提交的时候,commit())删除。如果没有事务就不能删除

 

相互转换

 

2.5.15 介绍一下Hibernate的缓存?
 

一、why(为什么要用Hibernate缓存?)

 

Hibernate是一个持久层框架,经常访问物理数据库。

 

为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。

 

缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。

 

为了提供访问速度,把磁盘或数据库访问变成内存访问。

 

二、what(Hibernate缓存原理是怎样的?)Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存。

 

1.Hibernate一级缓存又称为“Session的缓存”。

Session缓存内置不能被卸载,Session的缓存是事务范围的缓存(Session对象的生命周期通常对应一个数据库事务或者一个应用事务)。

一级缓存中,持久化类的每个实例都具有唯一的OID。

 

2.Hibernate二级缓存又称为“SessionFactory的缓存”。

由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。

第二级缓存是可选的,是一个可配置的插件,默认下SessionFactory不会启用这个插件。

Hibernate提供了org.hibernate.cache.CacheProvider接口,它充当缓存插件与Hibernate之间的适配器。

 

面试:

   Hibernate中的缓存分一级缓存和二级缓存。

   一级缓存就是Session级别的缓存,在事务范围内有效是,内置的不能被卸载。二级缓存是SesionFactory级别的缓存,从应用启动到应用结束有效。是可选的,默认没有二级缓存,需要手动开启。

    保存数据库后,在内存中保存一份,如果更新了数据库就要同步更新。

 什么样的数据适合存放到第二级缓存中?

很少被修改的数据 帖子的最后回复时间 
经常被查询的数据   电商的地点
2) 不是很重要的数据,允许出现偶尔并发的数据   

3) 不会被并发访问的数据   

4) 常量数据

扩展:hibernate的二级缓存默认是不支持分布式缓存的。使用memcahe,redis等中央缓存来代替二级缓存。

 

2.5.16 简单讲一下webservice使用的场景?


webservice是一个SOA(面向服务的编程)的架构,它是不依赖于语言,不依赖于平台,可以实现不同的语言间的相互调用,通过Internet进行基于Http协议的网络应用间的交互。

 

异构系统(不同语言)的整合
不同客户端的整合 浏览器、手机端(android,ios.塞班)、微信单、PC端等终端来访问


实实在在的列子:
       天气预报:可以通过实现webservice客户端调用远程天气服务实现的。

       单点登录:一个服务是所有系统的登录

 

2.5.17 简单介绍一下activiti?


    Activiti是一个业务流程管理(BPM)和工作流系统,适用于开发人员和系统管理员。其核心是超快速,稳定的BPMN2流程引擎。它易于与 Spring集成使用。

 

主要要在OA中,把线下流程放到线上。 把现实生活中一些流程固话定义到系统中,然后通过输入表单数据完成业务。

 

他可用在OA系统的流程管理中:

请假流程 小于三天,一级主管审批,大于三天二级才能审批。

报销流程 1000 2000 3000>....

 

如果你想找专门这方面的工作,要下去复习。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

被开发耽误的大厨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值