java 面试宝典

1.类的加载过程:

一个类的加载到执行总共两个过程,编译和执行,编译就是把我们的java文件编译成.class字节码文件,执行就是把.class加载进内存并解析生成对象的过程
主要阶段有5个 加载 连接 初始化 使用 销毁 加载 通过类加载器(双亲委派机制)将class文件加载到内存中去 连接 分为 验证
准备 解析三个阶段
验证就是验证字节码是否符合文件规范(文件格式,元数据,字节码,符号引用),准备阶段主要是为变量分配内存并赋初值,解析就是将常量池的符号引用替换为直接引用
初始化 执行类构造器的一个过程就是对静态变量的一个初始化, 使用 销毁

2.双亲委派机制

几个名词 启动类加载器 扩展类加载器 应用程序加载器 自定义加载器 自下而上的一种加载方式 检测是否加载过
没加载过往上一层,加载过就无需再加载了,直到启动类加载器,如果都没有就会抛出异常(classnotfoundException)从上往下去加载

3.jvm 虚拟机原理

整个过程首先将java文件编译为class字节码文件,然后通过类加载机制将class文件放到运行时数据区中,运行时数据区分为
堆栈方法区,程序计数器,本地方法区, 堆 java虚拟机内存最大的一块,被所有线程共享,主要存放对象实例 栈
线程私有,每个方法都会创建一个栈帧(局部变量,操作数栈,动态链接,方法出口)请求的深度大于虚拟机所允许的深度就会抛出stackoverflowerror异常,虚拟机在动态扩展时无法申请到足够的内从就会抛出oom异常
方法区:存储类信息,静态变量,常量,编译后的代码也会抛出oom异常

本地方法栈:以native 就是调用本地c++本地接口 程序计数器:就是记录字节码文件的行号

4.堆的内存分配

伊甸园区,幸存0.1区,老年代,永久代(元空间)

5.垃圾回收的过程

判断对象是否死亡:引用计数法(每有一个对象引用计数器加一,没有引用为0,无法解决循环依赖的问题),可达性分析法(通过gcroot往下搜索,对象没有gc
root 引用说明对象不可用) 通过上述算法找到垃圾之后进行回收, 标记清除 标记整理 老年代对象较多,只有少量回收的对象,适用标记 清除
整理 复制算法 适用于新生代对象较少 分代回收算法

6.hashmap原理

底层是散列表 也就是 数组和链表实现的 在jdk1.8之后加入了红黑树
在存储的时候通过hash函数计算hashcode存储到对应的位置,当hash与下表冲突之后就把相同的hashcode值放到上一个节点之前形成了列表,有几个关键的参数,负载因子0.75,容量16,就是当数组占到12个的时候就会扩容,

1.7与1.8 区别
1.7列表采用头插法,这样在多线程操作的情况下容易出现循环列表,18之后改为了尾插法同时加入和红黑树概念,在8的时候转化为红黑树,在6的时候重新转化为了链表,8和6是为了避免链表和红黑树智检来回切换造成性能的降低
1.7是先扩容后插入,1.8则是先插入后扩容 9次扰动=5次异或+4次移位 2=一次异或+一次移位 头插法 尾插法

为什么是2的n次 计算得时候-1后所有为都是1 所以其实就是hashcode的低位值,与运算高位没有参加运算,增加碰撞几率,右移 散列性更好

7.concurrenthashmap 变化

使用分段锁,就是将一个数组的每一个节点分段
1.7segment 就是一个小的hashmap,继承了reentrantlock ,直接加锁
1.8取消了segments字段,直接采用node节点,使用CAS方法及synchronized关键字

8.mysql优化

类型字段用enum,用join代替子查询,建立索引,水平分表 ,垂直分表

9.b树与b+树的区别

b+树,非叶子节点只保存了索引和指针,叶子节点保存了具体的数据 b树,每个节点都保存了key和value 所以查根节点附近的元素时速度会更快
相同的数量量显然b树比b+树高,这样oi的次数显著就提高了,时间就慢了
B+树只有叶结点存储数据。叶结点连起来正好是所有数据的有序序列,可以用来做全表顺序扫描或者范围查询。B+tree带有顺序访问指针,是红⿊黑树不不具备的。
红黑树是二叉树,这样树很高 oi的次数同时也会增加

