Spring源码解析(二)BeanDefinition的Resource定位

   IOC容器的初始化过程主要包括BeanDefinition的Resource定位、载入和注册。在实际项目中我们基本上操作的都是ApplicationContex的实现,我们比较熟悉的ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、XmlWebapplicationContext等。ApplicationContext的具体继承体系如下图所示:

  

   其实,不管是XmlWebApplicationContext还是ClasspathXmlApplicationContext 他们的区别只是Bean的资源信息来源不一样而已,最终都会解析为统一数据结构BeanDefinition。

下面我们源码的解析就从高富帅的ClassPathXmlApplicationContext开始。

        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("classpath*:test.xml");

构造方法:

/*** Create a new ClassPathXmlApplicationContext, loading the definitions* from the given XML file and automatically refreshing the context.* @param configLocation resource location* @throws BeansException if context creation failed*/public ClassPathXmlApplicationContext(String configLocation) throws BeansException {this(new String[] {configLocation}, true, null);}

最终调用构造方法:

/*** Create a new ClassPathXmlApplicationContext with the given parent,* loading the definitions from the given XML files.* @param configLocations array of resource locations* @param refresh whether to automatically refresh the context,* loading all bean definitions and creating all singletons.* Alternatively, call refresh manually after further configuring the context.* @param parent the parent context* @throws BeansException if context creation failed* @see #refresh()*/public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) {refresh();}}

1.设置父级上下文,最终是给AbstractApplicationContext的parent属性赋值,AbstractApplicationContext是ApplicationContext最顶层的实现类。

2.设置XML文件的位置,调用了AbstractRefreshableConfigApplicationContext的setConfigLocations方法

/*** Set the config locations for this application context.* <p>If not set, the implementation may use a default as appropriate.*/public void setConfigLocations(String[] locations) {if (locations != null) {Assert.noNullElements(locations, "Config locations must not be null");this.configLocations = new String[locations.length];for (int i = 0; i < locations.length; i++) {this.configLocations[i] = resolvePath(locations[i]).trim();}}else {this.configLocations = null;}}

3.刷新容器,调用了AbstractApplicationContext的refresh方法,这是一个模板方法,具体的操作都是有子类去实现的。

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.//刷新容器前的准备工作
            prepareRefresh();// Tell the subclass to refresh the internal bean factory.//由子类实现容器的刷新(重启)ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context./*容器使用前的准备工作*/prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.//甚至beanFacotry的后置处理
                postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.//调用BeanFactory的后置处理器,这些后置处理是在Bean定义中想容器注册的
                invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.//注册Bean的后置处理器,在Bean的创建过程中调用
                registerBeanPostProcessors(beanFactory);// Initialize message source for this context.//对上下文中的消息源进行初始化
                initMessageSource();// Initialize event multicaster for this context.//初始化上下文的事件
                initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.//初始化其他特殊的Bean
                onRefresh();// Check for listener beans and register them.//向容器注册监听Bean
                registerListeners();// Instantiate all remaining (non-lazy-init) singletons.//实例化所有非延迟加载的Bean
                finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.//发布容器事件,结束refresh过程
                finishRefresh();}catch (BeansException ex) {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;}}}
prepareRefresh():主要是设置启动时间、状态等等;
我们着重看一下刷新容器的obtainFreshBeanFactory()方法:
/*** Tell the subclass to refresh the internal bean factory.* @return the fresh BeanFactory instance* @see #refreshBeanFactory()* @see #getBeanFactory()*/protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {//销毁已有容器,重新创建容器并加载Bean
        refreshBeanFactory();ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled()) {logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);}return beanFactory;}
AbstractApplicationConetxt的refreshBeanFactory()方法是一个抽象方法,是由它的子类AbstractRefreshableApplicationContext实现的,从类的命名上可以看出这个类主要就是进行容器Refresh用的。
/*** This implementation performs an actual refresh of this context's underlying* bean factory, shutting down the previous bean factory (if any) and* initializing a fresh bean factory for the next phase of the context's lifecycle.*/@Overrideprotected final void refreshBeanFactory() throws BeansException {//如果容器已经存在则销毁容器中的bean并关闭容器if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {//创建beanFacotryDefaultListableBeanFactory beanFactory = createBeanFactory();beanFactory.setSerializationId(getId());customizeBeanFactory(beanFactory);//根据bean定义的方式(XML、注解等)不同,由子类选择相应的BeanDefinitionReader去解析
            loadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}

