Open Feign 源码解析(一) --- FactoryBean的妙用

什么是Open Feign?

OpenFeign 是 Spring Cloud 全家桶的组件之一, 其核心的作用是为 Rest API 提供高效简洁的 RPC 调用方式

搭建测试项目

服务接口和实体

项目名称

cloud-feign-api

实体类
public class Order implements Serializable {private Long id;private String name;public Order() {}public Order(Long id, String name) {this.id = id;this.name = name;}
}public class User implements Serializable {private Long id;private String name;public User() {}public User(Long id, String name) {this.id = id;this.name = name;}
}public class Result <T> implements Serializable
{private Integer code;private String message;private T data;public Result(Integer code, String message, T data) {this.code = code;this.message = message;this.data = data;}public Result(T data) {this(200, "操作成功", data);}
}

服务提供方

项目名称

cloud-feign-server

依赖 (pom.xml)
<dependencies><!--实体类--><dependency><groupId>org.example</groupId><artifactId>cloud-feign-api</artifactId><version>1.0-SNAPSHOT</version></dependency><!-- 注册中心 nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
配置文件(application.yml)
server:port: 9001spring:application:name: cloud-feign-servercloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址
配置类

启动类
@SpringBootApplication
@EnableDiscoveryClient
public class FeignServerMain {public static void main(String[] args){SpringApplication.run(FeignServerMain.class,args);}
}
控制器
@RestController
public class OrderServerController {@GetMapping(value = "/order/get/{id}")public Order getPaymentById(@PathVariable("id") Long id){return new Order(id, "order");}
}@RestController
public class UserServerController {@GetMapping(value = "/user/get/{id}")public User getUserById(@PathVariable("id") Long id){return new User(id, "user");}
}

服务消费方

项目名称

cloud-feign-client

依赖 (pom.xml)
<dependencies><!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--实体类--><dependency><groupId>org.example</groupId><artifactId>cloud-feign-api</artifactId><version>1.0-SNAPSHOT</version></dependency><!-- 注册中心 nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
配置文件(application.yml)
server:port: 9000spring:application:name: feign-order-clientcloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址
配置类
@Configuration
public class DefaultConfiguration {}@Configuration
public class OrderConfiguration {}@Configuration
public class UserConfiguration {}
启动类
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = {DefaultConfiguration.class}) // 开启feign
@EnableDiscoveryClient
public class FeignClientMain {public static void main(String[] args){SpringApplication.run(FeignClientMain.class,args);}
}
控制器
@RestController
public class OrderClientController {@Resourceprivate OrderService orderService;@GetMapping(value = "/consumer/feign/order/get/{id}")public Result<Order> getOrderById(@PathVariable("id") Long id){Order order = orderService.getOrderById(id);return new Result<>(order);}
}@RestController
public class UserClientController {@Resourceprivate UserService userService;@GetMapping(value = "/consumer/feign/user/get/{id}")public Result<User> getUserById(@PathVariable("id") Long id){User user = userService.getUserById(id);return new Result<>(user);}
}
服务接口
// http://localhost:9000/consumer/feign/order/get/1
@FeignClient(value = "cloud-feign-server", contextId = "order", configuration = OrderConfiguration.class)
public interface OrderService {@GetMapping(value = "/order/get/{id}")Order getOrderById(@PathVariable("id") Long id);
}// http://localhost:9000/consumer/feign/user/get/1
@FeignClient(value = "cloud-feign-server", contextId = "user", configuration = UserConfiguration.class)
public interface UserService {@GetMapping(value = "/user/get/{id}")User getUserById(@PathVariable("id") Long id);
}

问题:为何只定义接口而没有实现类?

思路分析

问题一:如何动态生成实现类做到?

动态代理 (cglib, jdk)

问题二:代理对象如何交给spring容器?

把Bean交给spring容器的方法:

1.xml 声明bean <bean id=“”, class=“”>

