springboot初始化

一、 SpringBean

1. Spring Bean

1) Bean定义

Bean是什么,Bean是特殊的对象,交由Spring管理的Java对象,这类对象在创建的时候会根据spring的一些注解,和IOC,属性如果使用@Autowired的话,会自动赋值。Bean和对象的区别是:bean是spring拖管的,里面的属性也是有值的,对象的话直接new 里面的属性是空的,bean是特殊的对象,spring会对单例Bean进行缓存。



2) Bean生命周期

Spring在创建Bean的时候,首先根据ComponetScan注解来扫描具体哪个包下的所有类,如果类上带有Controller,Service,Response,Componet等注解就会创建对应的Bean对象,并且如果是单例的就会缓存到Bean对象池中,如果是多例的话,不缓存,使用的时候直接创建。BeanDefinition信息包括:





Spring创建Bean的过程:获取类的class对象,通过BeanFactory获取类的元信息(有哪些注解,属于哪个class,依赖哪个Bean等),为每个Bean的元信息都创建BeanDefinition一个对象,并且缓存到Map中(BeanFactory就是用来创建BeanDefinition对象的),在此过程中,如果需要对创建的BeanDefinition加工处理,则可以实现Spring提供的接口BeanFactoryPostProcessor,实现类交给Spring拖管,然后可以在这里面添加一些加工处理的代码,用于创建BeanDefinition对象前后执行的方法。

创建好BeanDefinition,遍历Map实例化Bean,注意,如果是多例的对象不需要实例化,填充属性,此时需要为其他Bean创建对象,创建对象使用class方式,利用反射实例化对象,实例化过程中,如果类实现Aware接口(一共9个,每个Aware接口对应不同的功能,如:获取Bean名称,获取BeanFactory,初始化执行的回调函数等等),在不同的时期执行该接口相关的回调函数,此时使用了模板模式,然后初始化对象,在这个过程,如果创建了BeanPostProcessor(post过滤器),则执行这里面的方法,这里面的方法是创建Bean的前置执行的方法,类似于切面,对所有Bean都可以在这里拦截进行加工处理。

如果该类使用了AOP,需要使用代理创建该类的动态代理的子类,如果没有使用AOP,则直接把创建好的对象放到单例池中(Map集合,key是bean的名称,value是创建的Bean)。

先实例化,再初始化,如果不开启AOP就是普通的对象,如果是开启了,那就是生成的代理对象。

3) 特殊接口

1. 通过注解方式扫描Bean

启动的时候,可以通过XML方式和注解方式扫描Bean,如果是注解的方式,扫描Bean的时候需要通过@Componet(“路径”)指定一下扫描路径。

public class StartSpring { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); } }
@Configuration @ComponentScan("com.test") public class AppConfig { }

2. 准备BeanDefinition集合

Bean在创建的时候,spring会根据类上是否有Componet等注解,然后生成beandefinition对象,生成好后放到map中,此时还没有生成正真的对象呢,当把BeanDefinition都准备好后放到Map中,遍历Map获取每个BeanDefinition,调用getBean(“bean名称”)才创建Bean,如果是单例就缓存起来,如果是多例就直接创建不缓存。

BeanDefinition的作用就是获取Class信息,获取类模板信息就可以修改类的信息,所以修改类可以在这个过程做操作。



3. Spring创建代理对象

场景:Spring整合Mybatis的时候,需要为Mapper生成代理对象,在BeanFactory创建BeanDefinition的时候,有一些属性依赖Mapper接口,这时候就需要为Mapper创建代理对象,并且缓存到单例池中,这时候使用这种方式创建代理对象即可。