第一步: 这个方法会判断如果已存在容器,则先销毁所有的Bean并且关闭容器,这也是为了保证容器的唯一性。

第二步:createBeanFactory()创建了一个DefaultListableBeanFactory,这个类是BeanFacotry最高级的实现,有了它就有个容器最基本的功能了。

/*** Create an internal bean factory for this context.* Called for each {@link #refresh()} attempt.* <p>The default implementation creates a* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}* with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this* context's parent as parent bean factory. Can be overridden in subclasses,* for example to customize DefaultListableBeanFactory's settings.* @return the bean factory for this context* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping*/protected DefaultListableBeanFactory createBeanFactory() {//新建一个DefaultListableBeanFactoryreturn new DefaultListableBeanFactory(getInternalParentBeanFactory());}

 DefaultListableBeanFactory的继承关系:

我们看到DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,也就是说最终BeanDefinition的注册工作是由它和它的子类来完成的。

第三步:loadBeanDefinitions(beanFactory),这个方法也是一个抽象方法。因为Bean定义方式不同(XML、注解等),会有多个子类分别去实现具体的解析。

此处,调用的是AbstractXmlApplicationContext的实现:

/*** Loads the bean definitions via an XmlBeanDefinitionReader.* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader* @see #initBeanDefinitionReader* @see #loadBeanDefinitions*/@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// Create a new XmlBeanDefinitionReader for the given BeanFactory.XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// Configure the bean definition reader with this context's// resource loading environment.beanDefinitionReader.setEnvironment(this.getEnvironment());//ApplicationContext继承了ResourceLoader接口,所以this是可以直接使用的beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));// Allow a subclass to provide custom initialization of the reader,// then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);//委派模式,具体事情委派给beanDefinitionReader去做
        loadBeanDefinitions(beanDefinitionReader);}

 进入loadBeanDefinitions(XmlBeanDefinitionReader reader):

/*** Load the bean definitions with the given XmlBeanDefinitionReader.* <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}* method; hence this method is just supposed to load and/or register bean definitions.* @param reader the XmlBeanDefinitionReader to use* @throws BeansException in case of bean registration errors* @throws IOException if the required XML document isn't found* @see #refreshBeanFactory* @see #getConfigLocations* @see #getResources* @see #getResourcePatternResolver*/protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = getConfigResources();if (configResources != null) {reader.loadBeanDefinitions(configResources);}String[] configLocations = getConfigLocations();if (configLocations != null) {reader.loadBeanDefinitions(configLocations);}}

 

该方法调用了XmlBeanDefinitionReader父类AbstractBeanDefinitionReader的loadBeanDefinitions方法:

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {Assert.notNull(locations, "Location array must not be null");int counter = 0;for (String location : locations) {counter += loadBeanDefinitions(location);}return counter;}

 

l循环加载location并返回加载个数,最终调用了本类的loadBeanDefinitions(String location, Set<Resource> actualResources)方法,actualResources为null:

/*** Load bean definitions from the specified resource location.* <p>The location can also be a location pattern, provided that the* ResourceLoader of this bean definition reader is a ResourcePatternResolver.* @param location the resource location, to be loaded with the ResourceLoader* (or ResourcePatternResolver) of this bean definition reader* @param actualResources a Set to be filled with the actual Resource objects* that have been resolved during the loading process. May be {@code null}* to indicate that the caller is not interested in those Resource objects.* @return the number of bean definitions found* @throws BeanDefinitionStoreException in case of loading or parsing errors* @see #getResourceLoader()* @see #loadBeanDefinitions(org.springframework.core.io.Resource)* @see #loadBeanDefinitions(org.springframework.core.io.Resource[])*/public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {ResourceLoader resourceLoader = getResourceLoader();if (resourceLoader == null) {throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");}//resourceLoader是ClasspathXmlApplicationContext,ApplicationContext接口本身继承了ResourcePatternResolver接口if (resourceLoader instanceof ResourcePatternResolver) {// Resource pattern matching available.try {//location转为Resource完成定位工作Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);int loadCount = loadBeanDefinitions(resources);if (actualResources != null) {for (Resource resource : resources) {actualResources.add(resource);}}if (logger.isDebugEnabled()) {logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");}return loadCount;}catch (IOException ex) {throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex);}}else {// Can only load single resources by absolute URL.Resource resource = resourceLoader.getResource(location);int loadCount = loadBeanDefinitions(resource);if (actualResources != null) {actualResources.add(resource);}if (logger.isDebugEnabled()) {logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");}return loadCount;}}
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location)将location转成了Resource[],这一步完成了资源的定位工作。
它调用了PathMatchingResourcePatternResolver的getResources方法:
 1 public Resource[] getResources(String locationPattern) throws IOException {
 2         Assert.notNull(locationPattern, "Location pattern must not be null");
 3         //是否以classpath*:开头
 4         if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
 5             // a class path resource (multiple resources for same name possible)
 6             //是否为Ant-style路径
 7             //? 匹配任何单字符
 8             //* 匹配0或者任意数量的字符
 9             //** 匹配0或者更多的目录
10             if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
11                 // a class path resource pattern
12                 return findPathMatchingResources(locationPattern);
13             }
14             else {
15                 // all class path resources with the given name
16                 return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
17             }
18         }
19         else {
20             // Only look for a pattern after a prefix here
21             // (to not get fooled by a pattern symbol in a strange prefix).
22             int prefixEnd = locationPattern.indexOf(":") + 1;
23             if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
24                 // a file pattern
25                 return findPathMatchingResources(locationPattern);
26             }
27             else {
28                 // a single resource with the given name
29                 return new Resource[] {getResourceLoader().getResource(locationPattern)};
30             }
31         }
32     }

 

 根据location写法,解析方式也不同:

1、前缀为classpath*

  1)文件路径路径中包含*和?

    调用findPathMatchingResources方法

  2)文件路径中不含*和?

    调用findAllClassPathResources方法

2.、前缀为classpath

  1)文件路径路径中包含*和?

  调用findPathMatchingResources方法
 2)文件路径中不含*和?
  调用DefaultResourceLoader的getResource方法new一个ClasspathResource并返回,如果资源文件根本就不存在,此处也不会校验。
findPathMatchingResources和findAllClassPathResources具体都干了什么呢?
先看一下findAllClassPathResources:
/*** Find all class location resources with the given location via the ClassLoader.* @param location the absolute path within the classpath* @return the result as Resource array* @throws IOException in case of I/O errors* @see java.lang.ClassLoader#getResources* @see #convertClassLoaderURL*/protected Resource[] findAllClassPathResources(String location) throws IOException {String path = location;if (path.startsWith("/")) {path = path.substring(1);}ClassLoader cl = getClassLoader();Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));Set<Resource> result = new LinkedHashSet<Resource>(16);while (resourceUrls.hasMoreElements()) {URL url = resourceUrls.nextElement();result.add(convertClassLoaderURL(url));}return result.toArray(new Resource[result.size()]);}

   protected Resource convertClassLoaderURL(URL url) {
return new UrlResource(url);
}
这个方法很简单,根据具体的location通过classLoader的getResources方法返回RUL集合,根据URL创建UrlResource并返回UrlResource的集合。
再来看一下findPathMatchingResources方法:
/*** Find all resources that match the given location pattern via the* Ant-style PathMatcher. Supports resources in jar files and zip files* and in the file system.* @param locationPattern the location pattern to match* @return the result as Resource array* @throws IOException in case of I/O errors* @see #doFindPathMatchingJarResources* @see #doFindPathMatchingFileResources* @see org.springframework.util.PathMatcher*/protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {String rootDirPath = determineRootDir(locationPattern);String subPattern = locationPattern.substring(rootDirPath.length());Resource[] rootDirResources = getResources(rootDirPath);Set<Resource> result = new LinkedHashSet<Resource>(16);for (Resource rootDirResource : rootDirResources) {rootDirResource = resolveRootDirResource(rootDirResource);if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));}else if (isJarResource(rootDirResource)) {result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));}else {result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));}}if (logger.isDebugEnabled()) {logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);}return result.toArray(new Resource[result.size()]);}
1.String rootDirPath = determineRootDir(locationPattern),获取location前缀classpath*/classpath
2.Resource[] rootDirResources = getResources(rootDirPath),调用的上面讲到的getResources方法,返回classpath根路径的Resource[],如果是classpath会返回一个Resource,
如果是classpath*会放回所有的classpath路径。
3.遍历根路径Resource[],doFindPathMatchingFileResources方法就是获取给定路径下的所有文件,根据指定的文件名test*.xml去模糊匹配,返回的是FileSystemResource。所以location为classpath:test*.xml可能会找不到文件。

 

 

 



