Spring IOC - ConfigurationClassPostProcessor源码解析

news2024/11/18 1:24:42
        上文提到Spring在Bean扫描过程中,会手动将5个Processor类注册到beanDefinitionMap中,其中ConfigurationClassPostProcessor就是本文将要讲解的内容,该类会在refresh()方法中通过调用invokeBeanFactoryPosstProcessors(beanFactory)被调用。
        5个Processor类列表如下:
类名
是否BeanDefinitionRegistryPostProcessor
是否BeanFactoryPostProcessor
是否BeanPostProcessor
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
EventListenerMethodProcessor
DefaultEventListenerFactory
        ConfigurationClassPostProcessor中两个核心方法将被调用,其主要作用如下:

方法名

作用

postProcessBeanDefinitionRegistry()

(1)@Conditional注解条件解析

(2)被以下注解标注的类及内部类

@Component、@PropertySources、@PropertySource、@ComponentScans、@ComponentScan、@Import、@ImportResource、@Bean、接口中的@Bean

postProcessBeanFactory()

对full模式的配置类BeanDefinition进行CGLib增强:把CGLib生成的子类型设置到beanDefinition中

        postProcessBeanDefinitionRegistry()源码及注释如下:


	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);
		// 执行解析和扫描
		processConfigBeanDefinitions(registry);
	}
        进入processConfigBeanDefinitions(registry)方法,其主体逻辑流程图如下:
        
        其中标红步骤为两个关键步骤:

        1. 通过beanDefinition判断是否为配置类

        Spring专门提供了一个工具类:ConfigurationClassUtils来检查是否为配置类,并标记full、lite模式,逻辑如下:

        2. 解析配置类
        doProcessConfigurationClass即为真正的对个注解的解析方法,其逻辑流程图如下:

        具体的源码及详细注释如下:

        1. 主流程processConfigBeanDefinitions方法:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		// 候选配置类定义列表
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		// 获取容器中所有bean定义的名字
		String[] candidateNames = registry.getBeanDefinitionNames();

		for (String beanName : candidateNames) {
			// 获取bean定义
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			// org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass
			// Bean Definition是否已经被标记为配置类(full、lite模式)
			if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			// 查看bean是否是ConfigurationClass
			//被@Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean标记
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				// 是配置类,则加入到候选列表
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// Return immediately if no @Configuration classes were found
		// 如果为空,即找不到被@Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean标记的类,则立即返回
		if (configCandidates.isEmpty()) {
			return;
		}

		// Sort by previously determined @Order value, if applicable
		// 对需要处理的BeanDefinition排序,值越小越靠前
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

		// Detect any custom bean name generation strategy supplied through the enclosing application context
		// 检查是否有自定义的beanName生成器
		SingletonBeanRegistry sbr = null;
		if (registry instanceof SingletonBeanRegistry) {
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
				//获取自定义的beanName生成器
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
						AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
					// 如果spring有自定义的beanName生成器,则重新赋值
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

		//如果环境对象为空,则创建新的环境对象
		if (this.environment == null) {
			this.environment = new StandardEnvironment();
		}

		// 构造一个配置类解析器,用来把bean定义的重要信息提取转化为ConfigurationClass
		// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		// 候选配置类定义列表(查重)
		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		// 存储已解析的配置类列表
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			// 解析配置类
			parser.parse(candidates);
			// 配置类不能被申明为final(因为CGLIB的限制),除非它声明proxyBeanMethods=false
			// 在@Configuration类中,@Bean方法的实例必须是可重写的,以适应CGLIB
			parser.validate();

			// 获取解析器中解析出的配置类ConfigurationClass: parser.getConfigurationClasses()
			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			// 过滤掉已解析的配置类
			configClasses.removeAll(alreadyParsed);

			// 构造一个bean定义读取器
			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			// 读取ConfigurationClass获取衍生bean定义并注册到容器
			// 核心方法,将完全填充好的ConfigurationClass实例转化为BeanDefinition注册入IOC容器
			this.reader.loadBeanDefinitions(configClasses);
			// 加入已解析配置类
			alreadyParsed.addAll(configClasses);
			// 清空候选配置类定义列表
			candidates.clear();
			// 如果容器中bean定义有新增
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				// 查找出新增的配置类bean定义 start
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
		// 如果想要在@Configuration类中使用ImportAware接口,需要将ImportRegistry注册为bean。这样,@Configuration类就可以
		// 使用ImportRegistry来获取导入的类信息
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}

		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
			// Clear cache in externally provided MetadataReaderFactory; this is a no-op
			// for a shared cache since it'll be cleared by the ApplicationContext.
			// 如果MetadataReaderFactory是由外部提供的,则需要清楚其缓存。但是,如果MetadataReaderFactory是由ApplicationContext共享的,则不需要进行清除,
			// 因为ApplicationContext会自动清楚共享缓存
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}

        2. 其中检查是否为配置类方法ConfigurationClassUtils.checkConfigurationClassCandidate

