南京大学软件学院服务端开发期末整理。

# 多选题

# 核心技术

  1. DI(Dependency Injection)
    1. 保留抽象接口,让组件(Component)依赖于抽象接口,当组件要与其他实际的对象发生依赖关系时,由抽象接口来注入依赖的实际对象
  2. AOP(Aspect Oriented Programming)
    1. 通过预编译方式运行期间动态代理实现程序功能的统一维护的一种技术
    2. 利用 AOP 可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率

# 依赖注入

# AOP

# 横切关注点类型

日志、安全、事务、缓存(可选:继承、委托)

# Advice 注解类型

  • @Before
  • @After:不区分是否正常结束
  • @AfterReturning
  • @AfterThrowing
  • @Around:结合前面所有类型,around 可以是综合,之前、之后、结束、抛异常之后。

被切方法成功运行之后会执行:After、AfterReturning、Around

# AspectJ 可以实现

  1. 指定在哪些方法上切入
  2. 获取参数
  3. 限定包路径
  4. 限定 bean 名称,白名单或黑名单
  5. 限定在特定注解上切入

# Spring MVC

# 谁有 @Component 的效果?

@Controller@Service@Repository 三个注解本身有 @Component 的实例化效果。

# 重定向

控制器处理完成后可以返回逻辑视图名,也可以重定向到其他 url

  • http 状态码:302 FOUND
    • HTTP 状态码 302 表示 “Found”(或者有时被称为 “Temporary Redirect” 临时重定向)。当一个 Web 服务器返回这个状态码时,它通常是在告诉客户端,请求的资源暂时被移动到了一个不同的 URI(统一资源标识符)。
  • 控制器 return redirect:<url>

# Spring MVC 获取参数的几种方式

  1. 表单 (form) 参数,转成 model
    1. 成员类型可能要自己实现 Converter 进行转换
    2. 可以用 @Valid 校验
    3. form 是 html 里定义的
  2. 路径参数
    1. @PathVariable
    2. 例: /book/{id}
  3. 请求参数 / 查询参数
    1. @RequestParam
    2. 例: /challenge?model=2
  4. json 请求体
    1. @RequestBody ,会用到 HttpMessageConverter 消息转换器
    2. Rest API

# Spring Data JDBC/JPA

# 数据表创建和初始化

三种方式,java 代码初始化有两种。

  1. commandlinerunner
  2. applicationrunner

三种方式关于 id 字段的处理:第一种方法需要主动地获取数据库新的 id 是什么,但二 / 三种没有需要

# 脚本

schema.sql 表创建

data.sql 数据初始化

java 代码初始化

CommandLineRunner 接口

ApplicationRunner 接口

# 异常体系

  • SQLException
    • 发现异常很难恢复
    • 难以确定异常类型
  • Hibernate 异常
    • 定义了许多具体异常,方便定位
    • 对业务对象侵入
  • Spring 提供与平台无关的异常
    • DataAccessException
    • 具体异常,方便定位
    • 隔离具体数据库

# SpringData MongoDB\Redis

# Nosql 特点

键值对存储、列存储、文档存储、图形数据库

img

# MongoDB 特点

基于分布式文件存储的开源数据库系统,类似 json 格式存储

MongoDB Shell 是 MongoDB 自带的交互式 Javascript shell, 用来对 MongoDB 进行操作和管理的交互式环境

# MongoDB 概念

img

# Redis 特点

  • 分布式存储
  • 内存数据库,存在内存里,常用于作缓存
  • 可以持久化,但不太重要,不是主要用途
  • 可以集群式部署
    • 主从复制,主机写,从机并发读
  • key-value 的 Hash 表结构,区分大小写

# Redis 数据类型

  1. String
  2. List
  3. Hash
  4. Set

# Redis 指定序列化器

img

mongodb 没有这个要求,因为它会把所有值都转成 json 串。

redis 默认使用 JDK 序列化,但 JSON 序列化更常用。

# Spring Security

划分为两类:

  1. 针对客户 web 请求权限控制
  2. 针对方法级的权限控制

# 实现方法级别的安全

img

# 获取当前登录的用户

img

# 配置属性