转载于:https://www.cnblogs.com/monkey0307/p/8436134.html

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

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

相关文章

单片机(MCU)如何才能不死机之对齐访问(Aligned Access)

从一个结构体说起。如下&#xff0c;在 STM32F0 的程序中&#xff0c;我们定义了一个结构体My_Struct &#xff0c;那么这个结构体占用多少内存呢&#xff1f;struct Struct_Def { uint8_t Var_B; uint16_t Var_W0; uint16_t Var_W1; uint32_t Var_DW; }; struct Struct_Def My…

小米的隔空充电,看起来好酷

昨天是1月29号&#xff0c;昨天小米发布了一个隔空充电技术&#xff0c;很火爆&#xff0c;大胆想&#xff0c;如果有一条无线充电的高速公路&#xff0c;那电动汽车还担心没有电吗&#xff1f;—— 雷总的微博原文隔空充电技术&#xff1a;如科幻电影一般&#xff0c;拿着手机…

同事用void把我给秀翻了!

1、聊一聊今天跟大家推荐的这首歌最近应该挺火的&#xff0c;不过没办法插入AGA的原版歌曲&#xff0c;大家觉得不错可以去找找原版歌曲收录一下。昨天建立了"最后一个bug"技术交流群,由于群成员超过200就无法直接通过群二维码加入&#xff0c;如果大家想加入扫描下面…

Spring Boot之自定义属性

选择Spring Boot主要是考虑到它既能兼顾Spring的强大功能&#xff0c;还能实现快速开发的便捷。我想大多数人也是出于这个原因选择了Spring Boot,如果不是特殊应用场景&#xff0c;就只需要在application.properties中完成一些属性配置就能开启各模块的应用。而不像传统的XML配…

一个老工程师的工作经历和思考

在这里不敢以”资深”工程师自居&#xff0c;因为学历和技术水平确实一般。为什么说“老”呢&#xff1f;因为工作时间确实够长&#xff0c;已经接近20年。下面把自身工作和学习经历和大家分享一下&#xff0c;使初学者能够得到一些有用的东西。2000年毕业&#xff0c;机械电子…

电子工程师都在看什么?送你一份“修炼宝典”

现如今&#xff0c;形形色色的公众号如繁星一般让人眼花缭乱。近几年科技的飞速发展&#xff0c;让更多人开始关注科技&#xff0c;甚至成为极客。然而学习是永无止境的&#xff0c;如何才能追赶如此高速的发展&#xff1f;曾经&#xff0c;我也是不知道去看哪些&#xff0c;便…

DataCleaner(4.5)第一章

Part1. Introduction to DataCleaner  介绍DataCleaner |--What is data quality(DQ)  数据质量&#xff1f;|--What is data profiling?   数据分析&#xff1f;|--What is datastore?     数据存储&#xff1f;   Composite datastore   综合性数据存储 |…

约瑟夫斯问题-java版数组解法和链表解法

10个人围成一圈&#xff0c;从1到10编号&#xff0c;从1开始数&#xff0c;数到3或3的倍数的位置&#xff0c;则该位置的人出局&#xff0c;求最后剩下哪一个号&#xff1f; 数组解法&#xff1a; 数组存放数组&#xff1a;a[10]存在1到10编号人 数组遍历到尾部又从头遍历&…

少写点if-else吧,它的效率有多低你知道吗?

# 干了这碗鸡汤我要再和生活死磕几年。要么我就毁灭&#xff0c;要么我就注定铸就辉煌。如果有一天&#xff0c;你发现我在平庸面前低了头&#xff0c;请向我开炮。--杰克凯鲁亚克if-else涉及到分支预测的概念&#xff0c;关于分支预测上篇文章《虚函数真的就那么慢吗&#xff…

为什么不能在中断上半部休眠?