//检查给定的BeanDefinition是否是一个配置类的候选者(或一个在配置/组件类中声明的嵌套组件类)
	//并对其进行相应的标记处理
	//被@Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean标记
	public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

		//获取bean定义信息中的class类名
		String className = beanDef.getBeanClassName();
		//如果className为空,或者bean定义信息中的factoryMethod不等于空,那么直接返回
		if (className == null || beanDef.getFactoryMethodName() != null) {
			return false;
		}

		AnnotationMetadata metadata;
		//通过注解注入的BeanDefinition都是AnnotatedGenericBeanDefinition,实现了AnnotatedBeanDefinition
		//Spring内部的BeanDefinition都是RootBeanDefiniton,实现了AbstractBeanDefinition
		//此处主要用于判断是否是归属于AnnotatedBeanDefinition
		if (beanDef instanceof AnnotatedBeanDefinition &&
				className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
			// Can reuse the pre-parsed metadata from the given BeanDefinition...
			// 从当前bean的定义信息中获取元素
			metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
		}
		//判断是否是Spring中默认的BeanDefinition
		else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
			// Check already loaded Class if present...
			// since we possibly can't even load the class file for this Class.
			//获取当前bean对象的Class对象
			Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
			//如果class实例是下面4种类或者接口的子类,父接口等任何一种情况,直接返回
			if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
					BeanPostProcessor.class.isAssignableFrom(beanClass) ||
					AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
					EventListenerFactory.class.isAssignableFrom(beanClass)) {
				return false;
			}
			//为给定类创建新的AnnotationMetadata
			metadata = AnnotationMetadata.introspect(beanClass);
		}
		//如果上述两种情况都不符合
		else {
			try {
				//获取className的MetadataReader实例
				MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
				//读取底层类的完整注解元数据,包括带注解方法的元数据
				metadata = metadataReader.getAnnotationMetadata();
			}
			catch (IOException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Could not find class file for introspecting configuration annotations: " +
							className, ex);
				}
				return false;
			}
		}
		// 获取Bean Definition的元数据被@Configuration注解标注的属性字典值
		Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
		// 如果bean被@Configuration注解标注,并且proxyBeanMethods属性的值为true,则标注当前配置类为full,即需要代理增强,为一个代理类
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		// 如果存在,或者isConfigurationCandidate返回true,则标注当前配置类为lite,即不需要代理增强,为一个普通类
		// 标注了Configuration注解且proxyBeanMethods属性的值为false,则为lite模式
		// 没有标注Configuration注解,但是标注了Component、ComponentScan、Import、ImportResource、@Bean注解、则为lite模式
		else if (config != null || isConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		// 组件类不存在@Configuration注解,直接返回false
		else {
			return false;
		}

		// 获取配置类的排序顺序,设置到组件定义属性中
		// It's a full or lite configuration candidate... Let's determine the order value, if any.
		//Bean Definition是一个标记为full/lite的候选项,如果有order属性就设置order属性
		Integer order = getOrder(metadata);
		if (order != null) {
			//设置bean的order值
			beanDef.setAttribute(ORDER_ATTRIBUTE, order);
		}

		return true;
	}

        3. 真正干活的方法doProessConfigurationClass