2.@ComponentScan + @Sevice/@Controller/@Repository/@Componet

3.@Import(XXX.class)

4.ImportSelector 接口 -> 返回类名数组

5.ImportBeanDefinitionRegistrar 接口 -> registerBeanDefinitions

6.@Bean 注解

7.FactoryBean 接口 -> getObject()

8.SingletonBeanRegistry.registerSingleton(); API

前五种方法bean的创建过程是交给spring负责的,流程如下

class -> bean definition -> bean -> put in cache

在这里插入图片描述

如何把一个第三方的对象(完全由程序员控制对象创建过程)交给Spring管理?

1.factoryBean

2.SingletonBeanRegistry.registerSingleton();

3.@Bean

问题三:多个接口需要写多个对应的factoryBean类吗?

不需要

1)只要定义一个factoryBean类,把接口的Class作为变量传给factoryBean

2) 针对不同的接口需要创建不同的factoryBean对象,每个factoryBean对象所持有的接口类型是不同的。

class FeignClientFactoryBean implements FactoryBean<Object> {private Class<?> type; // 接口类型@Overridepublic Object getObject() throws Exception {// 返回代理对象return Proxy.newProxyInstance(this.getClassLoader(),new Class<?>[] {type}, new InvocationHandler());}
}
问题四:一个factoryBean类如何创建多个持有不同的接口类型的对象?

1)创建多个Bean Definition

​ BeanDefinitionBuilder.build()

2)每个Bean Definition 指定不同的接口类型

​ BeanDefinitionBuilder.addPropertyValue(String name, @Nullable Object value)

​ BeanDefinitionBuilder.addConstructorArgValue(@Nullable Object value)

问题五:如何优雅地把自定义的Bean Definition交给Spring?

ImportBeanDefinitionRegistrar 接口

-> registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)

@Import、ImportSelector、ImportBeanDefinitionRegistrar的使用和区别

1)@Import(XXX.class)一般配合ImportSelector或者ImportBeanDefinitionRegistrar使用

2)ImportSelector返回的是全类名数组,用于选择需要的配置类

3)ImportBeanDefinitionRegistrar提供BeanDefinitionRegistry,用于注册自定义的Bean Definition

问题六:如何获取带有@FeignClient注解的接口以及注解信息?

包扫描

