硬核!啃透京东大牛的Spring Cloud万字手册,终获月薪25kOffer

154 阅读15分钟

Hello,今天给各位童鞋们分Spring Cloud,赶紧拿出小本子记下来吧!

image.png

一、整体项目结构

1、项目结构

​ 首先我们创建一个父工程,然后其的POM文件是:

image.png 我们当前使用的spring-cloud的版本是Finchley.RELEASE。

二、eureka注册中心使用

1、项目创建

​ 我们再添加一个子模块,用于eureka注册中心

2、项目结构

image.png 可以看到我们的eureka注册中心是很简单的。

3、项目内容

1)、POM文件

<project xmlns="maven.apache.org/POM/4.0.0"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>

    <artifactId>spring-cloud-demo</artifactId>

    <groupId>com.fev</groupId>

    <version>1.0</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>euerka-server1</artifactId>

<dependencies>

    <dependency>

        <groupId>org.springframework.cloud</groupId>

        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>

    </dependency>

</dependencies>

这里引用了spring-cloud-starter-netflix-eureka-server,用于注入eureka客户端内容

2)、application文件

server:

port: 7001

spring:

application:

name: eureka-server1	#应用名称

eureka:

instance:

hostname: localhost #本服务实例名称

client:

#需不需要将自己注册到注册中心 false表示不用,因为其本身就是server

register-with-eureka: false

#需不需要从注册中心获取其他的服务,由于是服务端,其是维护服务的,所以其也为false

fetch-registry: false

service-url:

# 表示注册中心地址,向这个地址来注册实例

  defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

3)、Application类

@SpringBootApplication

@EnableEurekaServer

public class EuerkaServerApplication1 {

public static void main(String[] args) {

    SpringApplication.run(EuerkaServerApplication1.class,args);

}

}

这个也很简单,只需要加@EnableEurekaServer表明这个是eureka服务端

4、启动服务

image.png ​ 我们启动后,就可以输入http://localhost:7001就可以看到eureka服务端的界面了,目前没有向它注册的实例,所以是Noinstances available。

三、eureka客户端

​ 下面我们就编写客户端来向服务端注册

1、项目创建

​ 我们再在父工程创建一个客户端子模块

2、项目结构

image.png

3、项目内容

1)、POM文件

<project xmlns="maven.apache.org/POM/4.0.0"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>

    <artifactId>spring-cloud-demo</artifactId>

    <groupId>com.fev</groupId>

    <version>1.0</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>eureka-client1</artifactId>

<dependencies>

    <dependency>

        <groupId>org.springframework.cloud</groupId>

        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

    </dependency>

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-web</artifactId>

    </dependency>

</dependencies>

这里是引用了一个spring-cloud-starter-netflix-eureka-client来注入eureka客户端相关的内容

2)、application文件

server:

port: 7005

eureka:

instance:

hostname: localhost

instance-id: eureka-client7005

client:

# 从注册中心获取注册信息

fetch-registry: true

# 将本服务注册到注册中心

register-with-eureka: true

service-url:

defaultZone: http://localhost:7001/eureka,http://localhost:7001/eureka

 defaultZone: http://localhost:7001/eureka

spring:

application:

name: eureka-client

3)、Application类

@SpringBootApplication

@EnableEurekaClient

public class EurekaClient1 {

public static void main(String[] args) {

    SpringApplication.run(EurekaClient1.class,args);

}

}

这里和前面注册中心类似,只是这里加的是客户端注@EnableEureClient。

4、启动服务

​ 我们现在启动客户端服务,就可以在7001注册中心看到这个注册的实例了。

image.png

5、拓展说明

​ 上面我们只是简单的说明了注册中心与客户端的关系,不过其实你要使用这种微服务模型,简单是有三个服务。

​ 注册中心:这个用来给所有的客户端注册的。