import org.springframework.beans.factory.FactoryBean; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyFactoryBean implements FactoryBean { @Override public Object getObject() throws Exception { // 创建Object代理对象 需要创建什么代理对象就创建什么代理对象 Proxy.newProxyInstance(MyFactoryBean.class.getClassLoader(), new Class[]{Object.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return proxy; } }); return null; } @Override public Class<?> getObjectType() { return null; } }

4. 通过各种Aware接口设置想要的值

Aware一共有9个,每种都有不同的用途。以下是两种例子:

import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService implements BeanFactoryAware, BeanNameAware { @Autowired private User user; private BeanFactory beanFactory; private String beanName; /** * 获取当前Bean创建的BeanFactory对象 * @param beanFactory * @throws BeansException */ @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } /** * 获取当前Bean在单例池中的名称 * @param beanName */ @Override public void setBeanName(String beanName) { this.beanName = beanName; } }

5. 初始化完需要执行的方法可使用接口

通过InitializingBean可以对Bean初始化完成后需要执行的方法,继承该方法,可以在初始化完成后执行该操作。

import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService2 implements InitializingBean { @Autowired private User user; @Override public void afterPropertiesSet() throws Exception { // TODO 初始化完成后执行的操作 } }

2. 循环依赖

1) 循环依赖过程

构造方法的循环依赖是无法解决的,因为对象在创建的时候要调用构造函数,调用的时候还要去创建依赖的对象,依赖的对象反过来依赖自己,还没有实例化对象,无法提前暴露对象。



Spring创建循环依赖的对象,并为其赋属性值的过程是:

如A依赖B,B依赖A,A在实例化后,在为属性B赋值的时候,B也会像A一样实例化,然后进行属性赋值,当为A赋值的时候回去缓存中查找,如果A没有进行AOP操作的话,A在实例化后就会把它的原始对象(原始对象是属性还没有填充值的对象)放到单例池中,二级缓存中,提前暴露给别的Bean引用,只是此时的A还不完整,就放到二级缓存中。B从一级缓存中获取没有找到A,就从二级缓存中查找,发现找到了提前暴露的A,然后赋值引用,完成属性的赋值,然后返回A,A继续操作后续过程。

但是如果A发生了AOP的话,就不能把原始对象直接放到二级缓存中了,因为AOP最终要生成代理对象,代理对象和原始对象不是一个的话,为B赋值的是原始对象的引用,为A赋值的是代理对象(放在一级缓存单例池中的对象),这样就违背了单例模式了。

此时A在实例化后把它自己放到三级缓存中,把实例对象传进去,生成一个lambad表达式(() -> getEarlyBeanReference(beanName,mbd,bean原始对象)),不直接生成代理对象,后面谁用到谁再生成,然后赋值B属性的时候,创建B的Bean对象,B引用A的时候,会从一级缓存获取发现没有,从二级缓存获取也发现没有,从三级缓存获取,拿到了lambad表达式,然后执行获取A的代理对象,此时A的代理对象其实是提前生成了,B帮忙生成的,生成后会把A的代理对象放到二级缓存中,然后引用指向了B中的Bean的A属性,然后返回A。



创建代理对象具体代码:



A填充B的属性,执行Aware,init操作后(此时还是针对的是A的原始对象,不是代理对象),执行完后当要开始生成代理对象的时候,还是会从一级缓存获取一下,发现没有,然后从二级缓存获取,发现二级缓存中存在A的代理对象(spring中代理对象里面有一个引用,指向了原始对象),然后获取后直接把该对象放到一级缓存中,完成了初始化过程。

这里有一个细节,为了保证前后对象的唯一性,通过名称找到的对象是不是自己的对象,在B中提前生成的A的代理对象后,会把对象放到earlyProxyReferences map中,当A在执行完第五步属性赋值完后开始生成代理对象前,要到这个map中通过beanName名称获取到对应的对象,然后判断是自己的代理对象,就不生成代理对象了,然后再从一级缓存,二级缓存中查找,如果不是自己的对象,则调用wrapIfNecessary方法进行生成代理对象。



2) 三级缓存

一级缓存是最终存放Bean对象的Map集合,二三级缓存是为解决循环依赖而创建的,在循环依赖过程中,一个对象提前暴露的话会存在二级缓存中,如果这个对象在初始化的时候,进行了AOP操作,就需要生成代理对象,会先在三级缓存中存放一个要生成代理对象的lambad表达式,后面谁依赖的了就执行lambad表达式,生成代理对象,但是由于不是自己生成的,生成完后的代理对象需要放到二级缓存中。

二级缓存是为了提前暴露对象而产生的,三级缓存是为了AOP对象需要生成代理对象产生的。如果A没有循环依赖,但是进行了AOP操作,也是会生成一个lambad表达式放到三级缓存中,但是没有其他Bean依赖A,所以三级缓存中的lambad表达式始终都不会执行到,执行不到就不会放到二级缓存中,当A执行到最后的时候,从二级缓存中获取代理对象是获取不到的,所以自己生成。

三级缓存包括:

Ø singletonObjects:一级缓存,用于存放最终的Bean对象,一级缓存对于循环依赖没有什么帮助;

Ø earlySingletonObjects:二级缓存,存放不完整的对象,创建提前暴露的对象放在里面,提前暴露的对象可能还没有进行完全的初始化,属性还没有赋值完成或者是完全;

Ø singletonFactories:三级缓存,用于存放对原始对象的包装的一个lambad表达式,里面存储的是() -> getEarlyBeanReference(beanName,mbd,bean原始对象),还没有进行创建代理对象,只是存一个表达式,value是ObjectFactory;

Ø earlyProxyreferences:其实还包括一个缓存,他用来记录某个原始队形是否进行了AOP了。

3) 为什么需要三级缓存