10sychronized 关键字与reentrotlock
(1)ReentrantLock 显示获得、释放锁,synchronized 隐式获得释放锁
(2)ReentrantLock 可响应中断、可轮回,synchronized 是不可以响应中断的,为处理
锁的不可用性提供了更高的灵活性
(3)ReentrantLock 是API 级别的,synchronized 是JVM 级别的
(4)ReentrantLock 可以实现公平锁
(5)ReentrantLock 通过Condition 可以绑定多个条件

11.sychronzied锁升级的一个过程

自旋锁:果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不
需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),
等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。
偏向锁:顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只
有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,减少加锁/解

的一些CAS 操作(比如等待队列的一些CAS 操作),这种情况下,就会给线程加一个
偏向锁。如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,
JVM 会
消除它身上的偏向锁,将锁恢复到标准的轻量级锁。
轻量级锁:轻量级锁是由偏向所升级来的,偏向锁运行在一个线程进入同步块的情况
下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁;
重量级锁:我们知道,我们要进入一个同步、线程安全的方法时,是需要先获得这个
方法的锁的,退出这个方法时,则会释放锁。如果获取不到这个锁的话,意味着有别的线
程在
执行这个方法,这时我们就会马上进入阻塞的状态,等待那个持有锁的线程释放锁,
然后再把我们从阻塞的状态唤醒,我们再去获取这个方法的锁。这种获取不到锁就马上进
入阻
塞状态的锁,我们称之为重量级锁。

12 volatile 关键字,他是如何保证可见性,有序性

volatile 可以保证线程可见性且提供了一定的有序性,但是无法保证原子性。在JVM 底
层volatile 是采用“内存屏障”来实现的。
观察加入volatile 关键字和没有加入volatile 关键字时所生成的汇编代码发现,加入
volatile 关键字时,会多出一个lock 前缀指令,
lock 前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3 个功
能:
I. 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面
的指令排到内
存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
II. 它会强制将对缓存的修改操作立即写入主存;
III. 如果是写操作,它会导致其他CPU 中对应的缓存行无效。

13.Atomic原子类及底层原理

简单理解就是多线程操作的情况下先获取一个值,看谁先发起cas操作,判断时候和获取的时候值一样 如果一样就修改值,如果不一样就重新获取再进行修改
jdk1.8之前内就是通过cas机制不断地循环判断,这样在并发量高的情况下很容易造成资源和性能的浪费,所以在1.8之后对atomci原子类进行了优化加入了分段锁的概念

在LongAdder底层实现过程中,有个base值,当并发量提升之后就会分段实施cas操作,把基数分不到多个段中,这样就降低了更新同一个数值时的cas操作,当一段cas操作失败之后就会去其他段进行操作,如果获取总值就把剩下的加起来

14 oom 以及遇到这种情况怎么处理的,是否使用过日志分析工具

OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”,当JVM 因为没有足够的
内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error。 处理过程:首先通过内存映射分析工具如Eclipse
Memory Analyzer 堆dump 出的异常 堆转储进行快照解析确认内存中的对象是否是必要的, 也就是先分清楚是内存泄漏Memory
Leak 还是Memory Overflow 如果是内存泄漏可 通过工具进一步查看泄露的对象到GC Roots 的引用链,
就能找到泄露对象是怎么通过路径与GC Roots 相关联导致垃圾收集器无法回收他们如 果不存在泄露就检查堆参数-Xmx 与-Xms
与机器物理 内存对比是否还可以调大从代码上检测是否是某些对象的生命周期过长持有状态时间 过长尝试减少代码运行期间的内存消耗。

15 Redis 缓存雪崩?击穿?穿透?

缓存雪崩:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数
据库短时间内承受大量请求而崩掉。
缓存击穿:key 对应的数据存在,但在redis 中过期,此时若有大量并发请求过来,这
些请求发现缓存过期一般都会从后端DB 加载数据并回设到缓存,这个时候大并发的请求
可能会瞬间把后端DB 压垮。
缓存穿透:key 对应的数据在数据源并不存在,每次针对此key 的请求从缓存获取不
到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id 获取用户信
息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库