# Actuator

  • 提供许多端点 (endpoints), 监测程序运行情况
    • /actuator,查询所有暴露端点
    • /actuator/configprops,查询配置属性
    • /actuator/health,健康检查
    • /actuator/beans,包含 bean 依赖关系
  • 想要获得更多端点如何配置: include: "*" 暴露所有端口,否则只暴露 /actuator/health

# Rest API

  • HTTP 协议的四个操作方式的动词:GET、POST、PUT、DELETE
    • CRUD:Create、Read、Update、Delete
    • GET: Read
    • POST: Create
    • PUT: Update
    • DELETE: Delete

# RequestMapping/

实现控制器的时候 RequestMapping 可以加在?类、方法上面

# RestController

在 Controller 类中:

@RestController  

等价于:

@Controller  
@ResponseBody  

想要某个方法返回的 Java 对象转成 json 格式串@ResponseBody 注解加在特定函数上

# 响应头与响应体

  • 状态行:由 HTTP 协议版本、状态码、状态码描述三部分构成,它们之间由空格隔开。

# OAuth2

解决分布式系统中,权限控制的问题

# 消息中间件

# 定义

  • 提供消息服务的应用程序
  • 主要用于组件之间的解耦,消息的发送者服务知道消息使用者的存在,反之依然

消息代理 broker:

img

# JMS

  • Java Message Service
    • Jms 规定了 ConnectionFactory、Connection、Session 等接口 / 类

# ActiveMQ

支持协议

  • JMS
  • AMQP: Advanced Message Queueing Protocol
  • MQTT: Message Queuing Telemetry Transport

支持 Native 内存模式和 JVM 内存模式

  • Native 可以绕过 JVM,加快访问

消息持久化

分布式架构

# 关键概念

  • Message:类似广播,生产端
  • Destination:队列或主题。消费端
    • 三种指定方式:
      • application.yml(default-destination)
      • @Bean(Destination 对象)
      • 直接 String 指定

# 接收模式

拉取和推送都需要消息转换器做反序列化。

# 3.5.1. 拉取模式 pull model

JmsTemplate 支持

访问 URL

# 3.5.2. 推送模式 push model

需要定义消息监听器

@JmsListener

# RabbitMQ

实现 AMQP 协议

# 概念

  • ConnectionFactory、Connection、Channel
  • Exchange:
    • Default、Direct、Topic、Fanout、Headers、Dead letter
  • Queue
  • Routing key:exchange 根据 routing key 确定消息发往哪个队列
    • JMS 里没有这个概念
  • Binding key

img

一些 gpt 解释:

  1. connectionFactory: 这是一个创建连接到 RabbitMQ 服务器的工厂类。它封装了与 RabbitMQ 服务器连接所需的配置信息,如主机名、端口、用户名和密码。
  2. Connection: 这是客户端和 RabbitMQ 服务器之间的网络连接。ConnectionFactory 用于创建这个连接。
  3. Channel: 一旦建立了连接,Channel 就是在这个连接上进行通信的途径。可以将其视为连接内的一个轻量级会话。在 RabbitMQ 中,几乎所有的操作都是通过 Channel 来完成的,如声明队列、交换机或发布消息等。
  4. Exchange: 交换机是 RabbitMQ 的一个消息路由机制。它指定消息应该如何路由到不同的队列。RabbitMQ 提供了几种类型的交换机:
    1. Default Exchange: 默认交换机是一个直接交换机,用于路由特定 Routing Key 的消息。
    2. Direct Exchange: 直接交换机将消息路由到具有特定 Routing Key 的队列。
    3. Topic Exchange: 主题交换机基于模式匹配 Routing Key 来路由消息。
    4. Fanout Exchange: 扇出交换机将收到的消息广播到它所知道的所有队列。
    5. Headers Exchange: 头交换机基于消息头信息而不是 Routing Key 来路由消息。
    6. Dead Letter Exchange: 死信交换机用于处理无法正常处理的消息,如被拒绝或超时的消息。

# 如何查看镜像中的网卡信息和 ip 地址

docker run --rm -it busybox

cat /etc/hosts

ip a

# Integration

# Service activators

  • MessageHandler
    • 处理完流就截止
  • GenericHandler
    • 有返回值

# Gateways

