Java一轮复习
一、Java基础知识
基础知识
1、Java的特点
- 对象、类库、平台、可靠、多线程
2、Java与c++的区别
- 内存(访问(指针)、管理(GC))
- 继承
- 重载
3、数据类型
- 8大基本数据类型
- 与包装类的区别
- 拆装箱(valueOf、intValue)
- 缓存机制(装箱的时候)
- byte、short、Integer、Long、Character
4、⭐String
- String、StringBuffer、StringBuild的区别
- 是否是线程安全的 synchronized
- 是否是可变的
- 性能的差异
- 为什么string是不可变的 final
- 字符串常量池
- 作用、意义:避免重复创建浪费资源
- intern方法
- 常量折叠
- 常见问题
- new一个字符串创建了几个对象
5、常见知识
- JDK、JRE、JVM
- final(类、方法、基本数据类型、引用类型)
- static 静态的、全局的,属于类
- ==和equals
- equals和hashcode
面向对象
1、特征:封装继承多态
2、面向过程和面向对象
- 面向过程是把解决问题的过程写成函数去执行
- 面向对象是先抽象成对象,再用对象的方法进行解决
3、接口和抽象类
- 相同点
- 都不能被实例化
- 都包含抽象的方法
- 都可以有默认的方法
- 不同
- 意义
- 类之间的关系;行为的约束
- 继承的方式
- 成员变量
- 意义
4、深拷贝和浅拷贝:是否被拷贝引用类型的成员变量
5、类之间的关系 (4):依赖、关联(组合、聚合)、继承、实现
6、泛型:java是伪泛型,用于类型检查,编译的时候会进行类型擦除
- 指定了类型也可以放其他的类型,利用反射拿到add方法
7、比较器:comparable和comparator
- comparable是内部比较,实现接口重写compareTo方法
- comparator是外部比较器,compare是函数式接口(new TreeMap<>((Integer o1, Integer o2)-> o2 - o1 );)
异常(Throwable)
1、错误(编译器不会检查、程序无法处理):OOM
2、异常(程序可以通过catch捕获)
- 受检查的异常 CheckedException:IO相关、classNotFound
- 不受检查的异常(运行时异常) RuntimeException:空指针
反射
1、优缺点
- 灵活
- 安全问题、开销
2、原理:创建类时会创建Class的对象
3、方式
- 对象:.getClass
- 类:.class
- 类路径:Class.forName(“类路径”)
注解(元数据)
1、本质是接口
2、编译期扫描,利用反射
IO和序列化
1、Unix的5中IO
- 同步阻塞、同步非阻塞、IO多路复用、异步IO、信号驱动IO
2、java中的3中IO
- BIO
- ⭐NIO(New IO、Not Block IO):用一个线程监听,对应的事情在启用其他线程去做
- Selector:
- Channel
- Buffer
- AIO
3、IO分类
- 流向:输入输出
- 类型:
- 字符:字符转字节很耗时间,并且不知道对应编码很容易乱码
- 字节
- 角色:节点流、处理流
4、IO对应设计模式
- 适配器模式
- InputStream想用Reader这种可以自由操作字符,需要适配器InputStreamReader转换
- 装饰者模式
- InputStream可以read一个字节,包装成Reader可以read一个字节,包装成BufferReader可以read一个字符串
5、序列化
- 场景:网络传输、持久化 -> 将数据结构(list)或者对象转为二进制流
- 序列化方式
- java.io.Serializable
- serialVersionUID用于版本号控制
- 其他(例如grpc的protobuf等)
- java.io.Serializable
- 阻止某个字段序列化
- Transient:加在属性上
- 静态变量不加也不会序列化
jdk1.8的新特性
1、Lambda表达式
- @FunctionInterface可以使用lambda表达式代替
- 4种函数式接口类型
2、流式计算:Stream
- 存储交给容器和数据库,计算交给流
- 类型:集合、数组、io、静态
3、时间类API
- 新增java.time中LocalTime
- 解决时区问题
- 解决格式化问题
- 日期和时间分离
4、Optional:减少空指针异常
二、容器
Collection
1、List
- ArrayList
- 实现:object数组
- 线程不安全 -> CopyOnWriteArrayList
- ⭐扩容
- 初始是0,第一次到默认值10,每次变为1.5倍(每次扩一半)
- Arrays.CopyOf 本质上是调用System.arraycopy方法
- add前一次到位,ensureCapacity
- Vector
- 实现:object数组
- 线程安全:synchronized
- 与ArrayList的区别
- 线程安全、扩容(vector每次扩一倍)
- LinkedList
- 实现:双向链表
- 线程不安全 -> ConcurrentLinkedQueue
- ⭐与ArrayList的区别
- 数据结构不同
- 存储不同(前后指针、扩容的部分)
- 访问不同(随机访问)
- 新增和删除不同(数组和链表的不同)
2、Set
- HashSet
- 实现:HashMap
- 线程不安全
- TreeSet
- 实现:红黑树
- 线程不安全
3、Queue
- PrioriQueue
- 实现:Object数组实现的小顶堆
- 线程不安全
- Duque(接口)
- 实现:ArrayDuque:Object数组 + 双指针
4、Collections工具类
- 查找:BinarySearch
- 排序:sort
- 加锁:synchronizedList
Map
1、⭐HashMap
- 实现:拉链法(数组+链表)
- 线程不安全 -> ConcurrentHashMap
- ⭐扩容
- 默认为16,当指定大小时扩到原来的2的幂次倍(方便散列)
- hash % length == hash & (length - 1),前提是length是2的幂次方
- 扩容会rehash
- 当链表长度大于8时,先检查数组长度是否大于64,没有就先扩容,再将链表转为红黑树
- 默认为16,当指定大小时扩到原来的2的幂次倍(方便散列)
- ⭐put和get原理
- put,先封装成node,再用hashcode进行散列
- get,利用hashcode找到节点
- ⭐3大参数
- capacity,当前数组长度
- loadFactory,加载因子
- threshold,阈值,超过这个要扩容,等于上面俩的乘积
2、HashTable
- 实现:数组+链表
- 线程安全:线程安全,用synchronized锁整张表
- 扩容:默认值为11,扩容为2n+1
- ⭐与HashMap的区别
- 数据结构(链表大于8不会变红黑树)
- 线程安全/效率
- 存储(能不能存null)
- 扩容
- ⭐与concurrentHashMap比较
- synchronized锁整张表
- CAS+自旋拿头节点,synchronized锁头节点
3、TreeMap
- 实现:红黑树
4、为什么要用容器
- 存储数据结构和数据类型的多样性
三、JVM
内存模型
1、进程
- 堆
- 存储对象实例和数组
- 分类:新生代、老生代
- 方法区
- 存储class文件获取的相关信息,1.8后叫元空间
- 结构
- 类元信息:类的字段、方法、版本
- 运行时常量池:字面量、符号引用
- 字符串常量池(1.8后移动到堆中)
- 静态变量(1.8后移动到堆中)
- JIT即时编译缓存
2、线程
- 程序计数器
- 读取指令,记录代码执行的位置
- 代码的执行和上下文的切换
- VM栈
- 方法的调用(压栈)
- 栈帧
- 局部变量表:局部变量,对象的引用
- 操作数栈:临时变量和中间结果
- 动态链接
- 方法返回地址
- 本地方法栈
- 同vm栈,调用的是本地库中的方法
3、本地内存(堆外内存)
- 元空间(方法区)
- 直接内存:NIO作为缓冲区
垃圾回收机制(GC)
1、垃圾回收算法
-
分类
- 标记-清除:碎片
- 标记-复制
- 标记-整理
-
标记算法
-
三色标记
- 基本算法
- 白色:1、使用可达性算法分析,初始都为白色;2、尚未被垃圾回收器访问;3、垃圾回收器标记后还未白色为不可达
- 灰色:对象被垃圾回收器访问过,但对象上至少还有一个引用没扫描
- 黑色:访问过且所以引用都被访问过(不会指向白色对象)
- 过程
- 初始时,全部对象都是白色的
- GC Roots直接引用的对象变成灰色
- 从灰色集合中获取元素:
- 将本对象直接引用的对象标记为灰色
- 将本对象标记为黑色
- 重复步骤3,直到灰色的对象集合变为空
- 结束后,仍然被标记为白色的对象就是不可达对象,视为垃圾对象
- 缺陷:针对并发标记
- 多标
- 并发执行的时候程序将一个已标记的对象的引用断开(浮动垃圾)
- 漏标
- 并发标记访问该对象将其置为黑色后程序又添加了引用,该对象没有被访问到
- 多标
- 基本算法
-
读写屏障
Object E = D.next; D.next = null; B.next = E;
- 读屏障:第一步,当读取引用对象的时候,一律记录下来
- 写屏障:第二、三步,当黑色指向白色的引用被建立时,在属性赋值的前后加入一些处理,类似于AOP
-
增量更新和原始快照:主要与写屏障结合
- 原始快照(Snapshot At The Beginning,SATB)
- 当灰色对象指向白色对象的引用被断开时,就将这条引用关系记录下来。当扫描结束后,再以这些灰色对象为根,重新扫描一次。
- 增量更新
- 当黑色指向白色的引用被建立时,就将这个新的引用关系记录下来,等扫描结束后,再以这些记录中的黑色对象为根,重新扫描一次。相当于黑色对象一旦建立了指向白色对象的引用,就会变为灰色对象。
- 原始快照(Snapshot At The Beginning,SATB)
-
-
⭐分代收集
- 部分收集
- Minor GC
- 主要算法:标记-复制:新生代,因为大量的对象死亡,存活少
- 出现原因:新生代空间不足
- 过程:
- 先进行空间分配担保,如果老生代没有足够空间,就Major GC有时也叫整堆收集
- 将Eden和Survivor0中存活的对象移动到Survivor1中,然后清空这两个区域,交换1和0的位置
- 每次Minor GC存活对象年龄+1,年龄为15时移到老生代
- Major GC
- 主要算法:标记-清除、标记-整理,大量对象存活,没有额外空间进行复制
- 有时Major GC也指整堆收集
- 混合GC
- Minor GC
- 整堆收集
- 收集java堆和方法区
- 原因:
- 老生代空间不足
- 方法区空间不足
- System.gc
- 分配担保不足
- 部分收集
2、垃圾收集器
-
Serial收集器
- 新生代收集器
- GC算法:新生代标记复制,老生代标记整理
- 特点:单线程
-
ParNew收集器
- 新生代
- Serial+多线程
-
Parallel Scavenge
- 新生代收集器
- GC算法:新生代标记复制,老生代标记整理
- 特点:关注吞吐量(cpu利用率)
- JDK1.8 默认使用的是 Parallel Scavenge + Parallel Old
-
Serial Old
-
Parallel Old
-
⭐CMS(并发标记清除)
- GC算法:标记清除
- 标记方法:写屏障 + 增量更新
- 特点:关注停止时间(用户体验)
- 过程
- 初始标记:记录与root直接相连对象,STW: Stop-The-World
- 并发标记:重新寻找存活对象
- 重新标记:记录上一阶段中落下的存活对象,STW: Stop-The-World
- 并发清除:标记清除
- 优缺点
- 并发收集,停顿时间,用户体验
- cpu资源敏感,无法清除浮动垃圾(并发标记中产生的)
-
⭐G1收集器
-
算法:局部标记复制,整体是标记整理
-
标记方法:写屏障+SATB
-
特点:关注吞吐量
-
分代收集
- minor gc(stw)
- 初始标记
- rset
- 复制
- mix gc
- 初始标记(stw)
- 并发标记
- 最终标记(stw)
- 清除(stw)
-
并行与并发
-
可预测停止时间
-
空间整合
-
-
缺点
- 大内存,适合服务器
-
3、回收
- 类的回收
- 所有实例被回收
- 加载器被回收
- 没有被引用
- 对象的回收
- 可达性分析
- 引用计数法
- 常量的回收
- 没有string对象引用
类的生命周期(5)
1、类的加载:磁盘加载到内存
- 类的加载器
- BootstrapClassLoader:顶层,java自带的类
- ExtendClassLoader:java拓展类
- AppClassLoader:自己和第三方的类
- 类的加载方式
- 双亲委派机制
- 先交给父类加载器去加载
- 好处:避免类重复,避免核心api被篡改
- 双亲委派机制
- 类的加载过程
- 获取类的二进制流
- 将二进制转为方法区数据结构存储
- 在堆中创建Class对象
2、类的连接:读取类
- 验证:格式是否合规
- 准备:静态变量分配内存并付初始值
- 解析:符号引用转成直接引用
3、类的初始化
- 给静态变量赋值
4、类的使用
5、类的卸载
- 没有实例
- 没有加载器
- 没有引用
对象
1、对象的生命周期(3)
(1)对象的创建
- 加载类信息(方法区)
- 在堆中分配空间
- 初始化零值
- 设置对象头
- init方法
(2)对象的使用
(3)对象的死亡
- 引用的分类
- 强引用
- 软引用
- 弱引用
- 虚引用
- 对象是否需要GC的判断
- 可达性分析
- GC Root的对象(vm栈中的引用的对象、本地方法栈的引用的对象、方法区类的静态属性引用的对象)
- 对象回收:不会直接回收,会判断第二次
- 引用计数法
- 可达性分析
2、对象的内存布局
- 对象头
- mark words
- 锁信息
- hashcode
- 分代年龄
- class point
- 数组长度
- mark words
- 实例数据
- 对齐填充:8个字节的整数倍
3、对象的访问
- 句柄:局部变量表的reference 指向堆中的一个表,表里分别指向对象和类
- 指针:局部变量表的reference 中存储的直接就是对象的地址,由对象指向类
jvm调优
1、目的:减少gc频率
2、堆的调优
- 大小:
- 最小-Xms:2G
- 最大-Xmx:2G
- 新生代
- -Xmn:2G
- -XX:NewSize=2G,-XX:MaxNewSize:2G
- -XX:SurvivorRatio=8(Eden:Survivor = 8:2)
- 老生代
- -XX:NewRatio=2(新生代:老年代 = 1:2)
- 永久代
- -XX:MetaspaceSize
2、cms参数
3、g1参数
4、jvm优化
- cpu满排查
- jps
- top -Hp pid
- jstack pid 打印线程
- 内存满排查
- jps
- jinfo pid 查看虚拟机情况
- jstat gc pid 查看gc情况
- jmap -heap pid堆信息
- jmap -histo:live pid
- jmap -F -dump:format=b,file = ./jmap.phrof pid
四、并发编程
线程
1、线程与进程
- 进程是程序的执行的基本单位,线程是最小单位
- 一个进程包含多个线程,线程共享进程中的资源
- 线程也有私有的结构
2、线程与协程
- 轻量级线程,用户态线程
3、多线程
- 为什么要多线程
- 多核优势
- 发展趋势
- 引发的问题
- 内存泄漏 ThreadLocal
- 死锁
- 请求与保持
- 循环等待
- 不剥夺
- 互斥
- 线程不安全
4、线程的生命周期
- 创建
- 继承Thread
- Runnerable
- Callable 有返回值
- 线程池
- 等待
- 等待超时
- 运行/就绪
- 阻塞
- 死亡
5、内存结构
- 程序计数器
- vm栈
- 本地方法栈
6、ThreadLocal
- 场景:每个线程有单独的实例
- 实现:将自己作为键保存在thread中的threadlocalmap中
- 缺点
- 内存泄漏
- map的key(ThreadLocal)为弱引用,因为gc时需要回收threadlocal
- value是强引用,一旦key被回收,value就不可见了,导致内存泄漏,所以要及时remove
- 内存泄漏
锁机制
1、宏观分类
- 乐观锁
- 定义:每次在操作数据的时候都没人改
- 代表:CAS,对比交换
- ABA问题 :一开始为A,被改成B,又改回来A -> 版本号机制
- 悲观锁
- 定义:每次在操作数据的时候都有人改
- 代表:
- ⭐Synchronized锁
- 用法
- 方法:ACC_SYNCHRONIZED
- 同步代码块:monitorenter monitorexit
- 锁升级
- 无锁 01:CAS
- 偏向锁 01:很少的线程进行操作,记录线程的id,下次来直接拿锁不用验证
- 轻量级锁 00:双向绑定(LockRecord、markword)
- 重量级锁 10:Markword指向ObjectMonitor
- 用法
- ⭐Lock
- ReentrantLock(与Syn的区别)
- 基于Api(类),基于JVM(关键字)
- 等待可中断
- 选择性通知(condition)
- 获得锁的状态
- 需要手动释放
- 实现了公平锁
- 实现:AQS
- 抽象的队列同步器
- 通过应该双向队列维护等待进程
- CountDownLatch 、Semaphore、CycleBarry
- ReentrantLock(与Syn的区别)
- ⭐Synchronized锁
2、其他分类
- 可重入锁
- 一旦线程得到锁,就可以再次获得锁,避免死锁,但要释放多次
- 公平锁/非公平锁
- 公平锁,线程要拿锁需要在等待队列中排队,一定时间后超时
- 非公平锁,线程先尝试获得锁,不行再排队
- 共享锁和独占锁
- 独占锁:互斥
- 共享锁:读锁
3、锁的优化
- 锁升级
- 锁粗化
- 检测到一串操作对同一把锁进行操作,就把锁的范围扩大
- 锁消除
- 堆上的某个数据不会逃逸出去被其他线程利用,认为不需要锁,就消除
volatile关键字
1、JMM
- 线程使用变量时,先拷到本地,用完再刷回去
- ⭐并发的特性
- 原子性
- 有序性
- 可见性
2、⭐volatile
- 特性:保证有序性(禁止指令重排)、保证可见性;修饰变量/属性
- 缺点:不保证原子性
并发组件
1、原子类
- 保证原子性
- 原理:CAS+自旋
- 代表
- 基本类型:AtomInteger(Increment)
- 数组:AtomIntegerArray
2、⭐线程池
- 为什么用线程池
- 减低资源消耗
- 提高响应速度
- 方便管理
- 3大方法(Executors):队列长度最大为Integer.MAXVALUE导致OOM
- 单个newSingleThreadExecutor()
- 固定newFixThreadExecutor()
- 可伸缩newCacheThreadExecutor()
- 7大参数:ThreadPoolExecutor
- 核心线程数:最少工作的线程
- 最大线程数:当队列满了最多工作的线程
- 工作队列:当达到核心线程数后将任务加入队列,阻塞队列
- 等待时间:任务在线程池中等待时间
- 时间单位:TimeUtil
- 线程池创建方式:默认
- 拒绝策略
- AbortPolicy 直接抛异常
- CallerRunsPolicy 让提交的那个线程干
- DiscardPolicy 直接丢弃
- DiscardOldestPolicy 把第一个等待的丢了执行这个
并发容器
- ConcurrentHashMap
- CopyOnWriteArrayList
- ConcurrentLinkedQueue
- BlockQueue(用的ReentrantLock,队列满了就会阻塞)
限流算法
1、定义
- 限流是指在系统面临高并发、大流量请求的情况下,限制新的流量对系统的访问,从而保证系统服务的安全性
- 在计算机网络中,限流就是控制网络接口发送或接收请求的速率,它可防止DoS攻击和限制Web爬虫
2、⭐限流算法
- 计数器法
- 定义:最简单的限流算法就是计数限流了,例如系统能1分钟同时处理100个请求,保存一个计数器,处理了一个请求,计数器加一,一个请求处理完毕之后计数器减一
- 场景:指定线程池大小,指定数据库连接池大小,nginx连接数
- 具体方法(原子类、countDownLatch)
- 在一开始的时候,设置一个计数器counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100 并且该请求与第一个请求的间隔时间还在1分钟之内,那么说明请求数过多,拒绝访问
- 如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置counter
- 缺点:临界值问题
- 在0:59时,瞬间发送了100个请求,并且1:00有瞬间发送了100个请求
- 解决:滑动时间窗口
- 将一个时间单位划分的更细,每个小窗口设置一个计数器
- 问题:可以解决临界值问题,但依然无法解决短时间的突发流量(10ms内来了1wQPS)
- 漏桶算法
- 定义:漏桶算法限流的基本原理为:水(对应请求)从进水口进入到漏桶里,漏桶以一定的速度出水(请求放行),当水流入速度过大,桶内的总水量大于桶容量会直接溢出
- 进水口(对应客户端请求)以任意速率流入进水漏桶(流入速率随机)
- 漏桶的容量是固定的,出水(放行)速率也是固定的(流出速率固定)
- 漏桶的容量是不变的,如果处理速率太慢,桶内水量就会超出了桶的容量,则后面流入的水就会溢出,表示请求拒绝
- 场景
- 削峰:有大量流量进入时,会发生溢出,从而限流保护服务可用
- 缓冲:不至于直接请求到服务器,缓冲压力
- 实现:阻塞队列BlockQueue
- 缺点:应对突发流量时,无法灵活应对(突然从100QPS(每秒查询率)变到1wQPS)(太平滑了也不好)
- 定义:漏桶算法限流的基本原理为:水(对应请求)从进水口进入到漏桶里,漏桶以一定的速度出水(请求放行),当水流入速度过大,桶内的总水量大于桶容量会直接溢出
- 令牌桶算法
- 定义:
- 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
- 根据限流大小,设置按照一定的速率往桶里添加令牌;
- 桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
- 请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
- 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流;
- 好处:可以自定义速率,灵活的应对突发情况
- 定义:
- 分布式限流
- GateWay限流
- Nginx限流:漏桶
- Redis+Lua脚本限流
五、Spring框架
Spring基础
1、定义:轻量级Java开发框架
2、⭐特点
- IoC控制反转
- 将对象的创建和管理交给Spring框架来做
- AOP面向切面编程
- 将与业务代码无关但被业务代码共同调用的部分封装起来,降低耦合
- 分类
- Spring AOP
- 实现方式:动态代理
- JDK 动态代理:基于接口的
- Cglib动态代理:基于类/继承的
- 实现方式:动态代理
- AspectJ AOP
- 与SpringAOP的区别
- 增强时机不同,spring是运行时,aspectJ是编译时
- AspectJ性能更好
- 与SpringAOP的区别
- Spring AOP
3、Spring的模块
- Data模块:jdbc、orm
- Web模块
- AOP
- Aspects
- Spring Core:Bean、提供IOC
- Test
4、⭐SpringBean
- 定义:SpringBean就是被Spring管理的对象
- 注解
- @Component
- @Service
- @Repository
- @Controller
- @Configuration + @Bean:作用于方法上返回bean
- 注入
- @Autowired:byType
- @Resource:byName
- @Inject
- 作用域
- Singleton:全局唯一的实例
- prototype:原型,每次请求都会创建新的
- request:每次http请求创建,仅在这个请求内有效
- session:会话中有效
- global-session
- ⭐Bean的生命周期(7)
- 解析
- 通过xml/注解/配置类进行找到需要装配的bean,并将其封装为beanDefinition
- Class中的描述信息太少,String用BeanDefinition来描述一个bean对象,包括class信息、自动绑定模式(byname、bytype)
- 通过xml/注解/配置类进行找到需要装配的bean,并将其封装为beanDefinition
- 注册
- 调用BeanDefinitionRegistry的registry方法进行注册,将bean按照<名字,BeanDefinition>的形式保存在BeanDeifintionMap中
- 获取
- 调用AbstractBeanFactory的doGetBean方法获取bean对象
- 检查三级缓存中有没有这个对象
- 尝试从父容器中获取对象
- 调用AbstractBeanFactory的doGetBean方法获取bean对象
- 实例化(创建):一般说实例化是从这里开始,上面说的是加载过程
- 判断bean的作用域、类是否已经加载
- 执行前置的beanpostprocessor函数
- bean的实例化并放入三级缓存中
- 执行后置函数,postprocessorafterInstantiation
- 依赖注入/属性填充
- 给属性赋值
- ⭐循环依赖问题
- 三级缓存
- SingletonObject
- EarlySingletonObject
- SingletonFactory
- 过程:当A实例化后就放入第三级缓存中,当检查到有依赖B时先查看缓存中是否有对象,有的话就继续进行(如果在3级缓存中就把他放入2级缓存),没有就先执行依赖对象B的实例化过程
- 为什么要3级缓存?
- 原因:当需要产生代理对象的时候,B从三级缓存中拿出A,然后会将A的代理对象A’放入第二级缓存
- 如果去掉第三级,A在实例化的时候就得放入代理对象
- 如果去掉第二级,同样循环依赖A的C对象也会产生一个A的代理对象A’',导致依赖的不是同一个对象
- 原因:当需要产生代理对象的时候,B从三级缓存中拿出A,然后会将A的代理对象A’放入第二级缓存
- 三级缓存
- 初始化
- 是否实现了Aware接口
- 前置函数
- 执行init方法
- 是否实现了initializationBean接口
- 是否有initMethod方法
- 后置函数:AOP,生成代理对象
- 销毁
- DisposalBean的destroy方法
- 是否有自定义的destroy方法
- 解析
5、BeanFactory和ApplicationContext(待进一步了解)
- 相同点:都是spring的IoC容器
- 不同点
- 功能不同:A是B的接口的子类
- 加载Bean的方式不同:懒加载和一次性加载
- 创建方式不同:编程、配置
- 注册方式不同:手动、自动
6、事务(声明式事务:@Transaction)
- 作用范围
- 方法
- 类:所有方法
- 接口:不推荐
- 参数
- 传播级别:A调用了B,B回滚,A回滚吗
- 默认是会
- 也可以设置不回滚
- 隔离级别
- default(按数据库的来)
- ……
- 传播级别:A调用了B,B回滚,A回滚吗
7、设计模式
- 单例:bean
- 工厂:bean的创建
- 代理:aop
8、MyBits
- 半ORM映射工具(Object Relational Mapping)
- 二级缓存
- 主键生成策略
- 自增
- uuid
- redis生成
- 雪花
SpringMVC
1、是Spring中一个重要模块(Web),思想是将业务、数据和视图分离
2、常用注解
- @PathVariable:路径传值
- @RequestParam:接收传参
- @RequestBody:json解析成对象
SpringBoot
1、目的:简化Spring的配置和开发流程
- 创建独立的spring应用程序
- 嵌入式的tomcat
- 简化maven配置
- 自动配置spring
2、⭐SpringBoot的自动装配原理:@SpringBootApplication
- @SpringBootApplication注解可以分解为3个注解
- @ComponentScan,扫描路径下的所有bean并加载
- @SpringBootConfiguration实际上也是@Configure配置类
- @EnableAutoConfiguraion注解中@import了一个@AutoConfigurationImportSelector类,会调用SelectImports方法从Meta-INF下的spring-factories中加载配置类到IoC容器中,然后再加载他们import的类完成自动装配
SpringCloud
1、微服务
-
微服务是分布式架构的一种,将单体架构中高度耦合的代码拆解成多个项目,每个项目独立开发和部署完成一部分业务称为一个微服务。多个微服务组成微服务集群。
-
spring cloud是微服务系统架构的一站式解决方案
2、spring cloud的优缺点
- 优点
- (低耦合)代码耦合低,模块开发独立
- (低成本)减轻了单个模块的开发成本
- (低配置)配置简单,只需要加注释
- (跨平台)支持不同语言不同平台
- (不同库)不同模块可以使用不同的数据库
- 缺点
- 微服务多,治理成本高
- 分布式开发成本高(考虑容错等)
3、主要组成部分(Spring Cloud Netflix)
- API网关
- 根据请求不同定位到不同的微服务,屏蔽真正的地址
- 组件:Zuul、Nacos(对比)、Nginx、Gateway
- 负载均衡
- 将网络流量分到多个服务器上,提高整体的效率
- 组件:Feign、Ribbon
- 注册发现
- 服务将自己的模块信息注册到公共组件让调用者可以找到
- 组件:
- 集中式:Nginx
- CP:一致性、分区容错性
- 选举master会导致不可用
- 进程式:Eureka
- AP:可用性、分区容错性
- 节点间是平等的
- 查到的消息
- 自我保护机制(一段时间(90s)没响应进入,等待恢复退出)
- AP:可用性、分区容错性
- 集中式:Nginx
- 通信方式
- SpringCloud用的是Http(Restful),也可以用RPC的方式
- 容错机制
- 服务的调用过程可能会出问题
- 断路器
- 监听故障,发送一个预期的备用响应,避免长时间等待或者抛出异常引起雪崩
- 状态:开、半开、关闭
- 概念
- 扇出:服务A调用B,B调用C,调用链
- 雪崩:某个服务错误导致等待占用更多的资源最终导致系统崩溃
- 服务降级:关闭一些服务
- 服务熔断:多次调用失败就直接熔断,返回预设响应
- 服务隔离:不同http请求用不同的线程池
- 组件:Hystrix
- 配置中心
- 统一配置
- 组件:Spring cloud Config
- 消息总线
- 配合config实现配置的动态刷新
- 组件:spring cloud Bus —— 消息中间件实现(RabbitMQ、Kafka)
- 消息驱动
- 屏蔽消息中间件的差异
- 组件:springcloud stream
- 链路追踪
- 请求链路
- 组件:spring cloud sleuth
3、spring cloud与dubbo的区别
- 定位不同:SpringCloud定位为微服务架构下的一站式解决方案;Dubbo 关注点主要在于服务的调用和治理
- 生态环境不同:SpringCloud依托于Spring平台,具备更加完善的生态体系;而Dubbo一开始只是做RPC远程调用,生态相对匮乏,现在逐渐丰富起来
- 调用方式:SpringCloud是采用Http协议做远程调用,接口一般是Rest风格,比较灵活;Dubbo使用RPC进行调用。
- 组件差异比较多,例如SpringCloud注册中心一般用Eureka,而Dubbo用的是Zookeeper
4、补充:RPC框架
- rpc与http
- rpc是一种设计,http是一种协议
- rpc一般有传输协议和序列化协议