​ 生产者与消费者:这是表示其他的两个服务,其对于注册中心来说都是客户端,都注册到注册中心。然后这两个客户端之间通过注册中心来相互调用,例如一个服务(消费者)需要去另一个服务(生产者)调用接口获取信息,比如一个客户服务需要调用订单服务获取这个客户的所有订单信息,这个关系也是相对的,也可以是要获取一个订单的具体客户信息。

6、eureka额外设置

1)、eureka客户端配置

server:

port: 7005

eureka:

instance:

hostname: localhost

# 主机ID

instance-id: eureka-client7005

# 展示主机IP

prefer-ip-address: true

client:

# 从注册中心获取注册信息

fetch-registry: true

# 将本服务注册到注册中心

register-with-eureka: true

service-url:

 defaultZone: http://localhost:7001/eureka

spring:

application:

name: eureka-client

这里与前面相比,多了几个属性。一个是instance-id,这个表示的是展示的主机ID,然后prefer-ip-address表示的是需要展示的IP(不然展示的就是hostname主机名称),我们以这个配置启动客户端,再看eureka注册中心页面:

image.png 可以看到我们现在多例一个实例eureka-client7005,就是我们设置的instance-id。这里有一个问题,我们只是重启了服务,但为什么原来的实例名称LAPTOP-QR83QEC0:eureka-client:7005还是存在?这里我们下面再讲。

2)、eureka注册中心的自我保护

​ 这个自我保护就是:我们知道一般在客户端与注册中心之间是通过定时发送心跳包来确定两服务都还是开启的,但可能存在暂时性的网络波动,我们一般不会例如因为一次或短期的几次就判定这个客户端已经挂了,直接将这个注册的服务就删除了,而是注册中心开启自我保护模式,即使发送心跳没有回复我们也先暂时保留,也不是直接删除,这个也是上面重启出现两个实例名称的原因。

​ 下面我们就来关闭这个注册中心的自我保护机制,来让我们自己决定在什么情况下就可以删除注册的客户端了

​ 注册中心的配置:

server:

port: 7001

spring:

application:

name: eureka-server1

eureka:

instance:

hostname: localhost #本服务实例名称

client:

#需不需要将自己注册到注册中心 false表示不用将注册为server,因为其本身就是server

register-with-eureka: false

#需不需要从注册中心获取其他的服务,由于是服务端,其是维护服务的,所以其也为false

fetch-registry: false

service-url:

  defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

server:

# 设置不自我保留false,默认为true

enable-self-preservation: false

# 有多久没有心跳就删除客户端实例 (单位: ms)

eviction-interval-timer-in-ms: 5000

客户端配置:

server:

port: 7005

eureka:

instance:

hostname: localhost

# 主机ID

instance-id: eureka-client7005

# 展示主机IP

prefer-ip-address: true

# 表示多久向注册中心发生一次心跳

lease-renewal-interval-in-seconds: 1

# 心跳的到期时间

lease-expiration-duration-in-seconds: 2

# 主机IP

client:

# 从注册中心获取注册信息

fetch-registry: true

# 将本服务注册到注册中心

register-with-eureka: true

service-url:

 defaultZone: http://localhost:7001/eureka

spring:

application:

name: eureka-client

可以看到这里主要是设置了4个属性,注册中心与客户端分别的两个。这个就能我们自己看着多久删除实例了。

四、OpenFeign使用

​ 首先我们需要知道OpenFeign是用来干什么的,我们使用微服务一般是会在两个服务之间进行服务调用,这个调用一般是可以使用Http请求,但在微服务中我们一般是不会自己自己去写Http请求来获取&解析返回结果的。在微服务的整个生态中一般会有组件来封装这个Http请求,来将这些http请求直接封装为接口调用的形式,让你无感知Http请求,而是直接以接口的方式来获取另一个微服务的返回。OpenFeign就是一种怎样的组件,其就是一个接口形式的Http请求,具体的Http实现由OpenFeign内部实现。

1、项目使用

1)、生产者(被请求方)

​ 我们先在原来的EurekaClient1提供一个Controller方法。

