Spring源码十:BeanPostProcess

news2024/11/22 9:19:44

上一篇Spring源码九:BeanFactoryPostProcessor,我们看到ApplicationContext容器通过refresh方法中的postProcessBeanFactory方法和BeanFactoryPostProcessor类提供预留扩展点,他可以在Spring容器的层面对BeanFactroy或其他属性进行修改,所以我们经常说BeanFactoryPost Processor是Spring容器层面的一个扩展点。

但是,我们除了在容器层面外我们有没有粒度更小一点的扩展处理呢?比我们能否直接修改我们的Bean呢?

接下来咱们进入Spring给我们预留另外一个比较重要的扩展点,也就是我们bean的后置处理器BeanPost Processor。

BeanPostProcessor初探

在Spring框架中,BeanPostProcessor是一个核心接口,它允许我们在Spring容器实例化、配置和初始化Bean之前或之后进行一些额外的处理。通过实现这个接口,我们可以在Bean的生命周期的特定点插入自定义逻辑,以增强或修改Bean的行为。本文将深入探讨BeanPostProcessor的定义、用途、使用实例、注册方式以及其在Spring应用中的重要性和应用场景。

1. BeanPostProcessor接口定义

BeanPostProcessor接口定义了两个主要方法:

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
  • postProcessBeforeInitialization:在Bean初始化之前被调用。这通常指的是在Bean的init-method或者实现了InitializingBean接口的afterPropertiesSet方法之前。
  • postProcessAfterInitialization:在Bean初始化之后被调用。

这些方法的主要作用是允许开发者在Bean的初始化过程中进行定制化处理,这种处理可以是对Bean属性的修改、添加日志、检查标记接口或对Bean进行代理等操作。

2. BeanPostProcessor的目的与应用

BeanPostProcessor的主要目的是通过改进Bean的初始化过程,提高Spring容器中Bean的管理和使用效率。具体应用场景包括:

  • 属性修改:在Bean初始化之前或之后,对Bean的某些属性进行修改,以满足特定需求。
  • 日志记录:在Bean的初始化过程中,记录日志信息,以便于调试和监控。
  • 标记接口检查:检查Bean是否实现了特定的标记接口,并根据检查结果进行相应处理。
  • 代理增强:对Bean进行代理,以添加额外的功能,例如AOP(面向切面编程)中的增强功能。
3. BeanPostProcessor使用实例

下面是一个简单的使用实例,展示了如何在Bean初始化前后添加日志:

@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before Initialization: " + beanName);
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("After Initialization: " + beanName);
        return bean;
    }
}

在这个例子中,我们实现了BeanPostProcessor接口,并在postProcessBeforeInitializationpostProcessAfterInitialization方法中分别添加了日志输出。这使得每当一个Bean在初始化之前或之后,这两个方法都会被调用,并输出相应的日志信息。

4. BeanPostProcessor的注册

要使BeanPostProcessor生效,必须将其注册到Spring容器中。注册方式有多种,包括注解配置、XML配置和Java配置。

  • 注解配置:如果使用@Component注解,Spring会自动检测并注册BeanPostProcessor:
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
    // 方法实现如上
}
  • XML配置:在XML文件中显式声明BeanPostProcessor:
<bean class="com.example.CustomBeanPostProcessor" />
  • Java配置:通过@Bean注解注册BeanPostProcessor:
@Configuration
public class AppConfig {
    @Bean
    public BeanPostProcessor customBeanPostProcessor() {
        return new CustomBeanPostProcessor();
    }
}
5. BeanPostProcessor的工程化应用

BeanPostProcessor在工程化应用中具有重要意义。通过在Bean的生命周期中插入定制化逻辑,开发者可以实现许多高级功能,例如:

  • AOP(面向切面编程):通过代理技术,在Bean的方法调用前后添加额外逻辑,例如日志记录、事务管理、安全检查等。
  • 依赖注入的增强:在Bean初始化前后对其依赖进行进一步的配置和优化,例如动态注入特定依赖。
  • Bean管理:在Bean的整个生命周期中,进行统一的管理和监控,例如资源的初始化和释放、性能监控等。
6. 深入理解BeanPostProcessor的工作机制