// 通过读取源类的注释、成员和方法来构建完整的ConfigurationClass
	// 1.处理@Component
	// 2.处理每个@PropertySource
	// 3.处理每个@ComponentScan
	// 4.处理每个Impoit
	// 5.处理每个@ImportResource
	// 6.找配置类中@Bean注解的方法(找出Class中存在@Bean标注的方法,加入到ConfigurationClass的beanMethods属性)
	// 7.找接口中@Bean注解的方法
	// 8.存在父类则递归处理
	@Nullable
	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			// 递归处理嵌套的成员类
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		// 处理@PropertySources和@PropertySource注解
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		// 处理@ComponentScans和@ComponentScan注解
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				// 配置类使用了注解@ComponentScan -> 立即执行扫描。
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		// 处理@Import注解
		// getImports:递归获取@Import导入的类
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		// Process any @ImportResource annotations
		// 处理@ImportResource注解
		// 读取locations, reader属性,封装存放在importResources map集合中
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		// 处理@Bean方法
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		// 处理接口的默认方法实现,从jdk8开始,接口中的方法可以有自己的默认实现,因此如果这个接口的方法加了@Bean注解,也需要被解析
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

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

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

相关文章

dsm 和 大五人格

问题记录 1. 九型人格好像有很多层, 各层会有对应? 笔迹分析. 2. 还要结合行为吧? 书: dsm5 失序的人格 动力取向精神 问题: 大五人格和dsm的渊源, 觉得dsm太粗略了,搞个大五海洋. 问题: 很内向, 然后喜欢摄影, 也喜欢看b站上其他专业博主上传的摄影vlog. 跟他交流的时候又…

图的广度优先遍历讲解附Java代码加详细注释

目录 引入 代码实现 复杂度分析 引入 类比树的广度优先遍历&#xff08;层序遍历&#xff09;&#xff0c;通过一个队列不断地实现出队的同时把左右孩子入队的操作实现广度优先遍历&#xff0c;值得注意的是图是否有环的情况。 用相似的方法可以实现图的广度优先遍历&#…

Linux:Docker的介绍(1)

Docker官网 Docker: Accelerated Container Application Developmenthttps://www.docker.com/ docker是什么&#xff1f; 是一种轻量级的‘虚拟机’ 在Linux容器里运行应用的开源工具 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个…

跟着步骤,快速实现图书行业小程序商城

跟着步骤&#xff0c;快速实现图书行业小程序商城 打造独特图书购物体验&#xff0c;小程序商城制作指南 轻松搭建图书馆与书店的线上商城小程序 值得一试的图书教材小程序商城搭建方法 图书商城小程序制作指南&#xff0c;助你成为行业领袖 实战教程&#xff1a;如何制作…

LabVIEW对多个同一类型控件进行操作

LabVIEW对多个同一类型控件进行操作 有时候LabVIEW要多多个同一类的控件进行操作&#xff0c;如对tab中某个page中所有String控件设为dissable。就可以用如下的方式。className是获取不同类型的控件。通过类型选择&#xff0c;可以选择所有的String控件&#xff0c;并可对特定…

Hydra(九头蛇海德拉)教程

Hydra 参数 hydra <参数> <IP地址> <服务名> 参数案例说明-l-l root登录账号-L-L userName.txt用户文件-p-l 123456登录密码-P-P passwd.txt密码文件-e-e nsrn 空密码 s 用户名即密码 r 用户名和密码相反&#xff08;如root的密码为toor&#xff09;-s-s 21指…

python基于VGG19实现图像风格迁移

目录 1、原理 2、代码实现 1、原理 图像风格迁移是一种将一张图片的内容与另一张图片的风格进行合成的技术。 风格&#xff08;style&#xff09;是指图像中不同空间尺度的纹理、颜色和视觉图案&#xff0c;内容&#xff08;content&#xff09;是指图像的高级宏观结构。 实…

小程序制作(超详解!!!)第十一节 成绩计算器

设计一个计算学生平均成绩的小程序。当输入学生信息和各门功课成绩并提交后&#xff0c;能够显示学生的信息及平均成绩。 1.index.wxml <view classbox><view classtitle>成绩计算器</view><input placeholder"请输入你的名字" placeholder-c…

台积电熊本厂力拚明年如期量产 | 百能云芯