16 怎么保证Redis 缓存和数据库的数据一致性

写的话先删除缓存在更新db 在重新更新缓存,读流程第一步先读缓存,如果缓存没读到,则去读DB,之后再异步将数据刷回缓存
binlog用来刷新缓存是一个很棒的选择

17 spring原理

核心主要有两部分组成IOC(Inversion of control)控制反转和AOP(Aspect oriented programming)面向切面编程
什么是IOC:调用类中的方法不是通过new它的对象来实现而是通过spring配置来创建类对象,而IOC又有两种操作方式(配置文件方式和注解方式)
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

18 动态代理(cglib 与 JDK)

JDK 动态代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。但是,事实上使用中并不是遇到的所有类都会给你实现一个接口。因此,对于没有实现接口的类,就不能使用该机制。而CGLIB则可以实现对类的动态代理。

19 Spring 事务实现方式

1、编码方式
所谓编程式事务指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理。
2、声明式事务管理方式
声明式事务管理又有两种实现方式:基于xml配置文件的方式;另一个实在业务方法上进行@Transaction注解,将事务规则应用到业务逻辑中

20 Spring MVC 运行流程

1.spring mvc将所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责对请求 进行真正的处理工作。
2.DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller.
3.DispatcherServlet请请求提交到目标Controller
4.Controller进行业务逻辑处理后,会返回一个ModelAndView
5.Dispathcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象
6.视图对象负责渲染返回给客户端。

21 Spring 框架中用到了哪些设计模式

代理模式—在AOP和remoting中被用的比较多。
单例模式—在spring配置文件中定义的bean默认为单例模式。
模板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
前端控制器—Spring提供了DispatcherServlet来对请求进行分发。
视图帮助(View Helper )—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里。
依赖注入—贯穿于BeanFactory / ApplicationContext接口的核心理念。
工厂模式—BeanFactory用来创建对象的实例。

22 Spring Bean 的生命周期

简单理解spring的生命周期

  1. 实例化 实例化一个bean,也就是new一个对象
  2. ioc依赖注入 按照spring上下文实例化bean进行配置,ioc注入
  3. setbeanname 如果这个bean实现了beannameaware接口,会调用他的setbeanname方法传递spring中bean的id
  4. beanfactoryAvare实现 如果这个bean实现了beanfactoryaware接口,会调用他的setbeanfactory方法传递工厂本身可以用这个方式获取其他bean
  5. applicationcontentAware 如果这个bean实现了applicationcontentaware接口,会调用他的setbeanname方法传递spring上下文
  6. postporcessbeforeinitiazation 如果这个bean关联了beanpostprocessor就会调用postprocessbeforeintialization,beanpostprocessor经常被用作bean内容的修改,可以用作缓存技术
  7. init-method 如果bean在spring中配置文件中配置了init-method属性会自动调用器配置的初始化方法
  8. postprocessAfterInittalization 如果这个bean关联了postprocessAfter将会调用postprocessAfterInittalization,完成这个工作就可以应用这个bean了
  9. destory 当bean不在需要时,如果这个bean实现了disposablebean这个接口就会其实现的destory方法
    10.destory-method 如果这个bean配置了destory-method属性就会调用器配置的销毁方法

23 spring依赖注入的四种方式

setter,静态工厂,实例工厂,构造器注册

24,淘汰策略

1.volatile-lru:从已经设置过期时间的数据集中,挑选最近最少使用的数据淘汰
2.volatile-ttl:从已经设置过期时间的数据集中,挑选即将要过期的数据淘汰
3.volatile-random:从已经设置过期时间的数据集中,随机挑选数据淘汰
4.allkeys-lru:从所有的数据集中,挑选最近最少使用的数据淘汰
5.allkeys-random:从所有的数据集中,随机挑选数据淘汰
6.no-enviction:禁止淘汰数据

25.redis为什么快

  1. Redis是纯内存数据库,一般都是简单的存取操作,线程占用的时间很多,时间的花费主要集中在IO上,所以读取速度快。
  2. 再说一下IO,Redis使用的是非阻塞IO,IO多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程切换时上下文的切换和竞争。
  3. Redis采用了单线程的模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。
  4. 另外,数据结构也帮了不少忙,Redis全程使用hash结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。
  5. 还有一点,Redis采用自己实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大。

