spring的bean创建流程源码解析

文章目录

    • IOC 和 DI
    • BeanFactory
    • ApplicationContext
      • 实现的接口
        • 1、BeanFactory接口
        • 2、MessageSource 国际化接口
        • 3、ResourcePatternResolver,资源解析接口
        • 4、EnvironmentCapable接口,用于获取环境变量,配置信息
        • 5、ApplicationEventPublisher 事件发布接口
      • Application 的继承体系
    • Bean 的创建执行流程
    • Spring 给开发者留下的扩展点
    • 结合代码描述
    • Bean 的创建流程总结:
    • 面试中说的三级缓存是什么?
      • 为什么是三级缓存而不是二级缓存?

本文基于 Spring 5.0.2.RELEASE

spring 是 Java Web 开发中,非常常用的框架。它的基础作用是为我们生成对象。这里涉及到 2 个概念。IOC 和 DI。

IOC 和 DI

IOC:Inversion of Control,控制反转。强调的是原来在程序中创建Bean的权利反转给第三方,这里的第三方,其实就是 spring 框架。

DI:依赖注入。比如 UserService类,依赖 UserDao,但是 UserDao 是由 BeanFactory 注入给 UserService 的。

BeanFactory

1、BeanFactory是Spring的早期接口,称为Spring的Bean工厂.它提供了一系列的方法,用于获取 Bean
在这里插入图片描述
BeanFactory 的默认实现类是 DefaultListableBeanFactory,DefaultListableBeanFactory主要是管理 BeanDefinition 对象,并根据BeanDefinition 对象创建 Bean 对象。
在这里插入图片描述
在DefaultListableBeanFactory中,实现了BeanDefinitionRegistry接口,这个接口定义了关于 BeanDefinition 的注册、移除、查询等一系列的操作。

DefaultListableBeanFactory还继承了DefaultSingletonBeanRegistry,这个类中,实现了三级缓存,创建单例 Bean 的操作。????BeanDefinition对象,怎么传给DefaultSingletonBeanRegistry对象的????

DefaultListableBeanFactory总结起来,就是可以增删改查BeanDefinition定义对象,并通过父类方法,生成 Bean 对象。但是它并没有把 xml 文件,或者注解,解析成BeanDefinition对象的功能。这个功能是在ApplicationContext实现的。

ApplicationContext

实现的接口

1、BeanFactory接口

ApplicationContext 继承了很多接口,每增加一个接口,都增加了对应的功能。
在这里插入图片描述
但是,ApplicationContext 中,并不是继承了 DefaultListableBeanFactory,而是实现了 BeanFactory接口,并引入了 DefaultListableBeanFactory 作为属性,由 DefaultListableBeanFactory 实现 BeanFactory 相关的功能。(组合优于继承)

在这里插入图片描述

注意看,AnnotationConfigApplicationContext中包含 beanFactory 属性,在它的父类 AbstractApplicationContext中,实现了 getBean 等 方法,都是调用 beanFactory 来实现的。

@Override
public Object getBean(String name) throws BeansException {assertBeanFactoryActive();return getBeanFactory().getBean(name);
}

ApplicationContext 相比 BeanFactory,还多继承了四个接口,功能如下:

2、MessageSource 国际化接口
public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);System.out.println(context.getMessage("hi", null, Locale.CHINA));System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
}

在 resources 目录下创建三个配置文件,注意设置 idea 的 properties 编码格式是 utf-8
messages.properties 空内容
messages_en.properties 填写内容 hi=hello
messages_zh.properties 填写内容 hi=你好

运行代码,控制台打印如下,说明国际化生效

你好
hello
3、ResourcePatternResolver,资源解析接口

可以用于读取配置文件

public static void main(String[] args) throws IOException {ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);//读取类类路径下文件Resource[] resources1 = context.getResources("classpath:application.properties");//读取jar 包中的类路径文件,注意带个 * 号Resource[] resources2 = context.getResources("classpath*:META-INF/spring.factories");//读取文件目录下的文件Resource[] resources3 = context.getResources("file:/tmp/test.log");
}
4、EnvironmentCapable接口,用于获取环境变量,配置信息
  public static void main(String[] args) throws IOException {ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);//读取application.properties 中的内容System.out.println(context.getEnvironment().getProperty("server.port"));//读取系统环境变量System.out.println(context.getEnvironment().getProperty("JAVA_HOME"));}
