万字长文入门SpringBoot,你的入门指南(二)(IOC,AOP,Redis,Spring Secur ...

打印 上一主题 下一主题

主题 1528|帖子 1528|积分 4584

前言

本文继承对SpringBoot其中知识点做一个总结,参考册本刘水镜的SpringBoot 趣味实战课,仅作为笔记举行参考。
单元测试



  • 无副作用:单元测试不能对业务代码造成影响
  • 可重复运行:多次运行结果一致
  • 独立且完备:单元测试不依赖外部环境或其他模块的代码
    独立且完备,是说我们要为Service层的一个方法写单元测试,那么在运行这个单元测试时,就不能真的去访问数据库。对此 ,我们需要使用Mock。
Junit

Junit是Java的一个单元测试框架,也是Spring Boot默认的单元测试工具。

常用注解:

示例:

运行这个测试类,控制台会打印出雷同如下的日志:

使用测试,在UserServiceImpl类中按Ctrl+Shift+T快捷键,并在弹出的菜单中选择Create New Test命令

利用Spring Boot中默认的Mock框架——Mockito来写测试代码:
  1. @SpringBootTest
  2. class UserServiceImplTest {
  3.     @InjectMocks
  4.     UserServiceImpl userService;
  5.     @Mock
  6.     UserMapper userMapper;
  7.     @Test
  8.     @DisplayName("Test Service getById")
  9.     void getById() {
  10.         when(userMapper.selectById(1)).thenReturn(new User().setName("wxchyy").setEmail("wxchyy@mail.com"));
  11.         User user = userService.getById(1);
  12.         assertEquals("wxchyy", user.getName());
  13.     }
  14. }
复制代码
非常处置惩罚

Java中有非常完备的非常机制,所有的非常类型都有一个共同的“先人”——Throwable。这里就不重复累述了,有需要的可以去JAVA根本知识总结(万字长文一文了解JAVA所有根本知识)(上)。

为了使我们的非常返回更加清晰,我们需要两个辅助类——MessageEnum和Result。
MessageEnum类

Result类

结果:


日志

日志记录的是程序的运行情况,包罗用户的各种操作、程序的运行状态等信息。日志框架中其实还有两个更具体的分类——日志门面和日志实现。日志门面定义接口,日志实现接口。这里介绍一种:SLF4J+Logback。
在application.yml文件中举行一些简单的配置:

输出:

IOC

IOC(Inversion of Control,控制反转)并不是一种技能,而是一种编程思想,最常见的实现方式叫作“依赖注入”​,还有一种方式叫作“依赖查找”​。传统的程序设计中,对象之间的依赖关系通常由对象自身来创建和管理。例如,一个类在其内部直接创建它所依赖的其他对象。而 IOC 的核心思想是将对象的创建和依赖关系的管理控制权从对象本身转移到外部容器(如 Spring 容器)。也就是说,对象不再自行创建和管理其依赖的对象,而是由外部容器负责创建和注入这些依赖对象。
依赖查找
依赖查找会自动获取,在需要的时间通过调用框架提供的方法来获取对象,而且在获取时需要提供相关的配置文件路径、key等信息来确定获取对象的状态。
依赖注入
依赖注入则会被动地等待容器为其注入依赖对象,由容器通过类型大概名称将被依赖对象注入相应的对象中。
 有了IOC就不需要手动创建对象了,只需要通过@Autowired注解即可,别的 IOC 可以将对象之间的依赖关系从代码中分离出来,降低对象之间的耦合度。
使用IOC以后,类通过容器摆脱了对具体实现的依赖,只对接口有依赖。依赖抽象的接口,屏蔽具体的实现,可以降低代码的耦合度,也很符合面向对象设计的依赖倒转原则。
AOP

概念

AOP(Aspect Oriented Programming,面向切面的程序设计)也是一种编程思想。Spring AOP 主要接纳动态署理的方式,有两种具体实现:
JDK 动态署理:基于接口实现,署理对象实现了目标对象的接口。当调用署理对象的方法时,会先实行切面的逻辑,再调用目标对象的方法。
CGLIB 动态署理:通过继承目标对象的类来生成署理对象,适用于没有实现接口的类。
具体可检察JAVA根本知识总结(万字长文一文了解JAVA所有根本知识)(并发+反射+注解+动态署理+类加载器)(下)
AOP用在什么地方



  • Advice(通知)​:想要让AOP做的变乱
  • JoinPoint(毗连点)​:允许AOP通知的地方,比如,在方法被调用前查抄权限,这个“方法被调用前”就是一个JoinPoint
  • Pointcut(切入点)​:用于筛选JoinPoint的条件。只有符合Pointcut条件的JoinPoint才会实行Advice
  • Aspect(切面)​:一个包含Advice和Pointcut的集合,完备地定义了符合什么条件时做什么事。
  • Before:在目标方法实行前调用Advice
  • After[finally]​:在目标方法实行完成后调用Advice
  • After-Returning:在目标方法乐成实行后调用Advice
  • After-Throwing:在目标方法抛出非常后调用Advice
  • Around:一般表明为围绕/包裹目标方法调用Advice,是可定制化调用的Advice
    Before可以在目标方法实行前做一些变乱,如解析哀求参数、举行权限查抄等;After可以在目标方法实行完成跋文录一些日志;After-Returning可以与Before配合计算目标方法实行时间;After-Throwing可以在目标方法抛出非常后做一些处置惩罚;而Around基本上可以做以上所有的变乱。根据AOP的本事与特点,我们通常会在以下场景中使用AOP:
  • 参数查抄
  • 日志记录
  • 非常处置惩罚
  • 性能统计
  • 事务控制
  • 缓存处置惩罚
  • 权限控制
使用AOP

1 引入依赖

2 创建AspectController类:

3 创建一个切面类WebAspect,在类上加上@Aspect注解,可以用来标识该类为一个AOP的切面

4 日志记录,在AspectController类中添加如下接口

5 WebAspect类中添加如下代码

before方法中获取了哀求路径(path)​、完备类路径、目标方法名、参数信息。after方法中打印了目标方法名。afterReturning方法中打印了目标方法的返回值。@Pointcut用来定义切点;execution是用来匹配毗连点的实行方法;public代表要匹配访问权限为public的方法;第一个代表返回值为任意类型;com.shuijing.boot.aop为包路径;第二个代表前面包路径下的任意类;第三个*代表任意方法;​(…)代表任意参数。
日志的输出内容

SpringBoot的启动流程


核心代码就只有一个@SpringBootApplication注解和SpringApplication.run(HelloApplication.class,args)。我们沿着run方法来“顺藤摸瓜”​,进入SpringApplication类,看看run方法的具体实现:


Spring Boot应用启动时做的所有操作都在这个方法内里。固然,在调用上面这个run方法之前,还创建了一个SpringApplication实例。注意:先通过静态方法的run,再静态方法中创建对象实例,实行上面的run实例方法。可以看到,方法的返回值类型为ConfigurableApplicationContext,这是一个接口,我们真正得到的是AnnotationConfigServletWebServerApplicationContext实例。通过类名可知,这是一个基于注解的Servlet Web应用上下文。
总结一下:
1 启动入口
Spring Boot 应用的启动通常从一个包含 main 方法的类开始,这个类会使用 @SpringBootApplication 注解,它是一个组合注解,包含了 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 注解。在 main 方法中,调用了 SpringApplication.run 方法来启动 Spring Boot 应用。


  • @SpringBootConfiguration表示被注解的元素为一个Spring Boot配置类
  • @EnableAutoConfiguration负责开启自动配置的注解
  • @ComponentScan用于配置扫描的包路径
    2 创建 SpringApplication 实例
    SpringApplication.run 方法内部会创建一个 SpringApplication 对象,该对象负责应用的启动和配置。在创建 SpringApplication 对象的过程中,会举行以下操作:
  • 推断应用类型:判断应用是 Web 应用(如 Servlet Web 应用或 Reactive Web 应用)还是非 Web 应用。
  • 查找并加载初始化器(ApplicationContextInitializer):初始化器可以在应用上下文刷新之前对其举行定制化配置。
  • 查找并加载监听器(ApplicationListener):监听器可以监听应用启动过程中的各种变乱,如应用启动变乱、应用停当变乱等。
  • 推断主应用类:确定包含 main 方法的类。
    3 调用 run 方法
    创建 SpringApplication 对象后,会调用其 run 方法,该方法是启动流程的核心,主要实行以下步调:
  • 发布启动变乱:在启动过程开始时,会发布一个 ApplicationStartingEvent 变乱,通知所有监听器应用正在启动。
  • 配置环境:
    创建并配置 Environment 对象:Environment 对象包含了应用的配置信息,如系统属性、环境变量、配置文件等。
    加载配置文件:Spring Boot 会自动加载 application.properties 或 application.yml 等配置文件,并将其中的配置信息加载到 Environment 对象中。
    发布 ApplicationEnvironmentPreparedEvent 变乱:通知监听器应用的环境已经准备好。
  • 创建应用上下文
    根据之前推断的应用类型,创建相应的应用上下文(ApplicationContext)。例如,如果是 Servlet Web 应用,会创建 AnnotationConfigServletWebServerApplicationContext;如果是非 Web 应用,会创建 AnnotationConfigApplicationContext。
    发布 ApplicationContextInitializedEvent 变乱:通知监听器应用上下文已经初始化。
  • 准备应用上下文
    应用初始化器:调用之前加载的初始化器对应用上下文举行定制化配置。
    注册主应用类:将包含 main 方法的类注册到应用上下文中。
    发布 ApplicationPreparedEvent 变乱:通知监听器应用上下文已经准备好。
  • 刷新应用上下文
    创建并启动嵌入式服务器:如果是 Web 应用,会根据配置创建相应的嵌入式服务器(如 Tomcat、Jetty 等),并启动服务器。
    扫描并注册 Bean:Spring Boot 会扫描项目中的类,将带有 @Component、@Service、@Repository 等注解的类注册为 Bean。
    自动配置:根据 @EnableAutoConfiguration 注解,Spring Boot 会根据项目的依赖和配置,自动配置一些常用的组件和功能,如数据源、日志框架等。
    发布 ApplicationStartedEvent 变乱:通知监听器应用已经启动。
  • 调用 CommandLineRunner 和 ApplicationRunner
    如果项目中定义了实现 CommandLineRunner 或 ApplicationRunner 接口的 Bean,Spring Boot 会在应用启动完成后调用它们的 run 方法,实行一些初始化操作或命令利用命。
  • 发布 ApplicationReadyEvent 变乱
    通知监听器应用已经准备好接收哀求。
    4 启动完成
    当上述步调都实行完毕后,Spring Boot 应用就乐成启动,嵌入式服务器开始监听指定的端口,等待客户端的哀求。
Redis

Redis(Remote Dictionary Server,远程字典服务)是一个开源的、使用ANSI C语言编写、支持网络、基于内存且可持久化的key-value数据库,提供多种语言的API及丰富的数据布局。Redis的实行速度十分快,最根本的缘故原由就是它是基于内存的。
Redis的用途

1 缓存
Redis的读写速度快,所以对于一些查询频率很高但修改很少的数据来说,使用Redis举行缓存再合适不外。
2 排行榜
Redis支持有序集合的(zset)的数据布局,可以实现各种复杂的排行榜应用。
3 计数器
使用Redis的incr命令可以实现累加功能,不光性能好,而且能从容应对高并发的哀求。
4 交际关系
Redis支持Hash类型数据,对于点赞列表、收藏列表、关注列表、粉丝列表等举行存储都有很好的性能。
5 消息队列
Redis 支持发布 / 订阅模式,允许用户将消息发布到指定的频道,其他订阅了该频道的用户可以接收到这些消息。这种模式可以用于实现一般的消息队列功能。
5 分布式锁
Redis利用其性能上风、具有原子性的命令SETNX,大概借助Lua脚本可以实现分布式锁的功能。
Redis使用

1 安装Redis

Windows系统可以直接去官网下载。
2 启动

Redis的默认端口是6379,别的lettuce是Spring Boot默认的Redis客户端。我们将它的依赖移除,换成我们最常用的Jedis依赖。在pom.xml文件中添加如下依赖:

添加配置,添加完依赖以后,我们还需要添加一些Redis的相关配置,以便我们的工程找到刚刚安装的Redis。具体配置如下:


启动工程,访问RedisController中的hello接口,可以看到接口返回了字符串world。然后我们去Redis中验证一下,在命令行中使用get命令来看一看key为hello的值:

SpringSecurity

Spring Security 是 Spring 生态系统中用于提供身份验证和授权功能的强大框架。它为 Java 应用程序,尤其是基于 Spring 的应用,提供了全面且可定制的安全解决方案。
它的核心功能主要是身份验证和授权。
身份验证(Authentication):这是确认用户身份的过程。Spring Security 支持多种身份验证方式,如基于表单的登录、HTTP 基本认证、OAuth、OpenID Connect 等。例如,在 Web 应用里,用户输入用户名和密码登录时,Spring Security 会验证这些信息是否与存储的用户凭证匹配。
授权(Authorization):在用户通过身份验证后,Spring Security 会根据用户的角色和权限来决定其是否有权限访问特定的资源或实行特定的操作。比如,管理员角色可能有权限访问所有功能,而普通用户只能访问部分功能。
工作原理:
Spring Security 基于 Servlet 过滤器(在 Servlet 应用中)或 WebFilter(在 WebFlux 应用中)构建。当有哀求进入应用时,这些过滤器会拦截哀求并举行相应的安全查抄。主要的工作流程如下:


  • 哀求拦截:过滤器链会拦截所有进入应用的哀求。
  • 身份验证:过滤器会查抄哀求中是否包含用户的身份信息(如用户名和密码),如果有,则举行身份验证。
  • 授权查抄:如果用户通过了身份验证,过滤器会根据用户的角色和权限查抄用户是否有权限访问哀求的资源。
  • 哀求处置惩罚:如果用户通过了身份验证和授权查抄,哀求会被转发到相应的控制器举行处置惩罚。
    认证:SpringSecurity从最基本的HTTP Basic到常用的HTTP Form,再到LDAP、OpenID及OAuth等,Spring Security险些支持市面上所有主要的认证方式。
    授权:
    实现授权的配置
    使用HttpSecurity举行配置。
    实现授权的注解

    需要配合@EnableGlobalMethodSecurity一起使用。
    使用:
    1 添加依赖

    2 访问网址时

    因为我们没有举行任何配置,所以Spring Security会创建一个默认值,用户名为user,密码为一串随机字符串,而且会在项目的启动日志中打印出来:

    密码是随机的,每次都不相同。
    2 自定义用户
    继承WebSecurityConfigurerAdapter类,实现安全配置:

    在内存中定义一个用户(用户名为shuijing,密码为123456)​,使用BcryptPasswordEncoder对其举行加密。
RabbitMQ

MQ(Message Queuing,消息队列)最初是为了解决金融行业的特定业务需求而诞生的。RabbitMQ 是一个开源的消息署理软件,也就是消息队列中间件,它实现了高级消息队列协议(AMQP),被广泛应用于各种分布式系统中。
RabbitMQ架构模型总体可以分为客户端和服务端两部分。客户端包罗生产者和消费者;服务端包罗虚拟主机、交换器及队列。两者通过毗连和信道举行通讯。

整体的流程很简单:生产者(Producer)将消息发送到服务端(Broker)​,消费者(Consumer)从服务端获取对应的消息。固然,生产者在发送消息前需要先确定发送给哪个虚拟主机(Virtual Host)的哪个交换器(Exchange)​,再由交换器通过路由键(Routing Key)将消息转发给与之绑定(Binding)的队列(Queue)​。最后,消费者到指定的队列中获取本身的消息举行消费。
核心概念

生产者:生产者是消息的发送方,将要发送的信息封装成肯定的格式,发送给服务端。消息通常包罗消息体(payload)和标签(label)​。
消费者:消费者是消息的接收方,负责消费消息体。
虚拟主机:虚拟主机用来对交换器和队枚举行逻辑隔离。在同一个虚拟主机下,交换器和队列的名称不能重复。这一点雷同于Java中的package,在同一个package下,不能出现相同名称的类或接口。
交换器:交换器负责接收生产者发来的消息,并根据规则分配给对应的队列。它不生产消息,只是消息的搬运工。
队列:队列负责存储消息。生产者发送的消息会被存放到这里,而消费者从这里获取消息。
毗连和信道

毗连和信道(Connection&Channel)是客户端与服务端通讯的桥梁。在发送和接收消息时,都需要通过毗连和信道与服务端通讯。毗连和信道的关系如图12-2所示,一个毗连包含了多条信道。毗连就是TCP毗连(AMQP毗连是通过TCP实现的)​,在毗连的根本上可以创建信道。毗连是线程共享的,但信道是私有的。

信道的主要作用是为了包管线程之间互不干扰,同时节约资源。如果不消信道的话,全用AMQP毗连,会淹灭大量操作系统资源,因为毗连是TCP毗连,需要三次握手才气毗连,遇到高并发的情况,每个线程都需要创建一个毗连就会极大的淹灭资源。
业务场景

第一,消息队列天生具备异步处置惩罚的功能。第二,消息队列可以作为系统之间的沟通桥梁,且不受系统技能栈束缚。第三,队列的特性可以给高并发的业务提供缓冲。
异步处置惩罚:在传统的同步处置惩罚模式下,一个使命的实行需要等待另一个使命完成后才气继承,这会导致整体处置惩罚流程的壅闭。而消息队列允许生产者将消息发送到队列后,无需等待消费者处置惩罚完消息就可以继承实行后续操作,实现了使命的异步实行。
系统解耦:消息队列可以作为系统之间的沟通桥梁,且不受系统技能栈束缚。在一个大型的分布式系统中,各个子系统之间可能存在复杂的依赖关系。如果直接举行系统间的调用,当某个子系统发生变化或出现故障时,会影响到与之相关的其他子系统。消息队列通过引入一个中间层,使得生产者和消费者之间不需要直接通讯,而是通过消息队枚举行解耦。生产者只需要将消息发送到队列,消费者从队列中获取消息举行处置惩罚,两者之间的依赖关系被弱化。
缓冲削峰:队列的特性可以给高并发的业务提供缓冲。在高并发场景下,系统可能会面对瞬间的大量哀求,如果直接处置惩罚这些哀求,可能会导致系统资源耗尽、性能降落甚至瓦解。消息队列可以作为一个缓冲区,将大量的哀求临时存储在队列中,然后由消费者按照肯定的速率从队列中取出消息举行处置惩罚,从而平滑了哀求的流量,克制了系统因瞬间高并发而瓦解。
工作模式

RabbitMQ支持7种工作模式



  • 简单模式:这是 RabbitMQ 最根本的工作模式。生产者直接将消息发送到指定的队列,而消费者则从该队列中接收消息。在这个过程中,消息的流转路径非常简单直接,没有复杂的路由逻辑。

  • 工作队列模式:也被称为使命队列模式。多个消费者可以同时订阅同一个队列,队列中的消息会按照肯定的规则(如轮询)分发给差别的消费者举行处置惩罚。这样可以实现使命的负载均衡,进步系统的处置惩罚本事。

  • 广播模式:生产者将消息发送到扇形交换器(Fanout Exchange),扇形交换器会将接收到的消息广播到所有与之绑定的队列中,每个绑定的队列都可以有一个或多个消费者,这些消费者都能接收到相同的消息。

  • 路由模式:生产者将消息发送到直连交换器(Direct Exchange),并在发送消息时指定一个路由键(Routing Key)。直连交换器会根据这个路由键将消息路由到绑定键(Binding Key)与之匹配的队列中。

  • 动态路由模式:也称为主题模式。动态路由模式可以被看作路由模式的升级版。生产者将消息发送到主题交换器(Topic Exchange),并指定路由键。主题交换器根据路由键和绑定键的匹配规则举行消息路由,绑定键可以使用通配符(* 匹配一个单词,# 匹配零个或多个单词),使得消息的路由更加灵活。



  • 远程模式:在这种模式下,客户端(生产者)发送一个哀求消息到队列,并在消息中指定一个回调队列。服务器端(消费者)从队列中获取哀求消息举行处置惩罚,然后将处置惩罚结果发送到回调队列中,客户端从回调队列中获取处置惩罚结果。
  • 生产者确认模式:这是一种用于包管消息可靠发送的模式。在生产者确认模式下,生产者将消息发送到 RabbitMQ 后,RabbitMQ 会向生产者发送一个确认消息,告知生产者消息是否乐成接收。生产者可以根据这个确认消息来判断消息是否发送乐成,如果发送失败,可以举行重试等操作。
Web管理端

1 启动RabbitMQ服务,访问http://localhost:15672

2 RabbitMQ为我们提供了一个默认账号,用户名和密码都是guest。我们用这个账号登录后,会进入概览界面

实践

1 添加依赖
Spring Boot集成RabbitMQ需要添加对应的Starter依赖

2 添加配置
在application.yml文件中添加如下配置

3 简单模式
生产者:

Spring Boot为我们封装了操作RabbitMQ的工具——RabbitTemplate。我们在之前学习Redis时也打仗到了RedisTemplate。这两个工具都是将基本的操作举行了进一步封装,让我们用起来更加方便。
消费者:

参数

输出:

Elasticsearch

Elasticsearch 是一个基于 Apache Lucene 构建的开源、分布式、RESTful 搜刮引擎,在数据搜刮、分析和存储方面表现出色,被广泛应用于日志分析、全文搜刮、商业智能等众多领域。
Elasticsearch这类搜刮引擎在搜刮方面之所以可以或许胜过传统的关系型数据库,最重要的一点是两者的索引方式差别。传统关系型数据是正排索引,而Elasticsearch是倒排索引。
传统的正向索引是从文档到词的映射,即记录每个文档包含哪些词。而倒排索引则是从词到文档的映射,它记录了每个词在哪些文档中出现过以及出现的位置等信息。通过倒排索引,Elasticsearch 可以快速定位到包含特定词的文档,大大进步了搜刮服从。
使用

1 下载Elasticsearch安装包,并解压缩后进入bin目次,然后运行elasticsearch.bat,访问http://localhost:9200。

看到雷同于上面的返回值,就阐明Elasticsearch已经乐成启动。
2 添加依赖

3 添加配置

4 创建文档实体类



  • @Document:用来标识文档实体类。indexName属性用来指定索引名称;createIndex=false表示不自动创建索引,默认为true,建议关闭生产环境
  • @Id:用来指定索引的Id字段,注意是org.springframework.data.annotation.Id
  • @Field:用来标识文档的字段。store属性用来设置是否持久化;analyzer用来指定索引的分词器;searchAnalyzer用来指定查询时的分词器,如果没有,则使用analyzer
Elasticsearch的CRUD

Spring Boot提供了专门用于Elasticsearch的Repository——ElasticsearchRepository。创建ArticleRepository并继承ElasticsearchRepository:


创建Controller
在ElasticsearchController中依次调用ArticleRepository的各个方法:

注意,使用之前记得先创建索引。
数据同步

在通常情况下,Elasticsearch不负责生产数据,一般都会先将数据同步到Elasticsearch,然后由Elasticsearch完成搜刮。开发人员涉及最多的场景就是将数据库的数据同步到Elasticsearch。同步可以分为两种类型:一种是全量同步;另一种是增量同步。全量同步通常只会举行一次,是在初始同步时举行的。之后,数据库发生增加、删除、修改的操作时,只会将变化同步过去,这就是增量同步了。增量同步可以接纳定时同步或及时同步的方案来实现。
1 定时同步:这种方案很简单,只需要编写定时使命,每隔一段时间就将该时间段产生的变化同步到Elasticsearch即可。Elastic官方提供的Logstash接纳的就是定时同步方案。
2 及时同步:以MySQL为例,及时同步方案一般都是基于binlog实现的。binlog是MySQL记录数据发生增加、删除、修改操作的二进制日志。它可以用来检察数据库的变更汗青、数据库的增量备份和规复,以及MySQL的主/从复制。阿里巴巴的开源MySQL同步组件canal就是基于binlog实现的。
总结

本文总结了包罗IOC,AOP,Redis,Spring Security,RabbitMQ,Elasticsearch。
参考文献

SpringBoot 趣味实战课 刘水镜
写在最后

 如果想要更深入学习JAVA后端开发的内容,关注我,下期更精彩
       请大家肯定肯定要关注!!!
请大家肯定肯定要关注!!!
请大家肯定肯定要关注!!!
友友们,你们的支持是我一连更新的动力~
   
    创作不易,求关注,点赞,收藏,谢谢~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

灌篮少年

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表