@RestController

public class Client1Controller {

@Value("${server.port}")

private Long nowPort;

@RequestMapping(value = "getClientInfo",method = RequestMethod.GET)

public String getClientInfo(@RequestParam("name") String name)

{

    return name + " request client, now port is : "+ nowPort;

}

}

然后我们启动,请求http://localhost:7005/getClientInfo,就能获取当前的返回:

now port is : 7005

2)、消费者(请求方)

​ 现在我们来创建client2项目,所有的POM&application.yml都与client1项目相同,但我们需要改下配置文件的端口,我们改为7006,以及实例id(instance-id)。下面我们来写下client2的Controller,用其使用OpenFeign来调用client1的方法。

​ 要使用OpenFeign我们首先需要引入maven依赖:

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-openfeign</artifactId>

然后在Application类中添加注解@EnableFeignClients。

然后我们来写Controller&Service的内容:

image.png @FeignClient(name = "eureka-client") // 被调用的服务名称

public interface FeignServiceClient {

@RequestMapping(value = "/getClientInfo",method = RequestMethod.GET)

String getClientInfo(@RequestParam("name") String name);

}

我们可以看到FeignServiceClient,其通过@FeignClient注解,然后写上name被调研的服务名称,再使用接口结合@RequestMapping就是一个接口方法就完成了其他服务的调用,同时这个Http请求对于我们来说是透明的,我们直接面向的是一个接口方法。

​ 然后我们请求http://localhost:7006/client2/getClientInfo?name=client2,就可以看到其调用7005机器的返回:

client2 request client, now port is : 7005

2、openFeign的属性设置

1)、超时配置

我们在client2中添加yml配置:

ribbon:

ReadTimeOut: 5000 #建立连接的超时时间

connectionTimeOut: 5000 # 请求的超时时间

这里为什么是ribbon开头的呢?因为Feign是集成了ribbon。

​ 然后我们需要修改client1中的Controller方法:

@RestController

public class Client1Controller {

@Value("${server.port}")

private Long nowPort;

@RequestMapping(value = "/getClientInfo",method = RequestMethod.GET)

public String getClientInfo(@RequestParam("name") String name)

{

    try {

        Thread.sleep(6000L);

    } catch (InterruptedException e) {

        e.printStackTrace();

    }

    return name + " request client, now port is : "+ nowPort;

}

}

这里我们让它睡眠了6秒钟,我们再通过http://localhost:7006/client2/getClientInfo?name=client2请求client2就会报超时异常了:

java.net.SocketTimeoutException: Read timed out

at java.base/java.net.SocketInputStream.socketRead0(Native Method) ~[na:na]

at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:116) ~[na:na]

at java.base/java.net.SocketInputStream.read(SocketInputStream.java:171) ~[na:na]

at java.base/java.net.SocketInputStream.read(SocketInputStream.java:141) ~[na:na]

at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:246) ~[na:na]

at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:286) ~[na:na]

at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:345) ~[na:na]

五、hystrix的使用

1、hystrix的介绍

​ hystrix是断路器,一般用于方法调用的降级、熔断,而且一般是用于远程调用的,例如结合OpenFeign来使用。

2、项目使用

​ 我们所有前面的client2服务,然后要使用hystrix我们需要引入依赖

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>

1)、简单使用

​ 我们先写一个简单的Service:

image.png 然后我们在原来的Controller再添加另一个方法

@RequestMapping(value = "client2/executionHystrix",method = RequestMethod.GET)

public String executionHystrix()

{

return hystrixClientService.hystrixInfo();

}

我们现在请求http://localhost:7006/client2/executionHystrix,其返回是:

success

下面我们改下service的方法,添加睡眠4秒钟

/**

 * @see @HystrixProperty的其他属性可以看 {https://github.com/Netflix/Hystrix/wiki/Configuration}

 * @return

 */

@HystrixCommand(fallbackMethod = "hystrixExecutionFailure",

commandProperties = {

        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")

})