5、ApplicationEventPublisher 事件发布接口
// 自定义事件
public class UserLoginEvent extends ApplicationEvent {public UserLoginEvent(Object source) {super(source);}
}
//发布事件public static void main(String[] args) throws IOException {ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);context.publishEvent(new UserLoginEvent("username"));}
//接收事件
@Component
public class UserMessageService {@EventListenerpublic void sendMessage(UserLoginEvent event){Object source = event.getSource();System.out.println("欢迎用户:"+source);}
}

当调用publishEvent发布事件时,会调用到 sendMessage 方法。(观察者模式)

总结:
1、BeanFactory 类,用于生成 Bean。
2、ApplicationContext,继承了 BeanFactory 接口,引入了 DefaultListableBeanFactory对象,体现了组合优于继承的原则。
3、ApplicationContext,实现了其他接口,增加了国际化,事件发布,资源解析,读取配置等功能。

Application 的继承体系

在这里插入图片描述

常用的 ApplicationContext 有 3 个,分别是
AnnotationConfigApplicationContext,用于加载注解配置类的ApplicationContext
FileSystemXmlApplicationContext,用于加载磁盘路径下的xml配置的ApplicationContext
ClassPathXmlApplicationContext,用于加载类路径下的xml配置的ApplicationContext
如果 spring 中加入了 Web 相关的组件,那么使用的 ApplicationContext 是
XmlWebApplicationContext,用于web环境下,加载类路径下的xml配置的ApplicationContext
AnnotationConfigWebApplicationContext,用于web环境下,加载磁盘路径下的xml配置的ApplicationContext

Bean 的创建执行流程

以解析 xml 文件为例,使用ClassPathXmlApplicationContext,流程如下:

1、加载xml配置文件,解析获取配置中的每个的信息,封装成一个个的BeanDefinition对象;
2、将BeanDefinition存储在一个名为beanDefinitionMap的Map<String,BeanDefinition>中;
3、ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;
4、创建好的Bean实例对象,被存储到一个名为singletonObjects的Map<String,Object>中;
5、当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。

在这里插入图片描述

Spring 给开发者留下的扩展点

主要有两个地方,
1 是对 beanDefinitionMap 进行添加,修改,只要向 beanDefinitionMap 中添加了 BeanDefinition,就会被 Spring 生成 Bean。
2 是当 Bean 创建完成后,可以对 Bean 对象进行修改,比如 AOP,注解,都是生成代理 Bean,替换原有的 Bean。

流程如下:

结合代码描述

代码入口:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:application-single.xml");UserService userService = context.getBean("userServiceBeanId",UserService.class);System.out.println(userService.getName());

