本人面试积累面试题
- 多线程
- 微服务
- JVM
- KAFKA
- MYSQL
- Redis
- SpringBoot/Spring
1.面向对象的三个特征
封装,继承,多态,有时候也会加上抽象。
2.多态的好处
允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)。主要有以下优点:
可替换性:多态对已存在代码具有可替换性
可扩充性:增加新的子类不影响已经存在的类结构
接口性:多态是超类通过方法签名,向子类提供一个公共接口,由子类来完善或者重写它来实现的。
灵活性
简化性
3.代码中如何实现多态
实现多态主要有以下三种方式:
- 接口实现
- 继承父类重写方法
- 同一类中进行方法重载
4.接口和抽象类的区别
比较 | 抽象类 | 接口 |
---|---|---|
默认方法 | 抽象类可以有默认的方法实现 | java 8之前,接口中不存在方法的实现. |
实现方式 | 子类使用extends关键字来继承抽象类.如果子类不是抽象类,子类需要提供抽象类中所声明方法的实现. | 子类使用implements来实现接口,需要提供接口中所有声明的实现. |
构造器 | 抽象类中可以有构造器 | 接口中不能 |
和正常类区别 | 抽象类不能被实例化 | 接口则是完全不同的类型 |
访问修饰符 | 抽象方法可以有public,protected和default等修饰 | 接口默认是public,不能使用其他修饰符 |
多继承 | 一个子类只能存在一个父类 | 一个子类可以存在多个接口 |
添加新方法 | 想抽象类中添加新方法,可以提供默认的实现,因此可以不修改子类现有的代码 | 如果往接口中添加新方法,则子类中需要实现该方法. |
5.父类的静态方法能否被子类重写
不能。重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法,我们一般称之为隐藏。
6.什么是不可变对象
不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类。
7.静态变量和实例变量的区别?
静态变量存储在方法区,属于类所有。实例变量存储在堆当中,其引用存在当前线程栈。
8.java 创建对象的几种方式
采用new
通过反射
采用clone
通过序列化机制
前2者都需要显式地调用构造方法。造成耦合性最高的恰好是第一种,因此你发现无论什么框架,只要涉及到解耦必先减少new的使用。
9.String s1=”ab”, String s2=”a”+”b”, String s3=”a”, String s4=”b”, s5=s3+s4请问s5==s2返回什么?
返回false。在编译过程中,编译器会将s2直接优化为”ab”,会将其放置在常量池当中,s5则是被创建在堆区,相当于s5=new String(“ab”);
10.java中==和eqauls()的区别,equals()和`hashcode的区别
== 是运算符,用于比较两个变量是否相等,而equals是Object类的方法,用于比较两个对象是否相等。默认Object类的equals方法是比较两个对象的地址,此时和 == 的结果一样。换句话说:基本类型比较用==,比较的是他们的值。默认下,对象用==比较时,比较的是内存地址,如果需要比较对象内容,需要重写equal方法
11.equals()和hashcode()的联系
hashCode()是Object类的一个方法,返回一个哈希值。如果两个对象根据equal()方法比较相等,那么调用这两个对象中任意一个对象的hashCode()方法必须产生相同的哈希值。
如果两个对象根据eqaul()方法比较不相等,那么产生的哈希值不一定相等(碰撞的情况下还是会相等的。)
12.a==b与a.equals(b)有什么区别
如果a 和b 都是对象,则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较。例如,String 类重写 equals() 方法,所以可以用于两个不同对象,但是包含的字母相同的比较。
13.3*0.1==0.3返回值是什么
false,因为有些浮点数不能完全精确的表示出来。
14.a=a+b与a+=b有什么区别吗?
+=操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换。
15.& 和 &&的区别
&是位操作,而&&是逻辑运算符。另外需要记住逻辑运算符具有短路特性,而&不具备短路特性。
16.String、StringBuilder、StringBuffer的区别及使用场景
String一旦定义就不可改变,可空赋值。操作少量数据时使用。
StringBuilder 可改变,线程不安全。操作单线程大量数据时使用。
StringBuffer 可改变,线程安全。操作多线程大量数据时使用
17.ArrayList、Vector和LinkedList的区别及使用场景
ArrayList和Vector都是使用数组方式存储数据,允许按序号索引元素,但是插入数据会涉及到元素移动等内存操作,所以索引快插入慢。
ArrayList懒加载 默认大小10 每次扩容1.5倍 线程不安全 性能较高
Vector 实例化时初始化 默认大小10 每次扩容2倍 线程安全 性能较低 已弃用
LinkedList 使用双向链表方式存储数据,插入只需要记录本项的前后项,索引需要向前或向后进行遍历,所以插入速度较快,线程不安全,频繁在任意位置插入和删除的情况可以使用,如果需要多线程访问,可以使用Connections.synchronizedList()或ConcurrentLinkedQueue
多读少写建议使用CopyOnWriteArrayList
CopyOnWriteArrayList原理是发生修改的时候复制一份
多写少读或读写比较均匀建议使用Connections.synchronizedList
18.List和Map的区别
List是存储单列数据的集合,Map是存储键值对双列数据的集合。
List存储的数据是有顺序且可重复的,Map存储的数据是无顺序,键不可重复,值可重复的。
19.HashMap和HashTable的区别
HashMap是Map接口的实现,非线程安全,允许空键值。
HashTable是Dictionary的子类,线程安全,不允许空键值。几乎被淘汰,建议使用ConcurrentHashMap来替代它。
HashMap使用的是快速失败迭代器,在迭代器创建后,除非通过迭代器自身的remove或者add方法,其他任何方式的修改都会抛出异常。
19.HashMap底层实现原理和扩容机制
JDK1.8以前:数组+单链表的组合,以键值对的方式存储元素。
DK1.8及以后:引入红黑树结构,添加元素时,若链表个数大于8,链表会转换为红黑树,反之小于6时会修剪或还原成链表结构。选择6和8可以有效防止频繁的链表和红黑树转换。
扩容条件:
1.存放新值的时候当前已有元素个数大于阈值。
2.存放新值的时候当前存放数据发生hash碰撞(当前key计算的hash值换算出来的数组下标位置已经存在值)
默认容量是16,负载因子0.75,所以扩容阈值是12。
每次扩容的容量是原有的2倍。
20.sleep()和wait()的区别
sleep()是Thread类的,wait()是Object类的方法
sleep不会释放锁,wait会释放锁。
sleep可在任意地方使用,wait notify notifyAll只能在synchronized块\方法中使用。
sleep必须捕获异常,而wait不需要。
21.抽象类和接口的区别、以及使用场景
1.抽象类中可以有构造方法、静态方法、普通方法、普通成员变量。接口中不能有。
2.抽象类中的抽象方法访问类型可以是public、protected和默认类型,接口中只能是public。
3.抽象类中的静态成员变量访问类型可以任意,接口中只能是public的。
4.一个类只能继承一个类,但是可以实现多个接口。
5.抽象类和子类为“是不是”的关系。主要用于为一些类提供公共实现代码。
6.接口和实现为“有没有”的关系。主要用于代码的扩展性和可维护性。
22.Overload(重载)和Override(重写)的区别
重载是一个类中多态性的一种表现,在一个类中定义了多个同名的方法,他们有不同的参数列表。
重写是父类与子类之间多态的一种表现,子类中定义了与父类有相同名称和参数的方法时,子类对象使用该方法会调用子类中的定义。
23.forward(转发)和redirect(重定向)的区别
forward是服务器请求资源,服务器访问目标URL,把响应内容发给用户,用户不知道数据是从哪来的。
redirect是服务器向客户端发送一个状态码,告知重新请求该URL。
24.什么是序列化
序列化就是一种用来处理对象流的机制,就是将对象的内容进行流化,可以对流化后的对象进行读写操作,也可以将流化后的对象传输于网络之间。
可通过实现java.io.Serializable接口来实现序列化。
25.Mybatis中 #{} 和 ${}的区别
#{}是预编译,可防止SQL注入。
${}是直接拼接在SQL语句中。
26.Spring Boot的核心注解是什么,它是由哪几个注解组成的
核心注解:@SpringBootApplication
包含:
@SpringBootConfiguration 实现配置文件功能
@EnableAutoConfiguration 打开自动配置功能
@CompoentScan 组件扫描功能
27.SpringBoot 怎么读取配置文件
属性上使用@Value注解
类上使用@ConfigurationProperties注解
28.SpringCloud和Dubbo的区别
SpringCloud采用基于HTTP的REST API,Dubbo采用RPC方式。
29.SpringCloud的Hystrix断路器特性
请求熔断:请求服务失败量超过一定比例(默认50%)断路器会切换到开路状态,这时所有请求不会发送到后端服务,断路器在保持开路状态一段时间后(默认5秒),自动切换到半开路状态。这时如果下一次请求成功,断路器切回闭路状态,否则重新切换到开路状态。
服务降级:对于查询操作,可以实现一个fallback方法。当请求服务出现异常时,可以使用fallback方法返回的值。
依赖隔离:通过线程池来实现资源隔离,比如一个服务调用另外两个服务,如果这两个服务在同一线程池,那么如果一个服务卡住,后面的请求又来了,就会导致后面的请求都会卡住等待。
请求缓存:缓存上次请求结果,返回给后续请求。
请求合并:把多个请求合并成一个请求,提升效率。
30.浅拷贝和深拷贝的区别
浅拷贝: 基础数据类型复制值,引用类型复制引用地址,修改一个对象的值,另一个对象也随之改变。
深拷贝: 基础数据类型复制值,引用类型在新的内存空间复制值,新老对象不共享内存,修改一个值,不影响另一个。
深拷贝相对浅拷贝速度慢,开销大。
31.jdk1.8的新特性
①lambda 表达式
②方法引用
③加入了base64的编码器和解码器
④函数式接口
⑤接口允许定义非抽象方法,使用default关键字即可
⑥时间日期类改进
32.cookie和session的区别
存储位置不同:cookie放在客户端电脑,session放在服务器端内存的一个对象
存储容量不同:cookie <=4KB,一个站点最多保存20个cookie,session是没有上限的,但是性能考虑不要放太多,而且要设置session删除机制
存储数据类型不同:cookie只能存储ASCll字符串,session可以存储任何类型的数据
隐私策略不同:cookie放在本地,别人可以解析,进行cookie欺骗,session放在服务器,不存在敏感信息泄露
有效期不同:可以设置cookie的过期时间,session依赖于jsessionID的cookie,默认时间为-1,只需要关闭窗口就会失效
33.什么是跨域?跨域的三要素
跨域指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制协议、域名、端口
注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域
34.tomcat三个默认端口及其作用
8005:这个端口负责监听关闭tomcat的请求。
8009:接受其他服务器的请求
8080:用于监听浏览器发送的请求
35.throw 和 throws 的区别?
throw:抛出一个异常。
throws:声明一个异常。
36.序列化和反序列化
序列化: 把对象转为字节序列的过程,在传递和保存对象时,保证了对象的完整性和可传递性,便于在网络传输和保存在本地文件中。
反序列化: 把字节序列转为对象的过程,通过字节流的状态和信息描述,来重建对象。
序列化特点:
将对象转为字节流存储到硬盘上,当JVM噶了的话,字节流还会在硬盘上等待,等待下一次JVM的启动,把序列化的对象,通过反序列化为原来的对象,减少储存空间和方便网络传输(因为是二进制)。
37.你知道什么是单点登录吗?
单点登录(SSO:Single Sign On): 同一账号在多系统中,只登录一次,就可以访问其他系统。多个系统,统一登录。
列如:在一个公司下,有多个系统,比如淘宝和天猫,你登录上淘宝,就不用再去登录天猫了。
① Cookie: 用cookie为媒介,存放用户凭证。登录上父应用,返回一个加密的cookie,访问子应用的时候,会对cookie解密校验,通过就可以登录。不安全和不能跨域免登。
分布式session实现: 用户第一次登录,会把用户信息记录下来,写入session,再次登录查看session是否含有对应信息。session系统不共享,使用缓存等方式来解决。
③重定向: 父应用提供一个GET方式的登录接口A,用户通过子应用重定向连接的方式访问这个接口,如果用户还没有登录,则返回一个登录页面,用户输入账号密码进行登录,如果用户已经登录了,则生成加密的token,并且重定向到子应用提供的验证token的接口B,通过解密和校验之后,子应用登录当前用户,虽然解决了安全和跨域,但是没前两种简单。
38.如何防止表单提交
①js屏蔽提交按钮。
②给数据库添加唯一约束。
③利用Session防止表单重复提交。会有一个token标记,表单提交的时候拦截器会检查是否一致,不一致就不通过。
④使用AOP切入实现。自定义注解,然后新增切入点,然后每次都记录过期时间,然后做比较。
39.说说你对红黑树的理解
①根节点是黑色。
②节点是黑色或红色。
③叶子节点是黑色。
④红色节点的子节点都是黑色。
⑤从任意节点到其子节点的所有路径都包含相同数目的黑色节点。
红黑树从根到叶子节点的最长路径不会超过最短路径的2倍。保证了红黑树的高效。
40.为什么链表长度大于8,并且表的长度大于64的时候,链表会转换成红黑树
因为链表长度越长,哈希冲突概率就越小,当链表等于8时,哈希冲突就非常低了,是千万分之一,我们的map也不会存那么多数据,如果真要存那么多数据,那就转为红黑树,提高查询和插入的效率。
41.为什么转成红黑树是8呢?而重新转为链表阈值是6呢?
因为如果都是8的话,那么会频繁转换,会浪费资源。
多线程
1.线程是什么?多线程是什么?
线程: 是最小的调度单位,包含在进程中。
多线程: 多个线程并发执行的技术。
2.守护线程和用户线程
守护线程: jvm给的线程。比如:GC守护线程。
用户线程: 用户自己定义的线程。比如:main()线程。
Thread.setDaemon(false)设置为用户线程
Thread.setDaemon(true)设置为守护线程
3.线程的各个状态
新建(New): 新建一个线程。
就绪(Runnable): 抢夺cpu的使用权。
运行(Running): 开始执行任务。
阻塞(Blocked): 让线程等待,等待结束进入就绪队列。
死亡(Dead): 线程正常结束或异常结束。
4.为什么 wait()、notify()、notifyAll()方法定义在 Object 类里面,而不是 Thread 类?
① 锁可以是任何对象,如果在Thread类中,那只能是Thread类的对象才能调用上面的方法了。
② java中进入临界区(同步代码块或同步方法),线程只需要拿到锁就行,而并不关心锁被那个线程持有。
③ 上面方法是java两个线程之间的通信机制,如果不能通过类似synchronized这样的Java关键字来实现这种机制,那么Object类中就是定义它们最好的地方,以此来使任何Java对象都可以拥有实现线程通信机制的能力。
5.start()和run()的区别
start()方法: 是启动线程,调用了之后线程会进入就绪状态,一旦拿到cpu使用权就开始执行run()方法,不能重复调用start(),否则会报异常。
run()方法: 就相当于一个普通的方法而已。直接调用run()方法就还只有一个主线程,还是会顺序执行,也可以重复调用run()方法。
6.实现多线程的方式
①继承Thread类。
②实现Runnable接口
③实现Callable接口
④线程池
7.Runnable和Callable的区别
①Runnable没有返回值,Callable有返回值。
②Runnable只能抛出异常,不能捕获,Callable 能抛出异常,也能捕获。
8.线程池的七大参数
corePoolSize: 核心线程数,创建不能被回收,可以设置被回收。
maximumPoolSize: 最大线程数。
keepAliveTime: 空闲线程存活时间。
unit: 单位。
workQueue: 等待队列。
threadFactory: 线程工程,用于创建线程。
handler: 拒绝策略。
1.事务的隔离级别
答:
1.读已提交-----读取其他事务已经提交的数据
2.读未提交-----读取其他事务还未提交的数据–可能出现脏读
3.可重复读-----同一个事务多次读取同一个数据,尽可能的保证数据的一致性但是可能出现幻读
4.串行读------确保每个事务读取的都是最新的数据,但是他的并发是最低的
2.事务的传播行为有哪些
答:
REQUIRED(有就加入,没有就开):如果当前没有事务,就会新建一个事务,如果当前已经存在一个事务就会加入到这个事务中,这个是默认的事务传播机制
SUPPORTS(有就加入,没有就不开):如果当前存在事务,则会加入事务,如果当前没有事务则是以非事务的方式继续进行
MANDATORY(有就加入,没有就抛出异常):如果当前存在事务,则会加入事务,如果当前没有事务则抛出异常
REQUIRES_NEW(必须开启一个事务,不管有没有都会开一个新的事务):表示创建一个新的事务,如果对当前存在事务,则把当前的事务挂起,也就是说不管外部方法是否开启事务,这里的内部方法都是会开启新的事务,并且开启的事务之间都是相互独立的互不干扰
NOT_SUPPORTED(有就挂起):以非事务进行运行,如果当前存在事务就将当前的事务进行挂起
NEVER(有就抛出异常):以非事务进行运行,如果当前存在事务则会抛出异常
NESTED:嵌套事务,如果调用方法已经有了一个事务,那么当前的方法将会嵌套再该事务中执行,如果当前调用的方法没有事务,那么当前的方法将会开启一个新的事务
3.spring的事务在那些情况下会失效
答:
1.spring事务会在除了使用公共的public的方法上使用的时候会失效
2.如果方法是被fina或者staticl进行修饰的时候也是导致事务的失效的,原因是事务使用的是动态代理进行实现的如果使用了final进行修饰的时候就会导致事务里面的动态代理无法进行重写导致事务的失效
3.如果被try catch捕获或者报错被抛出的时候也是会导致事务的失效的’’
4. 多线程调用的时候也是会导致事务的失效,原因是spring的事务是和数据库进行连接的,同一个事务只能使用同一个数据库的连接,再多线程的场景下拿到的数据库的连接是不一样的所以不支持事务
5.使用了错误的传播行为例如Propagation.NOT_SUPPORTED也是不支持事务的
6.使用的是不支持事务的存储引擎,例如使用的是mysql的MyISAM
7.数据源没有进行配置事务管理器-----但是springboot框架里面使用的是默认开启了事务
8.调用的是本类的方法也是会导致事务的失效,主要是因为spring的事务是通过aop进行实现的,调用的本类的方法实际上是没有走代理的,所以无法进行增强也就是无法使用事务
9.service没有托管给spring也就是不是spring的bean所以也是会导致事务的失效
4.redis的数据类型有哪些
答:
String(字符串)
list(列表)
hash(哈希)
set(集合)
zset(有序集合)
5.redis的淘汰策略和删除策略
答:
删除策略:
定时删除(时间换空间)
创建一个定时器,当key设置了有过期时间,当过期时间到达的手定时器任务就会立即执行对键的删除操作
惰性删除(空间换时间)
数据到达过期时间,不做处理,等待下次访问数据的时候,我们需要判断
1).未过期,返回数据
2)过期了,删除数据,返回不存在
定期删除
就是周期性的轮询redis数据库中的失效性数据,采取随机抽取的策略,你利用过期数据占比的方式控制删除的频率
cpu性能的占用设置有峰值,检测频率可自定义的设置
内存压力不是很大,长期占用的内存的冷数据会被持续清理
周期性抽查存储空间(随机抽查,重点抽查)
淘汰策略:
1.易失数据
1.1.volatile-lru
挑选最近使用最少的数据淘汰
1.2volatile-lfu
挑选最近使用次数最少的数据进行
1.3volatile-ttl
挑选将要过期的数据进行淘汰
1.4volatile-random
任意选择数据进行淘汰
2.检测全库的数据
2.1allkeys-lru:挑选最近最少使用的数据淘汰
2.2allkeLyRs-lfu::挑选最近使用次数最少的数据淘汰
2.3allkeys-random:任意选择数据淘汰,相当于随机
3.放弃数据驱逐
3.1no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发OOM(Out Of Memory)
6.redis的持久化方式
持久化方式 | RDB | AOF |
---|---|---|
占用存储空间 | 小(数据级:压缩) | 大(指令级:重写) |
存储速度 | 慢 | 快 |
恢复速度 | 快 | 慢 |
数据安全性 | 会丢失数据 | 依据策略决定 |
资源消耗 | 高/重量级 | 低/轻量级 |
启动优先级 | 低 | 高 |
答:
RDB持久化
数据采用数据集快照的形式进行部分持久化模式,记录数据库的所有的键值对,在某一个时间点将数据写入的临时文件,持久化结束以后用这个临时文件替换上一次持久化的文件从而达到数据而定恢复但是这个存在数据丢失的现象
AOP持久化
是将所有的命令记录以redis的命令请求格式进行完全的持久化存储保存为aof文件,读的操作不进行记录
7.rocketMQ怎么保证消息的有序性
答:
8.linux常用指令有哪些
答:
cd
pwd---------查看当前路径
vim---------编辑
mkdir-------创建文件
ls/ll--------查看当前的文件有哪些
jps-------查看线程
9.spring/springboot的常用注解有哪些
答:
@Controller/@Service/@Component/@Repository/@SpringBootApplication/@Configuration/@Value/@Order/@Autowired/@Bean/@ComponentScan/@Transactional/@ServletComponentScan
10.mysql的底层使用的是什么数据结构
答:
使用的是B+Tree数据结构,并且再B+Tree的基础上加了优化添加了指针指向相邻的叶子结点的链表,这样就形成了带有顺序的指针的B+tree结构了,主要是为了提高区间访问的效率
11.我现在有一个表怎么查询去重
答:
例如现在有一张表A,里面有字段test1,test2,需要查询字段test1并且进行去重
方法1:如果在有索引的情况下可以使用这个
select distinct test1 from A;
方法二:
select test1 ,count(test1) from A where 1=1 group by test1 ;
12.spring的bean生命周期
答:
bean的生命周期分为五个阶段
1.实例化bean
2.bean赋值
3.初始化bean
4.使用bean
5.销毁bean
13.谈谈对AOP的理解
答:
Aop指的是面向切面编程.首先是实现逻辑
第一步:创建切面类
第二步:在切面类里面定义切入点引用
第三步:书写通知
然后就是里面的通知是分为5种
1.前置通知
2.后置通知
3.异常通知
4.环绕通知
5.当前方法返回结果后通知
优点:
1.提高了代码的可用性
2.提高了代码的维护性高效
3.业务功能的拓展更加的方便
4.合理使用会使代码更加的简洁
缺点:
1.代码的可读性导致增强类的代码可读性不强,容易忽视切面类的存在
2.由于AOP代理,底层是使用到了反射的机制,从某种程度上来说也是增加了GC的工作量.说到代理又得说到2个东西
1.静态代理
2.动态代理
首先静态代理就是自己创建代理类生成源代码再对其进行编译,再程序运行前代理类的.class文件就已经存在了,
至于动态代理就是在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术
这里的动态代理有2中
1.JDK动态代理
2.CGLIB动态代理
14.什么是IOC
答:
首先IOC指的是控制反转,就是我们创建bean资源交给spring来进行管理叫做ioc,然后再使用资源进行注入使用的是有叫做DI(依赖注入)
15.GC的理解以及怎么进行处理
答:
GC指的就是jvm的垃圾回收
GC中有关于四种引用:
1.强引用-------不会被回收
2.软引用------内存足够的情况下是不回收的,之后再不够的时候才会进行回收
3.弱引用------有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象
4.顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收
16.什么是雪花算法
答:
雪花算法实际上是有64位组成第一位是默认的占位符,后面的41位是时间戳在后面的10位空间和机器编号,再后面的就是序列号进行组成,这样的算法是mybatis-plus里面就已经集成了这个算法,在需要这个算法的字段上添加注解属性为@TableId(value=“id” ,type=IdType.ID_WORKER)
17.什么是单点登录
答:
18.谈谈乐观锁和悲观锁的理解
答:
乐观锁:
指的是在操作数据的时候非常乐观,认为别人不会同时修改数据,因此乐观锁是不会上锁的,只是在执行更新的时候判断一下再次期间别人是否修改了数据,如果修改了数据规则,则放弃本次唱操作,否则继续执行操作
悲观锁:
指的是在操作数据的时候非常悲观,认为别人会同时修改数据,因此再操作数据的时候就会直接将数据锁住,知道操作完成才会释放锁,上锁期间其他人不能修改数据
其中synchronized和lock的实现都是悲观锁
乐观锁的实现方式:
1.CAS机制和版本号控制
需要读写的内存位置(V)
进行比较的预期值(A)
拟写入的新值(B)
CAS操作逻辑如下:如果内存位置V的值等于预期的A值,则将该位置更新为新值B,否则不进行任何操作。许多CAS的操作是自旋的:如果操作不成功,会一直重试,直到操作成功为止。
版本控制:
版本号机制的基本思路是在数据中增加一个字段version,表示该数据的版本号,每当数据被修改,版本号加1。当某个线程查询数据时,将该数据的版本号一起查出来;当该线程更新数据时,判断当前版本号与之前读取的版本号是否一致,如果一致才进行操作。需要注意的是,这里使用了版本号作为判断数据变化的标记,实际上可以根据实际情况选用其他能够标记数据版本的字段,如时间戳等
19.mysql数据怎么恢复
答:
20.怎样保证中间件MQ不丢失
答:
21.什么是红黑树,简述
答:
22.简述ThreadLoad
答:
ThreadLoad是java中所提供的的线程本地存储机制,可以使用这个机制将数据缓存再某个线程内部,这个线程可以再任意时刻,任意方法中获取缓存的数据
那么Threadlocal是怎么做到线程之间的一个相互隔离:
Threadlocal在使用set方法的时候会将自己Threadlocal和对应的值给到ThreadlocalMap交给他进行管理Threadlocal作为键value作为值进行存储起来,总的来说就是threadlocal的底层是通过ThreadlocalMap来进行实现的,每个thread对象中都会存在一个ThreadlocalMap,map的key为Threadlocal对象,map的value为需要缓存的值
那么threadlocal在使用的过程中需要注意哪些事项:
1.如果在线程池中使用Threadlocal会造成内存泄露,因为当Threadlocal对象使用完之后,应该要把设置的key,value也就是Entry对象进行回收,但是线程池中的线程不会回收,而线程对象是通过强引用指向ThreadlocalMap,ThreadlocalMap也是通过强引用指向Entry对象,线程不被回收,从而出现内存泄露,解决方案就是在使用Threadlocal对象以后,手动的调用Threadlocal的remove方法,手动的清除Entry对象
应用场景:
当一个共享变量是共享的时候,但是有需要每个线程互相不影响,相互隔离,就可以使用Threadlocal
比如我们再传递当前用户的时候使用Threadlocal设置当前登录人以及获取当前登录人
还有就是再使用一些线程不安全的工具对象的时候,比如SimpleDateFormat日期解析的时候可以把这个放在Threadlocal里面进行解析获取
23.redis的雪崩/击穿
答:
24.创建线程的有几种方式
25.线程池有哪几种状态,每种状态有是代表什么
答:
分为运行状态-------关闭状态/停止状态-------整理状态--------终止状态
首先是running运行状态指的是:线程池创建或者调用的excute()方法后,处于运行状态,能够接收新的任务
关闭状态:当线程池调用shutdown方法的时候就会变为关闭状态,当前的线程池就不能再去接收新的任务,但是可以继续执行已经提交等待的任务队列的任务
停止状态:指的是当前线程池调用shutdownNow()方法的时候,线程池的状态就会变为shop状态,此时线程不在接收新的任务进来并且也不会再去执行其他的再队列中的任务,只会执行当前正在执行的任务
整理状态:中间状态不做任何的处理
终止状态:线程池内部的所有线程都已经全部终止,线程进入终止状态
26.springboot解决跨域的几种方式
跨域异常是只有再前端的时候才会出现跨域异常
现在是前后端分离的,前端和后端使用的是不同的端口,那么就会出现跨域的异常
这里的跨域建议使用nginx请求
27.springboot获取配置的几种方式
1.通过再Bean类里面使用@Value注解来进行获取配置文件里面的值-----设置一个一个的值进行绑定
2.通过再@ConfigurationProperties(prefix=“wsw”),这个时候配置文件里面wsw子项的所有的属性全部加载下来
28.什么是公平锁和非公平锁
微服务
1.微服务有哪些好处
答:
首先微服务的好处就需要拿微服务和单体架构进行比较
先来说说单体架构的有哪些缺点
1.可拓展性受限:比如现在我有一个单体架构的项目里面有一个模块的并发量非常高,这个时候可能会使用到nginx的负载均衡进行处理但是一番这么做,我目前也就可能会将这个项目进行搭建集群的形式进行处理,这样就会导致资源的浪费
2.维护更新成本比较高:因为随着时间的推移可能单体应用程序有可能会出现需求的迭代更新,里面的业务逻辑和代码有可能会变得越来越复杂庞大,难以理解维护和更新了,
3.高风险:比如现在程序里面出现了一个小的错误有可能就对导致整个应用程序的崩溃,所以存在比较高的风险,还有就是如果长时间的不进行更新单体应用也会有可能出现安全的危险
4.技术栈受到限制:单体架构里面的技术栈大体都是差不多的,这就可能会导致再项目里面如果想要使用到新的技术栈的和工具的能力难以实现收到了限制
5.团队协作复杂:单体架构再团队协作开发的过程当中会比较麻烦,比如提交代码这块吧,有可能就会导致代码的提交冲突
那么现在就来说说微服务有哪些
好处:
1.可扩展性:微服务架构允许根据需要独立的扩展需要的单个服务,从而不比扩展这个应用程序,这就提高了可扩展性
2.灵活性和快速开发:微服务运行开发团队独立设计开发和部署服务,这就大大提高了灵活性允许团队更快的退出新的功能和更新新的功能
3.故障隔离和容错性:单个微服务如果出现了故障通常是不会导致整个项目之间崩溃的,提高了应用程序的容错性,这样也就可以对于每个服务更加方便的进行维护
4.技术方便基本不收到限制,比如现在有一个服务是java书写的,但是现在有一个开源而定框架使用的是.net进行书写的我也可以直接拿过来进行使用
5.每个人负责自己对应的服务,并且可以根据对应的人员能力负责对应的服务板块
缺点:
1.基础设施成本增加,因为微服务应用通常需要更多的资源和基础设施资源,例如服务器,容器管理负载均衡等等都会进行增加运营成本
2.开发和维护成本:管理多个微服务的开发,测试,部署和维护需要更多的工程师的资源,这就可能会导致开发和维护的成本的增加
3.分布式系统的复杂性:微服务应用是分布式的系统,涉及多个独立运行的服务,还增加了系统的复杂性质,包括网络通信,故障处理和事务管理等等方面
4.服务治理he发现:管理多个微服务的发现,注册,版本控制和路由需要额外的复杂性,例如使用服务网格和API网关
5.自动化部署的要求:为了有效的部署多个微服务,需要建立自动化部署的流程,这就需要额外的工作和资源
6.版本的控制和回滚:管理不同版本的微服务以及版本之间的兼容性可能会变得复杂,特别是在需要回滚的时候
7.数据的一致性:不同的微服务可能都是用有各自的数据存储的,确保数据一致性和同步可能需要复杂的解决方案,如分布式事务或时间驱动的一致性
8.事务管理:管理过多个微服务的事务变得复杂,确保事务的一致性和隔离性需要额外的努力和技术
9.性能监控:在微服务环境中,跟踪性能问题和故障排查可能也会变得更加的复杂,因为问题可能涉及到多个服务,需要强大的监控和诊断工具才可以
10.故障排查:需要有效的方法来跟踪和诊断多个服务的故障,以便快速的恢复
2.微服务有哪些组件
答:
实际上一般我们只需要回答出来5个就可以了针对SpringCloud来进行回答出来(注册中心/远程调用/网关/熔断器/负载均衡)
1.注册中心
用于服务的注册和发现,管理微服务的地址信息,常见的有Nacos/Eureka等等
2.远程调用
主要是用于不同微服务之间进行通信和协作,常见的实现有:Fegin/OpenFegin/RestTemplate/Dubbo(RPC的形式进行远程调用,效率是最高的)
3.API网关
网关主要是作为微服务架构的入口,统一暴露服务,并且提供路由负载均衡安全认证等等的功能常见的网关有:Zuul/Gateway(常用的)等
4.分布式事务
保证跨多个微服务的一致性和原子性的操作常见的有Seata
5.熔断器
主要是用于防止微服务之间的故障扩散,提高系统的容错能力和可用性,并且也可以针对微服务过载的时候进行限制和降级处理常见的有:Hystrix
6.分布式追踪和监控:
主要是用于跟踪和监控微服务的请求流程和性能指标常见的有:SpringCloud Sleuth +Zipkin/SkyWalking
7.负载均衡:用于在分布式系统中将请求分配给不同的服务器节点,以达到均衡负载、提高系统性能和可伸缩性的目的。以下是一些常见的负载均衡算法:
4.注册中心是用来做什么的
答:注册中心主要是用于维护管理分布式系统中各个服务的地址和元数据组件的.还有就是主要也是为了发现服务和注册服务的
5.有哪些注册中心,并且说明这些注册中心有哪些区别
6.为什么微服务需要配置中心
7.什么是分布式?和微服务有什么区别
8.什么是CAP定理?为什么三者不能同时拥有
9.BASE理论了解吗?
10.什么是分布式事务
11.分布式事务有哪些常见的实现方案
12.有哪些分布式锁的实现方案
13.说下seata的XA和AT原理
JVM
14.类的生命周期
1.JVM的类加载机制
2.jvm内存区域
3.对象创建过程了解吗
4.对象内存分配方式
5.jvm创建对象是如何解多线程内存抢占的
6.对象内存布局和大小计算
7.jvm内存泄露的原因
8.如何判断对象仍然存活
9.垃圾收集算法了解吗
10.三色标记算法了解吗,三色标记优点
11.说一下CMS收集器的垃圾回收过程
12.G1垃圾回收器了解吗
13.为什么还要引入G1
14.你们的项目用的是什么垃圾收集器,为什么使用他
15.对象一定分配再堆中吗
16.了解那些jvm监控和故障处理工具
17.jvm的常见参数配置知道那些
18.有做过jvm调优吗
19.线上服务cpu占用过高怎么排查
20.内存飙高问题怎么排查
21.频繁minor gc怎么办
22.频繁Full GC 怎么办
23.定位以及解决OOM
KAFKA
1.kafka消息丢失有几种情况?如何解决
消息发送端:
acks=0:表示发送端producer不需要等待任何broker确认收到消息的回复,可以继续发送下一条消息,性能最高,但是也是最容易导致消息的丢失情况,大数据统计报表场景,对于性能的要求很高,对于丢失数据并不是特别的敏感的情况下可以使用这种
acks=1:至少要等待leader(主节点)已经成功的将数据写入本地log,但是不需要等待所有的follower(从节点)是否写入成功,就可以就绪发送下一条消息,这种情况下,如果foller(从节点)没有成功备份数据,而此时leader(主节点)又挂掉了,消息就会丢失
acks=-1或者all的时候:这就意味着leader(主节点)需要等待所有的备份写入日志,这种策略会保证只要有一个备份存活就不会丢失数据这个也是最强的数据保证,一般除非是金融级别或者是和钱敏感数据打交道的场景才会使用这个配置,但是如果这里所有的备份只有一个的情况下和ack=1是一样的还是会丢失数据
消息消费端:
如果消费这边配置的是自动提交,玩意消费到的数据还没有处理完,就自动提交offset了但是此时你的消费consumer直接进行了宕机,未处理完的数据丢失了下次也是消费不到的了,所以再消费端可以设置手动提交
2.kafka消息重复消费有几种情况怎么解决
答:
消息发送端:
发送消息如果配置了重试机制,比如网络抖动的时间过长导致发送端发送超时时,实际broker可能已经接收到消息,但是发送方会重新的发送消息消息接收端:如果消费这边的配置的是手动提交,刚拉取了一部分数据处理了一部分,但是还没有来得及提交,服务器挂了,下次重启优惠拉取相同的一批数据重复处理
一般消费端都是需要多消费幂等处理的
3.kafka线上消息积压如何解决
答:
1.线上有时因为发送方发送消息速度过快,或者消费方消费消息过慢,就会可能导致broker积压大量的未消费的消息
这种情况如果积压上百万消息需要紧急的进行处理,可以修改消费端程序,让其将接收到的消息快速的转发到其他的topic(可以设置很多个分区),然后再去启动多个消费者同时进行消费新的主题的不同的分区
2.由于消费数据格式变动或者消费者程序有bug,导致消费者一直消费不成功,也是会导致broker积压大量的未消费的消息,
这个情况该可以将这些消费不成功的消息转发到其他的队列里面去步入类似的死信队列中,后面再慢慢的分析私信队列里面的消息进行处理问题
MYSQL
1.事物的四大特性和隔离级别
原子性:不可分割的操作单元,要么全部成功,要么回滚。
一致性:如果执行事物之前数据库是一致的,那么执行后还是一致的。
隔离性:事物操作之间彼此独立和透明,互不影响。
持久性:事物一旦提交,其结果就是永久的。
未提交读:允许脏读,其他事物只要修改了数据,即使未提交,本事物也能看到修改后的数据值。
提交读:只能读取到已提交的数据。
可重复读(innoDB默认):无论其他事物是否修改并提交了数据,这个事物中的数据不受影响。
串行读:完全串行化的读,每次读都要获得锁,读写相互都会阻塞。
1.MySQL在哪些情况下不使用索引
like查询使用%开头不能使用索引,但用%结尾的可以使用索引。
where语句中使用<>或!=。
where语句中使用or,且没有把or中的所有字段加上索引。
where语句中对字段表达式操作。
where语句中使用NOT IN。使用简单的IN会使用索引。
1.MySQL分库分表策略
垂直切分:某个表字段过多,可以将不常用或字段长度较大的字段拆分出去到扩展表中。
水平切分:分为库内分表和分库分表,是根据表内数据的逻辑关系,按照不同的条件分散到多个数据库或表中。
1.为什么CgLib可以代理任何类,但还是需要JDK的动态代理?CgLib和JDK动态代理的区别。
这就不得不说到CgLib的特点:创建速度慢但执行速度快,而JDK的动态代理与其刚好相反:创建速度快但执行速度慢。
如果在程序运行时不断地使用CgLib去创建代理的话,系统运行的性能会大打折扣,所以建议一般在系统初始化时采用CgLib来创建代理,并放入Spring的ApplicationContext中。
1.创建索引有哪些注意事项
2.索引在那些情况会失效
3.事务的各个隔离级别是如何实现的
4.mysql的主从复制有了解吗
5.mysql主从同步延迟怎么处理
6.水平分表有哪几种路由方式
7.分库分表后如何实现不停机扩容
8.分库分表会带来什么问题
9.mysql数据库cpu飙升怎么解决
10.启动线程为什么是运行star()方法而不是run()方法
11.守护线程了解吗
12.线程之间有那些通信方式
13.ThreadLocal是什么
14.ThreadLocal有那些应用场景
15.现在有那些流行的微服务解决方案
16.说下微服务有哪些组件
17.说下HTTP和RPC的区别
18.说说有哪些负载均衡算法
Redis
1.redis到底是单线程还是多线程
单线程,内部是多线程的计算方式
2.redis单线程为什么还能这么快
答:
1.命令执行是基于内存操作的
2.命令执行是单线程操作,没有线程切换的开销
3.基于IO多路复用机制提升redis的I/O利用率
4.高效的数据存储结构:全局hash表以及多钟高效的数据结构,比如:跳表,压缩列表,链表
3.redis底层数据是如何用跳表来存储的
答:
将有序链表改造为支持近似折半查找的算法,可以进行快速的插入,删除,查找等操作
4.redis key过期了为什么内存没释放
答:
1.本身我再设置数据的时候是设置了过期时间,但是再修改数据的时候没有设置过期时间,这样就会导致这个数据变为了没有过期时间的数据了
2.使用的redis的删除策略有关
定期删除和惰性删除
惰性删除:redis过期了,不执行删除等到下次访问的时候再去删除
定期删除:redis过期了,不执行删除,redis没有定期的(或者定期轮询)那些数据过期了,再去将这些数据删除
5.redis key没设置过期时间为什么被redis主动删除了
答:
redis设置了过期时间但是也是有可能会导致被主动的删除的
1.redis人为的操作进行删除的
2.redis的淘汰策略有关
1.如果redis里达到了设置内存的最大值,这个时候会执行淘汰删除,
扫描当前库使用频率最少的
扫描当前库使用次数最少的
扫描当前库即将过期的
扫描当前库,随机进行删除
放弃驱逐淘汰删除
全库扫描使用频率最少的
全库扫描使用次数最少的
全库进行随机删除
6.redis淘汰key的算法lru与lfu区别
答:
LRU:删除使用频率最少的或者说是已经很久没有使用的key
IFU:删除使用次数最少的或者说是删除最近一段时间方位次数最少的数据
7.删除key的命令会阻塞redis吗
答:
会造成阻塞的
原因是
1.redis是单线程的所以如果出现大量的key删除,但是处理不过来就有可能会造成redis的阻塞
2.value的值非常的大也是会造成阻塞比如value的值都是占用了几个G了
3.还有就是再再删除一些链表或者集合的时候也是会造成阻塞的,如果这个链表或者集合是非常复杂的情况下
8.redis主从,哨兵,集群架构优缺点比较
答:
维度 | 优点 | 缺点 |
---|---|---|
主从 | ||
哨兵 | ||
集群 |
9.redis集群数据hash分片算法怎么回事
10.redis执行命令竟然有死循环阻塞bug
11.一次线上事故,redis主从切换导致了缓存雪崩
12.redis持久化rdb,aof,混合持久化是怎么回事
13.线上redis持久化策略一般如何设置
14.redis和数据库保证数据一致性
SpringBoot/Spring
102.springBoot如何优化启动速度
103.SpringBoot读取配置的6种方式
104.SpringBoot解决跨域的5种方式
106.单例Bean是单例模式
答:
一般来说单例模式是指再一个jvm,一个类只能被构造出来一个对象,有很多方法来实现单例模式,比如懒汉模式,但是我们通常来说的单例模式是有一个前提条件的就是规定再一个jvm中,name如果是有2个jvm中保证单例吗,那可能就需要分布式锁这些技术了,这里的重点就是再讨论再单例模式的时候需要考虑到他的范围
然而再Spring中的单例Bean也是一种单例模式,只不过范围比较小,范围就是beanName,beanName对应同一个Bean对象,不同的beanName可以对应不同的Bean对象(就算是在一个类里面也是可以的)
比如我现在创建一个对象@Component public class TestBean{}
这是一个TeatBean对象
下面我通过Bean注解来进行定义public TeatBean teatBean1{ return new TeatBean(); } public TeatBean teatBean2{ return new TeatBean(); }
public static void main (String[] args){AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();System.out.println(annotationConfigApplicationContext.getBean("testBean"));System.out.println(annotationConfigApplicationContext.getBean("testBean1"));System.out.println(annotationConfigApplicationContext.getBean("testBean2"));}
107.Bean的实例化和Bean的初始化有什么区别
Spring再创建Bean对象的时候,会先来创建一个java对象,会通过反射来执行类的构造方法从而得到一个java对象,而这个过程就是Bean的实例化
得到java对象以后,会进行依赖注入,依赖注入以后就会进行初始化的操作,而Bean的初始化就是调用前面创建出来的java对象中的特定方法,比如java对象实现了InitializingBean接口,那么初始化的时候就会执行java对象的afterPropertiesSet(),Spring只会执行这个方法,并不关心方法里面做了那些,我们可以再这个方法中去对某个属性进行验证,或者直接给某个属性进行赋值都可以,反而Bean的初始化就是执行afterPropertiesSet()方法,或者执行init-method指定的方法
108.Spring AOP是如何实现的?
Spring Aop是利用动态代理的机制,如果一个Bean实现了接口,那么就会采用jdk动态代理生成该接口的代理对象,如果一个Bean没有实现接口,那么就会采用CGLIB来生成当前类的一个代理对象,代理对象的作用就是代理原本的Bean对象,代理对象在执行某个方法,会在该方法的基础上增加一些切面的逻辑,使得我们可以利用AOP来实现一些诸如登录校验,权限控制,日志记录等统一功能
109.Spring中的事务是如何实现的?
Spring的事务底层使用的是AOP机制进行实现的
首先对于使用了@Transaction注解的Bean,Spring会创建一个代理对象作为Bean
当调用代理对象方法的时候,会判断该方法上是否添加了这个事务注解
如果进行添加了,那么就会利用事务管理器区创建一个数据库的连接
并且进行修改数据库连接的autocommit属性为false,禁止此链接的自动提交,这是实现Spring事务非常关键的一步
然后执行当前方法,方法回执行sql
执行完成方法以后,如果没有出现异常就会直接进行提交事务,如果是出现了异常就会进行事务的回滚操作
Spring事务的给你级别对象的就是数据库事务的隔离级别
Spring的事务传播机制指的就是和数据库进行的连接
Spring的事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那么实际上也就是新开一个和数据库的连接,再此连接上执行对应的sql
110.你是如何理解Spring事务的传播机制的,底层是如何实现的
答:
Spring的事务传播机制大白话解释就是有7种
1.当前有事务就加入,当前没有事务就新建
2.当前存在事务就加入,当前没有事务就报错
3.当前有事务就加入,当前没有事务就按照没有事务的形式执行
4.当前有事务就挂起重新创建一个事务,当前没有事务,重新创建一个事务
5.当前有事务就挂起,当前没有事务就按照没有事务进行执行
6.当前有事务就报错,当前没有事务就按照没有事务的方式进行执行
7.当前有是事务那么就在这个事务里面再嵌套一个事务进行执行,当前没有事务就新建一个事务
实际上Spring的事务就是一个Spring的事务对应的一个数据库的连接,新开一个事务就是新开一个和数据库的连接
111.那些情况下会导致Spring事务失效的,对应而定原因是什么
答:
1.方法内进行自调用:Spring事务是基于Aop的,只要会用代理对象调用某个方法的时候,Spring事务才会进行生效,而在一个方法中调用使用this.xxx()调用方法的时候,this并不是指的代理对象,所以就会导致事务的失效
解决方法就是把调用的方法拆分到另一个Bean中
或者自己注入自己
2.方法是使用的非Public的时候也是会导致事务的失效,因为Spring事务会基于Cglib来进行Aop的,而CGLIB会基于父子类来进行生效的,子类是代理类,父类是被代理类,如果父类中的某一个方法是私有的方法的时候,那么子类就没有办法重写他,也就没有办法额外的增加Spring事务的逻辑
3.方法使用final的时候或者static的时候也是不能进行重写他的,也就是会导致事务的失效
4.方法再执行的时候被trycatch的时候也是会导致事务的失效原因是事务无法进行捕获,也就是导致事务无法进行回滚
5.类没有交给Spring进行管理的时候事务也是会失效的
6.数据库不支持的事务
112.Spring 中的Bean创建的生命周期有哪些
答:
Spring中的Bean的生命周期简单的分为7步
1.推断构造方法–比如再有参和无参的构造方法中会优先使用无参的构造方法,如果没有但是有多个有参的构造方法,再这个情况下如果没有进行声明使用哪个就会报错,例如可以使用@Autoware的注解进行
2.实例化
3.填充属性,依赖注入
4.处理Aware回调
5.初始化前,处理@PostConstruct注解
6.初始化,处理InitilizingBean接口
7.初始化后,进行Aop
113.Spring中Bean是线程安全的吗
答:
Spring的Bean是不是线程安全的主要是看当前的Bean有没有状态,如果是有状态的那么就是线程安全的没有就是线程不安全的
那么什么是有状态什么是无状态了
有状态简单的来说就是具有存储数据的Bean
无状态指的是没有实例变量的对象,不能保存数据是不变的类
还有就是Spring的Bean是不是线程安全的和他的作用域是无关的,Bean的作用域只是代表的是Bean的生命周期对于任何生命周期的Bean都是一个对象,这个对象是不是线程安全的主要还是看这个Bean对象的本身
114.Spring中的启动流程是怎样的
答:
在创建Spring容器的时候也就是Spring启动的时候
首先会进行扫描,扫描到到的所有的BeanDefinition(BeanDefinition指的就是对应Bean的定义)对象,并存再一个Map中
然后筛选出非懒加载的单例BeanDefinition进行创建Bean,对于多例Bean不需要再启动过程中进行创建,对于多例Bean会在每次获取Bean的时候利用BeanDefinition去进行创建
利用BeanDefinition创建Bean也就是Bean的创建生命周期,这期间包括了合并BeanDefinition,推断构造方法,实例化,属性填充,初始化前,初始化,初始化后等操作,其中Aop就是发生再初始化后这一步骤中的
单例Bean创建完成以后,Spring就会发布一个容器启动事件
Spring启动结束
再源码中会更加的复杂,比如源码中会提供一些模板方法,让子类进行时间,比如源码中还会涉及到一些BeanFactoryPostProcessor和BeanPostProcessor的注册,Spring的扫描就是通过BeanFactoryPostProcessor来进行实现的,依赖注入就是通过BeanPostProcessor来实现的
在Spring启动的过程中还会区处理@Import等注解
115.SpringBootApplication注解有什么用?为什么一定要用它?
SpringBootApplication这个注解相当于是三个注解组成的分别是:
@SpringBootConfiguration(他其实就是相当于是@Configuration,表示当前是一个配置类)
@ComponenScan(Spring会进行扫描,扫描路径为当前再解析的这个类所在的包路径)
@EnableAutoConfiguration(这个注解会负责进行自动装配类的导入,也就是将项目中的自动装配类导入到Spring容器中,从而得到解析)
所以再使用SpringBoot的时候我们一般都是会加上@SpringBootApplication这个注解就是SpringBoot就会进行扫描,就会导入自动配置类并且进行解析