Spring IoC源码学习:invokeBeanFactoryPostProcessors 详解-程序员宅基地

技术标签: spring  程序员  学习  java  

小白也看得懂的 Spring IoC 核心流程介绍

Spring IoC源码学习:总览

Spring IoC源码学习:ApplicationContext 刷新前的配置

Spring IoC源码学习:obtainFreshBeanFactory详解

Spring IoC源码学习:parseDefaultElement详解

Spring IoC源码学习:parseCustomElement详解

Spring IoC源码学习:context:component-scan 节点详解

Spring IoC源码学习:invokeBeanFactoryPostProcessors详解

Spring IoC源码学习:registerBeanPostProcessors详解

Spring IoC源码学习:finishBeanFactoryInitialization详解

Spring IoC源码学习:getBean详解

Spring IoC源码学习:createBean详解(上)

Spring IoC源码学习:createBean详解(下)

Spring IoC源码学习:@Autowire 详解

Spring IoC源码学习:finishRefresh 详解

前言

==

随着 Spring IoC:context:component-scan节点解析 的结束,obtainFreshBeanFactory 方法的解析也告一段落,我们通过5篇文章,完整的介绍了obtainFreshBeanFactory 方法。本文将介绍 obtainFreshBeanFactory 方法之后的另一个重要方法——invokeBeanFactoryPostProcessors。

概述

==

本方法会实例化和调用所有 BeanFactoryPostProcessor(包括其子类 BeanDefinitionRegistryPostProcessor)。

BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。

BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor,比 BeanFactoryPostProcessor 具有更高的优先级,主要用来在常规的 BeanFactoryPostProcessor 检测开始之前注册其他 bean 定义。特别是,你可以通过 BeanDefinitionRegistryPostProcessor 来注册一些常规的 BeanFactoryPostProcessor,因为此时所有常规的 BeanFactoryPostProcessor 都还没开始被处理。

注:这边的 “常规 BeanFactoryPostProcessor” 主要用来跟 BeanDefinitionRegistryPostProcessor 区分。

正文

==

首先我们回到 AbstractApplicationContext.refresh() 方法,找到代码:invokeBeanFactoryPostProcessors(beanFactory) ,单击该行代码跳转到具体的实现。

invokeBeanFactoryPostProcessors


protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {

// 1.getBeanFactoryPostProcessors(): 拿到当前应用上下文beanFactoryPostProcessors变量中的值

// 2.invokeBeanFactoryPostProcessors: 实例化并调用所有已注册的BeanFactoryPostProcessor

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime

// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)

if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {

beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));

beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));

}

}

1.拿到当前应用上下文 beanFactoryPostProcessors 变量中的值,见代码块1详解

2.实例化并调用所有已注册的 BeanFactoryPostProcessor,见代码块2详解

代码块1:getBeanFactoryPostProcessors


public List getBeanFactoryPostProcessors() {

return this.beanFactoryPostProcessors;

}

这边 getBeanFactoryPostProcessors() 会拿到当前应用上下文中已经注册的 BeanFactoryPostProcessor,在默认情况下,this.beanFactoryPostProcessors 是返回空的。

如何添加自定义 BeanFactoryPostProcessor 到 this.beanFactoryPostProcessors 变量中了?

如果还有印象的话,我们在 Spring IoC:refresh前的环境准备 中的代码块12介绍过 customizeContext 方法,该方法是 Spring 提供给开发者的一个扩展点,用于自定义应用上下文,并且在 refresh() 方法前就被调用。在这边就可以通过该方法来添加自定义的 BeanFactoryPostProcessor。

简单的实现如下:

1.新建一个 ApplicationContextInitializer 的实现类 SpringApplicationContextInitializer ,并在 initialize 方法中写我们的逻辑。

package com.joonwhee.open.demo.spring;

import org.springframework.context.ApplicationContextInitializer;

import org.springframework.context.ConfigurableApplicationContext;

/**

  • @author joonwhee

  • @date 2019/1/19

*/