26 redis的数据类型

string list hash set zset geo pub、sub ,haperloglog

27 分布式锁与分布式事务

分布式锁与分布式事务
终于有人把“TCC分布式事务”实现原理讲明白了!

28 mysql 事务

原子性 一致性 隔离性 持久性 读取未提交的数据,读取已提交的数据,可重复读,可串行度
脏读

一个事务访问到了其他事务未提交的数据更改

例:事务A对一个数据进行了修改操作,但事务A未提交,事务B在查询时却查到了修改后的数据。

脏数据:事务B查询到事务A修改后的数据,但是事务A回滚了,此时事务B查询出来的数据就是脏数据。
幻读

在一个事务中进行了两次查询操作,得到的行数不一致

例:一个事务进行两次查询操作,在两次查询之间,另一个事务插入了新数据并提交事务,导致第二次查询多了一条数据。
不可重复读

在一个事务中进行了两次查询操作,得到的数据内容不一致

例:事务A进行两次查询操作,在两次查询之间,事务B对事务A所查询的数据进行了修改操作并提交事务,导致事务A第二次查询出的数据内容与第一次查询不一致。

29 redis 的存储方式

aof 持久化是以日志的方式记录服务器增删改操作,是通过追加的方式实现的,aof持久化有三种策略 每秒 每次 不记录
这样效率会有点影响,恢复数据的时候慢。 rdb 指定时间内将内存中的数据写入磁盘,优势是在数据备份方面是一个非常不错的选择,相对于aof来说
文件小,恢复数据比较快,也可以直接复制文件到其他服务器中使用,缺点是rdb方式是每隔一段时间备份一次,如果备份之前宕机
就会造成数据丢失,而且如果数据量大的时候会造成服务器超时

30 springboot的原理

内嵌了web应用容器,如tomcat和Jetty
1.可以不依赖tomcat等外部容器来独立运行的web项目,springboot的优点是能够以jar包的形式运行。
2.嵌入式的Servlet容器:我们不需要像以前那边先打个war包,然后再运行,在springboot看来这些都是多余的,我们可以选择他内嵌的tomcat、Jetty或者Undertow等容器来直接运行。
3.使pom文件配置更简化:我们只需要在 pom 文件中添加starter-web 依赖即可,无需像以前一样引入很多依赖而造成容易漏掉。
4.springboot不需要任何xml文件配置而能实现所有的spring配置。

Springboot的核心注解@SpringBootApplication,我们点进去可以看到包含了很多注解,其中@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan

@springbootconfiguration核心是@Configuration这个注解就是用来读取spring.factories文件
@EnableAutoConfiguration注解使用的是自动配置机制,是自动导入,通俗的说就是在代码中自动import操作。
@ComponentScan的作用是告诉Spring哪个包下面的类用了这个注解,那么就会被spring自动扫描并且放入bean容器。

springboot原理

31 线程池

spring线程池源码详解

32 springcloud 原理详解

springcloud原理详解

33 什么情况下行锁会上升到表锁

两个会话去执行不同的记录各不相干不会导致阻塞状态

索引失效的情况两个会话操作同一个数据的时候行锁会变成表锁

Where 查询条件中的字段没有索引时,更新操作会锁住全表 Mysql 的行锁是通过索引实现的!

34 mq 通过什么来防止重复读取

幂等性

所以我们消息重复消费其实有点类似这种情况,解决方案有以下几种:
1、对于需要保存到数据库的数据,我们可以设置某条数据的某个值,比如订单号之类的,设置一个唯一索引,这样的话即使重复消费也不会生效数据
2、乐观锁,也就是我们每次插入一条数据或者更新的时候判断某个版本号是不是与预期一样,如果不是,那么就不进行操作
3、使用redis进行存储,保留我们消费过的数据的每个特征,然后每次操作数据的时候先去redis进行判断,如果存在的话,那么这条数据就是重复消费的,然后我们可以丢弃或者做其他处理
1.消息丢失
-消息发送出去,由于网络问题没有抵达服务器。
1.做好容错方法(try-catch),发送消息可能会网络失败,失败后要有重试机制,可记录到数据库,采用定期扫描重发的方式。
2.做好日志记录,每个消息状态是否都被服务器收到都应该有记录。
3.做好定期重发,如果消息没有发送成功,定期去数据库扫描未成功的消息进行重发。
-消息抵达Broker,Broker要将消息写入磁盘(持久化)才算成功。此时Broker尚未持久化完成,宕机。
1.publisher也必须加入确认回调机制,确认成功的消息,修改数据库消息状态。
-自动ACK的状态下。消费者收到消息,但没有来得及回消息然后宕机。
1.一定要开启手动ACK,消费成功才移除,失败或者没有来得及处理就noACK并重新入队。