Spring 提供ClassPathScanningCandidateComponentProvider类做包扫描功能

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {private final List<TypeFilter> includeFilters = new LinkedList<>();private final List<TypeFilter> excludeFilters = new LinkedList<>();public Set<BeanDefinition> findCandidateComponents(String basePackage) {if (this.componentsIndex != null && indexSupportsIncludeFilters()) {return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);}else {return scanCandidateComponents(basePackage);}}private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);for (Resource resource : resources) {if (resource.isReadable()) {try {MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);// 第一次判断是否是候选组件if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setResource(resource);sbd.setSource(resource);// 第二次判断是否是候选组件if (isCandidateComponent(sbd)) {candidates.add(sbd);}}	}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}}}catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;}/** 用类型过滤器来判断是否是候选的组件 */protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;}/** 判断bean定义是否符合候选的组件:独立的并且是具体的(不是接口或抽象类)  可以重写 */protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata = beanDefinition.getMetadata();return (metadata.isIndependent() && (metadata.isConcrete() ||(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));}
}

源码解读

EnableFeignClients
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {// basePackages的别名String[] value() default {};// 扫描的包String[] basePackages() default {};// 扫描的包的classClass<?>[] basePackageClasses() default {};// 默认的配置类Class<?>[] defaultConfiguration() default {};// 手动传入的feign client对应的ClassClass<?>[] clients() default {};}
FeignClientsRegistrar
class FeignClientsRegistrarimplements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 注册默认配置 (启动类上面EnableFeignClients里面的defaultConfiguration属性值)registerDefaultConfiguration(metadata, registry);// 注册feign clientsregisterFeignClients(metadata, registry);}/** 注册默认配置的bean定义(FeignClientSpecification) */private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 从EnableFeignClients注解取出所有的属性值Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);// 如果有配置defaultConfigurationif (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {String name;if (metadata.hasEnclosingClass()) {name = "default." + metadata.getEnclosingClassName();}else {name = "default." + metadata.getClassName();}registerClientConfiguration(registry, name,defaultAttrs.get("defaultConfiguration"));}} /** 注册所有的feign client的bean定义(FeignClientFactoryBean) */public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 获取扫描器ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);Set<String> basePackages;// 获取EnableFeignClients注解中的属性 和 属性值Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());// 创建注解类型的过滤器用于过滤出带有FeignClient注解的类或接口AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);// 是否写了clients, @EnableFeignClients(clients = {ConsumerFeignClient.class}), 写了就会新增ClassFilter, 把过滤器添加到扫描器中, 然后通过扫描器器扫描制定的class所在的包final Class<?>[] clients = attrs == null ? null: (Class<?>[]) attrs.get("clients");if (clients == null || clients.length == 0) {// 扫描器添加注解过滤器scanner.addIncludeFilter(annotationTypeFilter);// 获取扫描包路径basePackages = getBasePackages(metadata);}else {final Set<String> clientClasses = new HashSet<>();basePackages = new HashSet<>();for (Class<?> clazz : clients) {// 指定class的包路径basePackages.add(ClassUtils.getPackageName(clazz));// 把指定的class的权限定类名放在clientClasses中clientClasses.add(clazz.getCanonicalName());}AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {@Overrideprotected boolean match(ClassMetadata metadata) {String cleaned = metadata.getClassName().replaceAll("\\$", ".");return clientClasses.contains(cleaned);}};// 把添加class的过滤器scanner.addIncludeFilter(new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));}/** 进行包扫描 */for (String basePackage : basePackages) {// 根据每一个包找出候选的bean定义Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;// 获取BeanDefinition的注解元信息AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();// 判断这个Bean定义是否是接口Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");// 获取FeignClient注解的属性值Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());// 获取FeignClient的名字String name = getClientName(attributes);// 注册每个feign client注册对应的配置(FeignClientSpecification)registerClientConfiguration(registry, name,attributes.get("configuration"));// 注册feign client的bean定义(FeignClientFactoryBean)registerFeignClient(registry, annotationMetadata, attributes);}}}}/** 获取扫描器 重写第二个isCandidateComponent */protected ClassPathScanningCandidateComponentProvider getScanner() {return new ClassPathScanningCandidateComponentProvider(false, this.environment) {@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {boolean isCandidate = false;// beanDefinition.getMetadata().isIndependent() (当前类是否是独立的(普通我们那样写的就是独立的, 而内部类(静态内部类除外)就算加了@Component, 它因为不是独立的所以返回false))if (beanDefinition.getMetadata().isIndependent()) {// bean定义对应的class不能是注解if (!beanDefinition.getMetadata().isAnnotation()) {isCandidate = true;}}return isCandidate;}};}/** 根据配置类生成并注册FeignClientSpecification的bean定义*/private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,Object configuration) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),builder.getBeanDefinition());}/** 生成并注册FeignClientFactoryBean的bean定义 */private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {// 获取要注入feign的class名称String className = annotationMetadata.getClassName();// FeignClientFactoryBean这个类就是它怎么通过FactorBean, 把所有的接口转换成对应类型的BeanDefinition的// BeanClass就是通过genericBeanDefinition方法设置进去的BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);// 校验FeignClient中的属性, fallback, fallbackFactory, 熔断降级的validate(attributes);// feignclient中的urldefinition.addPropertyValue("url", getUrl(attributes));// feignclient中的pathdefinition.addPropertyValue("path", getPath(attributes));// feignclient中的nameString name = getName(attributes);definition.addPropertyValue("name", name);// feignclient中的contextIdString contextId = getContextId(attributes);definition.addPropertyValue("contextId", contextId);// 要注册的feign的class名称 (把FeignClientFactoryBean属性Type给赋值了, 到时候获取bean的时候就是这个类型的Bean)definition.addPropertyValue("type", className);// feignclient中的decode404definition.addPropertyValue("decode404", attributes.get("decode404"));// feignclient中的fallbackdefinition.addPropertyValue("fallback", attributes.get("fallback"));// feignclient中的fallbackFactorydefinition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));// 自动注入类型是通过byTypedefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);String alias = contextId + "FeignClient";AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();// feignclient中的primaryboolean primary = (Boolean) attributes.get("primary"); // has a default, won't be// nullbeanDefinition.setPrimary(primary);// feignclient中的qualifierString qualifier = getQualifier(attributes);if (StringUtils.hasText(qualifier)) {alias = qualifier;}// 构建BeanDefinitionBeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,new String[] { alias });// 注册beanDefinitionBeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}
}
FeignClientFactoryBean
class FeignClientFactoryBeanimplements FactoryBean<Object>, InitializingBean, ApplicationContextAware {private Class<?> type;@Overridepublic Object getObject() throws Exception {return getTarget();}/*** @param <T> the target type of the Feign client* @return a {@link Feign} client created with the specified data and the context* information*/<T> T getTarget() {FeignContext context = this.applicationContext.getBean(FeignContext.class);Feign.Builder builder = feign(context);if (!StringUtils.hasText(this.url)) {if (!this.name.startsWith("http")) {this.url = "http://" + this.name;}else {this.url = this.name;}this.url += cleanPath();return (T) loadBalance(builder, context,new HardCodedTarget<>(this.type, this.name, this.url));}if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {this.url = "http://" + this.url;}String url = this.url + cleanPath();// 获取client, 重点, 它到时候要给到SynchronousMethodHandler中Client client = getOptional(context, Client.class);if (client != null) {if (client instanceof LoadBalancerFeignClient) {// not load balancing because we have a url,// but ribbon is on the classpath, so unwrapclient = ((LoadBalancerFeignClient) client).getDelegate();}if (client instanceof FeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient = ((FeignBlockingLoadBalancerClient) client).getDelegate();}builder.client(client);}Targeter targeter = get(context, Targeter.class);return (T) targeter.target(this, builder, context,new HardCodedTarget<>(this.type, this.name, url));}
}
DefaultTargeter
class DefaultTargeter implements Targeter {@Overridepublic <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget<T> target) {return feign.target(target);}}
Feign
public abstract class Feign {public static Builder builder() {return new Builder();}public <T> T target(Target<T> target) {return build().newInstance(target);}public Feign build() {SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,logLevel, decode404, closeAfterDecode, propagationPolicy);ParseHandlersByName handlersByName =new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,errorDecoder, synchronousMethodHandlerFactory);return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);}  
}
ReflectiveFeign
public class ReflectiveFeign extends Feign {private final InvocationHandlerFactory factory;@Overridepublic <T> T newInstance(Target<T> target) {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)) {DefaultMethodHandler handler = new DefaultMethodHandler(method);defaultMethodHandlers.add(handler);methodToHandler.put(method, handler);} else {methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));}}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;}
}