进入ClassPathXmlApplicationContext

	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {//1、返回一个classloader//2、返回一个解析器super(parent);  // ===> 1// 1、获取环境(系统环境、jvm环境)// 2、设置Placeholder占位符解析器,${xxx}.xml解析为完整文件名// 2、将xml的路径解析完存储到数组setConfigLocations(configLocations); //  ===>//默认为trueif (refresh) {//核心方法,入口refresh();  // ===>  magic happens here!}}

进入 Refresh 方法

	@Overridepublic void refresh() throws BeansException, IllegalStateException {// synchronized块锁(monitorenter --monitorexit)// 不然 refresh() 还没结束,又来个启动或销毁容器的操作//	 startupShutdownMonitor就是个空对象,锁synchronized (this.startupShutdownMonitor) {//1、【准备刷新】,设置了几个变量,也是准备工作prepareRefresh();   //  ===>// 2、【获得新的bean工厂】关键步骤,重点!//2.1、关闭旧的 BeanFactory//2.2、创建新的 BeanFactory(DefaluListbaleBeanFactory)//2.3、解析xml/加载 Bean 定义、注册 Bean定义到beanFactory(不初始化)//2.4、返回全新的工厂ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//3、【bean工厂前置操作 】为BeanFactory配置容器特性// 例如类加载器、表达式解析器、注册默认环境bean、后置管理器prepareBeanFactory(beanFactory);// ===>try {// 4、【bean工厂后置操作】此处为空方法,如果子类需要,自己去实现postProcessBeanFactory(beanFactory);  // ===> 空的!//5、【调用bean工厂后置处理器】,开始调用我们自己实现的接口//调用顺序一:先执行BeanDefinitionRegistryPostProcessor接口的方法,它也继承了BeanFactoryPostProcessor//调用顺序二:后执行BeanFactoryPostProcessor接口的方法,用于修改 beanDefinitioninvokeBeanFactoryPostProcessors(beanFactory);  // ===>  重头戏//6、【注册bean后置处理器】只是注册,但是还不会调用//逻辑:找出所有实现BeanPostProcessor接口的类,分类、排序、注册registerBeanPostProcessors(beanFactory);  // ===>  关键点// Initialize message source for this context.//7、【初始化消息源】国际化问题i18n,参照https://nacos.io/initMessageSource(); // ===> 就是往factory加了个single bean// Initialize event multicaster for this context.//8、【初始化事件广播器】初始化自定义的事件监听多路广播器// 如果需要发布事件,就调它的multicastEvent方法// 把事件广播给listeners,其实就是起一个线程来处理,把Event扔给listener处理// (可以通过 SimpleApplicationEventMulticaster的代码来验证)initApplicationEventMulticaster(); // ===> 同样,加了个bean// 9、【刷新】这是个protected空方法,交给具体的子类来实现//  可以在这里初始化一些特殊的 Bean// (在初始化 singleton beans 之前)onRefresh();  // ===> 空的!一般没人管它//10、【注册监听器】,监听器需要实现 ApplicationListener 接口// 也就是扫描这些实现了接口的类,给他放进广播器的列表中// 其实就是个观察者模式,广播器接到事件的调用时,去循环listeners列表,// 挨个调它们的onApplicationEvent方法,把event扔给它们。registerListeners();  // ===> 观察者模式//11、 【结束bean工厂初始化操作】//1、初始化所有的 singleton beans,反射生成对象/填充//2、 调用Bean的前置处理器和后置处理器// 关键点:getBean方法里完成finishBeanFactoryInitialization(beanFactory);  // ===>  关键点!// 12、结束refresh操作// 发布事件与清除上下文环境finishRefresh();} catch (BeansException ex) {} finally {}}}

Bean 的创建流程总结:

在这里插入图片描述

面试中说的三级缓存是什么?

三级缓存是为了解决循环依赖,也是为了存储单例对象

Spring提供了三级缓存存储 完整Bean实例 和 半成品Bean实例 ,用于解决循环引用问题

在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:

public class DefaultSingletonBeanRegistry ... { //1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"Map<String, Object> singletonObjects = new ConcurrentHashMap(256); //2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存" Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16); //3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存" Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}

三级缓存怎么解决循环依赖问题?核心就是先把半成品的 Bean 放入一个缓存中

假设 Bean1 和 Bean2 循环依赖,那么创建过程如下:
1、Bean1 实例化对象,但尚未初始化,将 Bean1 存储到三级缓存;
2、Bean1 属性注入,需要 Bean2,从缓存中获取,没有 Bean2;
3、Bean2 实例化对象,但尚未初始化,将 Bean2 存储到到三级缓存;
4、Bean2 属性注入,需要 Bean1,从三级缓存获取 Bean1,Bean1 从三级缓存移入二级缓存;
5、Bean2 执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
6、Bean1 注入 Bean2;
7、Bean1 执行其他生命周期过程,最终成为一个完整 Bean,存储到一级缓存,删除二三级缓存。

示例如下:

public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();context.registerBean(ConfigurationClassPostProcessor.class);context.registerBean(Config.class);context.refresh();context.close();
}@Configuration
static class Config {@Bean // 解析 @Aspect、产生代理public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {return new AnnotationAwareAspectJAutoProxyCreator();}@Bean // 解析 @Autowiredpublic AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {return new AutowiredAnnotationBeanPostProcessor();}@Bean // 解析 @PostConstructpublic CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {return new CommonAnnotationBeanPostProcessor();}@Beanpublic Advisor advisor(MethodInterceptor advice) {AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression("execution(* foo())");return new DefaultPointcutAdvisor(pointcut, advice);}@Beanpublic MethodInterceptor advice() {return (MethodInvocation invocation) -> {System.out.println("before...");return invocation.proceed();};}@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic Bean2 bean2() {return new Bean2();}
}static class Bean1 {public Bean1() {System.out.println("Bean1()");}@Autowired public void setBean2(Bean2 bean2) {System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());}@PostConstruct public void init() {System.out.println("Bean1 init()");}
}static class Bean2 {public Bean2() {System.out.println("Bean2()");}@Autowired public void setBean1(Bean1 bean1) {System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());}@PostConstruct public void init() {System.out.println("Bean2 init()");}
}

执行 main 方法后,打印如下:

Bean1()
Bean2()
Bean2 setBean1(bean1) class is: class com.spring.test.A17_1$Bean1
Bean2 init()
Bean1 setBean2(bean2) class is: class com.spring.test.A17_1$Bean2
Bean1 init()

为什么是三级缓存而不是二级缓存?

如果只是为了解决循环依赖问题,那么二级缓存就够了。为什么要设计三级缓存?为了解决循环依赖时,某个 Bean 需要被 AOP 增强,生成代理对象。

在Bean 的创建流程总结中,注意看图,Bean 的初始化方法执行完成后,才执行 BeanPostProcess的后处理方法,如果这个 Bean 被 AOP 增强了,会生成代理对象。

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法

// 方法一:实例化! Bean的构造函数在这里被调用!
instanceWrapper = createBeanInstance(beanName, mbd, args); 
//方法二:注入相关的属性!【关键点】
populateBean(beanName, mbd, instanceWrapper);
//方法三:初始化方法,这里会调用 BeanPostProcess
exposedObject = initializeBean(beanName, exposedObject, mbd);

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition) 方法

//调用Bean的前置处理器!我们自己实现的bean的processer在这里!
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);// 1、调用初始化方法, 处理 bean 中定义的 init-method,
//2、如果 bean 实现了 InitializingBean 接口,调用 afterPropertiesSet() 方法
invokeInitMethods(beanName, wrappedBean, mbd); // 调用Bean的后置管理器!用于生成代理aop也在这里偷偷做了手脚……注意aop调试时,看这句后wrappedBean的变化!
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); 

通过上述源码,可以看到,代理对象 生成,是在初始化方法执行后,才生成代理对象。

这个功能,在循环依赖场景下,就会存在问题。

刚才的 Bean1 和 Bean2 循环依赖时,打印如下:

1、Bean1()
2、Bean2()
3、Bean2 setBean1(bean1) class is: class com.spring.test.A17_1$Bean1
4、Bean2 init()
5、Bean1 setBean2(bean2) class is: class com.spring.test.A17_1$Bean2
6、Bean1 init()

如果是在初始化后,才对 Bean1 进行增强生成代理对象,那么是在第 6 步完成后,才生成代理对象。这样Bean2 setBean1(bean1) 注入的就是原始对象,不是代理对象。不符合预期。

所以 Spring 打了一个补丁,提供一个三级缓存,三级缓存中存储的不是未完成的对象,而是 Bean 的创建工厂ObjectFactory。当需要Bean1 对象时,执行的是 ObjectFactory 的 getObject 方法,这个方法中,也会进行是否 AOP 的判断,如果是,就生成代理对象。

源码如下:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

// () -> getEarlyBeanReference(beanName, mbd, bean) 是 ObjectFactory 匿名实现类 的简写
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

getEarlyBeanReference 方法

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;//是否存在实现InstantiationAwareBeanPostProcessors接口的类if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {//循环所有Bean后置处理器for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;//重点:开始创建AOP代理,exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}//总结//1、如果不调用后置,返回的bean和三级缓存一样//2、如果调用AOP后置,返回的就是代理对象//3、这就是三级缓存设计的巧妙之处!!!!Map<String, ObjectFactory<?>>// 结论:虽然二级缓存能解决循环依赖,但是使用不了aop了,也就是扩展点没有了return exposedObject;}

示例如下:修改 Bean1 代码增加一个 foo 方法,让 Bean1 被 AOP 增强

    static class Bean1 {public void foo() {}public Bean1() {System.out.println("Bean1()");}@Autowired public void setBean2(Bean2 bean2) {System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());}@PostConstruct public void init() {System.out.println("Bean1 init()");}}

再次执行,观察打印效果:

Bean1()
Bean2()
Bean2 setBean1(bean1) class is: class com.spring.test.A17_1$Bean1$$EnhancerBySpringCGLIB$$28831f11
Bean2 init()
Bean1 setBean2(bean2) class is: class com.spring.test.A17_1$Bean2
Bean1 init()

注意看,Bean1 对象,很早就变成了代理对象,说明是三级缓存生效了。注意:虽然很早就变成代理对象了,但是初始化方法还没执行。

总结下:

AOP 代理对象生成的时机?

1、如果存在循环依赖,是在三级缓存中生成代理对象

2、如果不存在循环依赖,是在 Bean 初始化方法执行后生成。

为什么需要三级缓存?