为了更好地理解BeanPostProcessor的工作机制,我们需要了解Spring容器的初始化过程。Spring容器在启动时,会进行以下几个主要步骤:

  1. 实例化Bean:Spring容器根据配置文件或注解,实例化所有的Bean。
  2. 属性注入:为每个Bean注入其依赖的属性,这些属性可以是其他Bean、基本类型、集合等。
  3. 调用BeanPostProcessor:在Bean初始化前后,调用所有注册的BeanPostProcessor的方法。
  4. 调用初始化方法:如果Bean实现了InitializingBean接口,调用其afterPropertiesSet方法;如果配置了init-method,则调用该方法。
  5. Bean就绪:Bean已经准备好,可以被应用程序使用。

在这个过程中,BeanPostProcessor的两个方法分别在第3步和第4步之间被调用,允许开发者在Bean的生命周期的关键节点进行干预和自定义操作。

7. 实践中的BeanPostProcessor应用

在实际开发中,BeanPostProcessor的应用非常广泛,下面列举几个常见的使用场景:

  • 自定义初始化逻辑:通过在postProcessBeforeInitialization方法中添加逻辑,可以在Bean初始化之前执行一些自定义操作。例如,为Bean的某些属性设置默认值,或进行特定的初始化操作。
  • 动态代理:在postProcessAfterInitialization方法中,可以使用JDK动态代理或CGLIB代理,为Bean添加AOP增强。例如,为某些方法添加事务管理、日志记录或安全检查。
  • 注解处理:通过在Bean初始化前后扫描特定注解,可以实现注解驱动的配置。例如,自定义注解用于标记需要进行特定处理的Bean,并在BeanPostProcessor中实现相应的逻辑。
8. BeanPostProcessor与其他Spring机制的结合

BeanPostProcessor常常与Spring的其他机制结合使用,以实现更复杂和强大的功能。例如:

  • BeanFactoryPostProcessor结合BeanFactoryPostProcessor允许在Bean定义加载后、Bean实例化前进行配置修改。通过结合使用这两个接口,可以在Bean定义和实例化的不同阶段进行干预,提供更细粒度的控制。
  • ApplicationContextAware结合:实现ApplicationContextAware接口的Bean可以获取到ApplicationContext实例,通过在BeanPostProcessor中对这些Bean进行处理,可以实现对整个应用上下文的操作。
  • @PostConstruct@PreDestroy结合:在Bean的生命周期中,通过BeanPostProcessor和这两个注解,可以实现复杂的初始化和销毁逻辑。例如,在Bean初始化后,调用特定方法进行资源的分配或初始化。
9. 高级使用技巧

在高级使用场景中,BeanPostProcessor可以用于实现更复杂的功能,例如:

  • 多层次代理:通过在postProcessAfterInitialization方法中多次对Bean进行代理,可以实现多层次的功能增强。例如,先添加事务管理,再添加日志记录。
  • 条件处理:在BeanPostProcessor中,可以根据特定条件对Bean进行不同的处理。例如,根据Bean的类型或注解,选择性地进行某些操作。
  • 性能优化:通过在Bean初始化前后进行性能监控,可以识别出性能瓶颈,并采取相应的优化措施。例如,记录Bean初始化的时间,找出耗时较长的操作。

BeanPostProcessor源码跟踪

refresh方法中的registerBeanPostProcessors

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing. 1、初始化上下文信息,替换占位符、必要参数的校验
			prepareRefresh();
			// Tell the subclass to refresh the internal bean factory. 2、解析类Xml、初始化BeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 这一步主要是对初级容器的基础设计
			// Prepare the bean factory for use in this context. 	3、准备BeanFactory内容:
			prepareBeanFactory(beanFactory); // 对beanFactory容器的功能的扩展:
			try {
				// Allows post-processing of the bean factory in context subclasses. 4、扩展点加一:空实现,主要用于处理特殊Bean的后置处理器
				postProcessBeanFactory(beanFactory);
				// Invoke factory processors registered as beans in the context. 	5、spring bean容器的后置处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation. 	6、注册bean的后置处理器
				//!!!!!!!!!!!!  这里 这里 今天看这里  !!!!!!!!!!!//
				registerBeanPostProcessors(beanFactory);
				//!!!!!!!!!!!!  这里 这里 今天看这里  !!!!!!!!!!!//

				// Initialize message source for this context.	7、初始化消息源
				initMessageSource();
				// Initialize event multicaster for this context.	8、初始化事件广播器
				initApplicationEventMulticaster();
				// Initialize other special beans in specific context subclasses. 9、扩展点加一:空实现;主要是在实例化之前做些bean初始化扩展
				onRefresh();
				// Check for listener beans and register them.	10、初始化监听器
				registerListeners();
				// Instantiate all remaining (non-lazy-init) singletons.	11、实例化:非兰加载Bean
				finishBeanFactoryInitialization(beanFactory);
				// Last step: publish corresponding event.	 12、发布相应的事件通知
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}


	/**
	 * Instantiate and register all BeanPostProcessor beans,
	 * respecting explicit order if given.
	 * <p>Must be called before any instantiation of application beans.
	 */
	protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
	}