总结:

设计:只需要定义接口 + 注解, 没有具体的实现类

解决方案:根据接口动态生成代理对象,把增强功能封装在里面,并把此对象交给spring管理

技术点:动态代理,factoryBean接口,包扫描,如何把自定义的Bean 定义交给spring(ImportBeanDefinitionRegistrar)

备份

<dependencies><!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--spring retry framework--><dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></dependency><!-- http 客户端-->
<!--        <dependency>-->
<!--            <groupId>org.apache.httpcomponents</groupId>-->
<!--            <artifactId>httpclient</artifactId>-->
<!--        </dependency>--><!--        <dependency>-->
<!--            <groupId>com.squareup.okhttp3</groupId>-->
<!--            <artifactId>okhttp</artifactId>-->
<!--        </dependency>--><dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId></dependency><dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId></dependency><!--实体类--><dependency><groupId>org.example</groupId><artifactId>cloud-feign-api</artifactId><version>1.0-SNAPSHOT</version></dependency><!-- 注册中心 nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

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

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

相关文章

Vue3的项目创建到启动

Vue3的项目创建 检查node版本创建 npm init vuelatest 安装依赖 项目启动 启动成功

java学习part15单例模式

107-面向对象(高级)-单例设计模式与main()的理解_哔哩哔哩_bilibili 1.单例 就是说在某些开发场景中&#xff0c;某个类只要有一个对象就足够使用了&#xff0c;不需要重复创建。 &#xff08;理解&#xff1a;比如说是数据库对象&#xff0c;使用时创建一个可以处理所有的数…

