Feign的使用及原理剖析

article/2025/9/2 13:29:58

feign使用及原理剖析


一、简介

Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了http调用流程。

Feign远程调用,核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,返回给调用者。

二、http client依赖

Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection。

可以通过修改 client 依赖换用底层的 client,不同的 http client 对请求的支持可能有差异。

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.2.RELEASE</version>
</dependency><!--使用Apache HttpClient-->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId><version>11.0</version>
</dependency>
#在配置文件中启用ApacheHttpClient
feign.httpclient.enabled=true

三、注解

1、@FeignClient

public @interface FeignClient {/***具有可选协议前缀的服务的名称。无论是否提供url,都必须为所有客户端指定名称。*/@AliasFor("name")String value() default "";// 过时的@DeprecatedString serviceId() default "";/***  当存在多个FeignClient调用同一个服务时,需要填写,否则无法启动*/String contextId() default "";// 指定FeignClient的名称@AliasFor("value")String name() default "";//返回值:外部客户端的@Qualifier值String qualifier() default "";// 全路径地址或hostname,http或https可选String url() default "";// 当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException异常boolean decode404() default false;// Feign配置类,可以自定义Feign的LogLevelClass<?>[] configuration() default {};// 容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑Class<?> fallback() default void.class;// 工厂类,用于生成fallback类实例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码Class<?> fallbackFactory() default void.class;// 定义当前FeignClient的统一前缀,类似于controller类上的requestMappingString path() default "";//是否将外部代理标记为主beanboolean primary() default true;
}

2、@EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {//等价于basePackages属性,更简洁的方式String[] value() default {};//指定多个包名进行扫描String[] basePackages() default {};//指定多个类或接口的class,扫描时会在这些指定的类和接口所属的包进行扫描Class<?>[] basePackageClasses() default {};//为所有的Feign Client设置默认配置类Class<?>[] defaultConfiguration() default {};//指定用@FeignClient注释的类列表。如果该项配置不为空,则不会进行类路径扫描Class<?>[] clients() default {};
}

四、原理

  • 启动时,若有@EnableFeignClients注解,则程序会进行包扫描,扫描所有包下所有@FeignClient注解的类,并将这些类注入到spring的IOC容器中。
  • 当定义的@FeignClient中的接口被调用时,通过JDK的动态代理来生成RequestTemplate。RequestTemplate中包含请求的所有信息,如请求参数,请求URL等。
  • RequestTemplate生成Request,然后将Request交给client处理,client默认是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
  • 最后client封装成LoadBaLanceClient,结合ribbon负载均衡地发起调用。

五、流程

流程图如下:feign原理图

从上图可以看到,Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的 Request 请求。通过Feign以及JAVA的动态代理机制,使得Java 开发人员,可以不用通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用。

1、启用

启动配置上检查是否有@EnableFeignClients注解,如果有该注解,则开启包扫描,扫描被@FeignClient注解的接口。

扫描出该注解后, 通过beanDefinition注入到IOC容器中,方便后续被调用使用。