public String hystrixInfo()

{

    try {

        Thread.sleep(4000L);

    } catch (InterruptedException e) {

        e.printStackTrace();

    }

    return "success ";

}

我们再来请求,前端返回的是

execution failure !!!

后端控制台已经报错了

java.lang.InterruptedException: sleep interrupted

at java.base/java.lang.Thread.sleep(Native Method)

at com.fev.service.HystrixClientService.hystrixInfo(HystrixClientService.java:27)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

同时这里还可以使用一个注解来确定默认的fallback方法DefaultProperties:

image.png ​ 这个如果@HystrixCommand没有指定方法的话就会使用默认的方法。

2)、结合openFeign

​ 我们前面有一个getClientInfo方法是调用client1的,现在我们来结合这个方法(这个方法也会超时,并且配置文件配了超时时间)

@FeignClient(name = "eureka-client")

public interface FeignServiceClient {

@RequestMapping(value = "/getClientInfo",method = RequestMethod.GET)

String getClientInfo(@RequestParam("name") String name);

}

这个方法的注解时使用的FeignClient,不过其实这个与hystrix断路器有规范。我们先实现FeignServiceClient接口:

@Component

public class FeignServiceClientHsytrix implements FeignServiceClient {

@Override

public String getClientInfo(String name) {

    return "FeignServiceClientHsytrix Failure !!!";

}

}

然后再处理FeignServiceClient:

@FeignClient(name = "eureka-client",fallback = FeignServiceClientHsytrix.class)

public interface FeignServiceClient {

@RequestMapping(value = "/getClientInfo",method = RequestMethod.GET)

String getClientInfo(@RequestParam("name") String name);

}

可以看到这个方法我们在FeignClient中添加了其fallback的处理类,这个其实就达到了异常服务降级的解耦。

​ 然后我们在启动的Application类添加@EnableHystrix注解,同时在application.yml添加:

feign:

hystrix:

enabled: true

来开启feign与hystrix的配合:

下面我们请求http://localhost:7006/client2/getClientInfo?name=client2,就可以看到:

FeignServiceClientHsytrix Failure !!!

2)、其他匹配

image.png 上面就是表示,如果在单位时间内容的请求数能有5个并且请求超时失败的比例达到60%,就开启断路器,然后在10后,再来执行判断看是继续熔断,还是服务已经能连通了。

3)、hystrix监控版

​ 我们要使用监控版需要引入:

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>

然后在启动类Application添加@EnableHystrixDashboard,并且还要注入一个bean

image.png 然后我们就能通过http://localhost:7006/hystrix看到监控界面:

image.png 然后我们输入监控的地址http://localhost:7006/hystrix.stream,同时加快延迟时间:

image.png 我们点击就能进入监控界面,(可能会出现界面在leading状态),这个时候我们随便请求下controller的方法就能看到界面了

image.png 然后我们请求多个方法:

image.png 这里橙色表示的是具体的接口,例如我们这里请求了3个方法,就有三块。然后左右的6种红色的就是对应的数量,例如接口请求成功的数量,接口请求失败的数量。关于这个界面具体的介绍已经有很多博文了,这里就不再赘叙了。

六、Spring Cloud Config的使用

1、简单介绍

​ Spring Cloud Config 就是一个配置中心,在微服务集群环境下其是一个配置中心,其他的微服务可以将它的配置文件都放在这里,在启动的时候就统一从配置中心获取。Spring Cloud Config 配置中心可以从git这些项目管理工具上获取,也可以从本地获取。

2、单配置中心项目创建

​ 我们先创建一个新项目作为配置中心服务

1)、POM文件

<project xmlns="maven.apache.org/POM/4.0.0"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>

    <artifactId>spring-cloud-demo</artifactId>

    <groupId>com.fev</groupId>

    <version>1.0</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>eureka-config-server</artifactId>