只需要写一个接口。 Gateways 是应用程序代码和消息系统之间的桥梁。它们抽象了消息发送和接收的细节,使得应用程序代码可以通过方法调用的方式与消息系统交互,而无需直接使用消息 API。这样可以使应用程序代码保持简洁,同时也便于测试。

  • 单向网关
  • 双向网关
    • requets channel 输入
    • repley channel 获得返回值(Spring 会在这个管道上一直等,同步)\

# Channel adapters

Adapters 则是用于将消息从一种格式转换为另一种格式,或者从一种传输协议转换为另一种传输协议。Inbound 把外部系统的消息格式转为 spring integration 消息,outbound 把 spring integration 消息转为外部系统消息。 例如,JMS 适配器可以将 JMS 消息转换为 Spring Integration 通用消息,HTTP 适配器可以将 HTTP 请求和响应转换为 Spring Integration 消息。

img

# 反应式编程

# 操作类型

创建、组合、过滤、转换、逻辑

# WebFlux

# 使用函数式编程范式来定义控制器

HandlerFunction 表示处理接收到的请求并生成响应的函数

RouterFunction 替代了 @RequestMapping 注解。它用于将接收到的请求路由到处理函数

# 函数式编程模型涉及的 4 个类型

RequestPredicate

RouterFunction

ServerRequest

ServerResponse

# 简答题

# Bean 的三种注入方式

# 构造方法

  • 在构造函数上使用 @Autowired 注解,用于自动注入构造函数参数所需要的 Bean。如果构造函数只有一个参数,则可以省略 @Autowired 注解。

@Autowired public MyService(MyDao myDao)

# setter 方法

public void setOrderRepo(OrderRepository orderRepo)

也可以加 Autowired 自动注入.

@Autowired public void setOrderRepo(OrderRepository orderRepo)

# 属性字段下加 @Autowired

@Autowired

private MyBean myBean;

# Spring 配置方案

  1. 自动化配置
  2. javaconfig - 第三方库
  3. XML 配置
  4. 混合配置

img

  1. 自动配置
    1. 组件扫描 component scanning
      • @Configuration 配置类
      • @ComponentScan 从当前包递归找加了注解 @Component 的类,并把它们都实例化出来。表示要在那些包路径下搜索。
    2. 自动装配 autowiring @Autowired
      • 用在构造器
      • 用在属性 setter 方法
      • 用在(私有)属性
      • required=false
        • 在 Spring 框架中使用 @Autowired 注解时, required=false 这一属性表示如果 Spring 容器中没有找到匹配的 bean 来注入,那么它将允许这个属性保持未设置状态,即不会抛出异常。
    3. 需要实例化的 Bean: @Component
      • 默认是单实例模式,在 Spring 上下文永远只有一个实例
      • 可以参数指定多实例,比如 @Component("soundsystem")
  2. Javaconfig
    1. 应用场景:自动化配置行不通,如第三方库
    2. 注解
      • @Configuration
      • @Bean(name=“..”)
    3. 注入
      • 调用方法
      • 通过方法参数自动装配(其他配置类,其他方法创建的 Bean)
    4. 注意与业务逻辑和领域代码分开:只用于创建,不会把业务逻辑放进来
  3. Xml
    1. 格式: <beans><bean>
    2. 无类型检查
    3. 构造器注入
      • constructor-arg
      • c - 命名空间
      • 注入字面量值
      • 注入集合
    4. 属性注入
      • p - 命名空间
      • util - 命名空间
    5. 建议:强依赖使用构造器注入
  4. 混合配置
    1. 在根配置类中导入其他配置类。
    2. JavaConfig 导入
      • @import(配置类,class,...)
      • @importResource(xml文件)
    3. XML 导入
      • <import resource=“xml 文件”/>
      • <bean class=“配置类”/>

# AOP 解决什么问题?开发要点是什么?

  • 通过预编译方式运行期间动态代理实现程序功能的统一维护的一种技术
  • 利用 AOP 可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率

AOP 面向接口的编程,增强模块化,将影响多个模块但又与业务逻辑不直接相关的问题提取出(安全、日志、事务、缓存等),实现横切关注点,提高代码的可维护性和可读性,降低耦合

