OpenFeign服务接口调用

OpenFeign服务接口调用

1、OpenFeign简介

​ Feign是一个声明性web服务客户端。它使编写web服务客户端变得更容易。使用Feign创建一个接口并对其进行注释。它具有可插入的注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud添加了对Spring MVC注释的支持,以及对使用Spring Web中默认使用的HttpMessageConverter的支持。Spring Cloud集成了Eureka、Spring Cloud CircuitBreaker以及Spring Cloud LoadBalancer,以便在使用Feign时提供负载平衡的http客户端。

官网:Spring Cloud OpenFeign

​ 一句话:openfeign是一个声明式的Web服务客户端,我们只需要创建一个Rest接口并在该接口上添加注解@FeignClint即可使用,OpenFeign基本上就是当前微服务之间调用的事实标准。

​ 官网演示了一个案例

  • 先在springboot应用中开启FeignClients

    @SpringBootApplication
    @EnableFeignClients
    public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}}
    
  • 然后写个接口StoreClient打上@FeignClint注解

    @FeignClient("stores")
    public interface StoreClient {@RequestMapping(method = RequestMethod.GET, value = "/stores")List<Store> getStores();@RequestMapping(method = RequestMethod.GET, value = "/stores")Page<Store> getStores(Pageable pageable);@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")Store update(@PathVariable("storeId") Long storeId, Store store);@RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\d+}")void delete(@PathVariable Long storeId);
    }
    

2、OpenFeign能干什么?

​ 前面在使用SpringCloud LoadBalancer+RestTemplate时,利用RestTemplate对http请求的封装处理形成了一套模版化的调用方法。

​ 但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。

​ 所以,OpenFeign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。 在OpenFeign的实现下,我们只需创建一个接口并使用注解的方式来配置它(在一个微服务接口上面标注一个**@FeignClient**注解即可),即可完成对服务提供方的接口绑定,统一对外暴露可以被调用的接口方法,大大简化和降低了调用客户端的开发量,也即由服务提供者给出调用接口清单,消费者直接通过OpenFeign调用即可。

OpenFeign同时还集成SpringCloud LoadBalancer

​ 可以在使用OpenFeign时提供Http客户端的负载均衡,也可以集成阿里巴巴Sentinel来提供熔断、降级等功能。而与SpringCloud LoadBalancer不同的是,通过OpenFeign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。

OpenFeign主要能干的事

  • 可插拔的注解支持,包括Feign注解和JAX-RS注解;
  • 支持可插拔的HTTP编码器和解码器;
  • 支持Sentinel和它的Fallback;
  • 支持SpringCloudLoadBalancer的负载均衡;
  • 支持HTTP请求和响应的压缩;

​ 我们目前是80消费者端的服务去调8001支付服务模块和8002订单服务模块,在80消费者端我们使用的是RestTemplate实现调用,但是,微服务之间也需要相互调用呢?8002调用8001呢?难不成每一个微服务想调用其他服务时再写一次RestTemplate?8001作为一个支付服务含有很多支付流水信息,会有很多其他微服务会调用它,根据解耦和面向接口的原则,我们最好在8001上写出对外暴露的接口,其他服务想调用它,就要找8001定义的接口。

3、使用

​ 架构说明图:

image-20240313105409039

3.1、新建module

​ 新建module,cloud-consumer-feign-order80,导入依赖。

pom.xml

 <dependencies><!--openfeign新加的坐标--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--SpringCloud consul discovery--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-discovery</artifactId></dependency><!-- 引入自己定义的api通用包 --><dependency><groupId>com.zm.cloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!--web + actuator--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--hutool-all--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><!--fastjson2--><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId></dependency><!-- swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

写YML

server:port: 80
spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name} 

主启动类

@SpringBootApplication
@EnableDiscoveryClient //使用consul为注册中心时注册服务
@EnableFeignClients  //开启OpenFeign功能并激活
public class MainOpenFeign80 {public static void main(String[] args) {SpringApplication.run(MainOpenFeign80.class,args);}
}

要把接口PayFeignApi创建在通用的api模块cloud-api-commons中

​ 修改cloud-api-commons模块,pom文件中添加openfeign的依赖

<!--openfeign-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

​ 参考微服务8001的Controller层,新建PayFeignApi接口