<dependencies>

    <dependency>

        <groupId>org.springframework.cloud</groupId>

        <artifactId>spring-cloud-config-server</artifactId>

    </dependency>

</dependencies>

2)、bootstrap.yml

​ 我们这里使用的配置文件与前面不同,是命名为bootstrap.yml,项目启动的是是最先加载bootstrap.yml文件,之后再是application.yml以及是其他的配置文件。

server:

port: 8000

spring:

application:

name: config-server

cloud:

config:

# 配置与加载可以查看:https://www.springcloud.cc/spring-cloud-greenwich.html#_spring_cloud_config_server

  server:

    git:

      # git 仓库地址

      uri: https://gitee.com/{你的git名称,目前这个是使用的国内的码云}/spring-config-server.git

      # 搜索的子目录  (注意这里还会同时加载获取上级目录配置文件的内容)

      search-paths: dev

      # 连接超时

      timeout: 10

      # git 账号  公共项目一般不需要

      username:

      # git 密码

      password:

3)、Application类

@SpringBootApplication

@EnableConfigServer

public class EurekaConfigServerApplication {

public static void main(String[] args) {

    SpringApplication.run(EurekaConfigServerApplication.class,args);

}

}

3、git项目创建

image.png 我们去码云创建一个git项目,然后直接在git项目下新建一个application.yml文件。

common:

tag: version-common-1

然后再建一个子目录dev,再在这个目录下面新建两个文件client1.yml、eureka-client.yml(用于之后我们用client1项目连接这个配置中心获取配置文件内容)。

​ client1.yml文件

client:

tag: version1

eureka-client.yml文件

application:

tag: eureka-client-version-1

4、项目测试

​ 现在我们启动项目

1)、eureka-client.yml

​ 我们通过http://localhost:8000/eureka-client.yml,来请求就可以从界面看到我们配置中心eureka-client.yml文件的内容了,同时我们可以看到其也有加载上级目录application.yml文件。

application:

tag: eureka-client-version-1

common:

tag: version-common-1

同时我们也可以通过http://localhost:8000/eureka-client.properties请求来看到另一种格式的内容:

application.tag: eureka-client-version-1

common.tag: version-common-1

2)、client1.yml

​ 不过这里我们再通过http://localhost:8000/client1.yml来查看client1.yml文件时会发现这里会404,并不能读取,这里应该是其内部的访问机制,我们可以通过http://localhost:8000/client1-a.yml,也就是加一个-a的后缀就能访问:

client:

tag: version1

common:

tag: version-common-1

同理通过http://localhost:8000/application-a.yml就能获取到application.yml文件。

common:

tag: version-common-1

5、访问文件的格式拼接

/{application}/{profile}[/{label}]

/{application}-{profile}.yml

/{label}/{application}-{profile}.yml

/{application}-{profile}.properties

/{label}/{application}-{profile}.properties

官方给出的规则是这个:application-应用名称(文件前缀名称)、profile-激活的环境(拼接的后缀),lable-表示分支名称。例如一个项目要加载,可以写两个文件都会加载,例如application.yml、application-dev.yml。

6、其他项目配置中心连接获取

​ 我们以原来的eureka-client1项目来改造

1)、POM依赖

​ 我们新增一个依赖:

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-config</artifactId>

2)、配置文件

​ 我们需要将原来的配置文件名称application.yml修改为bootstrap.yml,同时在配置文件中添加:

spring:

application:

name: eureka-client

cloud:

config:

  # 配置中心地址

  uri: http://localhost:8000

  # 加载的文件名称(一般是应用名称),加载的时候会自动拼接后缀 yml或 properties

  name: eureka-client,client1

name: eureka-client

  # 分支

  label: master

  # 当前激活的环境,也就是格式拼接例如我们可以新建一个文件 eureka-client-dev.yml

  profile: dev

这里的spring.cloud.config.name我们可以加载多个文件

3)controller类

image.png 我们访问这两个路径就能获取到配置的属性值了。