为了打补丁,让循环依赖的 AOP 的 Bean,能正常注入到其他 Bean 中。

1,2,3 三级缓存的作用是什么?

一级缓存存储已经完全创建好的单例对象

二级缓存存储未完成的,还被其他 Bean 引用的对象

三级缓存,用于存储未完成的对象工厂,且没被其他对象引用过。

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

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

相关文章

使用扩展卡尔曼滤波器进行包裹测量的状态估计

此示例说明如何使用扩展卡尔曼滤波器算法对涉及圆形包裹角度测量的 3D 跟踪进行非线性状态估计。对于目标跟踪&#xff0c;传感器通常采用球形框架来报告物体的方位角、距离和仰角位置。该组的角度测量值在一定范围内报告。例如&#xff0c;报告的方位角范围为- 180∘ 到180∘或…

UE5 GAS开发P34 游戏效果理论

GameplayEffects Attributes&#xff08;属性&#xff09;和Gameplay Tags&#xff08;游戏标签&#xff09;分别代表游戏中实体的特性和标识。 Attributes&#xff08;属性&#xff09;&#xff1a;Attributes是用来表示游戏中实体的特性或属性的值&#xff0c;例如生命值、…

【工具】录屏软件Captura安装使用及ffmpeg下载配置

开启技术视频创作&#xff0c;录屏软件林林总总&#xff0c;适合的、习惯的最好。 录屏软件Captura的使用及ffmpeg下载配置 1.Captura下载、安装2.FFmpeg下载、配置3.Captura屏幕录制试用、录制视频效果 1.Captura下载、安装 Captura主要是一个免费开源的录屏软件&#xff0c…

系统架构设计精华知识

数据流风格&#xff1a;适合于分阶段做数据处理&#xff0c;交互性差&#xff0c;包括&#xff1a;批处理序列、管理过滤器。调用/返回风格&#xff1a;一般系统都要用到&#xff0c;包括&#xff1a;主程序/子程序&#xff0c;面向对象&#xff0c;层次结构&#xff08;分层越…

20240330-1-词嵌入模型w2v+tf-idf

Word2Vector 1.什么是词嵌入模型&#xff1f; 把词映射为实数域向量的技术也叫词嵌⼊ 2.介绍一下Word2Vec 谷歌2013年提出的Word2Vec是目前最常用的词嵌入模型之一。Word2Vec实际是一种浅层的神经网络模型&#xff0c;它有两种网络结构&#xff0c;分别是连续词袋&#xff…

54位大咖演讲精华! 中国生成式AI大会圆满收官,TOP50企业榜单揭晓

54位大咖演讲精华&#xff01; 中国生成式AI大会圆满收官&#xff0c;TOP50企业榜单揭晓© 由 红板报 提供 智东西4月19日报道&#xff0c;为期两天的2024中国生成式AI大会&#xff0c;今日在京圆满收官。 54位产学研投嘉宾代表全程干货爆棚&#xff0c;报名咨询人数逾52…

操作符不存在:sde.st_geometry ^ !sde.st_geometry建议 SQL函 数st_intersects在内联inlining期间

操作符不存在&#xff1a;sde.st_geometry ^ &#xff01;sde.st_geometry建议 SQL函 数st_intersects在内联inlining期间 问题&#xff1a;最近在使用SQL图形处理函数处理图形时&#xff0c;莫名奇妙报如下错误&#xff0c;甚是费解 于是开始四处"寻医问药" 1、nav…

G1、CMS垃圾回收期专题

共同点 非阻塞 使用三色标记法 初始标记、并发标记、重新标记、并发清理 &#xff08;初始标记、重新标记需要stop world&#xff09; CMS垃圾回收器 缺点 浮动垃圾 有垃圾碎片 关注停顿时间&#xff0c;使用了效率最高的标记清除算法 G1垃圾回收器 缺点 需要配置高&…

MySQL表级锁——技术深度+1

引言 本文是对MySQL表级锁的学习&#xff0c;MySQL一直停留在会用的阶段&#xff0c;需要弄清楚锁和事务的原理并DEBUG查看。 PS:本文涉及到的表结构均可从https://github.com/WeiXiao-Hyy/blog中获取&#xff0c;欢迎Star&#xff01; MySQL表级锁 MySQL中表级锁主要有表锁…

【Java EE】 SpringBoot配置文件