开发要点:定义通知 Advice、切点 Pointcut、Aspect 等,确定在何时、何处切入,完成什么操作

  1. 通知 Advice:切面要做什么以及何时做
    1. 时间:方法前 / 后…
  2. 切点 Pointcut:指定在何处切
    1. Spring 只支持在方法的前后切
    2. 切点表达式指定逻辑
  3. 切面 Aspect:Advice 和 Pointcut 的结合
    1. 包含切的所有逻辑
  4. 连接点 Join point:方法、字段修改、构造方法
  5. 引入 introduction:引入新的行为和状态
    1. 给对象加入新的方法 / 状态,但不需要实现新的子类。在原来的对象上动态加入新的方法 / 状态。
    2. 引入方式
      • 创建需要增加的接口和实现类
      • 新建一个切面类,加 @Aspect
      • 在里面定义一个新增实现类的 static 接口,加上 @DeclareParents 注解
      • 实例化切面类 Bean
  6. 织入 Weaving:切面应用到目标对象的过程

# Spring Web 开发框架的分层

img

# Web 框架请求的处理过程

# 客户端请求参数分类

  1. 路径参数, @PathVariable
  2. 请求参数(查询参数), @RequestParam
  3. 表单参数,应用于前后端不分离的传统场景,默认,对应 model 对象,可以使用 @Valid 校验
  4. json 请求体,应用于前后端分离的场景,使用 @RequestBodyjson 格式转成 java 对象; @ResponseBody ,把 java 对象转成 json 格式

# 处理流程

  1. 控制器根据 url 捕获请求,控制器解析并获取参数,同时将转向业务层进行处理
  2. 涉及到数据的持久化则访问数据访问层,访问完毕后返回业务层,业务层处理完毕将结果数据返回到控制器
  3. 控制器的响应处理:
    1. 前后端分离:返回 Java 对象,同时加上 @ResponseBody 注解,表示返回的对象将被自动转换为 JSON 或其他格式。前端处理数据并渲染到页面上。
    2. 前后端不分离:控制器将数据赋值给 model 的属性,并返回视图名;根据视图名做视图解析,找到模板路径;通过第三方页面渲染,将 model 数据渲染到最终页面中,返回 html 格式文件。

在类的上方加注解 @RestController

# MVC 架构

Model-View-Controller

MVC 模式用于前后端不分离的开发场景

  • 模型 model:处理业务逻辑;存储、读取、处理数据
  • 控制器 controller:处理用户输入,处理客户端的请求;返回数据(model)
  • 视图 view:显示内容,model 和 view 结合

img

# Spring MVC 的请求过程

img

客户端请求在后端的处理过程,非常重要。 核心 DispatcherServlet,是 Spring 自己实现的 Servlet 容器。

  1. Web 容器开发的基本单元是 Servlet,请求 request 先到 servlet。
  2. spring 框架做参数解析,使用 handler mapping 根据 url 把请求转到对应的 controller。
  3. controller 处理请求和请求参数,把结果传给业务层
  4. 业务层处理业务逻辑,业务层访问数据层;业务层把处理结果返回控制器
  5. 控制器把结果返回给 servlet
  6. servlet 拿到了数据和逻辑视图名,找到视图解析器的第三方库
  7. 视图解析器渲染视图

# 关系型数据访问层开发的三种方法区别、相同点

  1. 使用 jdbcTemplate 简化 JDBC 访问(spring-boot-starter-jdbc)
  2. Spring Data JDBC(spring-boot-starter-data-jdbc)
  3. Spring Data JPA(spring-boot-starter-data-jpa)

区别:

  1. 数据表生成:1、2 需要 scheme.sql 脚本,3 不需要(根据领域类自动生成)
  2. 数据 model 的定义:
    1. 领域类注解:1 不需要为领域类加注解,2、3 要为领域类加注解(提供领域类和表结构的映射关系)
      1. 2:@Table, @Column,@Id
      2. 3: @Entity, @Id
    2. ID 字段的处理:1 需要手动获取数据库生成的 Id,2、3 不需要
  3. 数据库访问层接口 / 自定义查询:
    1. 1 需要自己实现接口;
    2. 2、3 不需要,2、3 都继承自 CrudRepository 接口,且可以自定义查询:
      1. 2、3 都可以使用 @Querry 定义查询逻辑
      2. 但 3 还可以使用基于方法名的 DSL 自定义查询,JPA 在自定义逻辑上更加灵活
      3. List<TacoOrder> findByDeliveryZip(String deliveryZip);
  4. 包路径:2、3 为领域类添加持久化的注解包路径不一样
    1. JPA 中的规范注解都来自 javax.persisitence.* ,因为不是 Spring 自己实现
    2. @Table,对象会基于领域类的名称映射到数据库的表上
    3. @Id
      • 有两个来自不同包的 @Id,注意区别
    4. @Column