4)、新建eureka-client-dev.yml文件

​ 然后我们再在git仓库的dev子目录下面新建一个文件eureka-client-dev.yml,添加属性:

dev:

eureka-client-dev: eureka-client-dev-version-1

然后我们通过http://localhost:8000/eureka-client-dev.yml访问:

application:

tag: eureka-client-version-1

common:

tag: version-common-1

dev:

eureka-client-dev: eureka-client-dev-version-1

可以看到其会同时获取到eureka-client.yml文件的内容,但并没有获取client1文件的内容。同时我们使用http://localhost:8000/eureka-client.yml访问的时候没有eureka-client-dev.yml的内容。

5)、eureka-client1项目改造

image.png ​ 我们再修改Controller:

然后重启通过http://192.168.152.1:7005/getClientDev就能直接获取到eureka-client-dev-version-1。不用再修改bootstrap.yml的name为eureka-client,client1,eureka-client-dev。

7、配置中心集成到eureka注册中心

​ 我们前面的demo是简单的使用,并没有依赖eureka注册中心,例如我们的配置中心并没有注册到注册中心,同时我们的eureka-client1项目获取配置中心是直接用http://localhost:8000这个url。现在我们就来将配置中心注册到注册中心,同时将client1项目获取改为应用名称,这个如果我们配置中心高可用,就能通过这个应用名称从注册中心获取到所有所有的配置中心集群,即使其中一台机器的配置中心服务挂了,还可以从其他机器获取到配置中心的内容。

1)、配置中心项目修改

​ 我们先引用一个依赖

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

然后在Application类添加注解@EnableEurekaClient注册到eureka注册中心。

@SpringBootApplication

@EnableConfigServer

@EnableEurekaClient

public class EurekaConfigServerApplication {

public static void main(String[] args) {

    SpringApplication.run(EurekaConfigServerApplication.class,args);

}

}

再修改配置文件,添加注册中心的内容

image.png 启动项目,通过http://localhost:7001/就能看到配置中心的注册

image.png

2)、eureka-client1项目修改

​ 对于client1项目的修改,我们需要修改原来的配置文件内容:

spring:

application:

name: eureka-client

cloud:

config:

  # 配置中心地址

uri: http://localhost:8000

  # 加载的文件名称(一般是应用名称),加载的时候会自动拼接后缀 yml或 properties

  name: eureka-client,client1

name: eureka-client

  # 分支

  label: master

  # 当前激活的环境,也就是格式拼接例如我们可以新建一个文件 eureka-client-dev.yml

  profile: dev

  discovery:

    #开启配置中心的服务发现

    enabled: true

    service-id: config-server  #config-sever项目配置的spring.application.name的值

可以看到我们注释原来的uri了,添加了discovery的两个属性enabled&service-id。重启项目,我们就再访问ConfigController类的方法,同样能获取到配置的内容。

8、配置中心本地获取配置文件

​ 我们现在新建项目让配置中心从本地加载,其他的POM、以及Application的类都与原来的配置中心服务相同,但我们需要修改bootstrap.yml文件,以及在本地新建一个文件夹放文件,文件夹的话我们保持与原来的git了警方相同

image.png

1)、bootstrap.yml文件

image.png ​ 我们启动项目(为了不影响,我们将原来的8000端口配置服务关闭)访问http://localhost:8001/eureka-client.yml:

application:

tag: eureka-client-version-1

可以看到这里与我们原来的git不同的是,其不能再加载根路径下的文件application.yml了(我们已经具体指定到dev了,所以需要将application.yml文件移到dev目录下)。

​ 我们不需要修改原来的client1,重新重启,同样能获取配置文件的值。

​ 目前我们如果我们修改了配置文件,其他服务需要重启才能加载,我们可以修改将其手动刷新,还可以通过bus消息总线来让其自动刷新,这里就先不再拓展了。

好啦,今天的文章就到这里,希望能帮助到屏幕前迷茫的你们