苍穹外卖项目笔记(5)——Redis

1 入门 1.1 Redis 简介 Redis 是一个基于内存的 key-value 结构数据库&#xff0c;官网链接&#xff08;中文&#xff09;&#xff1a;https://www.redis.net.cn 特点&#xff1a; 基于内存存储&#xff0c;读写性能高适合存储热点数据&#xff08;热点商品、资讯、新闻&am…

C语言进阶-程序环境和预处理

目录 ​编辑 翻译环境 编译过程 预定义符号 #define #define 定义标识符 #define 定义宏 注意 例1 例2 提醒 #和## ##的作用 ​编辑 #的作用 ​编辑 带副作用的宏参数 预处理结果 输出结果 宏和函数对比 宏的缺点 命名约定 #undef 文件包含 本地文件包含 库文件包含…

C#,《小白学程序》第九课:堆栈(Stack),先进后出的数据型式

1 文本格式 /// <summary> /// 《小白学程序》第九课&#xff1a;堆栈&#xff08;Stack&#xff09; /// 堆栈与队列是相似的数据形态&#xff1b;特点是&#xff1a;先进后出&#xff1b; /// 比如&#xff1a;狭窄的电梯&#xff0c;先进去的人只能最后出来&#xff1…

oracle第一部分答疑

基础知识&#xff1a;第一章 视频总结&#xff1a; 1.体系结构&#xff1a; 1.1 oracle server 包括 instance/database instance包括&#xff1a; 2.内存结构&#xff1a; sga pga 3.进程结构&#xff1a; databse包括&#xff1a; 1.参数文件&#xff1a; pfile spfile 2…

西南科技大学(数据结构A)期末自测练习一

一、填空题(每空0.5分,共5分) 1、数据结构是指( A )。 A、数据元素的组织形式 B、数据类型 C、数据存储结构 D、数据定义 2、数据结构被形式地定义为(D,R),其中D是( B )的有限集合,R是D上( D )的有限集合。 (1)A.算法B.数据元素C.数据操作D.逻辑结构 (2)A.操作B.…

C++二分查找或并集查找:交换得到字典序最小的数组

作者推荐 利用广度优先或模拟解决米诺骨牌 本文涉及的基础知识点 二分查找算法合集 题目 给你一个下标从 0 开始的 正整数 数组 nums 和一个 正整数 limit 。 在一次操作中&#xff0c;你可以选择任意两个下标 i 和 j&#xff0c;如果 满足 |nums[i] - nums[j]| < limi…

Sass基础知识详细讲解【附带表图】

文章目录 前言使用 SassRack / Rails / Merb插件缓存选项语法选择编码 Sass CSS扩展Sass 注释输出 Sass 脚本Sass -规则和指令Sass 控制指令和表达式 Sass 混入指令Sass 功能指令命名约定Sass 输出样式:nested:expanded:compact:compressedSass 扩展缓存存储自定义导入 后言 前…