特点jdbcTemplateSpring Data JDBCSpring Data JPA
数据表生成需要 scheme.sql 脚本需要 scheme.sql 脚本不需要
数据 model 的定义领域类注解(提供领域类和表结构的映射关系)不需要需要需要
ID 字段的处理手动生成不需要不需要
数据库访问层接口 / 自定义查询接口自己实现继承自 CrudRepository 接口继承自 CrudRepository 接口
@Querry 定义基于方法名的 DSL 自定义查询
包路径JPA 中的规范注解都来自 javax.persisitence.*

# 给了 Spring 框架,开发人员还要做什么?

img

  1. 实现接口
    1. UserDetailsService 接口:给 Spring 框架提供用户详细信息。用户信息注册、存储和查询。
      • 这里用到之前讲到数据访问层实现技术。
      • 和 spring security 解耦,只需要提供用户信息但不关心怎么实现。被 Spring Security 调用
  2. 实现密码加密 / 解密
    1. PasswordEncoder 加密和解密的过程,springboot 提供了若干个
    2. Bean 对象
  3. (optional) 实现登录页面
    1. 有默认页面,也可以自己实现
    2. /login , Spring 已经自动实现了对应的 Controller
  4. 调用 HTTPSecurity 进行权限设置,分为两种:
    1. SecurityFilterChain 实现,基于注入的 httpSecurity 对象
    2. 继承父类 WebSecurityConfigurerAdapter ,实现 configure 方法

PasswordEncoder:密码不能明文存储,需要加密后再存到数据库里

  • 需要定义 Bean

框架帮我们做了哪些事情:

  1. 实现用户登录控制器(get、post)
  2. 请求重定向到用户登录页面
    1. eg. 用户未登录时,访问 URL,服务端重定向到登录页面
  3. 通过 Filter 对设定的权限进行控制(自己只需要做权限的设定即可)

# 用户信息存储

  • 内存用户存储
    • 小型应用
  • JDBC 用户存储
    • 持久化数据
  • LDAP(Lightweight Directory Access Protocol) 用户存储
    • 轻量级目录访问协议,适用于大型企业,支持复杂查询和目录结构

# 属性来源

不仅仅是 Spring Boot 需要的属性,也包含我们自己定义的属性。

常见的是第一个和第四个,第二个和第三个偶尔用

  1. 配置文件 application.ymlapplication.properties ,两个文件可以混用
    1. server.port = 8090
  2. 命令行参数 commandLineArgs ,在程序中直接获取
    1. java -jar taco-cloud-sd-jdbc-0.0.3-SNAPSHOT.jar -- server.port=8081
  3. JVM 系统参数 -D
    1. java -Dserver.port=8091 -jar taco-cloud-sd-jdbc-0.0.3-SNAPSHOT.jar
  4. 操作系统环境变量
    1. set SERVER_PORT=8082、java -jar taco-cloud-sd-jdbc-0.0.3-SNAPSHOT.jar

# 接口设计的关键要求

接口设计的关键要求:

  1. 使用标准动词:GET\POST\PUT\DELETE,映射到 CRUD
  2. 使用 URI 来传达意图,URI 用名词不用动词,表明单复数 tacos
  3. 请求和响应使用 json,基本上大家都是用 json 标准,方便与第三方交流和解决问题
  4. HTTP 状态码表示结果:200、201(created)等

# OAuth2 流程图

img

主体对象:

  1. Authserver 授权服务器:授权、认证
  2. Authorization server 资源服务器
  3. Client application 客户端应用程序
  4. User 用户

过程:

其中使用授权码授权模式

  1. 用户使用第三方的应用程序,也就是客户端应用程序
  2. 客户端发现用户未登录,把用户请求重定向到授权服务器
    1. 授权服务器会维护合法的重定向地址,用于校验
  3. 授权服务器向用户索取用户名密码
  4. 用户名密码匹配,则授权服务器请求用户授权
  5. 授权服务器给客户端程序返回 code,重定向回到应用程序
    1. code 需要经过浏览器
  6. 客户端应用程序用 code 向授权服务器索取 token
    1. 用 code 交换 token
    2. token 不过浏览器,在应用程序服务端和授权服务器之间处理
  7. 客户端在请求头带上 token 调用资源服务器的 API
  8. 资源服务器验证 token,返回结果
    1. 授权服务器会用私钥给 token 签名,资源服务器用公钥验证 token 是否合法
    2. 第一次,资源服务器向授权服务器索取公钥,验证 Token 合法性
    3. Token 未过期时,不用再次索取公钥
    4. Token 过期时,才会重新索取公钥
  9. 客户端程序把结果返回给用户

补充:

密码没有在浏览器来回传送,但是如果没有密码即使拿到 code 也没用

用 code 向授权服务器换取 token 才需要密码

# RabbitMQ 组成

img

  • ConnectionFactory、Connection、Channel
  • Exchange:
    • Default、Direct、Topic、Fanout、Headers、Dead letter
  • Queue
  • Routing key:exchange 根据 routing key 确定消息发往哪个队列
    • JMS 里没有这个概念
  • Binding key

# Reactive Programming 反应式编程解决什么问题?

  • IO 密集型场景
  • 同步阻塞模型,阻塞线程多,CPU 利⽤率不⾼,性能下降 —— 轮询解决
  • 管理多线程,意味着更⾼的复杂性

# Java 的 Stream 和反应式流的区别

Java 的 stream 通常都是同步的,并且只能处理有限的数据集,本质上来说,它们只是使⽤函数来对集合进⾏迭代的⼀种⽅式

JDK9 中的 Flow API 对应反应式流

# 反应式流规范定义的 4 个接口

  • org.reactivestrea
  • ms.*

img

  • Publisher:数据发布者
    • subscribe
  • Subscriber:数据订阅者
    • onSubscribe 第一次建立连接时调用
    • onNext
    • onError
    • onComplete
    • 消费者:如果⽣产者产⽣数据的速度⾮常快,消费者会来不及处理,⽤回压来调节,根据消费者的速度来发送数据。
  • Processor:处理器,既是数据的消费者,也是数据的发布者
  • Subscription:协调者,在 Publisher 和 Subscriber 之间传递消息
    • request
    • cancel

每个环节可能会在不同的线程⾥处理,处理过程是异步的。

消费者驱动,消费者去请求发布者才会发布数据

# Spring Integration 集成流解决什么问题

提供了一系列的组件和配置选项,用于在应用程序中实现消息传递和集成模式。

解耦合、异步处理、轻量级集成

img

img

# map/flatMap

  • map:
    • 同步
    • 返回具体值
  • flatMap
    • 异步
    • 转换出来的返回结果还是一个流(Mono/Flux)
    • 可以并发处理,指定用哪个并发模型处理
      • 多个流并发处理结果合并成一个流,但结果顺序不可控
    • 扁平化
  • 并发模型(Schedulers 方法)
    • .immediate()
    • .single()
    • .newSingle()
    • .elastic()
    • .parallel()
    • img

# docker 的三部分

容器:一个轻量化的虚拟机

  • 哪三部分?
    • docker engine:服务端,管理一系列资源的生命周期,包括容器 containers、images 和 volumns
    • Client:一个命令行程序,和 docker daemon 交互
    • Registry:镜像仓库,上传和下载镜像 images
  • 每一部分由哪些部分构成?

img

# 容器与虚拟机的区别

容器是在 Linux 内核实现的轻量级资源隔离机制

虚拟机是操作系统级别的资源隔离,容器本质上是进程级的资源隔离

# Pod container 与 node 之间的关系

img

# Spring MVC 与 Spring WebFlux 的共性与不同

  • 不同
    • MVC:依赖多线程处理
    • WebFlux:在事件轮询中处理请求
      • 可以使用纯粹的函数式编程实现 Controller
  • 共性
    • WebFlux 也可以使用 Controller、RequestMapping 等注解
      • WebFlux 的参数和返回值可能是流

img

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

quas-modo 微信支付

微信支付

quas-modo 支付宝

支付宝

quas-modo 贝宝

贝宝