public class SpringApplicationContextInitializer implements ApplicationContextInitializer {

@Override

public void initialize(ConfigurableApplicationContext applicationContext) {

FirstBeanDefinitionRegistryPostProcessor firstBeanDefinitionRegistryPostProcessor = new FirstBeanDefinitionRegistryPostProcessor();

// 将自定义的firstBeanDefinitionRegistryPostProcessor添加到应用上下文中

applicationContext.addBeanFactoryPostProcessor(firstBeanDefinitionRegistryPostProcessor);

// …自定义操作

System.out.println(“SpringApplicationContextInitializer#initialize”);

}

}

2.将 SpringApplicationContextInitializer 作为初始化参数 contextInitializerClasses 配置到 web.xml 中。

contextInitializerClasses

com.joonwhee.open.demo.spring.SpringApplicationContextInitializer

这样,在启动应用时,FirstBeanDefinitionRegistryPostProcessor 就会被添加到 this.beanFactoryPostProcessors 中。

代码块2:invokeBeanFactoryPostProcessors


public static void invokeBeanFactoryPostProcessors(

ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {

// Invoke BeanDefinitionRegistryPostProcessors first, if any.

Set processedBeans = new HashSet();

// 1.判断beanFactory是否为BeanDefinitionRegistry,beanFactory为DefaultListableBeanFactory,

// 而DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,因此这边为true

if (beanFactory instanceof BeanDefinitionRegistry) {

BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

// 用于存放普通的BeanFactoryPostProcessor

List regularPostProcessors = new LinkedList();

// 用于存放BeanDefinitionRegistryPostProcessor

List registryProcessors = new LinkedList();

// 2.首先处理入参中的beanFactoryPostProcessors

// 遍历所有的beanFactoryPostProcessors, 将BeanDefinitionRegistryPostProcessor和普通BeanFactoryPostProcessor区分开

for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {

if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {

// 2.1 如果是BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor registryProcessor =

(BeanDefinitionRegistryPostProcessor) postProcessor;

// 2.1.1 直接执行BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法

registryProcessor.postProcessBeanDefinitionRegistry(registry);

// 2.1.2 添加到registryProcessors(用于最后执行postProcessBeanFactory方法)

registryProcessors.add(registryProcessor);

} else {

// 2.2 否则,只是普通的BeanFactoryPostProcessor

// 2.2.1 添加到regularPostProcessors(用于最后执行postProcessBeanFactory方法)

regularPostProcessors.add(postProcessor);

}

}

// Do not initialize FactoryBeans here: We need to leave all regular beans

// uninitialized to let the bean factory post-processors apply to them!

// Separate between BeanDefinitionRegistryPostProcessors that implement

// PriorityOrdered, Ordered, and the rest.

// 用于保存本次要执行的BeanDefinitionRegistryPostProcessor

List currentRegistryProcessors = new ArrayList();

// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.

// 3.调用所有实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessor实现类

// 3.1 找出所有实现BeanDefinitionRegistryPostProcessor接口的Bean的beanName

String[] postProcessorNames =

beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

// 3.2 遍历postProcessorNames

for (String ppName : postProcessorNames) {

// 3.3 校验是否实现了PriorityOrdered接口

if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {

// 3.4 获取ppName对应的bean实例, 添加到currentRegistryProcessors中,

// beanFactory.getBean: 这边getBean方法会触发创建ppName对应的bean对象, 目前暂不深入解析

currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));

// 3.5 将要被执行的加入processedBeans,避免后续重复执行

processedBeans.add(ppName);

}

}

// 3.6 进行排序(根据是否实现PriorityOrdered、Ordered接口和order值来排序)

sortPostProcessors(currentRegistryProcessors, beanFactory);

// 3.7 添加到registryProcessors(用于最后执行postProcessBeanFactory方法)

registryProcessors.addAll(currentRegistryProcessors);

// 3.8 遍历currentRegistryProcessors, 执行postProcessBeanDefinitionRegistry方法

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

// 3.9 执行完毕后, 清空currentRegistryProcessors

currentRegistryProcessors.clear();

// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.

// 4.调用所有实现了Ordered接口的BeanDefinitionRegistryPostProcessor实现类(过程跟上面的步骤3基本一样)

// 4.1 找出所有实现BeanDefinitionRegistryPostProcessor接口的类, 这边重复查找是因为执行完上面的BeanDefinitionRegistryPostProcessor,

// 可能会新增了其他的BeanDefinitionRegistryPostProcessor, 因此需要重新查找

postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

for (String ppName : postProcessorNames) {

// 校验是否实现了Ordered接口,并且还未执行过

if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {

currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));

processedBeans.add(ppName);

}

}