Python爬虫之代理IP与访问控制

目录 前言 一、代理IP 1.1.使用代理IP的步骤 1.2.寻找可用的代理IP 1.3.设置代理IP 1.4.验证代理IP的可用性 二、访问控制 2.1.遵守Robots协议 2.2.设置访问时间间隔 2.3.多线程爬取 总结 前言 在进行Python爬虫过程中&#xff0c;代理IP与访问控制是我们经常需要处…

贪吃蛇小游戏基本简单布局

代码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>Layui贪吃蛇小游戏</title> <link rel"stylesheet" href"https://cdn.bootcdn.net/ajax/libs/layui/2.5.7/css/layui.…

如何与死锁斗争!!!

其他系列文章导航 Java基础合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、死锁场景现场 二、死锁是如何产生的 三、死锁排查思路 四、sql模拟死锁复现 五、死锁的解决方案 前言 为避免影响业务&#xff0c;应尽可能避…

Django回顾【一】

一、Web应用程序 Web应用程序是一种可以通过Web访问的应用程序&#xff0c;程序的最大好处是用户很容易访问应用程序&#xff0c;用户只需要有浏览器即可&#xff0c;不需要再安装其他软件。应用程序有两种模式C/S、B/S。 C/S&#xff1a;客户端<----->服务端 例如My…

分类预测 | Matlab实现NGO-KELM北方苍鹰算法优化核极限学习机分类预测

分类预测 | Matlab实现NGO-KELM北方苍鹰算法优化核极限学习机分类预测 目录 分类预测 | Matlab实现NGO-KELM北方苍鹰算法优化核极限学习机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现NGO-KELM北方苍鹰算法优化核极限学习机分类预测&#xff08;完…

App的测试,和传统软件测试有哪些区别?应该增加哪些方面的测试用例?

从上图可知&#xff0c;测试人员所测项目占比中&#xff0c;App测试占比是最高的。 这就意味着学习期间&#xff0c;我们要花最多的精力去学App的各类测试。也意味着我们找工作前&#xff0c;就得知道&#xff0c;App的测试点是什么&#xff0c;App功能我们得会测试&#xff0…

Unreal Engine 学习笔记 (4)—— 多方向动画

1.创建混合空间 1.设置水平方向命名为Direction表示行进方向 -45,300表示向左前方45度方向行走-90,300表示向正左方90度方向行走-135,300表示向左后方45度方向行走-180,300表示向正后方行走右侧方向动画与上述左侧使用同样方法设置Run动画与Walk动画使用同样方法设置 2. 设置…

Java游戏 王者荣耀

GameFrame类 所需图片&#xff1a; package 王者荣耀;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList…

某思路等考通一级MSOffice的分析

看到有朋友寻求2021版的等级考试一级软件&#xff0c;秉承授人以鱼不如授人以渔的理念&#xff0c;特写这个帖子。 某思路等考通一级MSOffice&#xff0c;版本6.5。 用到的软件&#xff0c;ScanId&#xff0c;de4dot,dnSpy。 第一步&#xff1a;分析 软件启动后有在线激活提示&…

【索引优化与查询优化】

文章目录 1. 索引失效的案例1.1 最左优先1.2 主键插入顺序1.3 计算、函数、类型转换(自动或手动)导致索引失效1.4 范围条件右边的列索引失效1.5 非 条件索引失效1.6 like以通配符%开头索引失效1.7 OR 前后存在非索引的列&#xff0c;索引失效 2. 关联查询优化 1. 索引失效的案例…

FinOps和DevOps的未来会怎样?

FinOps&#xff08;或财务运营&#xff09;是一种文化实践&#xff0c;它将财务责任引入云的可变支出模型。这是一种将系统、最佳实践和文化相结合的战略方法&#xff0c;可提高组织了解云成本并做出明智决策的能力。 本质上&#xff0c;FinOps 是一个管理云运营费用&#xff…