[SpringCloud] Feign Client 的创建 (一) (四)

文章目录

      • 1.FeignClientsRegistrar
      • 2.完成配置注册
        • 2.1 registerDefaultConfiguration方法
        • 2.2 迭代稳定性
        • 2.3 registerFeignClients方法

1.FeignClientsRegistrar

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

FeignClientsRegistrar实现ImportBeanDefinitionRegistrar接口。

在这里插入图片描述

2.完成配置注册

    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    	// 注册默认的Configuration(其实就是在解析@EnableFeignClients注解)
        this.registerDefaultConfiguration(metadata, registry);
        // 1)扫描所有的@FeignClient接口, 即扫描到Feign接口
        // 2)将每个@FeignClient注解的configuration属性注册进一个缓存map
        // 3)根据@FeignClient注解元数据生成的FeignClientBeanFactory的BeanDefinition, 
        // 并将这个BeanDefinition注册进一个map
        this.registerFeignClients(metadata, registry);
    }
  1. registerDefaultConfiguration方法
  2. registerFeignClients方法

导入的类元数据就是启动类, 通过这个类的元数据可以获取到它上面所有的注解信息。

在这里插入图片描述

2.1 registerDefaultConfiguration方法

registerDefaultConfiguration():

//FeignClientsRegistrar.java
private void registerDefaultConfiguration(AnnotationMetadata metadata,
		BeanDefinitionRegistry registry) {
	//从类元数据中获取@EnableFeignClients注解
	//getAnnotationAttributes:获取类上指定注解的属性
	//该方法第二个参数true,表示将注解中class类型的属性转换为字符串类名暴露到返回到map中
	Map<String, Object> defaultAttrs = metadata
			.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
			
	//处理defaultConfiguration属性
	if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
		String name;
		//返回当前类是否在封闭类中声明(例如,当前类是一个内部/嵌套类,还是一个方法中的本地类)。
		//false代表当前类就是顶级类,此时是启动类,肯定是顶级类
		if (metadata.hasEnclosingClass()) {
			//如果当前是内部、嵌套、方法中的类,获取我的封闭类的类名
			name = "default." + metadata.getEnclosingClassName();
		}
		else {
			//返回false代表当前是就顶级类,直接获取类名
			name = "default." + metadata.getClassName();
		}
		registerClientConfiguration(registry, name,
				defaultAttrs.get("defaultConfiguration"));
	}
}

在这里插入图片描述

registerClientConfiguration():

//FeignClientsRegistrar.java
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
		Object configuration) {
	//获取一个BeanDefinition的构建者 专门构建FeignClientSpecification的BeanDefinition
	BeanDefinitionBuilder builder = BeanDefinitionBuilder
			.genericBeanDefinition(FeignClientSpecification.class);
	//为FeignClientSpecification的构造器设置参数
	builder.addConstructorArgValue(name);
	builder.addConstructorArgValue(configuration);
	//builder.getBeanDefinition()会构建对应的BeanDefinition实例
	//然后将其注册到Spring的注册表中
	registry.registerBeanDefinition(
			name + "." + FeignClientSpecification.class.getSimpleName(),
			builder.getBeanDefinition());
}

//BeanDefinitionBuilder.java
/**
 * Add an indexed constructor arg value. The current index is tracked internally
 * and all additions are at the present point.
 * 添加一个索引构造函数arg值。 内部跟踪当前索引,所有添加都在当前位置。
 */
public BeanDefinitionBuilder addConstructorArgValue(@Nullable Object value) {
	this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(
			this.constructorArgIndex++, value);
	return this;
}

在这里插入图片描述

将BeanDefinition注册到Spring注册表:

在这里插入图片描述

DefaultListableBeanFactory.registerBeanDefinition():