台积电&#xff08;TSMC&#xff09;正在积极推进其在日本熊本的新工厂项目&#xff0c;该项目正在如火如荼地建设中。根据了解&#xff0c;该工厂的员工总数将超过千人。台积电的目标是确保该新工厂按计划于2024年开始量产&#xff0c;这将使其成为首个因应客户需求和地缘政治…

食品行业小程序开发攻略

想要设计一个食品小程序商城&#xff0c;却担心自己没有任何设计经验&#xff1f;别担心&#xff0c;现在有了一些简单易用的小程序制作工具&#xff0c;零基础也能成为一个小程序商城设计师&#xff01;接下来&#xff0c;我们将一步步教你如何使用这些工具快速上手。 首先&am…

Easy Javadoc插件的使用教程

目录 一、安装Easy Javadoc插件 二、配置注释模板 三、配置翻译 一、安装Easy Javadoc插件 在idea的File-Settings-Plugins中搜索Easy Javadoc插件&#xff0c;点击install进行安装&#xff0c;安装完成后需要restart IDE&#xff0c;重启后插件生效。 二、配置注释模板 …

相关性网络图 |显著性标记

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 本期教程 写在前面 此图是一位同学看到后&#xff0c;想出的一期教程。 最近&#xff0c;自己的事情比较多&#xff0c;会无暇顾及社群和公众号教程。 1 安装和加载相关的R包 library(ggraph) library(tidy…

AI大模型在短视频处理和剪辑中的应用,文末送书

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

【Elasticsearch 未授权访问漏洞复现】

文章目录 一、漏洞描述二、漏洞复现三、修复建议 一、漏洞描述 ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java开发的&#xff0c;并作为Apache许可条款下的开放源码发布&am…

信号类型(通信)——QPSK、OQPSK、IJF_OQPSK调制信号

系列文章目录 《信号类型&#xff08;通信&#xff09;——仿真》 《信号类型&#xff08;通信&#xff09;——QAM调制信号》 文章目录 前言 一、QPSK通信调制信号 1.1、原理 1.2、仿真 二、OQPSK通信调制信号 1.1、原理 1.2、仿真 三、IJF_OQPSK通信调制信号 1.1、…

java--方法的其他形式

1.方法定义时&#xff1a;需要按照方法解决的实际业务需求&#xff0c;来设计合理的方法形式解决问题。 1.注意事项 ①如果方法不需要返回数据&#xff0c;返回值类型必须申明成void(无返回值申明)&#xff0c;此时方法内部不可以使用return返回数据。 ②方法如果不需要接收数…

[HNCTF 2022 WEEK2]easy_include 文件包含遇上nginx

这道纯粹记录 完全没想到 <?php //WEB手要懂得搜索if(isset($_GET[file])){$file $_GET[file];if(preg_match("/php|flag|data|\~|\!|\|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\|\/i", $file)){die("error");}include($file); }else{highlight_file(__…

【C#】winform打包,桌面快捷方式设置图标无效

使用visual stdiong的打包工具插件&#xff1a;Microsoft Visual Studio Installer Projects时&#xff0c;发现无论怎么设置软件的快捷方式图标&#xff0c;都无法设置成功&#xff0c;尝试多次设置快捷方式图标均无效。 此时使用Greenfish Icon Editor Pro软件对图片进行像素…

微信小程序获取最新隐私协议授权 以及涉及隐私API权限申请

最近小程序新推出的隐私政策&#xff0c;导致之前小程序一些相关api无法调用&#xff0c;甚至连登录都出现了问题... 话不多说&#xff0c;来点实在的 提示用户同意隐私授权的弹窗结构&#xff0c;样式就不粘了... <u-popup :show"showPrivacy" mode"cente…

如何利用自定义数据对象(元数据)实现全场景身份数据治理

在数字化时代背景下&#xff0c;5G、云计算、大数据、物联网、人工智能等技术的发展&#xff0c;为企业数据管理提供了基础技术支撑。数字化浪潮推动企业快速升级迭代&#xff0c;在数据管理和数字化转型过程中&#xff0c;企业内部的数据情况常常错综复杂&#xff0c;并伴随着…