sortPostProcessors(currentRegistryProcessors, beanFactory);

registryProcessors.addAll(currentRegistryProcessors);

// 4.2 遍历currentRegistryProcessors, 执行postProcessBeanDefinitionRegistry方法

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

currentRegistryProcessors.clear();

// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.

// 5.最后, 调用所有剩下的BeanDefinitionRegistryPostProcessors

boolean reiterate = true;

while (reiterate) {

reiterate = false;

// 5.1 找出所有实现BeanDefinitionRegistryPostProcessor接口的类

postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

for (String ppName : postProcessorNames) {

// 5.2 跳过已经执行过的

if (!processedBeans.contains(ppName)) {

currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));

processedBeans.add(ppName);

// 5.3 如果有BeanDefinitionRegistryPostProcessor被执行, 则有可能会产生新的BeanDefinitionRegistryPostProcessor,

// 因此这边将reiterate赋值为true, 代表需要再循环查找一次

reiterate = true;

}

}

sortPostProcessors(currentRegistryProcessors, beanFactory);

registryProcessors.addAll(currentRegistryProcessors);

// 5.4 遍历currentRegistryProcessors, 执行postProcessBeanDefinitionRegistry方法

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

currentRegistryProcessors.clear();

}

// Now, invoke the postProcessBeanFactory callback of all processors handled so far.

// 6.调用所有BeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法(BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor)

invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);

// 7.最后, 调用入参beanFactoryPostProcessors中的普通BeanFactoryPostProcessor的postProcessBeanFactory方法

invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

} else {

// Invoke factory processors registered with the context instance.

invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);

}

// 到这里 , 入参beanFactoryPostProcessors和容器中的所有BeanDefinitionRegistryPostProcessor已经全部处理完毕,

// 下面开始处理容器中的所有BeanFactoryPostProcessor

// Do not initialize FactoryBeans here: We need to leave all regular beans

// uninitialized to let the bean factory post-processors apply to them!

// 8.找出所有实现BeanFactoryPostProcessor接口的类

String[] postProcessorNames =

beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,

// Ordered, and the rest.

// 用于存放实现了PriorityOrdered接口的BeanFactoryPostProcessor

List priorityOrderedPostProcessors = new ArrayList();

// 用于存放实现了Ordered接口的BeanFactoryPostProcessor的beanName

List orderedPostProcessorNames = new ArrayList();

// 用于存放普通BeanFactoryPostProcessor的beanName

List nonOrderedPostProcessorNames = new ArrayList();

// 8.1 遍历postProcessorNames, 将BeanFactoryPostProcessor按实现PriorityOrdered、实现Ordered接口、普通三种区分开

for (String ppName : postProcessorNames) {

// 8.2 跳过已经执行过的

if (processedBeans.contains(ppName)) {

// skip - already processed in first phase above

} else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {

// 8.3 添加实现了PriorityOrdered接口的BeanFactoryPostProcessor

priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));

} else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {

// 8.4 添加实现了Ordered接口的BeanFactoryPostProcessor的beanName

orderedPostProcessorNames.add(ppName);

} else {

// 8.5 添加剩下的普通BeanFactoryPostProcessor的beanName

nonOrderedPostProcessorNames.add(ppName);

}

}

// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.