2.消息重复 -消息消费成功,事物已经提交,ack时,机器宕机。导致没有ack成功,Broker的消息重新由unack变成ready,并发送给其他消费者。 -消息消费失败,由于重试机制,自动又将消息发送出去。
1.消费者的业务消费接口应该设计未幂等性的。比如根据状态标示来进行判断。
2.使用重复表(redis/mysql),发送消息每一个都有业务的唯一标识,处理过就不用处理。
3.rabbitMQ的每一个消息都有redelivered字段,可以获取是否是被重新投递过来的,而不是第一次投递过来的。

3.消息积压 -消费者宕机积压 -消费者消费能力不足积压 -发送者发送流量太大
1.上线更多的消费者,进行正常消费。
2.上线专门的队列消费服务,讲消息先批量取出来,记录数据库,离线慢慢处理

35 对象的四种引用

强引用 只要引用存在,垃圾回收器永远不会回收
软引用 非必须引用,内存溢出之前进行回收,
弱引用 第二次垃圾回收时回收
虚引用 垃圾回收时回收,无法通过引用取到对象值

36 kafka高可用机制

kafka
0.8以后,提供了HA机制,就是replica副本机制。每个partition的数据都会同步到其他机器上,形成自己的多个replica副本。然后所有replica会选举一个leader出来,那么生产和消费都跟这个leader打交道,然后其他replica就是follower。写的时候,leader会负责把数据同步到所有follower上去,读的时候就直接读leader上数据即可。只能读写leader?很简单,要是你可以随意读写每个follower,那么就要care数据一致性的问题,系统复杂度太高,很容易出问题。kafka会均匀的将一个partition的所有replica分布在不同的机器上,这样才可以提高容错性。

kafka的这种机制,就有所谓的高可用性了,因为如果某个broker宕机了,也没事儿,因为那个broker上面的partition在其他机器上都有副本的,那么此时会重新选举一个新的leader出来,大家继续读写那个新的leader即可。这就有所谓的高可用性了。

1)写过程

写数据的时候,生产者就写leader,然后leader将数据落地写本地磁盘,接着其他follower自己主动从leader来pull数据。一旦所有follower同步好数据了,就会发送ack给leader,leader收到所有follower的ack之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为)

2)读过程

消费的时候,只会从leader去读,但是只有当一个消息已经被所有follower都同步成功并返回ack的时候,这个消息才能够被消费者读到。

37 threadlocal

是java提供本地线程的储存机制,有一个内部类是threadlocalMap,但是在thread线程中使用,将数据缓存到线程的内部,key为threadlocal value为缓存值。有get set方法,先获取当前线程,再通过线程获取threadlocalmap,最后根据threadlocal获取缓存值
使用弱引用,所以会出现内存泄漏的情况,当没有指向key的强引用就会被垃圾回收 map中的value还有值无法被回收,这个指跟当前线程的生命周期一样,所以在使用完成之后 使用remove操作 防止内存泄漏
使用场景,事务处理

38 mybatis 的执行过程

在这里插入图片描述
39 从浏览器地址栏输入网址,到网页彻底打开,中间过程详解

一:URL解析 名词解释: URL :(Uniform Resource Locator)统一资源定位符,用于互联网上不同资源的标示。

1:浏览器本地解析URL(一般是做格式化检查以及确认用http还是https协议,默认是http协议)。
2:浏览器本地缓存查找url对应ip。 3:本地host文件查找对应ip。 4:本地路由器dns查看对应ip。