registerBeanPostProcessors详解

	/**
	 *
	 * @param beanFactory
	 * @param applicationContext
	 */
	public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

		// 1、获取所有实现BeanPostProcessor接口的类
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

		// Register BeanPostProcessorChecker that logs an info message when a bean is created during BeanPostProcessor instantiation, i.e. when a bean is not eligible for getting processed by all BeanPostProcessors.
		// 记录下BeanPostProcessor的目标计数
		// +1是因为在此方法的最后会添加一个BeanPostProcessorChecker的类
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		// 2、添加BeanPostProcessorChecker(主要用于记录信息)到beanFactory中
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

		// Separate between BeanPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		// 3、初始化根据BeanPostProcessor是否实现Priority、Order接口进行分类,初始化各种类型的集合,分类装这些对象
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		// Spring自己内部的Bean后置处理器
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		// 4、遍历步骤一中所有后置处理器的名称
		for (String ppName : postProcessorNames) {
			// 实现PriorityOrdered类型的Bean
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				//  实现PriorityOrdered同时也实现类MergedBeanDefinitionPostProcessor接口,
				//  那么对应的bean实例添加到internalPostProcessors中,与实例化相关注解如@Autowired @Bean等关系密切,需要注意
				if (pp instanceof MergedBeanDefinitionPostProcessor) {
					internalPostProcessors.add(pp);
				}
			}
			// 实现Ordered类型的Bean
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// First, register the BeanPostProcessors that implement PriorityOrdered.
		// 5、将实现类PriorityOrder接口类型的bean先注入到BeanFactory中
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

		// Next, register the BeanPostProcessors that implement Ordered.
		// 6、将实现类Order接口类型的bean先注入到BeanFactory中
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		// 6、将实现类Order接口类型的bean先注入到BeanFactory中
		sortPostProcessors(orderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);

		// Now, register all regular BeanPostProcessors.
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		// 7.将无序普通的bean后处理器,注册到容器beanFactory中
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

		// Finally, re-register all internal BeanPostProcessors.
		// 8、最后,将Spring容器内部的BeanPostProcessor注册到Bean后置处理器中
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);

		// Re-register post-processor for detecting inner beans as ApplicationListeners,
		// moving it to the end of the processor chain (for picking up proxies etc).
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}

代码解析与重点总结
1. 获取所有实现BeanPostProcessor接口的类
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

此步骤从BeanFactory中获取所有实现了BeanPostProcessor接口的Bean名称。这是为了后续将这些Bean按照不同的规则进行处理和注册。

2. 添加BeanPostProcessorChecker
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

BeanPostProcessorChecker的作用是记录信息,当在BeanPostProcessor实例化过程中创建一个Bean时,它会输出一条信息日志。这一步是为了确保在BeanPostProcessor注册过程中Bean创建的可追溯性。

3. 根据类型分类BeanPostProcessor
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();

for (String ppName : postProcessorNames) {
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        priorityOrderedPostProcessors.add(pp);
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
        orderedPostProcessorNames.add(ppName);
    } else {
        nonOrderedPostProcessorNames.add(ppName);
    }
}

在这一步,所有的BeanPostProcessor根据是否实现了PriorityOrdered、Ordered接口进行分类,分别放入不同的列表中。这是为了后续按照优先级进行注册。

4. 按优先级顺序注册BeanPostProcessor
// 注册实现PriorityOrdered接口的BeanPostProcessor
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

// 注册实现Ordered接口的BeanPostProcessor
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String ppName : orderedPostProcessorNames) {
    BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
    orderedPostProcessors.add(pp);
    if (pp instanceof MergedBeanDefinitionPostProcessor) {
        internalPostProcessors.add(pp);
    }
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);

// 注册普通的BeanPostProcessor
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String ppName : nonOrderedPostProcessorNames) {
    BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
    nonOrderedPostProcessors.add(pp);
    if (pp instanceof MergedBeanDefinitionPostProcessor) {
        internalPostProcessors.add(pp);
    }
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

按照实现PriorityOrdered、Ordered接口以及普通BeanPostProcessor的顺序,分别注册这些BeanPostProcessor。这确保了不同优先级的BeanPostProcessor按正确的顺序执行。

5. 注册内部BeanPostProcessor
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);