@FeignClient(value = "cloud-payment-service")
public interface PayFeignApi {/*** 新增一条支付相关流水记录* @param payDTO* @return*/@PostMapping("/pay/add")public ResultData addPay(@RequestBody PayDTO payDTO);//通过id查询@GetMapping(value = "/pay/get/{id}")public ResultData getPayById(@PathVariable("id") Integer id);//删除@DeleteMapping("/pay/del/{id}")public ResultData delById(@PathVariable("id") Integer integer);//修改@PutMapping("/pay/update")public ResultData update(@RequestBody PayDTO payDTO);//查全部@GetMapping("/pay/getall")public ResultData getAll();//openfeign天然支持负载均衡演示@GetMapping("/pay/getInfo")public String myLB();}

​ 我们把原来微服务80的controller复制一份到cloud-consumer-feign-order80中,原来的内容全部删除,重新使用PayFeignApi实现。当应用启动时,Feign 使用 Java 的动态代理机制生成接口的实现。这个过程由 Spring Cloud 集成提供支持Feign 客户端在内部构建了请求的详细信息,并将接口方法调用转换为 HTTP 调用。

@RestController
public class OrderController {@Resourceprivate PayFeignApi payFeignApi;@PostMapping("/feign/pay/add")public ResultData addPay(@RequestBody PayDTO payDTO){System.out.println("1、使用本地addOrder新增订单功能(省略sql操作),2、开启addPay支付微服务远程调用");ResultData resultData = payFeignApi.addPay(payDTO);return resultData;}@GetMapping("/feign/pay/get/{id}")public ResultData getByID(@PathVariable("id") Integer id){System.out.println("-------支付微服务远程调用,按照id查询订单支付流水信息");ResultData payById = payFeignApi.getPayById(id);return payById;}@DeleteMapping("/feign/pay/del/{id}")public ResultData delById(@PathVariable("id") Integer id){System.out.println("-------支付微服务远程调用,按照id查删除订单支付流水信息");return payFeignApi.delById(id);}@PutMapping("/feign/pay/update")public ResultData update(@RequestBody PayDTO payDTO){System.out.println("-------支付微服务远程调用,修改订单支付流水信息");return payFeignApi.update(payDTO);}@GetMapping("/feign/pay/getall")public ResultData getAll(){return payFeignApi.getAll();}@GetMapping("/feign/pay/getInfo")public String mylb(){return payFeignApi.myLB();}}

​ 测试启动新的80服务,启动微服务8001和8002.

image-20240313140818314

​ 使用浏览器或者其他测试工具先查询一个看看

image-20240313141906611

​ 添加一个数据

image-20240313142141138

​ 查询全部

image-20240313142211054

​ 删除刚才新增的

image-20240313142303242

​ 下面测试负载均衡

image-20240313142738116

​ 因为OpenFeign默认的集成了LoadBalancer,所以就会负载均衡。

​ 梳理一遍

image-20240313144743890

​ 在使用Feign进行微服务间的通信时,当发出一个请求到PayFeignApi接口时,该请求实际上是通过Feign客户端进行代理的。Feign会根据你提供的@FeignClient(value = "cloud-payment-service")注解中的value值(即服务名)来定位目标服务。

​ 当请求到达PayFeignApi接口时大致流程:

  1. 路由到目标服务:Feign会根据我们在注解上填的微服务名称去注册中心(consul)找有没有这个微服务,然后找到这个微服务下的具体示例列表,因为Feign本身就有负载均衡能力,默认还是轮询的方式进行调度。
  2. 选择具体的端点:选择完目标示例之后,Feign会根据你定义的路径(/pay/add)来拼凑完整的URL,比如说选择了8001,那么URL就是:http://localhost:8001/pay/add;
  3. 发送请求:URL拿到后,Feign就使用HTTP客户端发送请求到服务提供者,然后就是具体的处理请求了,至此整个服务流程完成。

4、OpenFeign高级特性

4.1、OpenFeign超时控制

​ 在Spring Cloud微服务架构中,大部分公司都是利用OpenFeign进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,但是如果是业务比较复杂,服务要进行比较繁杂的业务计算,那后台很有可能会出现Read Timeout这个异常,因此定制化配置超时时间就有必要了。

image-20240313151559540

4.1.1、设置超时演示

​ 我们故意设置超时出错情况,让8001的服务睡一会儿。

服务提供方cloud-provider-payment8001故意写暂停62秒钟程序,把getById的controller修改一下,睡一会儿

//通过id查询
@GetMapping("/pay/get/{id}")
@Operation(summary = "查询",description = "通过ID查询")
public ResultData<Pay> payById(@PathVariable("id") Integer id){System.out.println("---------正在查询--------");try {TimeUnit.SECONDS.sleep(62);} catch (InterruptedException e) {throw new RuntimeException(e);}return ResultData.success(payServiceImp.getById(id));
}

​ 为啥是62秒???试出超时时间。

服务调用方cloud-consumer-feign-order80写好捕捉超时异常

@GetMapping("/feign/pay/get/{id}")
public ResultData getByID(@PathVariable("id") Integer id){System.out.println("-------支付微服务远程调用,按照id查询订单支付流水信息");ResultData payById = null;try {System.out.println("调用开始时间------>"+ DateUtil.now());payById = payFeignApi.getPayById(id);}catch (Exception e){e.printStackTrace();System.out.println("调用结束时间------>"+ DateUtil.now());ResultData.fail(ReturnCodeEnum.RC500.getCode(), e.getMessage());}return payById;
}

​ 开始测试,重新启动8001和80,浏览器输入localhost/feign/pay/get/1:

​ **注意:**如果测试的时候你的8002服务没有关闭的话,轮询还是存在的,可能第一次请求就是成功的,刷新一下就轮到8001了,你要不想轮询就把8002关闭。

image-20240313154153904

​ 所以说,OpenFeign的默认超时时间是60秒,所以故意弄的睡眠时间就是60秒以上。

​ 默认OpenFeign客户端等待60秒钟,但是服务端处理超过规定时间会导致Feign客户端返回报错。

​ 为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制,默认60秒太长或者业务时间太短都不好。

​ 这就需要在yml中开始配置了:

connectTimeout       连接超时时间readTimeout          请求处理超时时间

​ 官网默认配置参数

image-20240313155357974

全局配置

​ 在application.yml文件中配置

server:port: 80
spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:default:#连接超时时间connectTimeout: 3000#读取超时时间readTimeout: 3000

​ 重启80服务测试一下:

image-20240313185725554

​ 有1秒的误差,打印信息了。

指定配置

​ 还可以指定某个服务的超时时间,但是不能和default一起写,这样会覆盖掉default的时间,用你指定服务的超时时间。演示一下两者都写的情况。

server:port: 80
spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:default:connectTimeout: 3000            #连接超时时间readTimeout: 3000             #读取超时时间cloud-payment-service:connectTimeout: 5000readTimeout: 5000

​ 再次测试

image-20240313190714409

​ 结果是5秒,所以自己指点的时间就会覆盖掉默认设置的时间。

4.2、OpenFeign重试机制

​ OpenFeign的重试机制默认是关闭的

image-20240313192605874

​ 想开启重试就新增一个配置类FeignConfig并修改Retryer配置,把指定的8001超时时间设置为4秒

@Configuration
public class FeignConfig {@Beanpublic Retryer myRetryer(){//return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的//最大请求次数为(1+2)次,初始时间间隔为100ms,重试最大间隔时间为1秒return new Retryer.Default(100,1,3);}
}

​ 现在测试一下,如果走了重试应该就是5*3=15秒

image-20240313200641627

4.3、OpenFeign默认HttpClient修改

​ OpenFeign中http client,如果不做特殊配置,OpenFeign默认使用JDK自带HttpURLConnection发送HTTP请求,由于默认HttpURLConnection没有连接池、性能和效率比较低,如果采用默认,性能上不是最强大的,所以需要换掉推荐使用阿帕奇的HC5

​ 我们看到官网

image-20240313202234136

​ 我们先把超时重试的配置关闭。

@Configuration
public class FeignConfig {@Beanpublic Retryer myRetryer(){return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的//最大请求次数为(1+2)次,初始时间间隔为100ms,重试最大间隔时间为1秒//return new Retryer.Default(100,1,3);}
}

​ 修改POM文件,添加依赖

<!-- httpclient5-->
<dependency><groupId>org.apache.httpcomponents.client5</groupId><artifactId>httpclient5</artifactId><version>5.3</version>
</dependency>
<!-- feign-hc5-->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-hc5</artifactId><version>13.1</version>
</dependency>

​ 在application.yml配置文件中打开Apache HttpClient5

server:port: 80
spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:
#            default:
#             connectTimeout: 3000            #连接超时时间
#             readTimeout: 3000             #读取超时时间cloud-payment-service:connectTimeout: 5000readTimeout: 5000httpclient:hc5:enabled: true

​ 重启再看一下现在报错信息来至于哪里

image-20240313203114165

4.4、OpenFeign请求/响应压缩

对请求和响应进行GZIP压缩

Spring Cloud OpenFeign支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。

通过下面的两个参数设置,就能开启请求与相应的压缩功能:

spring.cloud.openfeign.compression.request.enabled=truespring.cloud.openfeign.compression.response.enabled=true

细粒度化设置

​ 对请求压缩做一些更细致的设置,比如下面的配置内容指定压缩的请求数据类型并设置了请求压缩的大小下限,只有超过这个大小的请求才会进行压缩:

spring.cloud.openfeign.compression.request.enabled=truespring.cloud.openfeign.compression.request.mime-types=text/xml,application/xml,application/json #触发压缩数据类型spring.cloud.openfeign.compression.request.min-request-size=2048 #最小触发压缩的大小

​ 我们可以在配置文件中设置一下

server:port: 80
spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:
#            default:
#             connectTimeout: 3000            #连接超时时间
#             readTimeout: 3000             #读取超时时间cloud-payment-service:connectTimeout: 5000readTimeout: 5000httpclient:hc5:enabled: truecompression:request:enabled: truemin-request-size: 2048 #最小触发压缩的大小mime-types: text/xml,application/xml,application/json #触发压缩数据类型response:enabled: true

​ 压缩效果在日志打印功能中展现…

4.5、OpenFeign日志打印功能

​ Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节,说白了就是对Feign接口的调用情况进行监控和输出。

日志级别