若上面都查不到,则要请求到dns服务器查找ip。 1::服务商dns检查缓存是否存在对应ip。 2:若不存在缓存或者缓存过期
则到根域名服务商上查找对应ip。
3:根域名服务商也是先检查缓存,若无,则检查域名为哪个dns服务商负责解析。然后请求转发。获得域名对应ip。

最后:ip返回到本地服务商的dns,本地服务商dns更新缓存。客户端成功拿到ip。(至于为啥一定要拿到ip,因为我们的tcp/ip协议很任性的只能识别ip)

二:网络传输通信 1:客户端拿到ip地址后,想要进行通信,先要和对方取得联系。看对方在不在,这个通常是由三次握手来实现。

三次握手补充:客户端和服务器一共发送三个包,同步连接双方的序列号和确认号并交换 TCP) 建立tcp连接。与目标服务器建立短连接。
通俗的说就是这样:

客户端:在家么,想去你家看看你。 服务器:在呀,欢迎来玩啊。 客户端:马上到。

2:三次握手完成后,tcp和服务器之间就建立了一个可靠地虚拟通道。浏览器知道了这个消息后。就将http请求消息打包,通过tcp协议发送给了服务端。

3:服务端收到请求报文后,处理报文信息,处理完成后将数据打包返回给客户端啊。
4:通信完成后,通过四次挥手,拆除tcp连接。客户端或服务器均可主动发起挥手动作。 5:最后,客户端拿到返回数据。

三:页面渲染 1:浏览器会将返回的HTML通过深度遍历解析成一个DOM树。 2:将CSS解析成 CSS Rule Tree 。
3:根据DOM树和CSSOM来构造 Rendering Tree。

40 spring循环依赖

[首先在springfamework整个体系当中,他的一个bean是由一个beandefinition来构建的,beandefinition是springbead的一个建模,然后我们要理解循环依赖的话,我们就要说一下bean的生命周期,生命周期的话大概分为以下几个步骤(实例化一个bean
也就是new
一个对象—spring容器启动扫描把beanname变成我们的beandefinition保存到beandefinationMap中然后进行遍历,然后对我们的spring这个beandefinition做一些基本的验证(是否单例,是否抽象,是否懒加载)—然后spring从这个它开始要实例化这个bean之前他回去我们这个容器当中,我们的spring单例池当中获取一遍,看他存不存在,有没有被创建,如果没有被创建,再去看下在我们的二级缓存中有没有被提前暴露,如果都没有我们就回去创建对象
最佳构造方法,创建完成并暴露出来
完成之后会做一些初始化工作,填充属性过程中发现依赖了别的对象,就会走y的生命周期,当时创建过程中会重新走一遍x的流程,重新走x的流程时候就会发现x已经暴露了,这样就会拿到objectfaction产生的x的对象,这样就产生了循环依赖)单例初始化的时候会走生命周期流程,构造注入是不能解决的,不能创建对象

就会实例化一个bean(依赖注入)属性填充—各种avware是否实现去回调
,接着做生命周期初始化的回调,例如postconstrout的,实现了initalizerbean的,用xml实现了自定义bean的--------完成代理–时间发布-----

为什么不直接获取x而是去获取objectfactory呢? 因为直接缓存一个x拿出来的就是一个x
我们很难对它做一个改变,如果是一个objectfactory,spring就可以通过beanpostprocessor这个接口
这个objectfacoty在产生这个x的过程中可以进行扩展和干预]()
循环依赖
动态代理

41 SpringApplication执行流程

SpringApplication的run方法的实现是我们本次旅程的主要线路,该方法的主要流程大体可以归纳如下:

1)
如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
推断并设置main方法的定义类。

2)
SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行伊始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。

3) 创建并配置当前Spring
Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。

4)
遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。

5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。

6)
根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。

7)
ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。

8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。

9)
最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。

10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。

12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。

13)
正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)

去除事件通知点后,整个流程如下:

总结

到此,SpringBoot的核心组件完成了基本的解析,综合来看,大部分都是Spring框架背后的一些概念和实践方式,SpringBoot只是在这些概念和实践上对特定的场景事先进行了固化和升华,而也恰恰是这些固化让我们开发基于Sping框架的应用更加方便高效。

42 netty

netty面试

43 hashmap

hashmap底层原理

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值