// 9.调用所有实现PriorityOrdered接口的BeanFactoryPostProcessor

// 9.1 对priorityOrderedPostProcessors排序

sortPostProcessors(priorityOrderedPostProcessors, beanFactory);

// 9.2 遍历priorityOrderedPostProcessors, 执行postProcessBeanFactory方法

invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

// Next, invoke the BeanFactoryPostProcessors that implement Ordered.

// 10.调用所有实现Ordered接口的BeanFactoryPostProcessor

List orderedPostProcessors = new ArrayList();

for (String postProcessorName : orderedPostProcessorNames) {

// 10.1 获取postProcessorName对应的bean实例, 添加到orderedPostProcessors, 准备执行

orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));

}

// 10.2 对orderedPostProcessors排序

sortPostProcessors(orderedPostProcessors, beanFactory);

// 10.3 遍历orderedPostProcessors, 执行postProcessBeanFactory方法

invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

// Finally, invoke all other BeanFactoryPostProcessors.

// 11.调用所有剩下的BeanFactoryPostProcessor

List nonOrderedPostProcessors = new ArrayList();

for (String postProcessorName : nonOrderedPostProcessorNames) {

// 11.1 获取postProcessorName对应的bean实例, 添加到nonOrderedPostProcessors, 准备执行

nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));

}

// 11.2 遍历nonOrderedPostProcessors, 执行postProcessBeanFactory方法

invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

// Clear cached merged bean definitions since the post-processors might have

// modified the original metadata, e.g. replacing placeholders in values…

// 12.清除元数据缓存(mergedBeanDefinitions、allBeanNamesByType、singletonBeanNamesByType),

// 因为后处理器可能已经修改了原始元数据,例如, 替换值中的占位符…

beanFactory.clearMetadataCache();

}

1.判断 beanFactory 是否为 BeanDefinitionRegistry。beanFactory 是在之前的 obtainFreshBeanFactory 方法构建的,具体代码在:AbstractRefreshableApplicationContext.refreshBeanFactory() 方法,代码如下。

@Override

protected final void refreshBeanFactory() throws BeansException {

if (hasBeanFactory()) {

destroyBeans();

closeBeanFactory();

}

try {

// 创建一个新的BeanFactory

DefaultListableBeanFactory beanFactory = createBeanFactory();

beanFactory.setSerializationId(getId());

customizeBeanFactory(beanFactory);

loadBeanDefinitions(beanFactory);

synchronized (this.beanFactoryMonitor) {

this.beanFactory = beanFactory;

}

}

catch (IOException ex) {

throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);

}

}

可以看出,我们构建的 beanFactory 是一个 DefaultListableBeanFactory ,而 DefaultListableBeanFactory 实现了BeanDefinitionRegistry 接口,因此 beanFactory instanceof BeanDefinitionRegistry 结果为 true。

3.4 获取 ppName 对应的 bean 实例,添加到 currentRegistryProcessors 中,准备执行。beanFactory.getBean 方法会触发创建 ppName 对应的 bean 实例对象,创建 bean 实例是 IoC 的另一个核心内容,之后会单独解析,目前暂不深入解析。

3.6 进行排序,该方法在下面也被调用了好几次,见代码块3详解。

代码块3:sortPostProcessors


private static void sortPostProcessors(List<?> postProcessors, ConfigurableListableBeanFactory beanFactory) {

Comparator comparatorToUse = null;

if (beanFactory instanceof DefaultListableBeanFactory) {

// 1.获取设置的比较器

comparatorToUse = ((DefaultListableBeanFactory) beanFactory).getDependencyComparator();

}

if (comparatorToUse == null) {

// 2.如果没有设置比较器, 则使用默认的OrderComparator

comparatorToUse = OrderComparator.INSTANCE;

}

// 3.使用比较器对postProcessors进行排序

Collections.sort(postProcessors, comparatorToUse);

}