  • NONE:默认的,不显示任何日志;BASIC:仅记录请求方法、URL、响应状态码及执行时间;
  • HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;
  • FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。

官网说明:

image-20240313204326264

image-20240313204046193

​ 我们在FeignConfig中可以配置日志,注意导包要导feign的。

@Configuration
public class FeignConfig {@Beanpublic Retryer myRetryer(){return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的//最大请求次数为(1+2)次,初始时间间隔为100ms,重试最大间隔时间为1秒//return new Retryer.Default(100,1,3);}@BeanLogger.Level feignLoggerLevel() {return Logger.Level.FULL;}
}

​ 然后在配置文件中开启日志的Feign客户端。

​ 这里有一个套路可以这样写:

公式(三段):logging.level + 含有@FeignClient注解的完整带包名的接口名+debug

完整的application.yml

server:port: 80
spring:application:name: cloud-consumer-openfeign-order####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: true #优先使用服务ip进行注册service-name: ${spring.application.name}openfeign:client:config:
#            default:
#             connectTimeout: 3000            #连接超时时间
#             readTimeout: 3000             #读取超时时间cloud-payment-service:connectTimeout: 5000readTimeout: 5000httpclient:hc5:enabled: truecompression:request:enabled: truemin-request-size: 2048 #最小触发压缩的大小mime-types: text/xml,application/xml,application/json #触发压缩数据类型response:enabled: true# feign日志以什么级别监控哪个接口
logging:level:com:zm:cloud:apis:PayFeignApi: debug 

​ 现在可以开始测试了,重启服务测试。

image-20240313205737543

​ 我们可以看到开了压缩和没开压缩的还是有些差别的,没开请求响应压缩看不到任何的请求响应信息。

重试机制控制台看到3次过程

​ 现在有了日志打印功能就可以看到具体的3次重试过程了。

​ 先把重试功能打开,为了快速展示我们把超时时间改成1秒,那么一会儿总时间应该是3秒。

@Configuration
public class FeignConfig {@Beanpublic Retryer myRetryer(){//return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的//最大请求次数为(1+2)次,初始时间间隔为100ms,重试最大间隔时间为1秒return new Retryer.Default(100,1,3);}@BeanLogger.Level feignLoggerLevel() {return Logger.Level.FULL;}
}

​ 重启服务开始测试。
image-20240313210815882

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/536612.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【符号链接】【bash】遍历目录下的每个子项目

为git_repos目录下的每个项目创建符号链接&#xff08;软链接&#xff09;&#xff0c;需要遍历该目录下的每个子项目&#xff0c;并使用ln -s命令为它们分别创建链接。 1. 创建脚本文件 创建一个文本文件来编写你的脚本。这可以通过任何文本编辑器完成&#xff0c;比如nano…

建设IAM/IDM统一身份管理,实现系统之间的单点登录(SSO)

企业实施身份管理的现状&#xff1a; 1.身份存储分散&#xff0c;不能统一供应诸多应用系统&#xff0c;企业用户信息常常存在于多个系统&#xff0c;如HR系统有一套用户信息&#xff0c;OA系统也有一套用户信息&#xff0c;身份存储不集中&#xff0c;不能统一地为诸多应用系…

linux系统docker容器可视化工具portainer

可视化工具portainer portainer可视化工具安装官网安装步骤docker命令安装创建admin登录后&#xff0c;选择local选项卡 中文版本 portainer可视化工具 portainer是一款轻量级应用&#xff0c;他提供图形化界面&#xff0c;用于方便的管理docker环境&#xff0c;包括单机环境和…

Pikachu 靶场搭建

文章目录 1 Pikachu 简介2 Pikachu 安装 1 Pikachu 简介 Pikachu是一个使用“PHP MySQL” 开发、包含常见的Web安全漏洞、适合Web渗透测试学习人员练习的靶场&#xff0c;运行Pikachu需要提前安装好“PHP MySQL 中间件” 的基础环境&#xff0c;可以使用集成软件来搭建&…

React路由结合Material UI的ListItemButton组件完成导航示例

React路由结合Material UI的ListItemButton组件完成导航示例 1、创建菜单列表NavigationList.jsx2、App.js 1、创建菜单列表NavigationList.jsx import React from react; import { ListItemButton, ListItemText, List } from mui/material; import { NavLink as RouterLink …

服务器折腾日志

突发噩耗&#xff1a;服务器用不了了。 具体表现为&#xff1a;可以进BIOS系统&#xff0c;但到了登陆界面键鼠失灵&#xff0c;无法输入密码 搜博客&#xff0c;曰&#xff1a; 解决键鼠失灵 1.进入命令行&#xff0c;执行&#xff1a; sudo apt install xserver-xorg-inp…

LeetCode-337题:打家劫舍III(原创)

【题目描述】 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为 root 。除了 root 之外&#xff0c;每栋房子有且只有一个“父“房子与之相连。一番侦察之后&#xff0c;聪明的小偷意识到“这个地方的所有房屋的排列 输入: root [3,2,3,null,3,…

vue上传文件夹+上传文件vue-simple-uploader

vue上传文件夹上传文件vue-simple-uploader 使用插件 在main.js引入 import uploader from vue-simple-uploaderVue.use(uploader);<el-dialog title"上传文件" :visible.sync"dialogFileVisible" width"50%" :before-close"handleFil…

从零开始学习深度学习库-1:前馈网络

你好&#xff01;欢迎来到这个系列的第一篇文章&#xff0c;我们将尝试用Python构建自己的深度学习库。在这篇文章中&#xff0c;我们将开始编写一个简单的前馈神经网络。我们将仅在这篇文章中处理前向传播&#xff0c;并在下一篇文章中处理网络的训练。这篇文章将介绍基本的前…

学习网络安全:记一次某网站渗透测试过程

本文作者&#xff1a; 汇智知了堂信安教学老师——辉哥 一、信息收集 网站界面 网站信息收集 &#xff08;1&#xff09;中间件信息 &#xff08;2&#xff09;目录扫描 思路&#xff1a;由于是cms的站&#xff0c;针对这种情况&#xff0c;我们可以收集cms的默认目录结构来…

java内部类的作用与优缺点

一、前言 很久没看到java内部类了&#xff0c;今天在审查代码时候&#xff0c;发现了java内部类&#xff0c;主要是内部类还嵌套了内部类。于是记录一下 二、java内部类的作用与优缺点 Java内部类&#xff0c;也称为嵌套类&#xff0c;是定义在另一个类&#xff08;外部类&am…

1.1计算机系统构成及硬件系统知识(上)

基础知识部分----chap01 主要议题&#xff1a; 数制转换&#xff1a;一般会涉及存取的计算&#xff1b;ip地址中变长子网掩码的计算题&#xff1b;&#xff08;难度较大&#xff09; 数的表示&#xff1a;二进制、十六进制&#xff1b; 计算机的组成&#xff1a;考察的较为深入…