在构建原始对象的时候,不知道是否会发生循环依赖,如A依赖B,B没有依赖A,二级缓存存的是对象,三级缓存存的是ObjectFactory,如果只利用二级缓存省去三级缓存,那么二级缓存应该存什么,一个Bean在初始化的时候,在执行到BeanPostProcessor的时候才知道自己要不要生成代理对象,存原始对象还是存代理对象,如果没有发生AOP,则需要存原始对象,如果发生了AOP则需要存储代理对象,这样就矛盾了,所以放到三级缓存中一个表达式,在使用的时候再创建是最好的选择,如果没有AOP则可以省去二级缓存和三级缓存其实也是可以的。

二、 SpringBoot

1. Refresh方法

Spring容器创建之后,会调用它的refresh方法,refresh的时候会做很多事情:比如完成配置类的解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造等等。

以web为例,对应的Spring容器为AnnotationConfigEmbeddedWebApplicationContext。它的refresh方法调用了父类AbstractApplicationContext的refresh方法:



1) prepareRefresh方法

表示在真正做refresh操作之前需要准备做的事情:

l 设置Spring容器的启动时间,撤销关闭状态,开启活跃状态;

l 初始化属性源信息(Property);

l 验证环境信息里一些必须存在的属性。

2) prepareBeanFactory方法

从Spring容器获取BeanFactory(Spring Bean容器)并进行相关的设置为后续的使用做准备:

l 设置classloader(用于加载bean),设置表达式解析器(解析bean定义中的一些表达式),添加属性编辑注册器(注册属性编辑器);

l 添加ApplicationContextAwareProcessor这个BeanPostProcessor。取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware这5个接口的自动注入。因为ApplicationContextAwareProcessor把这5个接口的实现工作做了;

l 设置特殊的类型对应的bean。BeanFactory对应刚刚获取的BeanFactory;ResourceLoader、ApplicationEventPublisher、ApplicationContext这3个接口对应的bean都设置为当前的Spring容器;

l 注入一些其它信息的bean,比如environment、systemProperties等。

注意:https://blog.csdn.net/z69183787/article/details/104364846

2. Springboot如何加载一个starter依赖组件并完成初始化

引入maven starter依赖,基本就满足了日常的web接口开发,而不使用springboot时,需要引入spring-web、spring-webmvc、spring-aop等等来支持项目开发。

实际上那些必要的依赖在spring-boot-starter-web中已经被引入了,说白了,starter可以当成是一个maven依赖组,引入这个组名就引入了所有的依赖。

对于其他一些starter,比如要使用redis、jpa等等,就不仅仅是引入依赖了,还需要实现一些初始的配置,比如常使用@Configuration,这个注解就会在springboot启动时去实例化被其修饰的类,前提是springboot配置了@EnableAutoConfiguration,而springboot启动类默认的@SpringBootApplication中默认包含了该注解,所以不用再显示引入,最后需要在starter项目中META-INF/spring.factories中添加:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.xxx.Xxx

这样在springboot启动时,才能正确加载自定义starter的配置。所以说,starter中简单来讲就是引入了一些相关依赖和一些初始化的配置。

为什么加了@Configuration注解还是要配置META-INF/spring.factories呢?因为springboot项目默认只会扫描本项目下的带@Configuration注解的类,如果自定义starter,不在本工程中,是无法加载的,所以要配置META-INF/spring.factories配置文件。