这是一个老生常谈的问题。我们先简单说下什么是中断「因为最近在群里看到有人竟然不懂什么是中断」。中断是计算机里面非常核心的东西&#xff0c;我们可以跑OS&#xff0c;可以多任务运行都因为中断的存在。假设你是一个CPU&#xff0c;你正在睡觉。你突然觉得肚子疼&#xff…

j.u.c系列(08)---之并发工具类:CountDownLatch

写在前面 CountDownLatch所描述的是”在完成一组正在其他线程中执行的操作之前&#xff0c;它允许一个或多个线程一直等待“&#xff1a;用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法&#xff0c;所以在当前计数到达零之前&#xff0c;await 方法会一直受…

巧用1个GPIO控制2个LED显示4种状态

很多电子产品有状态指示灯&#xff0c;比如电视机&#xff1a;待机状态亮红灯开机状态亮绿灯实现起来很简单&#xff0c;微控制器MCU的两个GPIO分别控制就行&#xff1a;不过资源总是紧张的&#xff0c;有时候会碰到GPIO不够用的情况。如果只用1个GPIO&#xff0c;可不可以实现…

大大大大数怎么求余?C语言

问题&#xff1a;一个特别大的数除以23求余数用C语言应该怎么算啊&#xff1f;比如23232323232323232323232323232323232323232323232323232323233除以23&#xff0c;怎么算余数&#xff1f;数据类型在计算机的存储是有大小限制的&#xff0c;所以才出现了大数求余这种问题&…

程序员因拒绝带电脑回家工作被开除!获赔19.4万元

近日&#xff0c;男子拒绝春节带电脑回家工作被开除的消息&#xff0c;成为了不少网友关注的焦点&#xff0c;引发网友共鸣。因为春节拒绝带工作电脑回家被开除&#xff0c;上海一位软件工程师起诉公司获赔19.4万元。2月2日&#xff0c;据上海浦东法院公众号消息&#xff0c;该…

随便写写(5)

也许是今年发生的事情太多了&#xff0c;所以比以前要更关注时事&#xff0c;虽然面对一些既成的事实&#xff0c;难免要进行痛心的思考。 昨天晚上关注了一下东方卫视播出的9.8特大尾矿库溃坝事故的后续报道&#xff0c;这起特大人为事故已经得到了认定&#xff0c;相关的责任…

利用C语言中的setjmp和longjmp,来实现异常捕获和协程

一、前言二、函数语法介绍与 goto 语句比较与 fork 函数比较与 Python 语言中的 yield/resume 比较三、利用 setjmp/longjmp 实现异常捕获四、利用 setjmp/longjmp 实现协程五、总结一、前言 在 C 标准库中&#xff0c;有两个威力很猛的函数&#xff1a;setjmp 和 longjmp&…

centos6.9系列LNMP环境的安装

一、Nginx 1.先解决Nginx的依赖关系&#xff1a; yum install -y pcre-devel openssl-devel 2.安装wget&#xff1a;sudo yum -y install wget 3.下载nginx的安装包&#xff1a;wget http://nginx.org/download/nginx-1.10.3.tar.gz 4.解压nginx文件包&#xff1a;tar xf nginx…

Linux 修改 ELF 解决 glibc 兼容性问题

转自&#xff1a;Soul Of Free Loophttps://zohead.com/archives/mod-elf-glibc/Linux glibc 问题相信有不少 Linux 用户都碰到过运行第三方&#xff08;非系统自带软件源&#xff09;发布的程序时的 glibc 兼容性问题&#xff0c;这一般是由于当前 Linux 系统上的 GNU C 库&am…

VS2010创建ATL工程及使用C++测试COM组件

VS2010创建ATL工程及使用C测试COM组件 1.创建ATL项目&#xff0c;取名MyCom 2. ATL 项目向导&#xff0c;勾选 【支持COM 1.0】和【支持部件注册器】&#xff0c;其他默认&#xff0c;点击完成。 3.在该项目中添加类 4.添加一个ATL简单对象 5. ATL 简单对象向导&#xff0c…

芯片IC附近为啥要放0.1uF的电容?看完秒懂~

数字电路要运行稳定可靠&#xff0c;电源一定要”干净“&#xff0c;并且能量补充一定要及时&#xff0c;也就是滤波去耦一定要好。什么是滤波去耦&#xff0c;简单的说就是在芯片不需要电流的时候存储能量&#xff0c;在需要电流的时候又能及时地补充能量。有读者看到这里会说…