文章目录 &#x1f340;配置文件的作用&#x1f334;SpringBoot配置文件&#x1f343;配置文件的格式&#x1f333;properties 配置文件说明&#x1f338;properties基本语法&#x1f338;读取配置文件&#x1f338;properties 缺点分析 &#x1f332;yml 配置文件说明&#x1…

Docker基本管理和虚拟化

一、docker的发展历史 https://www.cnblogs.com/rongba/articles/14782624.htmlhttps://www.cnblogs.com/rongba/articles/14782624.html 二、docker的概述 Docker是一个开源的应用容器引擎&#xff0c;基于go语言开发并遵循了apache2.0协议开源。 Docker是在Linux容器里运行…

附近商户-GEO数据结构的基本用法

10、附近商户 10.1、附近商户-GEO数据结构的基本用法 GEO就是Geolocation的简写形式&#xff0c;代表地理坐标。Redis在3.2版本中加入了对GEO的支持&#xff0c;允许存储地理坐标信息&#xff0c;帮助我们根据经纬度来检索数据。常见的命令有&#xff1a; GEOADD&#xff1a…

SpringBoot Bean管理(扫描、注册、注册条件)

Bean扫描 一般的扫描包路径 需要使用xml标签或者是ComponentScan(basePackages “xxx.xxx”) ,但是在SpringBoot中不需要。 在springBoot 项目的启动类中有一个注解&#xff1a;SpringBootApplication,这个注解包含以下注解&#xff1a; 但是ComponentScan并没有指明包路径…

开源协议的对比和商业上的安全使用

开源协议的对比和商业上的安全使用 开源组件是&#xff1a;“任何人都可以自由使用、更改和共享&#xff08;以修改或未修改的形式&#xff09;的软件”。当今企业依靠开源来加速开发、降低成本和推动创新。对开放源码的糟糕管理可能会使组织面临安全、法律和操作风险。 使用…

Maven基础篇5

私服 私服目的&#xff0c;方便公司内计算机通信&#xff0c;方便程序员们开发时资源互换&#xff1b; 1.Nexus私服 https://help.sonatype.com/repomanager3/download 可下载&#xff0c;第一个是服务器&#xff0c;第二个是工作空间 bin中有可执行命令 nexus.exe/run端口…

比亚迪唐EV和唐DM-p荣耀版上市,成为新能源汽车市场中的佼佼者!

随着环保理念的深入人心&#xff0c;新能源汽车市场正迎来前所未有的发展机遇。在这个变革的浪潮中&#xff0c;唐EV和唐DM-p荣耀版的上市无疑为市场注入了新的活力。它们凭借先进的技术、卓越的性能以及豪华配置&#xff0c;成为了新能源汽车市场中的佼佼者。然而&#xff0c;…

【飞桨AI实战】人体姿态估计:零基础入门,从模型训练到应用开发

前言 本次分享将带领大家从 0 到 1 完成一个人体姿态估计任务&#xff0c;覆盖数据准备、模型训练、推理部署和应用开发的全流程&#xff0c;项目将采用以PaddlePaddle为核心的飞桨深度学习框架进行开发&#xff0c;并总结开发过程中踩过的一些坑&#xff0c;希望能为有类似项…

增加PyQt5界面的交通流量预测(模型为CNN_GRU,CNN_BiGRU_ATTENTION,LSTM,Python代码)

1.效果视频&#xff1a;增加PyQt5界面的交通流量预测&#xff08;模型为CNN_GRU&#xff0c;CNN_BiGRU_ATTENTION&#xff0c;LSTM&#xff09;_哔哩哔哩_bilibili&#xff09; 2.三个模型和数据集的介绍 交通流量预测(python代码&#xff0c;压缩包中带有数据&#xff0c;CN…

曲线救国|基于函数计算FC3.0部署AI数字绘画stable-diffusion

曲线救国|基于函数计算FC3.0部署AI数字绘画stable-diffusion 基于函数计算FC2.0部署AI数字绘画stable-diffusion基于函数计算FC3.0部署AI数字绘画stable-diffusion总结 在经过了上一次曲线救国失败经历之后&#xff0c;失败经历参考博文&#xff1a;https://developer.aliyun.c…

docker-003镜像制作

步骤 使用docker commit提交容器使之成为镜像以ubuntu安装vim后的容器为例 1 ubuntu安装vim 启动容器 docker run -it --nameubuntu-vim ubuntu /bin/bash安装vim apt-get update apt-get install vim2 提交容器作为镜像 查看容器 docker ps -a提交容器作为镜像 命令格式&…