将Spring内部使用的BeanPostProcessor(实现MergedBeanDefinitionPostProcessor接口的)单独处理并注册。这些内部BeanPostProcessor与Bean的实例化相关注解(如@Autowired、@Bean)关系密切,需要特别注意。

6. 添加ApplicationListenerDetector
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));

最后,添加一个ApplicationListenerDetector,用于检测内部Bean是否作为ApplicationListeners(应用监听器),并将其移动到处理链的末尾,以便捕获代理等操作。

小结

BeanPostProcessor是Spring框架提供的一个强大工具,它允许我们开发者在Bean的生命周期中的特定点进行自定义操作。通过实现BeanPostProcessor接口,开发者可以插入自己的逻辑,以增强或修改Bean的行为。这个接口在AOP、依赖注入和Bean管理等方面都有着广泛的应用。正确地使用BeanPostProcessor可以极大地提高Spring应用的灵活性和可扩展性。

通过本文的详细介绍,相信大家对BeanPostProcessor有了更深入的理解。在实际开发中,合理地应用BeanPostProcessor,可以帮助我们更好地控制和管理Bean的生命周期,实现复杂的业务需求。

整体总结

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

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

相关文章

微信小程序遮罩层显示

效果展示&#xff1a; wxml页面&#xff1a; <view classmodal-mask wx:if{{showModal}}><view class"modal-container"><view classmodal-content></view><view classmodal-footer bindtap"closeImage">//这个/images/ind…

MATLAB——循环语句

一、for end语句 在该语法中&#xff0c;循环变量是用于迭代的变量名&#xff0c;它会在每次循环迭代中从向量或矩阵中取出一列的值。数值向量或者矩阵则表示了循环变量可以取值的范围&#xff0c;通常根据实际需要事先给定。一旦循环变量遍历完数值向量或者矩阵中的所有值&…

【server】3、注册中心与配置中心

1、服务注册与发现 1.1、consul 1.1.1 是什么 官网&#xff1a; Consul by HashiCorp spring-cloud-consul: Spring Cloud Consul :: Spring Cloud Consul gitHub 官网 &#xff1a;GitHub - hashicorp/consul: Consul is a distributed, highly available, and data cent…

如何检查购买的Facebook账号优劣?

Facebook 是全球最受欢迎的社交网络之一,为品牌广告提供了巨大的潜力。许多公司和营销人员使用 Facebook 来推广他们的产品和服务&#xff0c;经常会购买账号。当然也分出了很多账号&#xff0c;比如个人号&#xff0c;BM号&#xff0c;广告号&#xff0c;小黑号等等。 但是,有…

一键安装部署,在 Ubuntu 服务器上快速搭建基于 Ghost CMS的网站

我们在上一篇内容中讲过&#xff0c;如何使用 Helm 在 Kubernetes 集群上安装 WordPress&#xff0c;创建高可用性网站。而这次我们将基于另一个流行的内容管理系统 Ghost CMS 在 DigitalOcean 云主机进行建站。 Ghost 也是开源的内容管理系统&#xff08;CMS&#xff09;&…

【Arduino】ESP8266开发环境配置(图文)

ESP8266与ESP32开发很类似&#xff0c;相当于是低配版本的ESP32&#xff0c;其同样具有无线网络连接能力&#xff0c;功能强大&#xff0c;而且价格比ESP32更具有优势。接下来我们就来设置一下ESP8266的开发环境。 使用Arduino开发平台软件&#xff0c;选择首选项进行设置。 h…

论文解析——Transformer 模型压缩算法研究及硬件加速器实现

作者及发刊详情 邓晗珂&#xff0c;华南理工大学 摘要 正文 实验平台 选取模型&#xff1a; T r a n s f o r m e r b a s e Transformer_{base} Transformerbase​ 训练数据集&#xff1a;WMT-2014 英语-德语翻译数据集、IWSLT-2014 英语-德语互译数据集 Transformer模…

警翼警用记录仪视频格式化后恢复方法

警翼是国内较大的一家警用记录仪厂商&#xff0c;此品牌我们恢复过很多&#xff0c;此次遇到的是一个典型的误格式化的情况&#xff0c;我们来看看误格式化后如何恢复。 故障存储: 32G卡/fat32 故障现象: 客户提供的信息是在交接设备后没有及时备份而做出了初始化设备的操…