为什么配置了META-INF/spring.factories配置文件就可以加载?这里才是springboot实现starter的关键点,springboot的这种配置加载方式是一种类SPI(Service Provider Interface)的方式,SPI可以在META-INF/services配置接口扩展的实现类,springboot中原理类似,只是名称换成了spring.factories而已。

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

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

相关文章

基于 golang 从零到一实现时间轮算法 (三)

引言 本文参考小徐先生的相关博客整理&#xff0c;项目地址为&#xff1a; https://github.com/xiaoxuxiansheng/timewheel/blob/main/redis_time_wheel.go。主要是完善流程以及记录个人学习笔记。 分布式版实现 本章我们讨论一下&#xff0c;如何基于 redis 实现分布式版本的…

AD教程 (九)导线及NetLabel的添加

AD教程 &#xff08;九&#xff09;导线及NetLabel的添加 添加导线 绘制导线 点击放置,选择线&#xff0c;或者直接CtrlW快速绘制注意要与绘图工具中的线区别开来&#xff0c;导线是具有电气属性的&#xff0c;绘图工具中的线没有电气属性&#xff0c;只是辅助线绘制导线过程…

论文阅读:LOGO-Former: Local-Global Spatio-Temporal Transformer for DFER(ICASSP2023)

文章目录 摘要动机与贡献具体方法整体架构输入嵌入生成LOGO-Former多头局部注意力多头全局注意力 紧凑损失正则化 实验思考总结 本篇论文 LOGO-Former: Local-Global Spatio-Temporal Transformer for Dynamic Facial Expression Recognition发表在ICASSP&#xff08;声学顶会…

帧间快速算法论文阅读

Low complexity inter coding scheme for Versatile Video Coding (VVC) 通过分析相邻CU的编码区域&#xff0c;预测当前CU的编码区域&#xff0c;以终止不必要的分割模式。 &#x1d436;&#x1d448;1、&#x1d436;&#x1d448;2、&#x1d436;&#x1d448;3、&#x…

SpringCloudAlibaba - 项目完整搭建(Nacos + OpenFeign + Getway + Sentinel)

目录 一、SpringCloudAlibaba 项目完整搭建 1.1、初始化项目 1.1.1、创建工程 1.1.2、配置父工程的 pom.xml 1.1.3、创建子模块 1.2、user 微服务 1.2.1、配置 pom.xml 1.2.2、创建 application.yml 配置文件 1.2.3、创建启动类 1.2.4、测试 1.3、product 微服务 1…

【江协科技-用0.96寸OLED播放知名艺人打篮球视频】

Python进行视频图像处理&#xff0c;通过串口发送给stm32&#xff0c;stm32接收数据&#xff0c;刷新OLED进行显示。 步骤&#xff1a; 1.按照接线图连接好硬件 2.把Keil工程的代码下载到STM32中 3.运行Python代码&#xff0c;通过串口把处理后的数据发送给STM32进行显示 …

如何使用 JMeter 进行 HTTPS 请求测试?

本文将介绍如何使用 JMeter 测试 HTTPS 请求&#xff0c;并提供相关的技巧和注意事项。 在进行性能测试时&#xff0c;很多网站都采用了 HTTPS 协议。当我们测试 HTTPS 请求&#xff0c;如果服务端开启了双向认证&#xff0c;则需要客户端发送请求时带上证书。本文介绍如何在 …

cordova Xcode打包ios以及发布流程(ionic3适用)

第一步 1、申请iOS证书 2、导入证书到钥匙串 第二步 1、xcode配置iOS证书 1.1用Xcode打开你的项目&#xff08;我的Xcode版本是新版&#xff09; 修改如下图 回到基本信息设置界面&#xff0c;Bundie 这项填写&#xff0c;最先创建的那个appid&#xff0c;跟创建iOS描述文件时选…

解决方案中word中分页符的使用

在投标方案中要善于使用“分页符”&#xff0c;尽可能少使用分节符号&#xff0c;没有分页符前&#xff0c;你每次修改你的标书或者文件&#xff0c;增加或者修改内容后。你的格式字段前后都是会发生变化&#xff0c;如何稳定的保证结构呢&#xff0c;那就是分页符的使用&#…