//DefaultListableBeanFactory.java
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
		throws BeanDefinitionStoreException {
	//此时beanName为  default.启动类类名.FeignClientSpecification
	Assert.hasText(beanName, "Bean name must not be empty");
	Assert.notNull(beanDefinition, "BeanDefinition must not be null");

	if (beanDefinition instanceof AbstractBeanDefinition) {
		try {
			//验证这个bean定义。
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}
	//先尝试从注册表中获取
	BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
	if (existingDefinition != null) {
		//如果已经存在,判断是否允许覆盖,不允许就抛异常
		if (!isAllowBeanDefinitionOverriding()) {
			throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
		}
		else if (existingDefinition.getRole() < beanDefinition.getRole()) {
			// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
			if (logger.isInfoEnabled()) {
				logger.info("Overriding user-defined bean definition for bean '" + beanName +
						"' with a framework-generated bean definition: replacing [" +
						existingDefinition + "] with [" + beanDefinition + "]");
			}
		}
		else if (!beanDefinition.equals(existingDefinition)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Overriding bean definition for bean '" + beanName +
						"' with a different definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("Overriding bean definition for bean '" + beanName +
						"' with an equivalent definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		//覆盖,放入beanDefinitionMap
		this.beanDefinitionMap.put(beanName, beanDefinition);
	}
	else {
		//第一次注册
		//检查该工厂bean创建阶段是否已经开始,通过在此期间是否有任何bean被标记为已创建来判断
		if (hasBeanCreationStarted()) {
			// Cannot modify startup-time collection elements anymore (for stable iteration)
			// 不能修改启动中的集合元素(用于稳定的迭代)
			synchronized (this.beanDefinitionMap) {
				//先放入注册表
				this.beanDefinitionMap.put(beanName, beanDefinition);
				//beanDefinitionNames是一个可供遍历的beanName集合,bean创建阶段就是
				//先遍历该集合通过beanName再从beanDefinitionMap中获取BeanDefinition的
				
				//所以在创建阶段为了保证集合迭代稳定性,需要创建新的集合在新的集合上进行修改
				//创建一个新list,将beanDefinitionNames内容添加进去
				List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				//把新的beanName放到新的list中
				updatedDefinitions.add(beanName);
				//将新的集合替代旧的集合
				this.beanDefinitionNames = updatedDefinitions;
				//从工厂内部的手动单例名称集中删除指定名称,避免重复注册
				removeManualSingletonName(beanName);
			}
		}
		else {
			// Still in startup registration phase
			//仍处于启动注册阶段,不用考虑集合迭代稳定性问题
			//直接放入注册表、添加到可遍历集合中
			this.beanDefinitionMap.put(beanName, beanDefinition);
			this.beanDefinitionNames.add(beanName);
			//从工厂内部的手动单例名称集中删除指定名称,避免重复注册
			removeManualSingletonName(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}

	if (existingDefinition != null || containsSingleton(beanName)) {
		resetBeanDefinition(beanName);
	}
}
2.2 迭代稳定性

在这里插入图片描述

多线程的情况, 需要复制一个新的集合, 在新的集合中添加元素后, 替换原来旧集合。

  • 保证多线程情况下共享变量的可见性, 添加了volatile修饰。
    在这里插入图片描述

  • 保证有序性、原子性, 添加了synchronized关键字。

  • 迭代稳定性:
    一个线程正在修改集合中的数据,另一个线程正在迭代读取集合中的数据,由于加了volatile,导致读线程迭代的过程中,写线程对集合中的修改读线程是立即可见的,读线程读取的数据正好是写线程修改的数据,或者读线程一开始获取的个数是10个,遍历过程中,数量变多了变少了,发生这些变化都代表不稳定,并有可能引发错误。
    解决方案:修改的线程在原来集合基础上复制一个新的集合进行修改,等所有修改完成后,将整个新的集合替换掉原来旧的集合,而在修改过程中,其他线程访问的集合的地址还是指向旧的(类似写时复制的感觉)
    如果使用JUC并发包的集合,严重影响性能。

手动单例名称集中删除指定名称, 避免重复注册。

//DefaultListableBeanFactory.java
private void removeManualSingletonName(String beanName) {
	//Consumer:对给定的参数执行此操作。
	//set -> set.remove(beanName):对给定的set集合删除key为beanName的元素
	
	//Predicate:对给定参数计算此谓词。
	//set -> set.contains(beanName):对给定的set集合进行判断,包含key为beanName的元素就返回true
	updateManualSingletonNames(set -> set.remove(beanName), set -> set.contains(beanName));
}

//DefaultListableBeanFactory.java
//更新工厂内部的手动单例名称集。
private void updateManualSingletonNames(Consumer<Set<String>> action, Predicate<Set<String>> condition) {
	//检查这个工厂的bean创建阶段是否已经开始
	if (hasBeanCreationStarted()) {//一样存在迭代稳定性问题
		// Cannot modify startup-time collection elements anymore (for stable iteration)
		// 不能修改启动中的集合元素(用于稳定的迭代)
		synchronized (this.beanDefinitionMap) {
			if (condition.test(this.manualSingletonNames)) {
				//复制了一个集合,对复制的集合进行操作
				Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
				action.accept(updatedSingletons);
				this.manualSingletonNames = updatedSingletons;
			}
		}
	}
	else {
		// Still in startup registration phase
		// 判断this.manualSingletonNames这个集合是否包含key为beanName的元素
		if (condition.test(this.manualSingletonNames)) {
			//包含了就从manualSingletonNames集合删除这个key为beanName的元素
			action.accept(this.manualSingletonNames);
		}
	}
}

updateManualSingletonNames方法:

此方法不仅有迭代稳定性, 而且有双锁DCL

在这里插入图片描述

2.3 registerFeignClients方法
//FeignClientsRegistrar.java
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);
	//获取@EnableFeignClients注解的clients属性
	//该属性直接指定要加载哪些@FeignClient类,配置了这个属性就只会加载指定的
	final Class<?>[] clients = attrs == null ? null
			: (Class<?>[]) attrs.get("clients");
	if (clients == null || clients.length == 0) {
		//clients属性为空,则为扫描器指定条件,只扫描被@FeignClient注解的类
		scanner.addIncludeFilter(annotationTypeFilter);
		//获取扫描路径
		basePackages = getBasePackages(metadata);
	}
	else {
		//clients不空的情况
		final Set<String> clientClasses = new HashSet<>();
		basePackages = new HashSet<>();
		for (Class<?> clazz : clients) {//直接遍历指定的@FeignClient类
			//获取类所在的包路径
			basePackages.add(ClassUtils.getPackageName(clazz));
			//获取类的规范类名
			clientClasses.add(clazz.getCanonicalName());
		}
		//定义扫描过滤器,只扫描clientClasses中包含的类
		AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
			@Override
			protected boolean match(ClassMetadata metadata) {
			    //对扫描到的类进行匹配:
			    //获取当前扫描到的类的类名,转换成规范类名(处理内部类的情况)
				String cleaned = metadata.getClassName().replaceAll("\\$", ".");
				//判断clientClasses中是否包含这个类
				return clientClasses.contains(cleaned);
			}
		};
		//即要同时满足被@FeignClient注解,同时该类在@FeignClientd的clients属性中被指定
		scanner.addIncludeFilter(
				new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
	}

	for (String basePackage : basePackages) {
		//扫描包,获取候选组件的BeanDefinition
		Set<BeanDefinition> candidateComponents = scanner
				.findCandidateComponents(basePackage);
		for (BeanDefinition candidateComponent : candidateComponents) {
			//判断是否是具有注解元数据的BeanDefinition
			if (candidateComponent instanceof AnnotatedBeanDefinition) {
				// verify annotated class is an interface
				AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
				//获取注解的元数据
				AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
				Assert.isTrue(annotationMetadata.isInterface(),
						"@FeignClient can only be specified on an interface");
				//获取@FeignClient注解的属性
				Map<String, Object> attributes = annotationMetadata
						.getAnnotationAttributes(
								FeignClient.class.getCanonicalName());
				//获取FeignClient的名称(即@FeignClient注解的四个属性)
				//优先级contextId > value > name > serviceId
				//就是服务id、服务名称
				String name = getClientName(attributes);
				//注册ClientConfiguration,之前跟过
				//就是注册FeignClientSpecification,FeignClient规范
				registerClientConfiguration(registry, name,
						attributes.get("configuration"));
				//注册FeignClient的FactoryBean的BeanDefinition
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}
}
  • getBasePackages: clients属性为空, 扫描其路径。
  • getCanonicalName: 获取规范类名。
  • getClientName: 获取FeignClient的服务名称。
  • registerFeignClient: 注册FeignClient的FactoryBean。
  1. getBasePackages
//FeignClientsRegistrar.java
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
	//获取@EnableFeignClients注解的属性
	Map<String, Object> attributes = importingClassMetadata
			.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());

	Set<String> basePackages = new HashSet<>();
	for (String pkg : (String[]) attributes.get("value")) {//获取value属性
		if (StringUtils.hasText(pkg)) {
			basePackages.add(pkg);
		}
	}
	for (String pkg : (String[]) attributes.get("basePackages")) {//获取basePackages属性
		if (StringUtils.hasText(pkg)) {
			basePackages.add(pkg);
		}
	}
	for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {//获取basePackageClasses属性
		//获取类所在的包路径
		basePackages.add(ClassUtils.getPackageName(clazz));
	}

	if (basePackages.isEmpty()) {
		//如果还为空,获取之前@Import注解所标记的那个类所在的包路径
		basePackages.add(
				ClassUtils.getPackageName(importingClassMetadata.getClassName()));
	}
	return basePackages;
}

这些属性指定的扫描包路径, 是一个并集的关系。

  1. getCanonicalName

在这里插入图片描述

//Class.java
/**
 * Returns the canonical name of the underlying class as
 * defined by the Java Language Specification.  Returns null if
 * the underlying class does not have a canonical name (i.e., if
 * it is a local or anonymous class or an array whose component
 * type does not have a canonical name).
 * 返回Java语言规范定义的基础类的规范名称。如果基础类没有规范名称则返回null。
 * (例如,如果它是一个本地或匿名类,或者是一个元素类型没有规范名称的数组)
 * 
 * @since 1.5
 */
public String getCanonicalName() {
    if (isArray()) {
        //数组情况:
        //获取数组元素的规范名称
        String canonicalName = getComponentType().getCanonicalName();
        if (canonicalName != null)
            return canonicalName + "[]";
        else
            return null;
    }
    if (isLocalOrAnonymousClass())
        //本地或匿名类
        return null;
    Class<?> enclosingClass = getEnclosingClass();
    if (enclosingClass == null) { // top level class
        //当前类就是最顶层类
        return getName();
    } else {
        //内部类情况:
        String enclosingName = enclosingClass.getCanonicalName();
        if (enclosingName == null)
            return null;
        return enclosingName + "." + getSimpleName();
    }
}
  1. getClientName
//FeignClientsRegistrar.java
private String getClientName(Map<String, Object> client) {
	//注意,client是@FeignClient注解的属性
	if (client == null) {
		return null;
	}
	String value = (String) client.get("contextId");
	if (!StringUtils.hasText(value)) {
		value = (String) client.get("value");
	}
	if (!StringUtils.hasText(value)) {
		value = (String) client.get("name");
	}
	if (!StringUtils.hasText(value)) {
		value = (String) client.get("serviceId");
	}
	if (StringUtils.hasText(value)) {
		return value;
	}
    //看到优先级contextId > value > name > serviceId
	throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
			+ FeignClient.class.getSimpleName());
}
  1. registerFeignClient
//FeignClientsRegistrar.java
private void registerFeignClient(BeanDefinitionRegistry registry,
		AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
	//registry:Spring注册表
	//annotationMetadata:扫描到的被@FeignClient注解的类的注解数据
	//attributes:当前@FeignClient注解上相关的属性
	
	
	String className = annotationMetadata.getClassName();
	//FeignClientFactoryBean.class:FeignClient的工厂Bean,用来创建FeignClient实例的。
	
	//获取构建者构建FeignClientFactoryBean的BeanDefinition
	BeanDefinitionBuilder definition = BeanDefinitionBuilder
			.genericBeanDefinition(FeignClientFactoryBean.class);
	//@FeignClient注解的属性校验
	validate(attributes);
	//用@FeignClient注解的属性值为将来创建的FeignClientFactoryBean实例赋值
	definition.addPropertyValue("url", getUrl(attributes));
	definition.addPropertyValue("path", getPath(attributes));
	String name = getName(attributes);
	definition.addPropertyValue("name", name);
	String contextId = getContextId(attributes);
	definition.addPropertyValue("contextId", contextId);
	definition.addPropertyValue("type", className);
	definition.addPropertyValue("decode404", attributes.get("decode404"));
	definition.addPropertyValue("fallback", attributes.get("fallback"));
	definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
	definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
	//别名
	String alias = contextId + "FeignClient";
	//构建出beanDefinition实例
	AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
	
	//继续初始化,处理primary和qualifier属性配置
	boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
															// null

	beanDefinition.setPrimary(primary);

	String qualifier = getQualifier(attributes);
	if (StringUtils.hasText(qualifier)) {
		alias = qualifier;
	}
	//包装成BeanDefinitionHolder
	BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
			new String[] { alias });
	//注册
	BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

//BeanDefinitionReaderUtils.java
public static void registerBeanDefinition(
		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
		throws BeanDefinitionStoreException {

	// Register bean definition under primary name.
	// 获取beanName
	String beanName = definitionHolder.getBeanName();
	// 注册到注册表
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

	// Register aliases for bean name, if any.
	// 为beanName注册别名
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias : aliases) {
			registry.registerAlias(beanName, alias);
		}
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JQ 查看图片的好插件

效果图 插件官网 https://blog.51cto.com/transfer?https://github.com/fengyuanchen/viewer 使用 <!DOCTYPE html> <html lang"en"> <head><meta charset"utf-8"><link rel"stylesheet" href"css/viewer.c…

攻防世界——catfly

这道题我觉得很难&#xff0c;我当初刷题看见这道题&#xff0c;是唯一一道直接跳过的&#xff0c;现在掌握了一点知识才回来重新看 这道题在linux运行下是这样&#xff0c;我首先猜测是和下面这个time有关&#xff0c;判断达到一定次数就会给我flag 但是我找了好久都没找到那…

(九)信息融合方式简介

目录 前言 一、什么是信息融合&#xff1f; 二、集中式信息融合与分布式信息融合 &#xff08;一&#xff09;集中式融合 &#xff08;二&#xff09;分布式融合 1.简单信息融合 2.CI&#xff08;协方差交叉&#xff09;信息融合 3.无反馈的最优分布式融合 4.有反馈的…

反应式编程(一)什么是反应式编程

目录 一、背景二、反应式编程简介2.1 定义2.2 反应式编程的优势2.3 命令式编程 & 反应式编程 三、Reactor 入门3.1 Reactor 的核心类3.2 Reactor 中主要的方法1&#xff09;创建型方法2&#xff09;转化型方法3&#xff09;其他类型方法4&#xff09;举个例子 四、Reactor …

论文笔记:GPT-4 Is Too Smart To Be Safe: Stealthy Chat with LLMs via Cipher

ICLR 2024 reviewer评分 5688 1 论文思路 输入转换为密码&#xff0c;同时附上提示&#xff0c;将加密输入喂给LLMLLM输出加密的输出加密的输出通过解密器解密 ——>这样的步骤成功地绕过了GPT-4的安全对齐【可以回答一些反人类的问题&#xff0c;这些问题如果明文问的话&…

【C++】set和map

set和map就是我们上篇博客说的key模型和keyvalue模型。它们属于是关联式容器&#xff0c;我们之前说过普通容器和容器适配器&#xff0c;这里的关联式容器就是元素之间是有关联的&#xff0c;通过上篇博客的讲解我们也对它们直接的关系有了一定的了解&#xff0c;那么下面我们先…

蓝桥杯-python-常用库归纳

目录 日期和时间 datetime模块 date日期类&#xff0c;time时间类&#xff0c;datetime日期时间类 定义date&#xff08;年&#xff0c;月&#xff0c;日&#xff09; data之间的减法 定义时间&#xff08;时&#xff0c;分&#xff0c;秒&#xff09; 定义datetime&#xf…

42.HarmonyOS鸿蒙系统 App(ArkUI)实现横屏竖屏自适应

HarmonyOS鸿蒙系统 App(ArkUI)实现横屏竖屏自适应 媒体查询作为响应式设计的核心&#xff0c;在移动设备上应用十分广泛。媒体查询可根据不同设备类型或同设备不同状态修改应用的样式。媒体查询常用于下面两种场景&#xff1a; 针对设备和应用的属性信息&#xff08;比如显示…

【Linux】进程实践项目 —— 自主shell编写

送给大家一句话&#xff1a; 不管前方的路有多苦&#xff0c;只要走的方向正确&#xff0c;不管多么崎岖不平&#xff0c;都比站在原地更接近幸福。 —— 宫崎骏《千与千寻》 自主shell命令编写 1 前言2 项目实现2.1 创建命令行2.2 获取命令2.3 分割命令2.4 运行命令 3 源代码…

计算机服务器中了rmallox勒索病毒怎么办?rmallox勒索病毒解密数据恢复

网络技术的不断发展与应用&#xff0c;大大提高了企业的生产运营效率&#xff0c;越来越多的企业开始网络开展各项工作业务&#xff0c;网络在为人们提供便利的同时&#xff0c;也会存在潜在威胁。近日&#xff0c;云天数据恢复中心接到多家企业的求助&#xff0c;企业的计算机…

Python内置函数enumerate()

Python的内置函数enumerate()。在学习过程中遇到了一点小问题。记录一下。 enumerate() 是 Python 中常用的内置函数之一&#xff0c;它可以用来同时遍历序列的索引和对应的值。具体来说&#xff0c;enumerate() 接受一个可迭代对象作为参数&#xff0c;返回一个包含索引和值的…

vuees6新语法

vue的学习网站&#xff1a; https://www.runoob.com/vue2/vue-tutorial.html1.Vue的介绍 学习目标 说出什么是Vue能够说出Vue的好处能够说出Vue的特点 内容讲解 【1】Vue介绍 1.vue属于一个前端框架&#xff0c;底层使用原生js编写的。主要用来进行前端和后台服务器之间的…

Holiday Notice

Holiday Notice 放假通知 要是每个公司都能放假放的多&#xff0c;把加班折算放假落实到位&#xff0c;还怕我们不努力干活&#xff0c;巴不得把全年都干完了&#xff0c;然后休息。

HCIP【GRE VPN配置】

目录 实验要求&#xff1a; 实验配置思路&#xff1a; 实验配置过程&#xff1a; 一、按照图式配置所有设备的IP地址 &#xff08;1&#xff09;首先配置每个接口的IP地址 &#xff08;2&#xff09;配置静态路由使公网可通 二、在公网的基础上创建GRE VPN隧道&#xff0…

HarmonyOS实战开发-如何实现一个简单的健康生活应用(上)

介绍 本篇Codelab介绍了如何实现一个简单的健康生活应用&#xff0c;主要功能包括&#xff1a; 用户可以创建最多6个健康生活任务&#xff08;早起&#xff0c;喝水&#xff0c;吃苹果&#xff0c;每日微笑&#xff0c;刷牙&#xff0c;早睡&#xff09;&#xff0c;并设置任…

C++list的模拟实现

为了实现list&#xff0c;我们需要实现三个类 一、List的节点类 template<class T> struct ListNode {ListNode(const T& val T()):_pPre(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val; }; 二、List的迭代器…

2024年腾讯云服务器99元一年_老用户优惠续费不涨价

腾讯云99元一年服务器配置为轻量2核2G4M、50GB SSD盘、300GB月流量、4M带宽&#xff0c;新用户和老用户都可以购买&#xff0c;续费不涨价&#xff0c;续费价格也是99元一年。以往腾讯云优惠服务器都是新用户专享的&#xff0c;这款99元服务器老用户也可以购买&#xff0c;这是…

Spring Task 知识点详解、案例、源代码解析

简介&#xff1a;Spring Task 定时任务   所谓定时任务。就是依据我们设定的时间定时运行任务&#xff0c;就像定时发邮件一样&#xff0c;设定时间到了。邮件就会自己主动发送。 在Spring大行其道的今天&#xff0c;Spring也提供了其定时任务功能&#xff0c;Spring Task。同…

安装dalton过程中出现的pcre问题

在前面文章中&#xff0c;基于多种流量检测引擎识别pcap数据包中的威胁&#xff0c;并没有详细的说明dalton的安装。由于dalton提供了脚本./start-dalton.sh &#xff0c;执行之后会自动的安装各种依赖以及suricata&#xff0c;zeek&#xff0c;snort的容器环境。但是在实际执行…

编程新手必看!从零起步掌握Python的终极指南,Python简介(1)

1、Python语言的诞生 Python的作者&#xff0c;Guido von Rossum&#xff08;吉多范罗苏姆&#xff0c;中国Python程序员都叫他 龟叔&#xff09;&#xff0c;荷兰人。1982年&#xff0c;龟叔从阿姆斯特丹大学获得了数学和计算机硕士学位。然而&#xff0c;尽管他算得上是一位…