默认情况下,比较器为 OrderComparator;如果配置了 annotation-config,并且值为true,使用的是 AnnotationAwareOrderComparator( Spring IoC:context:component-scan节点解析 代码块17中设置了dependencyComparator 属性为 AnnotationAwareOrderComparator.INSTANCE),AnnotationAwareOrderComparator 继承自 OrderComparator,只是重写了部分方法,比较器的部分代码如下:

@Override

public int compare(Object o1, Object o2) {

return doCompare(o1, o2, null);

}

private int doCompare(Object o1, Object o2, OrderSourceProvider sourceProvider) {

// 判断o1是否实现了PriorityOrdered接口

boolean p1 = (o1 instanceof PriorityOrdered);

// 判断o2是否实现了PriorityOrdered接口

boolean p2 = (o2 instanceof PriorityOrdered);

// 1.如果o1实现了PriorityOrdered接口, 而o2没有, 则o1排前面

if (p1 && !p2) {

return -1;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

腾讯T3大牛总结的500页MySQL实战笔记意外爆火,P8看了直呼内行

腾讯T3大牛总结的500页MySQL实战笔记意外爆火,P8看了直呼内行
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-xpwJClP0-1713395389739)]

[外链图片转存中…(img-DAuh0XWc-1713395389740)]

[外链图片转存中…(img-iJpqm6If-1713395389740)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

[外链图片转存中…(img-kSV0uK3l-1713395389740)]

[外链图片转存中…(img-q7rbhtDn-1713395389740)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/2401_84009899/article/details/137901115

智能推荐

Gitlab Group Runner配置及相关问题记录_gitlab group runners-程序员宅基地

文章浏览阅读1.2k次,点赞32次,收藏27次。Gitlab Group Runner配置及相关问题记录_gitlab group runners

grep 多条件并行满足_使用grep -E进行多条件过滤-程序员宅基地

文章浏览阅读523次。project_A6078086216274=xxproject_A6078086227691=xxproject_A6078086224420=xxproject_A6078086233620=xxproject_A6078086240348=xxproject_A6078086358053=xxproject_A6078086366505=xxproject_A60..._grep -e

【100个 Unity实用技能】| Unity中常用的几种路径 分析,不同平台路径总结_unity 路径-程序员宅基地

文章浏览阅读7.8w次,点赞15次,收藏44次。在Unity中有很多种路径,尤其是在不同的平台上,同一种路径的写法可能最终是不一样的。本文就来总结一下Unity中的几种路径,以及简单的使用方法。_unity 路径

【深入理解Java虚拟机】【04】jdk命令行工具-程序员宅基地

文章浏览阅读154次。[TOC]jdk命令行工具0.概览名称作用jpsJVM Process Status Tool,显示系统中所有的Hotspot虚拟机进程jstatJVM Statistics Monitoring Tool,用于收集HotSpot虚拟机各方面的运行数据jinfoConfiguration info for java,显示..._com.sankuai.mms.boot.bootstrap

Android sdk 30 无法查看源码解决方案_android sdk33源码在platform-程序员宅基地

文章浏览阅读1.2k次。网上大多数是修改项目配置,我这里介绍一种伪装法,29到30变动不大,可以把29源码伪装成30源码。复制D:\program\androidsdk\sources 里面 29的文件夹,改名为30,然后修改D:\program\androidsdk\sources\android-30 里面package.xml、source.properties中 29的全改成30。然后重启 AndroidStudio..._android sdk33源码在platform

JavaScript在服务端(Node.js)和客户端的区别_我在本地电脑用js写了一个服务器,他和购买的服务器有什么区别?-程序员宅基地

文章浏览阅读7.1k次,点赞11次,收藏31次。JavaScript是一种脚本语言,一般运行在客户端,而Node.js可以使JavaScript运行在服务端。JavaScript包含三部分:ECMAScript、DOM、BOM三部分。ECMAScript是JavaScript的核心语法DOM是HTML和XML的应用程序接口(API),用于控制文档的内容与结构BOM(浏览器对象模型)可以对浏览器窗口进行访问和操作区别:在客户端,Ja..._我在本地电脑用js写了一个服务器,他和购买的服务器有什么区别?

随便推点

细说独特的APaaS软件门类-程序员宅基地

文章浏览阅读868次。文/明道云创始人任向晖美国软件企业Intuit公司在2000年前后推出了一个产品,叫做Quickbase,顾名思义,就是快速开发数据库(应用)。它开了行业先河,不仅提供低代码的企业应用开发环境,而让应用直接在一个平台上运行,用户不再需要额外编译代码和配置运行环境。当然,在那个年代,APaaS这个品类名称还没有出现,一般都统称这类应用为ASP。在接下来的十多年中,这个品类不断发展,不仅做出了Smartsheet,Outsystems, ServiceNow这些独立上市公司,像Salesforce和微软也都_apaas软件门类

单链表实现栈结构_3.3 请设计一个栈,除了常规栈支持的 pop 与 push 函数以外,还支持 min 函数,该 函-程序员宅基地

文章浏览阅读749次,点赞4次,收藏4次。最近遇到一种类型的算法题,是实现栈的pop、push、peek等方法的同时,还让实现min取得栈中最小值的功能, 题目最特殊的一点就是,要求时间复杂度为O(1)。具体的题目如下:请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。执行push、pop和min操作的时间复杂度必须为O(1)。示例:MinStack minStack = new MinStack();minStack.push(-2);minStack.push(0);_3.3 请设计一个栈,除了常规栈支持的 pop 与 push 函数以外,还支持 min 函数,该 函

计算机与CFD模拟仿真:技术的融合与应用_cfd怎么和算法融合-程序员宅基地

文章浏览阅读1.1k次,点赞30次,收藏19次。面对未来的挑战和发展趋势,我们期待计算机与CFD模拟仿真技术能够在更多的领域中发挥其强大的作用,推动科技的进步与发展。通过模拟风力发电机组在风场中的运行状态,我们可以优化风力发电机组的布局和设计,从而提高风力发电的效率和可靠性。未来的研究将更加注重于将人工智能技术应用于CFD模拟仿真中,以实现自动化建模、自动化求解和自动化后处理等功能,从而提高模拟仿真的效率和准确性。未来的研究将更加注重于开发多尺度模拟算法和技术,以实现不同尺度之间的平滑过渡和耦合,从而更好地模拟和理解流体的复杂行为。_cfd怎么和算法融合

java web整合海康威视录像机摄像SDK-程序员宅基地

文章浏览阅读3k次。java 项目demo目录结构java web整合海康威视录像机摄像SDK实现拍照功能。一、maven项目结构【提示】主要的是HCNetSDK.java文件,其中都是海康java调用dll文件获取摄像头资源的接口。(1)库文件配置如果是linux环境将库文件放在/usr/li..._java调用海康威视,实现hls实时预览和回放

python中print是什么意思中文-python中使用print输出中文的方法-程序员宅基地

文章浏览阅读1.9k次。看Python简明教程,学习使用print打印字符串,试了下打印中文,不行。编辑环境:IDLE上网搜了下解决办法,各种说法,试了两种:print u"学习"print (unicode("学习", encoding="utf-8"))保存时均还是会提示shell输出:?§?°??说明第二种还是有编码但还是不对。最后,加上最前面一行就好了,..._print从网站上获取的值是英文

Nested Logit模型拟合实战案例(SAS篇)-程序员宅基地

文章浏览阅读1k次,点赞2次,收藏3次。在《Nested Logit模型》一文中,我们给出了Nested Logit模型的基本结构。有了理论,下一步便是实操。本篇我们介绍如何利用SAS去拟合Nested Logit模型(所用的软件为SAS 9.4版本)。全文共约6500字,24张配图,详细介绍利用SAS创建Nested Logit模型的流程,以及模型背后的行为假设(Behavior Assumption)。可以非常自信的说,本文是截止目前介绍SAS拟合Nested Logit模型最详细的中文资料。本文可以让您了解到以下几个方面的内_nested logit模型