RFSoC Debug:Petalinux 不显示 flash选项

这个板子和NI的X410是一样的。 问题 不显示Flash选项 [*] Advanced bootable images storage Settings ---> boot image settings ---> Image storage media (primary flash) --->解决 在Block Design中添加SD卡或者Flash选项&#xff0c;否则就不会显示&#xff1…

HMM与LTP词性标注之命名实体识别与HMM

文章目录 知识图谱介绍NLP应用场景知识图谱&#xff08;Neo4j演示&#xff09;命名实体识别模型架构讲解HMM与CRFHMM五大要素&#xff08;两大状态与三大概率&#xff09;HMM案例分享HMM实体识别应用场景代码实现 知识图谱介绍 NLP应用场景 图谱的本质&#xff0c;就是把自然…

【Hello Algorithm】贪心算法

本篇博客介绍&#xff1a; 简单介绍下贪心算法 贪心算法 介绍贪心算法最小字典序的字符串拼接最多会议数切棍子的最小成本IPO灯塔问题 介绍贪心算法 贪心算法是一种极具有自然智慧的算法 它会使用以一种局部最功利的标准来做出一个当前看来最好的选择 如果说我们根据局部最优…

python图像处理 ——图像锐化

python图像处理 ——图像锐化 前言一、原理二、 空间域锐化滤波1.拉普拉斯算子&#xff08;Laplacian&#xff09;2.罗伯茨算子&#xff08;Roberts&#xff09;3.Sobel算子4.Prewitt算子5.Scharr算子 三、实验对比 前言 由于收集图像数据的器件或传输图像的通道存在一些质量缺…

【C/C++】什么是POD(Plain Old Data)类型

2023年11月6日&#xff0c;周一下午 目录 POD类型的定义标量类型POD类型的特点POD类型的例子整数类型&#xff1a;C 风格的结构体&#xff1a;数组&#xff1a;C 风格的字符串&#xff1a;std::array:使用 memcpy 对 POD 类型进行复制把POD类型存储到文件中&#xff0c;并从文…

idea中配置spring boot单项目多端口启动

参照文章 https://zhuanlan.zhihu.com/p/610767685 项目配置如下 下面为 idea 2023&#xff0c;不同版本的设置有区别&#xff0c;但是没那么大&#xff0c;idea 2023默认使用新布局&#xff0c;切换为经典布局即可。 在项目根目录的.idea/workspace.xml文件里添加如下配置 &l…

紫光同创FPGA编写的8画面分割器演示

适用于板卡型号&#xff1a; 紫光同创PGL50H开发平台&#xff08;盘古50K开发板&#xff09; 图(1) 盘古50K开发板 TOP 层逻辑框 图(2) TOP层逻辑框 video_copy_ux 将输入的一路RGB888信号复制成8份&#xff0c;每份画面内容相同&#xff0c;各路颜色有些差异&#xff1a; 第…

Linux重定向和缓冲区

文章目录 知识回顾&取地址重定向 重定向底层文件描述符分配规则dup2标准输出和标准错误的区别 缓冲区缓冲区总结 知识回顾 我们在之前有了解过输出重定向>, >>,可以让echo命令本来是打印到屏幕上而变成了把这些数据写到文件中&#xff0c;并且可以追加或者覆盖文…

力扣370周赛 -- 第三题(树形DP)

该题的方法&#xff0c;也有点背包的意思&#xff0c;如果一些不懂的朋友&#xff0c;可以从背包的角度去理解该树形DP 问题 题解主要在注释里 //该题是背包问题树形dp问题的结合版&#xff0c;在树上解决背包问题 //背包问题就是选或不选当前物品 //本题求的是最大分数 //先转…

HTML_案例1_注册页面

用纯html页面&#xff0c;不用css画一个注册页面。 最终效果如下&#xff1a; html页面代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>注册页面</title> </head>…

明御安全网关任意文件上传漏洞复现

简介 安恒信息明御安全网关(NGFW) 秉持安全可视、简单有效的理念&#xff0c;以资产为视角的全流程防御的下一代安全防护体系&#xff0c;并融合传统防火墙、入侵防御系统、防病毒网关、上网行为管控、VPN网关、威胁情报等安全模块于一体的智慧化安全网关。 较低版本的系统存…