图像信号处理器(ISP)基础算法及处理流程

&#x1f4aa; 专业从事且热爱图像处理&#xff0c;图像处理专栏更新如下&#x1f447;&#xff1a; &#x1f4dd;《图像去噪》 &#x1f4dd;《超分辨率重建》 &#x1f4dd;《语义分割》 &#x1f4dd;《风格迁移》 &#x1f4dd;《目标检测》 &#x1f4dd;《暗光增强》 &a…

录屏软件哪个好?3款宝藏软件,分享给你

在数字化时代&#xff0c;录屏软件因其强大的功能性和实用性&#xff0c;逐渐成为工作和生活中的得力助手。然而&#xff0c;市面上的录屏软件众多&#xff0c;选择一款适合自己的录屏软件却成为了一个难题。 不同的录屏软件在功能、性能、易用性等方面都有所不同&#xff0c;…

js之模糊搜索

多的不说 少的不唠 直接上代码

2.5 C#视觉程序开发实例1----设计一个IO_Manager

2.5 C#视觉程序开发实例1----设计一个IO_Manager 第一步目标&#xff1a; 1 实现获取IO触发信号Trig0 2 能够实现程序切换 3 图像处理后能够输出一个脉冲 1 IO 引脚定义 1.1 输入信号定义 1.2 输出信号定义 2 IO时序图 2.1 触发时序 2.2 切换程序时序图 3 IO_Manager.cs …

数据库表导出到excel

数据库表导出到excel:前置知识1 ALL_TAB_COLS 数据库表导出到excel:前置知识2 Quartz基本使用 数据库表导出到excel:前置知识3 项目封装的Quartz实现动态定时任务 数据库表导出到excel:前置知识4 业务和效果 发起清单下载control层InventoryDownloadLogController /* * */ pa…

用户资料门户的构建

1. 需求背景 老的页面停止维护了,且老旧, 功能单一,且页面分散. 急需做功能集成的平台化建设原先的用户资料查询没有做权限管控, 每一次查询都会消耗我们组的人力资源. 2. 项目介绍 2.1. 项目地址 服务地址: [公司内网服务(略)] 工蜂地址: [公司内网仓库(略)] 2.2 项目的价…

女性经济崛起,天润融通用客户感知挖掘市场潜力

每逢一年一度的国际妇女节&#xff0c;“女性”话题都会被郑重地讨论。 从消费市场上来说&#xff0c;最近几年女性群体正在拥有越来越大的影响力&#xff0c;甚至出现了“她经济”这样的专属词汇在最近几年被市场反复讨论。 毫无疑问&#xff0c;女性消费群体的崛起已经成为…

揭秘品牌成功秘诀:品牌营销策略的核心要素大公开

品牌营销作为企业战略中至关重要的一环&#xff0c;其核心是建立和传播品牌的独特魅力&#xff0c;使其在消费者心目中占据重要位置。 一个成功的品牌营销策略能够提升品牌的知名度和影响力&#xff0c;带来持续的销售和忠诚客户群体。 在当今竞争激烈的市场环境中&#xff0…

Prompt的万能公式和优化技巧

文章目录 前言一、万能公式二、优化技巧1.设定角色2.设定目标和动机3.引导主观回答4.预设条件5.做强调6.思维链&#xff08;COT&#xff09;7.巧用定界符 前言 随着LLM的发展&#xff0c;能给我们带来很多方便&#xff0c;但是又引出了一个新的问题就是我们该如何使用他们&…

明星代言方式8种助力品牌占领市场-华媒舍

1. 明星代言的重要性和市场价值 明星代言是一种常见的品牌推广方式&#xff0c;通过联系知名度高的明星来推广产品或服务&#xff0c;从而提升品牌的知名度和美誉度。明星代言能够借助明星的影响力和粉丝基础&#xff0c;将品牌信息传达给更广泛的受众&#xff0c;从而提高销量…

【数据结构】建堆的时间复杂度

一.向下调整建堆 1.二叉树层数与总节点个数关系 层数一定时&#xff0c;在二叉树节点个数最大的情况下&#xff0c;二叉树为满二叉树&#xff0c;如下图所示&#xff0c;可以清晰地看到在满二叉树中第h层有2^(h-1)个节点&#xff0c;总节点N就等于一个等比数列的求和&#xf…