@EnableFeignClients 是关于注解扫描的配置,使用了@Import(FeignClientsRegistrar.class)。在spring context处理过程中,这个Import会在解析Configuration的时候当做提供了其他的bean definition的扩展,Spring通过调用其registerBeanDefinitions方法来获取其提供的bean definition。

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {//在这个重载的方法里面做了两件事情://1.将EnableFeignClients注解对应的配置属性注入//2.将FeignClient注解对应的属性注入@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {//注入EnableFeignClients注解对应的配置属性registerDefaultConfiguration(metadata, registry);//注入FeignClient注解对应的属性registerFeignClients(metadata, registry);}}

FeignClientsRegistrar里重写了spring里ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法。也就是在启动时,处理了EnableFeignClients注解后,registry里面会多出一些关于Feign的BeanDefinition。

BeanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回的是FeignClientFactoryBean类。

FeignClientFactoryBean作为一个实现了FactoryBean的工厂类,那么每次在Spring Context 创建实体类的时候会调用它的getObject()方法。

这里的getObject()其实就是将@FeinClient中设置value值进行组装起来。

public Object getObject() throws Exception {FeignContext context = applicationContext.getBean(FeignContext.class);Feign.Builder builder = feign(context);if (!StringUtils.hasText(this.url)) {String url;if (!this.name.startsWith("http")) {url = "http://" + this.name;}else {url = this.name;}url += cleanPath();return loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, url));}if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {this.url = "http://" + this.url;}String url = this.url + cleanPath();Client client = getOptional(context, Client.class);if (client != null) {if (client instanceof LoadBalancerFeignClient) {// not lod balancing because we have a url,// but ribbon is on the classpath, so unwrapclient = ((LoadBalancerFeignClient)client).getDelegate();}builder.client(client);}Targeter targeter = get(context, Targeter.class);return targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url));
}

2、发起请求

ReflectiveFeign内部使用了jdk的动态代理为目标接口生成了一个动态代理类,这里会生成一个InvocationHandler统一的方法拦截器,同时为接口的每个方法生成一个SynchronousMethodHandler拦截器,并解析方法上的元数据,生成一个http请求模板RequestTemplate。

查看ReflectiveFeign类中newInstance方法是返回一个代理对象:

这个方法大概的逻辑是:

  1. 根据target,解析生成MethodHandler对象;
  2. 对MethodHandler对象进行分类整理,整理成两类:default 方法和 SynchronousMethodHandler 方法;
  3. 通过jdk动态代理生成代理对象,这里是最关键的地方;
  4. 将DefaultMethodHandler绑定到代理对象。
public class ReflectiveFeign extends Feign {@Overridepublic <T> T newInstance(Target<T> target) {//为每个方法创建一个SynchronousMethodHandler对象,并放在 Map 里面Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();for (Method method : target.type().getMethods()) {if (method.getDeclaringClass() == Object.class) {continue;} else if (Util.isDefault(method)) {//如果是 default 方法,说明已经有实现了,用 DefaultHandlerDefaultMethodHandler handler = new DefaultMethodHandler(method);defaultMethodHandlers.add(handler);methodToHandler.put(method, handler);} else {//否则就用上面的 SynchronousMethodHandlermethodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));}}/** * 设置拦截器* 创建动态代理,factory 是 InvocationHandlerFactory.Default,创建出来的是 * ReflectiveFeign.FeignInvocationHanlder,也就是说后续对方法的调用都会进入到该对象的 inovke 方* 法*/ InvocationHandler handler = factory.create(target, methodToHandler);// 创建动态代理对象T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);}return proxy;}}

最终都是执行了SynchronousMethodHandler拦截器中的invoke方法:

final class SynchronousMethodHandler implements MethodHandler {@Overridepublic Object invoke(Object[] argv) throws Throwable {// 根据输入参数,构造Http请求RequestTemplate template = buildTemplateFromArgs.create(argv);// 克隆出一份重试器Retryer retryer = this.retryer.clone();// 尝试最大次数,如果中间有结果,直接返回while (true) {try {return executeAndDecode(template);} catch (RetryableException e) {try {retryer.continueOrPropagate(e);} catch (RetryableException th) {Throwable cause = th.getCause();if (propagationPolicy == UNWRAP && cause != null) {throw cause;} else {throw th;}}if (logLevel != Logger.Level.NONE) {logger.logRetry(metadata.configKey(), logLevel);}continue;}}}
}

invoke方法方法首先生成 RequestTemplate 对象,应用 encoder,decoder 以及 retry 等配置,下面有一个死循环调用:executeAndDecode,从名字上看就是执行调用逻辑并对返回结果解析。

Object executeAndDecode(RequestTemplate template) throws Throwable {//根据  RequestTemplate生成Request对象Request request = targetRequest(template);if (logLevel != Logger.Level.NONE) {logger.logRequest(metadata.configKey(), logLevel, request);}Response response;long start = System.nanoTime();try {// 调用client对象的execute()方法执行http调用逻辑,//execute()内部可能设置request对象,也可能不设置,所以需要response.toBuilder().request(request).build();这一行代码response = client.execute(request, options);// ensure the request is set. TODO: remove in Feign 10response.toBuilder().request(request).build();} catch (IOException e) {if (logLevel != Logger.Level.NONE) {logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));}// IOException的时候,包装成 RetryableException异常,上面的while循环 catch里捕捉的就是这个异常throw errorExecuting(request, e);}//统计 执行调用花费的时间long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);boolean shouldClose = true;try {if (logLevel != Logger.Level.NONE) {response =logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);// ensure the request is set. TODO: remove in Feign 10response.toBuilder().request(request).build();}//如果元数据返回类型是 Response,直接返回回去即可,不需要decode()解码if (Response.class == metadata.returnType()) {if (response.body() == null) {return response;}if (response.body().length() == null ||response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {shouldClose = false;return response;}// Ensure the response body is disconnectedbyte[] bodyData = Util.toByteArray(response.body().asInputStream());return response.toBuilder().body(bodyData).build();}//主要对2xx和404等进行解码,404需要特别的开关控制。其他情况,使用errorDecoder进行解码,以异常的方式返回if (response.status() >= 200 && response.status() < 300) {if (void.class == metadata.returnType()) {return null;} else {return decode(response);}} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {return decode(response);} else {throw errorDecoder.decode(metadata.configKey(), response);}} catch (IOException e) {if (logLevel != Logger.Level.NONE) {logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);}throw errorReading(request, response, e);} finally {if (shouldClose) {ensureClosed(response.body());}}
}

Feign真正发送HTTP请求是委托给feign.Client的execute方法来做的:

public interface Client {Response execute(Request request, Options options) throws IOException;class Default implements Client {@Overridepublic Response execute(Request request, Options options) throws IOException {HttpURLConnection connection = convertAndSend(request, options);return convertResponse(connection, request);}}
}

注意:SynchronousMethodHandler并不是直接完成远程URL的请求,而是通过负载均衡机制,定位到合适的远程server服务器,然后再完成真正的远程URL请求。即:SynchronousMethodHandler实例的client成员,其实际不是feign.Client.Default类型,而是LoadBalancerFeignClient客户端负载均衡类型。

3、性能分析

Feign框架比较小巧,在处理请求转换和消息解析的过程中,基本上没什么时间消耗。真正影响性能的,是处理Http请求的环节。可以从这个方面着手分析系统的性能提升点。

六、总结

1、调用接口为什么会直接发送请求?

原因就是Spring扫描了@FeignClient注解,并且根据配置的信息生成代理类,调用的接口实际上调用的是生成的代理类。

2、请求是如何被Feign接管的?

  • Feign通过扫描@EnableFeignClients注解中配置包路径,扫描@FeignClient注解并将注解配置的信息注入到Spring容器中,类型为FeignClientFactoryBean;
  • 然后通过FeignClientFactoryBean的getObject()方法得到不同动态代理的类并为每个方法创建一个SynchronousMethodHandler对象;
  • 为每一个方法创建一个动态代理对象, 动态代理的实现是 ReflectiveFeign.FeignInvocationHanlder,代理被调用的时候,会根据当前调用的方法,转到对应的 SynchronousMethodHandler;
  • 这样我们发出的请求就能够被已经配置好各种参数的Feign handler进行处理,从而被Feign托管。

七、简单入门


一、示例

1.引入依赖

<!-- spring-cloud-starter-openfeign 支持负载均衡、重试、断路器等 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.2.RELEASE</version>
</dependency>
<!--使用Apache HttpClient-->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId><version>11.0</version>
</dependency>

2.添加配置

#配置文件启用ApacheHttpClient
feign.httpclient.enabled=true

3.开启支持

@SpringBootApplication
@EnableFeignClients
public class FeignProjectApplication {public static void main(String[] args) {SpringApplication.run(FeignProjectApplication.class, args);}}

4.编写接口

①单服务项目

@FeignClient(value = "demo", url = "http://localhost:8081/")
public interface UserCenter {/*** 获取用户信息* @param uid* @return*/@PostMapping("/user/getUser/{uid}")User getUser(@PathVariable(value = "uid") Integer uid);
}

②微服务项目

@FeignClient(name="user-center",  // 微服务名称url="${feign.service.user:user-center}",  // 微服务的服务名,用来定位到要调用哪个服务,可在配置文件中填写contextId = "user-center",  // 当有多个FeignClient调用同一个微服务时需要填写,否则会无法启动path="/user", // 固定的一个path,用于拼接整个urlfallback = UserCenterImpl.class // 熔断类,当请求超时或其他原因时,会调用熔断类里的方法,防止调用方请求过久
)
public interface UserCenter {/*** 获取用户信息* @param uid* @return*/@PostMapping("/getUser/{uid}")User getUser(@PathVariable(value = "uid") Integer uid);
}

③熔断类

@Service
public class UserCenterImpl implements UserCenter {@Overridepublic User getUser(Integer uid) {// 直接返回nullreturn null;}
}

5.配置自动添加token

@Configuration
public class FeignInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();requestTemplate.header("Authorization", request.getHeader("Authorization"));
//        requestTemplate.header("Authorization", "1kmhZwomS6LSQKXQKNjRibRORrCZnsnrTU9CcBGkQJ3DGL1soxIWegq/vF3UXdEm");}
}

6.编写controller接口

@CrossOrigin
@RequestMapping("/usercenter")
@RestController
public class UserController {@Autowiredprivate UserCenter userCenter;@PostMapping("/getUser/{uid}")public User getUser(@PathVariable(value = "uid") Integer uid){return userCenter.getUser(uid);}
}

7.接口请求结果

{"data": {"uid": 1001,"name": "张三",},"statusText": "查找成功","status": 200
}

二、一些其他配置

1.FormEncoder支持

@Configuration
public class FeignFormConfiguration {@Autowiredprivate ObjectFactory<HttpMessageConverters> messageConverters;@Bean@Primarypublic Encoder feignFormEncoder() {return new FormEncoder(new SpringEncoder(this.messageConverters));}
}

2.拦截器: 自动添加header或者token等

@Configuration
public class FeignInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();requestTemplate.header("Authorization", request.getHeader("Authorization"));//        requestTemplate.header("Authorization", "1kmhZwomS6LSQKXQKNjRibRORrCZnsnrTU9CcBGkQJ3DGL1soxIWegq/vF3UXdEm");}
}

3.ErrorCode: 可以自定义错误响应码的处理

1.引入依赖

<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.2</version>
</dependency>

2.配置自定义异常

@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TyaleErrorException extends Exception{/*** example: "./api/{service-name}/{problem-id}"*/private String type;/*** example: {title}*/private String title;/*** example: https://api/docs/index.html#error-handling*/private String documentation;/*** example: {code}*/private String status;
}

3.配置工具类

public class GsonUtil {private static Gson filterNullGson;private static Gson nullableGson;static {nullableGson = new GsonBuilder().enableComplexMapKeySerialization().serializeNulls().setDateFormat("yyyy-MM-dd HH:mm:ss:SSS").create();filterNullGson = new GsonBuilder().enableComplexMapKeySerialization().setDateFormat("yyyy-MM-dd HH:mm:ss:SSS").create();}protected GsonUtil() {}/*** 根据对象返回json   不过滤空值字段*/public static String toJsonWtihNullField(Object obj){return nullableGson.toJson(obj);}/*** 根据对象返回json  过滤空值字段*/public static String toJsonFilterNullField(Object obj){return filterNullGson.toJson(obj);}/*** 将json转化为对应的实体对象* new TypeToken<HashMap<String, Object>>(){}.getType()*/public static <T>  T fromJson(String json, Type type){return nullableGson.fromJson(json, type);}/*** 将对象值赋值给目标对象* @param source 源对象* @param <T> 目标对象类型* @return 目标对象实例*/public static <T> T convert(Object source, Class<T> clz){String json = GsonUtil.toJsonFilterNullField(source);return GsonUtil.fromJson(json, clz);}
}

4.自定义错误码

@Configuration
public class TyaleErrorDecoder implements ErrorDecoder {@Overridepublic Exception decode(String s, Response response) {TyaleErrorException errorException = null;try {if (response.body() != null) {Charset utf8 = StandardCharsets.UTF_8;var body = Util.toString(response.body().asReader(utf8));errorException = GsonUtil.fromJson(body, TyaleErrorException.class);} else {errorException = new TyaleErrorException();}} catch (IOException ignored) {}return errorException;}
}

4.自定义错误码

@Configuration
public class TyaleErrorDecoder implements ErrorDecoder {@Overridepublic Exception decode(String s, Response response) {TyaleErrorException errorException = null;try {if (response.body() != null) {Charset utf8 = StandardCharsets.UTF_8;var body = Util.toString(response.body().asReader(utf8));errorException = GsonUtil.fromJson(body, TyaleErrorException.class);} else {errorException = new TyaleErrorException();}} catch (IOException ignored) {}return errorException;}
}

http://chatgpt.dhexx.cn/article/yXRVLh4E.shtml

相关文章

Feign基本使用(超详细)

目录 一、Feign概述 二、Feign入门 1.创建服务提供者(provider) 2.创建feign接口 3、创建服务消费者(consumer) 三、Feign 原理 四、Feign优化 1、开启feign日志 2、feign超时问题 3、http连接池 4、gzip压缩 前言 当我们通过RestTemplate调用其它服务的API时&#xff0c;所…

简单理解Feign的原理与使用

文章目录 SpringCloud 总架构图一、简介1.1、负载均衡的概念2.2、Feign概念 二、入门案例2.1、导入依赖2.2、Feign的客户端2.3、调用Feign2.4、开启Feign功能2.5、启动测试2.6、Feign实现原理简单分析 三、负载均衡(Ribbon)四、熔断器支持五、请求压缩和响应压缩六、配置日志级…

【第四章】详解Feign的实现原理

1.1 Feign概述 这篇文章主要讲述如何通过Feign去消费服务&#xff0c;以及Feign的实现原理的解析。 Feign是Netflix开发的声明式、模板化的HTTP客户端&#xff0c;Feign可以帮助我们更快捷、优雅地调用HTTP API。 Feign是⼀个HTTP请求的轻量级客户端框架。通过 接口 注解的…

Feign底层原理分析-自动装载动态代理

本篇文章仅介绍Feign的核心机制&#xff0c;包括如何交由Spring容器托管、动态代理机制等内容&#xff0c;不会过分深入细节。 1、什么是Feign&#xff1f; 这里套用Feign官方Github上的介绍&#xff1a;“Feign是一个灵感来自于Retrofit、JAXRS-2.0、WebSocket的Java Http客户…

Feign原理 (图解)

1.1 简介&#xff1a;Feign远程调用的 Feign远程调用&#xff0c;核心就是通过一系列的封装和处理&#xff0c;将以JAVA注解的方式定义的远程调用API接口&#xff0c;最终转换成HTTP的请求形式&#xff0c;然后将HTTP的请求的响应结果&#xff0c;解码成JAVA Bean&#xff0c;放…

Feign的工作原理

Feign的工作原理 Feign是一个伪Java Http 客户端&#xff0c;Feign 不做任何的请求处理。Feign 通过处理注解生成Request模板&#xff0c;从而简化了Http API 的开发。开发人员可以使用注解的方式定制Request API模板。 在发送Http Request请求之前&#xff0c;Feign通过处理…

Linux命令——tar与gzip详解:文件的打包压缩与解压缩解打包

Linux系统中&#xff0c;最常用的打包命令就是tar了&#xff0c;不仅如此&#xff0c;tar命令还可以解打包解压缩&#xff0c;十分方便。如果单纯想压缩文件&#xff0c;就需要我们的gzip命令了。 使用tar打包归档的包叫做tar包&#xff0c;以.tar结尾 使用gzip压缩的文件&…

Linux 下使用 tar 命令打包指定目录下的所有文件,不包括目录

一&#xff0c;问题描述 无论是 Linux 系统&#xff0c;还是 macOS 系统&#xff0c;我们都可以使用 tar 命令进行文件的压缩打包。命令格式如下&#xff1a; # tar cvf xxxx.tar 要压缩的文件或目录名称但如果要压缩的目录层级比较多时&#xff0c;比如&#xff1a; # tar cvf…

linux tar (打包、压缩、解压)命令

打包程序&#xff1a;tar c: 创建文档t&#xff1a; 列出存档内容x&#xff1a;提取存档f&#xff1a; filename 要操作的文档名v&#xff1a;详细信息 一&#xff1a;打包 打包&#xff1a;是指把文件整合在一起&#xff0c;不压缩 1.将文件打包&#xff1a;tar cf a.ta…

(21)tar打包命令详解

Linux 系统中,最常用的归档(打包)命令就是 tar,该命令可以将许多文件一起保存到一个单独的磁盘中进行归档。不仅如此,该命令还可以从归档文件中还原所需文件,也就是打包的反过程,称为解打包。1.tar命令做打包操作  当 tar 命令用于打包操作时,该命令的基本格式为: …

vector中删除某个指定元素

class Solution { public:int removeElement(vector<int>& v, int val) {for (auto it v.begin(); it ! v.end(); it) {if (*it val) { // 条件语句v.erase(it); // 移除他it--; // 让该迭代器指向前一个}}return v.size();} };

vector删除指定元素

C vector中实际删除元素使用的是容器vecrot中std::vector::erase()方法。 C 中std::remove()并不删除元素&#xff0c;因为容器的size()没有变化&#xff0c;只是元素的替换。 1.std::vector::erase() 函数原型&#xff1a;iterator erase (iterator position);  //删除指定…

C++中vector中删除/添加指定位置处的元素

1、函数介绍 C中vector容器可以删除/添加制定位置处的元素&#xff0c;分别使用erase()与insert()函数。其中函数内需要两个参数&#xff0c;第一个为指定删除/添加的位置&#xff0c;第二个元素为删除/添加的元素值。 iterator insert(const_iterator _Where, _Ty&&…

C/C++ vector 删除指定元素

C vector 删除符合条件的元素C vector中实际删除元素使用的是容器vecrot中std::vector::erase()方法。C 中std::remove()并不删除元素&#xff0c;因为容器的size()没有变化&#xff0c;只是元素的替换。1.std::vector::erase()  函数原型&#xff1a;iterator erase (iterat…

【c++】vector中删除元素

目录 1.删除指定范围的元素2.删除指定大小的元素3.C20 std::erase, std::erase_if (std::vector)注意点 1.删除指定范围的元素 vector删除元素之pop_back(),erase(),remove() 向量容器vector的成员函数pop_back()可以删除最后一个元素. 而函数erase()可以删除由一个iterator指…

光纤光学原理相关基础知识点

记&#xff1a;研究生导师的方向是光纤光学和机器学习交叉的&#xff0c;导师给推荐了本书&#xff0c;书里面的理论和推导公式作为小白的我真的是不太行&#xff0c;后来在中国大学mooc上找了视频课跟着学习&#xff0c;顺便做下笔记&#xff0c;为日后使用&#xff0c;因此会…

机器人学重点知识点总结

机器人学重点知识点总结 坐标转换与机械臂运动学雅克比矩阵机械臂逆向动力学&#xff08;牛顿欧拉递推&#xff09;机械臂正向动力学运动轨迹生成动力学轨迹跟踪控制 这篇博客主要用来记录一下现代机器人学里面比较基础也比较重要的一些知识点&#xff0c;所有内容均仅仅记录是…

大学物理(下)知识点总结

大学物理&#xff08;下&#xff09;知识点总结&#xff08;持续更新&#xff09; 文章目录 大学物理&#xff08;下&#xff09;知识点总结&#xff08;持续更新&#xff09;静电场知识点总结习题精选 磁场知识点总结习题精选 期中复习总结电磁场知识点总结习题精选 狭义相对论…

Lumerical官方案例、FDTD时域有限差分法仿真学习(二)——宽带光栅耦合器(Broadband grating coupler (2D))

这个 2D FDTD 示例展示了如何获得光栅耦合器的宽带特性&#xff0c;并将结果与实验数据进行比较。 此外&#xff0c;该应用示例演示了多频波束计算的正确使用和优势&#xff0c;并将结果与标准单频波束计算进行了比较。 要更好地了解单频和多频波束计算之间的差异&#xff0c;请…

三维计算机视觉(四)--关键点

关键点又称为感兴趣的点&#xff0c;是低层次视觉通往高层次视觉的捷径&#xff0c;抑或是高层次感知对低层次处理手段的妥协。 RangeImage 1.关键点&#xff0c;线&#xff0c;面 关键点特征点&#xff1b; 关键线边缘&#xff1b; 关键面foreground&#xff1b; 上述三个概…