java面试笔记
2025-08-03 22:01:39 0 举报
AI智能生成
标题:《Java面试核心笔记》 本《Java面试核心笔记》是一份针对即将参加Java相关职位面试的求职者的参考材料。该笔记内容精炼,涵盖了Java基础知识、面向对象的特性、核心API、多线程和并发机制、JVM内存模型与垃圾回收、集合框架、常用设计模式、Spring框架及其它流行框架等面试常问重点。每一个主题都按照重要性排序,附加了面试中频繁出现的陷阱题和经典题目的解析。 这本笔记以简洁易懂的文字描述了复杂的概念,并提供简洁的代码示例以加深理解。同时,每一部分都附有关键性的语句和核心要点的速记,助你快速记忆和复习。编者还贴心地提供了模拟面试常见问题,帮助求职者自信应对面试官的挑战。 本笔记采用PDF格式,以方便读者检索和随时随地的阅读需求,无论是上下班的通勤路上或是休息之余,都是值得推荐的复习资料。这份笔记能帮助你夯实基础、掌握核心概念,为Java面试做好全面准备。
作者其他创作
大纲/内容
并发编程
现代计算机理论模型与工作原理
CPU在寄存器上执行操作的速度远大于在主存上执行的速度。
为了充分利用CPU资源,多个进程就必然要经常进行进程上下文切换。
速度比较:寄存器>CPU缓存>主内存
为了充分利用CPU资源,多个进程就必然要经常进行进程上下文切换。
速度比较:寄存器>CPU缓存>主内存
子主题
多线程环境下存在的问题
缓存一致性问题
在多处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一主内存
。当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致的情况,
为了解决一致性的问题,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作,这类协议有
MESI.
。当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致的情况,
为了解决一致性的问题,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作,这类协议有
MESI.
MESI缓存一致性协议简单说:缓存行(CPU里面的)必须时刻监听所有试图读该主存的操作,把缓存行标记4种状态。
M 修改
数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。
这样其他缓存行监听发现有人修改了,自己就变成无效状态,等这个数据更新到内存中,其他缓存行再更新。
这样其他缓存行监听发现有人修改了,自己就变成无效状态,等这个数据更新到内存中,其他缓存行再更新。
E 独享、互斥 (Exclusive
该Cache line有效,数据和内存中的数据一致,数据只存在于本Cache中。
如果有其他cache读取,就变为S状态。
如果有其他cache读取,就变为S状态。
S 共享 (Shared)
缓存行数据和内存中的数据一致,数据存在于很多Cache中。
I 无效 (Invalid)
该缓存行Cache line无效。
指令重排序问题
什么是线程
现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个Java程序,操作系统就会创建一个Java进程。
现代操作系统调度CPU的最小单元是线程,也叫轻量级进程,在一个进程里可以创建多个线程,
这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。
现代操作系统调度CPU的最小单元是线程,也叫轻量级进程,在一个进程里可以创建多个线程,
这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。
1、用户级线程(User-Level Thread) , 用户安装的软件程序
2、内核线程(Kernel-Level Thread),操作系统的
2、内核线程(Kernel-Level Thread),操作系统的
用户程序运行在用户空间下,用户级线程通过系统提供的交互接口来挂载到内核级线程。
当一个进程从用户空间进入内核空间时,它就不再有自己的进程空间了。这也就是为什么我们经常说线程上下文切换会涉及到用户态到内核态的切换原因所在
Java线程的生命周期
为什么用到并发
并发不等于并行:并发指的是多个任务交替进行,而并行则是指真正意义上的“同时进
行”。实际上,如果系统内只有一个CPU,而使用多线程时,那么真实系统环境下不能并行,
只能通过切换时间片的方式交替进行,而成为并发执行任务。真正的并行也只能出现在拥有多
个CPU的系统中。
行”。实际上,如果系统内只有一个CPU,而使用多线程时,那么真实系统环境下不能并行,
只能通过切换时间片的方式交替进行,而成为并发执行任务。真正的并行也只能出现在拥有多
个CPU的系统中。
并发的优点:
1. 充分利用多核CPU的计算能力;
2. 方便进行业务拆分,提升应用性能;
1. 充分利用多核CPU的计算能力;
2. 方便进行业务拆分,提升应用性能;
并发产生的问题:
高并发场景下,导致频繁的上下文切换
临界区线程安全问题,容易出现死锁的,产生死锁就会造成系统功能不可用
高并发场景下,导致频繁的上下文切换
临界区线程安全问题,容易出现死锁的,产生死锁就会造成系统功能不可用
排查线程死锁
jps 。用来输出JVM中运行的进程状态信息
jstack 进程号。jstack主要用来查看某个Java进程内的线程堆栈信息。
jstack 进程号。jstack主要用来查看某个Java进程内的线程堆栈信息。
CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个
任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这
个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。
任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这
个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。
什么是JMM模型
Java内存模型(Java Memory Model简称JMM)是一种抽象的概念,
JMM描述的是一组规则,通过这组规则控制程序中各个变量在
共享数据区域 和 私有数据区域 的访问方式
JMM描述的是一组规则,通过这组规则控制程序中各个变量在
共享数据区域 和 私有数据区域 的访问方式
Jmm 就是JVM 里面对应上面讲的计算机原理抽象出来的CPU 工作内存和主内存 的控制规则。
Java内存模型内存交互操作
以上关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内
存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完
成
存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完
成
并发编程三大特性
•可见性
•原子性
•有序性
•可见性
•原子性
•有序性
volatile保证可见性与有序性,但是不能保证原子性,
要保证原子性需要借助synchronized、Lock锁机制,同理也能保证有序性与可见性。
因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块。
因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块。
只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码顺序不一致,
此过程叫指令的重排序。
此过程叫指令的重排序。
如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序
指令重排发生在哪个阶段?
执行器编译阶段
cpu运行时
执行器编译阶段
cpu运行时
指令重排序的意义是什么?
JVM能根据处理器特性(CPU多级缓存系统、多核处理器等)
适当的对机器指令进行重排序,使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能。
JVM能根据处理器特性(CPU多级缓存系统、多核处理器等)
适当的对机器指令进行重排序,使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能。
大量使用cas 和volatile 会有什么问题
synchronized
ReentrantLock
AbstractQueuedSynchronizer
ReentrantLock
AbstractQueuedSynchronizer
加锁目的:序列化访问临界资源,即同一时刻只能有一个线程访问临界资源(同步互斥访问)。
不过有一点需要区别的是:当多个线程执行一个方法时,该方法内部的局部变量
并不是临界资源,因为这些局部变量是在每个线程的私有栈中,因此不具有共享
性,不会导致线程安全问题。
并不是临界资源,因为这些局部变量是在每个线程的私有栈中,因此不具有共享
性,不会导致线程安全问题。
锁定义分类:
显示锁, ReentrantLock,实现juc里Lock,实现是
基于AQS实现,需要手动加锁跟解锁
ReentrantLock lock(),unlock();
基于AQS实现,需要手动加锁跟解锁
ReentrantLock lock(),unlock();
隐式锁 ,Synchronized加锁机制
Jvm内置锁,不需要手动加锁与解锁
Jvm会自动加锁跟解锁
Jvm内置锁,不需要手动加锁与解锁
Jvm会自动加锁跟解锁
synchronized原理详解
synchronized内置锁是一种对象锁(锁的是对象而非引用),作用粒度是对 象,可以用来实现对临界资源的同步互斥访问,是可重入的。
加锁的方式:
同步实例方法,锁是当前实例对象
同步类方法,锁是当前类对象
同步代码块,锁是括号里面的对象
同步类方法,锁是当前类对象
同步代码块,锁是括号里面的对象
synchronized关键字被编译成字节码后会被翻译成monitorenter 和
monitorexit 两条指令分别在同步块逻辑代码的起始位置与结束位置
monitorexit 两条指令分别在同步块逻辑代码的起始位置与结束位置
那么有个问题来了,我们知道synchronized加锁加在对象上,对象是如何记录锁
状态的呢?
状态的呢?
锁状态是被记录在每个对象的对象头(Mark Word)中,下面我们一
起认识一下对象的内存布局
起认识一下对象的内存布局
认识对象的内存结构:
对象头:比如 hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间,数组长度(数组对象)等
对象实际数据:即创建对象时,对象中成员变量,方法等
对齐填充:对象的大小必须是8字节的整数倍
对象头:比如 hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间,数组长度(数组对象)等
对象实际数据:即创建对象时,对象中成员变量,方法等
对齐填充:对象的大小必须是8字节的整数倍
1.实例对象内存中存储在哪?
如果实例对象存储在堆区时:
实例对象内存存在堆区,实例的引用存在栈上,实例的元数据class存在方法区或者元空间
实例对象内存存在堆区,实例的引用存在栈上,实例的元数据class存在方法区或者元空间
2. Object实例对象一定是存在堆区的吗?
不一定,如果实例对象没有线程逃逸行为
不一定,如果实例对象没有线程逃逸行为
从jdk 1.7开始已经默认开始逃逸分析,
创建50万个对象, 可能堆内存的对象小于50w,实验的时候把内存调大一些,避免垃圾回收。
逃逸分析还有2点要求:方法返回void, 那些对象只存在于调用这个方法的线程栈中。
创建50万个对象, 可能堆内存的对象小于50w,实验的时候把内存调大一些,避免垃圾回收。
逃逸分析还有2点要求:方法返回void, 那些对象只存在于调用这个方法的线程栈中。
关闭逃逸分析后,JIT没做任何优化,50w对象都在堆区。
然后加上逃逸分析的参数,堆区只有8万的对象。(这时候方法的返回也是void)
//如果方法返回是void 那只存在本线程中,这种情况会优化。
//private static void alloc()
// 如果这个方法把对象返回,JIT就不会优化了,因为有可能被其他线程引用。
// private static TulingStudent alloc()
//private static void alloc()
// 如果这个方法把对象返回,JIT就不会优化了,因为有可能被其他线程引用。
// private static TulingStudent alloc()
jvm的优化
锁的粗化
锁的消除
java锁
锁住同步资源失败 线程是否要阻塞
线程是否要锁住同步资源
锁住:悲观锁
不锁住:乐观锁
锁住同步资源失败 线程是否要阻塞
阻塞
不阻塞:自旋锁,适应性自旋锁
多个线程竞争同步资源的流程细节有没有区别
无锁
不锁住资源,多个线程中只有一个能修改资源成功,其它线程会重试
偏向锁
同一个线程执行同步资源时自动获取资源
轻量级锁
多个线程竞争同步资源时,没有获取资源的线程自旋等待锁释放
重量级锁
多个线程竞争同步资源时,没有获取资源的线程阻塞等待唤醒
多个线程竞争锁时是否要排队
公平锁
排队
非公平锁
先尝试插队,插队失败再排队
一个线程的多个流程能不能获取同一把锁
能:可重入锁
不能:非可重入锁
多个线程能不能共享一把锁
能:共享
不能:排他锁
JVM内置锁优化升级过程
偏向锁:
轻量级锁:
前后2个线程之间的重叠时间很短, 这时候就是轻量级锁+自旋锁 (自适应)
自旋锁:
AbstractQueuedSynchronizer(AQS)
整体抽象概念
ReentrantLock 可重入锁+悲观锁
private ReentrantLock lock = new ReentrantLock(true);
演示基本用法, 可重入锁,公平锁
//默认创建的是独占锁,排它锁;同一时刻读或者写只允许一个线程获取锁
lock.lock();
lock.unlock();
演示基本用法, 可重入锁,公平锁
//默认创建的是独占锁,排它锁;同一时刻读或者写只允许一个线程获取锁
lock.lock();
lock.unlock();
Semaphore可用于流量控制,限制最大的并发访问数
一次只有两个线程执行 acquire(),只有线程进行 release() 方法后
才会有别的线程执行 acquire()
才会有别的线程执行 acquire()
简单应用例子
源码解析
类的初始化
获取锁的方法semaphore.acquire();
acquire()
acquireSharedInterruptibly(int arg)
tryAcquireShared(int acquires)
doAcquireSharedInterruptibly(int arg)
addWaiter(Node mode)
enq(final Node node)
shouldParkAfterFailedAcquire(Node pred, Node node)
parkAndCheckInterrupt
释放锁方法 semaphore.release();
release()
releaseShared(int arg)
tryReleaseShared(int releases)
com.it.edu.aqs.AbstractQueuedSynchronizer#doReleaseShared
unparkSuccessor(Node node)
CountDownLatch
CountDownLatch.countDown()
CountDownLatch.await();
CountDownLatch.await();
CyclicBarrier
cyclicBarrier.await();
删栏可以重复执行,他执行完一轮就会重置初始值,下次又可以执行了。countdownlanch 不能。
BlockingQueue阻塞队列
BlockingQueue 队列的核心内容就下面2个:
ReentrantLock & Condition(只能在独占模式使用)(AQS里实现)
ReentrantLock & Condition(只能在独占模式使用)(AQS里实现)
ArrayBlockingQueue 基于数组,数组不可扩容
生产者消费者的应用
源码分析
ArrayBlockingQueue(int capacity)
put(E e)
lockInterruptibly
notFull.await()
addConditionWaiter
fullyRelease(Node node)
release(savedState)
unparkSuccessor(h)
isOnSyncQueue(node)
checkInterruptWhileWaiting(node)
transferAfterCancelledWait(node)
acquireQueued(node, savedState)
reportInterruptAfterWait(int interruptMode)
take()
lockInterruptibly 获取锁
dequeue()
notFull.signal()
doSignal(first)
transferForSignal(first)
enq(node)
DelayQueue
HashMap
Jdk7-HashMap的扩容产生死锁问题的分析
put(K key, V value)
addEntry(hash, key, value, i)
resize(2 *table.length);
transfer(newTable)
HashMap(int initialCapacity)
HashMap存在数据丢失示例
ConcurrentHashMap 使用分段锁
Jdk8-HashMap的如何解决扩容产生死锁问题
putVal(hash(key), key, value, false, true)
扩容方法:resize()
Executor框架
ThreadPoolExecutor
Future<T> submit(Callable<T> task)
execute(Runnable command)
addWorker方法
Worker类
runWorker(this)
getTask方法
processWorkerExit方法
release(1)
tryRelease
无注释版execute(Runnable command)
addWorker(Runnable firstTask, boolean core)
Worker(Runnable firstTask)
runWorker(Worker w)
getTask()
processWorkerExit(w, completedAbruptly)
高仿版ThreadPoolExecutor
execute(Runnable task)
addWorker(task,true)
new Worker(r)
runWorker(this);
getTask()
processWorkerExit(worker,completedAbruptly);
TuLingExecutorService
TulingThreadPoolExecutor
DefaultPolicyHandler
shutdown()
Fork/Join 框架
并发与并行
任务性质分类
计算密集型
如何充分利用多核CPU,计算很大数组中所有整数的和?
普通线程池计算递归任务存在的问题
大量上下文切换,计算速度反而没有单线程快
只要数组长度大于线程数,就会一直计算不出结果
IO密集型
fork/join的使用
ForkJoinTask
RecursiveTask 并行计算,同步有返回值
ForkJoinPool
执行fork/join任务
fork/join框架原理
ScheduledThreadPoolExecutor
schedule
newScheduledThreadPool构造方法
new DelayedWorkQueue()
offer方法
任务排序 siftUp(i, e)
scheduledAtFixedRate
scheduledWithFixedDelay
线程池任务的提交 源码分析scheduleWithFixedDelay
delayedExecute(t)
Mysql
Mysql索引数据结构
为什么mysql选用B+Tree
1. 二叉树
2.红黑树(平衡二叉树)
3. B-Tree结构
4.B+Tree结构
索引是怎么支撑千万级表的快速查找?
5. hash索引
6. MyISAM存储引擎索引实现
7.InnoDB存储引擎索引实现
为什么InnoDB表必须有主键,并且推荐使用整型的自增主键?
为什么非主键索引结构叶子节点存储的是主键值?
8.索引最左前缀原理
为什么用联合索引?
联合索引的底层存储结构长什么样?
Mysql索引类型与索引方法
一,索引类型
1.1,普通索引
1.2,唯一索引(UNIQUE)
1.3,主键索引(PRIMARY KEY)
1.4,复合索引
1.5,全文索引(FULL TEXT)
聚集索引与非聚集索引
聚集索引
非聚集索引
二,索引方法
Mysql执行计划与索引详解
Explain工具介绍
Explain分析示例
explain中的列
1. id列
2. select_type列
3. table列
4. type列
5-9
10. Extra列
索引最佳实践以及索引失效
1-3最左前缀法则
4-5
6-9
10-11
mysql索引优化实战
Mysql如何选择合适的索引
常见sql深入优化
Order by与Group by优化
1.最左前缀法则
case3-5
case6-8
Using filesort文件排序原理详解(知道即可)
filesort文件排序方式
单路排序:
双路排序(又叫回表排序模式)
总结
分页查询优化
1、根据自增且连续的主键排序的分页查询
2、根据非主键字段排序的分页查询
Join关联查询优化
1嵌套循环连接 Nested-Loop Join(NLJ) 算法
2基于块的嵌套循环连接 Block Nested-Loop Join(BNL)算法
对于关联sql的优化
in和exsits优化
in
exists
count(*)查询优化
深入理解Mysql锁与事务隔离级别
锁分类
从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)
从对数据操作的粒度分,分为表锁和行锁
2.1 表锁
2.2 行锁
间隙锁
事务(Transaction)及其ACID属性
原子性(Atomicity)
一致性(Consistent)
隔离性(Isolation)
持久性(Durable)
并发事务处理带来的问题
更新丢失(Lost Update)
脏读(Dirty Reads)
不可重读(Non-Repeatable Reads)
幻读(Phantom Reads)
行锁与隔离级别案例分析
1、行锁演示
事务隔离级别
读未提交(Read uncommitted)
读已提交(Read committed)
可重复读(Repeatableread)
MVCC机制详解(见有道云笔记)
可串行化
(Serializable)
(Serializable)
锁的优化问题
无索引 行锁会升级为表锁
行锁分析
死锁
优化建议
数据库乐观锁如何实现幂等性
JVM
类加载机制深度解析
1、类加载过程
2、类加载器和双亲委派机制
Java里有如下几种类加载器
自定义类加载器示例
双亲委派机制
AppClassLoader加载类的双亲委派机制源码
为什么要设计双亲委派机制?
打破双亲委派
JVM整体结构及内存模型
JVM内存参数设置
StackOverflowError示例
JVM内存参数大小该如何设置?
栈帧
局部变量表:方法中定义的局部变量以及方法的参数存放在这张表中
操作数栈:以压栈和出栈的方式存储操作数的
动态链接
方法返回地址
内存模型
JVM内存分配机制与垃圾回收算法
1.JVM内存分配与回收
1.1 对象优先在Eden区分配
Minor GC和Full GC 有什么不同呢?
1.2 大对象直接进入老年代
1.3 长期存活的对象将进入老年代
1.4 对象动态年龄判断
1.5 Minor gc后存活的对象Survivor区放不下
1.6 老年代空间分配担保机制
1.7 Eden与Survivor区默认8:1:1
2.如何判断对象可以被回收
可达性分析算法
常见引用类型
finalize()方法最终判定对象是否存活
2.5 如何判断一个类是无用的类
3.垃圾收集算法
3.1 标记-清除算法
3.2 复制算法
3.3 标记-整理算法
3.4 分代收集算法
垃圾收集器
1.1 Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)
1.2 Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))
1.3 ParNew收集器(-XX:+UseParNewGC)
1.4 CMS收集器(-XX:+UseConcMarkSweepGC(old))
1.5 G1收集器(-XX:+UseG1GC)
2. 如何选择垃圾收集器
JVM调优工具详解
jps 查看java进程
Jmap 生成堆转储文件
MAT (咕泡)
OOM内存泄漏
Jstack 查看线程堆栈信息
jstack找出占用cpu最高的线程堆栈信息
Jinfo 实时查看和调整JVM配置参数
Jstat 查看虚拟机性能统计信息
阿里巴巴Arthas详解
GC日志详解
jvisualvm
监控远端Java进程(咕泡)
JVM性能优化指南(咕泡最后一课)
1.发现问题:
2.排查问题:
3. 解决方案
spring
IOC源码解析
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); 为我们做了什么?
this()
看一下父类的构造器
new AnnotatedBeanDefinitionReader(this);
AnnotationConfigUtils.registerAnnotationConfigProcessors
ClassPathBeanDefinitionScanner
this.register(annotatedClasses);
AnnotatedBeanDefinitionReader#register
this.refresh(); //容器刷新
initApplicationEventMulticaster(初始化事件多播器)
AbstractApplicationContext#registerListeners(把事件监听器注册到多播器上去)
AbstractApplicationContext#invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
TulingLog tulingLog = tcx.getBean(TulingLog.class); 容器中的过程是什么
AbstractBeanFactory#getBean
AbstractBeanFactory#doGetBean
transformedBeanName 转换
beanName
beanName
DefaultSingletonBeanRegistry#getSingleton 去缓存
中获取bean
中获取bean
AbstractBeanFactory#getObjectForBeanInstance 对
缓存中的获取的bean进行后续处理
缓存中的获取的bean进行后续处理
AbstractBeanFactory#isPrototypeCurrentlyInCreation
判断原型bean的依赖注入
判断原型bean的依赖注入
AbstractBeanFactory#getParentBeanFactory 检查父
容器加载bean
容器加载bean
AbstractBeanFactory#getMergedLocalBeanDefinition 将
bean定义转为RootBeanDifination
bean定义转为RootBeanDifination
DefaultSingletonBeanRegistry#getSingleton根据
scope 的添加来创建bean
scope 的添加来创建bean
AbstractAutowireCapableBeanFactory#createBean创建
bean的方法
doCreateBean 真
正的创建bean的逻辑
AbstractAutowireCapableBeanFactory#createBeanInstance
调用构造函数创建对象
判断是否需要提早暴露对象(mbd.isSingleton() && this.allowCircularReferences && i
sSingletonCurrentlyInCreation(beanName));
sSingletonCurrentlyInCreation(beanName));
DefaultSingletonBeanRegistry#addSingletonFactory
暴露对象解决循环依赖
AbstractAutowireCapableBeanFactory#populateBean
给创建的bean进行赋值
initializeBean对
bean进行初始化
bean进行初始化
invokeAwareMethods
调用XXAware接口
applyBeanPostProcessorsBeforeInitialization 调用bean的后置处理器进行对处理
invokeInitMethods
对象的初始化方法
对象的初始化方法
.InitializingBean#afterPropertiesSet 调用
InitializingBean的方法
String initMethodName = mbd.getInitMethodName(); 自定义的初始化方法
DefaultSingletonBeanRegistry#addSingleton 把创建好的实
例化好的bean加载缓存中
例化好的bean加载缓存中
AbstractBeanFactory#getObjectForBeanInstance对创建的
bean进行后续的加工
spring 只能解决单例对象的setter 注入的循环依赖,不能解决构造器注入
IOC容器的核心思想见xmind.
Aop源码
配置类上加了@EnableAspectJAutoProxy,分析这个给容器中添加了什么组件?
2.2)所有我们来分析AspectJAutoProxyRegistrar类是用来干什么的?
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)
3)所以我们现在可以分析一下AnnotationAwareAspectJAutoProxyCreator 他是什么一个鬼?
3.1)所以我们首先来分析AnnotationAwareAspectJAutoProxyCreator 实现了BeanFactoryAware接口 做了什么工作?
3.2)还发现了AnnotationAwareAspectJAutoProxyCreator 实现了BeanPostProcessor接口(后置处理器的特性)
②:postProcessAfterInitialization
3.3)还发现了AnnotationAwareAspectJAutoProxyCreator 实现了InstantiationAwareBeanPostProcessor接口(后置处理器的一种,在实例化之前进行调用)
postProcessBeforeInstantiation
findCandidateAdvisors()
5:代理对象调用目标方法
@EnableAspectJAutoProxy(exposeProxy = true) 这个东东是用来干什么的?
简单案例
配置类
这节课总结
Spring 如何自定义注解?
1.定义注解
2.实现注解(AOP面向切面)
3.使用注解
事务管理
Spring事务三大接口介绍
2.1)PlatformTransactionManager: (平台)事务管理器
2.2)TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则
2.3)TransactionStatus: 事务运行状态
@EnableTransactionManagement注解来给我们容器加入了什么组件
通过@Import导入了TransactionManagementConfigurationSelector组件
事务的基本原理
xml文件配置事物
其他面试题
Spring事务不生效的原因
事物的性能和AOP的性能哪个高一些?
事物注解@Transactional 加在 4个地方都可以
java try catch 异常后还会继续执行吗
微服务安全以及认证中心
动手搭建微服务认证中心实现微服务鉴权
1)创建工程名称为tulingvip09-ms-auth-server,加入依赖
主要是添加spring-cloud-starter-oauth2
主要是添加spring-cloud-starter-oauth2
2)添加注解写配置文件
2.1)作为认证服务器,那么就会有认证服务器的配置
①:第三方客户端配置,配置哪些应用可以来访问我们认证服务器。
②:针对用户的配置,也就是说,第三方客户端带入过来的用户名,密码我认证中心怎么去验证他的正确
性?
③:针对 资源服务器 来校验令牌的配置。
2.2)认证服务器的安全配置
问题?userDetailsService怎么来? passwordEncoder怎么来
写一个UserDetailService实现 UserDetailsService接口用于用户登陆认证的。
创建AuthenticationManager
动手搭建微服务(资源服务器)
1)创建工程名称为tulingvip09-ms-alibaba-order,加入依赖
2)写配置(资源服务器的配置)
3)资源服务器的安全配置
生产最佳实践
认证服务器的配置修改
客户端信息
TokenStore
RedisTokenStore改json序列化
ModifyRedisTokenStoreBpp
JsonSerializationStrategy
TulingUserDetailService
在网关层面做认证
网关处理全局异常
一:基于密码模式(Session)的单点登陆
sso-portal-password 密码模式的前端
二:基于授权码模式(Session)的单点登陆
sso-portal-code 验证码模式的前端
sso-auth-code-server 认证中心
三个有效期 作用说明
客户端的session 时间:
认证中心的session 时间:
修改token 的有效期
上面的是基于session, 还有cookie的
3:JWT令牌 (Json token web) 一个有意义的字符串 用来做身份认证
接入jwt的认证服务器的改造sso-auth-code-jwt-server
怎么生成kepPair对象?
网关改造:sso-api-gateway-jwt
sso-product-jwt-server
mybatis
mybatis的体系结构
一、myBatis 核心概念
核心对象的作用域与生命周期
UserMapper.xml
mybatis-config.xml
示例
接口式编程
二、全局的 configuration 配置
环境配置
设置
别名
mappers映谢器
类型处理器
在resultMap中指定 typeHandler:
三、mapper 文件
sql语句块statement
Mapper中的元素
select 用法及属性
insert&update&delete 用法
参数映射
参数拼接${}
结果集映射
结果集自动映射
resultMap
嵌套结果映射
关联 association
引入外部Select
Mybatis核心应用配置与原理
1,2级缓存处理
1级缓存使用场景
一级缓存的使用条件:
一级缓存源码解析:
缓存获取 :
缓存的存储:
一级缓存的实现 PerpetualCache
2级缓存使用场景:
2级缓存示例:
2级缓存使用条件:
2级缓存清除条件:
2级缓存源码解析:
清除缓存
获取缓存关键源码!
保存2级缓存 !
动态化SQL
<trim>示例说明:
<set>元素说明:
<where>元素说明:
<sql> 元素说明:
<bind> 变量使用
一、执行流程解析
配置文件解析 configuration:
配置元素解析构建器
sql statement 构建流程源码:
会话创建 SqlSession
会话构建源码解析:
方法执行 StatementHandler
StatementHandler 源码解析
二、myBatis插件开发
分页插件实现:
实现目标分解
Pagelandler
修改sql语句
spring 集成myBatis
关于Mapper 单例情况下是否存在线程安全的问题?
每次查询都会创建一个新的 SqlSession 会话,一级缓存还会生效吗?
SpringMvc
1.spring mvc 设计思想与体系结构组成
1.回顾servlet 与jsp 执行过程
2、spring mvc 功能特性:
3、请求处理流程
4、spring mvc 示例
web.xml servlet配置:
编写Control 方法:
配置spring-mvc.xml
二、mvc 体系结构详解
spring mvc 框架解决的问题
mvc 各组件执行流程
HandlerMapping 详解
Handler 类型
HandlerAdapter详解
ViewResolver 与View 详解
一、 MVC拦截处理
HandlerExceptionResolver 异常处理
HandlerExceptionResolver 结构
SimpleMappingExceptionResolver 示例:
HandlerInterceptor 调用拦截
演示HandlerInterceptor
二、RequestMapping注解的使用与原理
演示基于注解配置mvc mapping
调用执行源码解析:
springboot
Spring Boot快速开始及核心功能介绍
为啥我只要引入 spring-boot-starter-parent 和 spring-boot-starter-web就可以快速开
发mvc的项目
3.1)pom分析
3.2) 我们来分析看下 spring-boot-starter-web(场景启动器)为我项目中导入 web开发需要的jar包依赖
4)多profile切换
4.1)yml支持多模块文档块
4.2) 多yml|properties文件的环境切换
application.yml (用于激活不同环境的配置文件)
application-dev.yml
application-prod.yml
4.3)激活指定环境配置的方法
4.4)设置jvm参数
4.5) springboot关于打包问题总结
4.5.1):打成指定的jar名称的
4.5.2)若出现工程中出现多个mainclass的时候需要指定主启动类
4.5.3)如何打出一个war包
6)springboot 的web开发
6.2)springboot是如何整合springmvc功能的(WebMvcAutoConfiguration)
6.2.1)自动装配的组件
6.2.2)如何扩展springmvc的配置
A:如何往容器中添加一个拦截器
B:往容器中增加一个过滤器
C:往容器中增加一个servlet
filter , 拦截器Interceptor , AOP的区别 ?
7)如何全面接管springboot的mvc配置(让springboot给我们自动配置的功能失效, 不推荐)
8)springboot错误处理机制?如何定制错误页面?
8.1)我们来看springboot为我们自动配置的异常处理的一些bean
ErrorMvcAutoConfiguration.ErrorPageCustomizer(错
误页面定制器)
误页面定制器)
BasicErrorController (基础错误控制器)
this.resolveErrorView
DefaultErrorViewResolver#resolveErrorView
错误视图解析器
错误视图解析器
this.resolve 响应码精准匹配视图
resolveResource(errorViewName, model)
浏览器效果:(需要返回自己定义的错误页面 包含了自定义的错误异常信息)
第一步:我们定义一个全局异常处理器,然后返回看执行效果
第二步:在异常处理器中 进行重定向
继续优化
页面返回的属性字段是在哪里配置的???
springboot整合篇
1:springboot整合jdbc
1.1:导入的maven依赖
1.2:配置相关数据源:
1.3:测试
1.4)jdbc 以及数据源的自动装配原理
1.4.1)数据源的自动装配
1.4.2)jdbcTemplate自动装配
测试:
2.springboot如何优雅的整合druid +监控
2.1)加入druid的依赖
2.2)配置druid的数据源属性
2.3)对应的接受配置类
2.4)配置数据源监控,配置一个statViewSerlvet(后端管理) WebStatFilter sql监控
3:springboot整合 mybaits
3.1) 导入maven依赖
3.2)让我们看下mybatis自动配置类给我们配置了什么组件
3.3)把sql写在方法上(mapper文件上必须加上@Mapper注解)
3.4)把sql写在配置文件上
4:整合Redis
4.1)导入的maven依赖
4.2)自动装配的组件
4.3)所需的配置类,我们去RedisProperties配置类
4.4)使用redis 自动配置的默认的redisTemplate是使用jdk自带的 序列化工具
5)整合Swagger2
第一步:加入maven依赖
5.4)加入swagger2的配置
5.5)在配置类上开启swagger2的文档
5.6)使用示例:
5.7)整合rabbitmq
5.7.1)我们项目中为什么需要使用消息中间件?
①:异步
②:解耦 (用户下订单成功,然后发送一条减少库存的消息发送到mq中)
③:流量削峰
5.7.2)rabbtimq的核心概念理解?
5.7.4) 整合三板斧
①:导入maven依赖
②:查看自动配置以及对应的自动配置属性类
③:在yml文件中 配置关键属性
6)springboot整合 actuator 监控管理
6.1)监控访问路径前缀
6.2) http 健康监控端点 默认只暴露了 health,info端点
6.3)具体端点分析
springboot 自动装配原理
@SpringbootApplication注解分析
@SpringBootConfiguration
@Configuration
@EnableAutoConfiguration
@AutoConfigurationPackage
AutoConfiqurationImportSelector
AutoConfigurationImportSelector#selectImports
@ComponentScan
4)自动装配原理前的不得不说的几个注解
4.1)通过@Import注解来导入ImportSelector组件
核心代码:
1.2)通过@Import导入ImportBeanDefinitionRegistrar 从而进来导入组件
1.3)spring底层条件装配的原理@Conditional
该情况下会加载二个组件
自定义启动器步骤
接下来我们依靠 自动装配原理来分析出spring Boot的jar包的启动流程.
Spring Cloud Alibaba
微服务注册中心Nacos
3.3)Nacos服务端搭建
Nacos client服务端的搭建
①:三板斧之:第一板斧 加入依赖
②:三板斧之:第二板斧写注解
③:第三板斧之:写配置文件 **注意**server-addr:不需要写协议
④:验证我们的order-center注册到我们的nacos上
5:Nacos 领域模型划分以及概念详解
5.2) Nacos的集群模式
5.2.1)首先 我们需要安装我们的nginx
5.2.2)安装 我们的nacosserver(搭建三个集群端口分别为8849 ,8850,8851)
脑裂
微服务接入配置中心的步骤
①:添加依赖包spring-cloud-alibaba-nacos-config
②:编写配置文件,需要写一个bootstrap.yml配置文件
6.2)怎么解决 生产环境,测试环境,开发环境相同的配置。(配置通用)
6.3)不同微服务的通用配置。
6.3.1)通过 shared-dataids 方式
6.3.1)通过 ext-config方式
6.3.3)各个配置的优先级
微服务客户端负载均衡组件Ribbon
1.3)自定义的负载均衡算法(随机)
1.4)通过Ribbon组件来实现负载均衡(默认的负载均衡算法是 轮询)
①:创建整合Ribbon的工程:
1.5)Ribbon的内置的负载均衡算法
1.6)Ribbon的细粒度自定义配置
1.7)解决Ribbon 第一次调用耗时高的配置
1.8)Ribbon 自定义负载均衡策略
进阶版本1:
进阶版本2
什么是Feign
1.3)在我们工程中怎么添加Feign
1.3.2)调用者工程tulingvip03-ms-alibaba-feign-order
1.3.3)我们服务提供者tulingvip03-ms-alibaba-feign-product 的controller 需要实现我们的productCenterFeignApi接口,防止修
改
二:如何自定义Feign
2.2)基于yml文件细粒度配置
2.3)使用Feign原生的注解配置(需要修改契约) 了解即可
2.4)拦截器的应用配置
2.4)Feign调用优化方案
2.5)Feign的生产实践 (以Feign的超时说了算)
微服务限流容错降级Sentinel实战
3.2)如何在工程中快速整合Sentinel
3.3)我们需要整合Sentinel-dashboard(哨兵流量卫兵)
四:sentinel监控性能指标详解
4.1)实时监控面板
4.2)簇点链路 用来显示微服务的所监控的API
4.3)流控设置
4.4)降级规则
①:rt(平均响应时间)
②:异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):
③:异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):
4.5)热点参数:
五:Sentinel-dashboard 控制台和 我们的微服务通信原理.
一:Ribbon整合Sentinel
二:OpenFeign整合我们的Sentinel
四:Sentinel 规则持久化
1)原生模式
2)Pull拉模式
3)推模式:push(已Nacos为例) 生产推荐使用
3.1)、原理简述
3.2)改造方案
微服务改造方案:
Sentinel-dashboard改造方案:
5)优化错误页面
5)使用sentinel必须解决的问题
6)针对来源编码实现
常见限流算法精讲
计数器法
滑动时间窗口算法
漏桶算法
令牌桶算法
微服务分布式事务解决方案Seata
二:什么是分布式事务?
三:什么是Seata?
3.1)角色划分:
2.1)工作原理
三:快速开始搭建Seata环境
3.1)Seata-server环境搭建
3.2)微服务搭建步骤
第一步:添加pom依赖
第二步:写注解
第三步:写配置添加代理数据源配置
第四步:修改配置文件 yml中添加配置文件
微服务网关GateWay
二:搭建SpringCloudGateWay的三板斧
三:GateWay的核心概念
3.1)基本核心概念.
3.2)路由断言工厂
3.3)自定义谓词工厂
第二步:书写一个配置类,用于接受配置
第三步:在yml配置中
3.4)过滤器工厂
①:添加请求头。
②:添加请求参数
③:为匹配的路由统一添加前缀
④:自定义过滤器工厂
写一个自定义的内部类实现
GateWayFilter接口 和ordered接口
GateWayFilter接口 和ordered接口
⑤:自定义全局过滤器
GateWay+Sentinel1.6.3版本整合
GateWay+Sentienl全局异常处理
dubbo与spring cloud的区别
调用方式
采用Dubbo协议,接口一般是Java的Service接口
采用HTTP协议,接口一般是Rest风格
注册中心
主要使用Zookeeper
支持Eureka、Consul等多种注册中心
性能
使用Netty的NIO方式,性能较好
依赖于HTTP协议,通信效率相对较低
组件差异
可定制性强,灵活性高
集成了Spring全家桶,提供全面的开发体验
协议支持
默认使用自定义的RPC协议,也支持HTTP、REST等
通常使用HTTP协议,支持AMQP、RSocket等
Redis
Redis的单线程和高性能
Redis核心数据结构
String
Hash结构
List
Set结构
ZSet有序集合结构
StringRedisTemplate与RedisTemplate
Redis持久化
RDB快照(snapshot)
AOF(append-only file)
Redis Lua脚本
Redis集群方案比较
哨兵模式
高可用集群模式
4、Redis集群原理分析
缓存设计
缓存穿透
缓存失效
缓存雪崩
Redis高并发分布式锁实战
开发规范与性能优化
一、键值设计
三、客户端使用
Rabbitmq
RabbitMQ有什么优缺点?
MQ的优点 :解耦、异步、削峰。
缺点有以下几个
系统可用性降低
系统复杂度提高
一致性问题
Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
RabbitMQ,他的好处在于可以支撑高并发、高吞吐、性能很高,同时有非常完善便捷的后台管理界面可以使用。
另外,他还支持集群化、高可用部署架构、消息高可靠支持,功能较为完善。
RocketMQ,是阿里开源的,经过阿里的生产环境的超高并发、高吞吐的考验,性能卓越,同时还支持分布式事务等特殊场景。
Kafka的优势在于专为超高吞吐量的实时日志采集、实时数据同步、实时数据计算等场景来设计。
MQ 有哪些常见问题?如何解决这些问题?
消息的顺序问题
消息的重复问题
如何保证RabbitMQ消息的可靠传输?
生产者丢失消息:
消息队列丢数据:消息持久化。
为什么不应该对所有的 message 都使用持久化机制?
首先,必然导致性能的下降,因为写磁盘比写 RAM 慢的多,message 的吞吐量可能有 10 倍的差距。
。另外一种处理原则是:仅对关键消息作持久化处理(根据业务重要程度),且应该保证关键消息的量不会导致性能瓶颈。
消费者丢失消息:消费者丢数据一般是因为采用了自动确认消息模式,改为手动确认消息即可!
消息积压处理办法:临时紧急扩容:
将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入 mq 里面去,
如何保证高可用的?RabbitMQ 的集群
普通集群模式
镜像集群模式
设计MQ思路
mq 得支持可伸缩性吧,就是需要的时候快速扩容
mq 的数据要不要落地磁盘
mq 的可用性
数据 0 丢失
ZooKeeper
二、部署与常规配置
常规配置文件说明
客户端命令
node数据的增删改查
三、Zookeeper节点介绍
1.节点类型
1.PERSISTENT(持久节点)
2.PERSISTENT_SEQUENTIAL(持久序号节点)
3.EPHEMERAL(临时节点)
EPHEMERAL_SEQUENTIAL(临时序号节点)
3.节点属性说明(stat)
2.节点的监听(watch)
4.权限设置(acl)
zookeeper客户端
Zookeeper集群
1.集群部署
集群角色说明
3.选举机制
4.数据同步机制
3.Zookeeper典型使用场景实战
1.分布式集群管理
功能实现:
分布式注册中心
分布式JOB
分布式锁
Dubbo
Dubbo 中的SPI机制
先来了解一下 JAVA自带的SPI
Dubbo的SPI机制:
1.Dubbo 快速入门
快速演示Dubbo的远程调用
基于Dubbo实现服务集群:
基于spring IOC维护Dubbo 实例
提供者配置provide.xml
提供者服务暴露代码:
消费者配置consumer.xml
消费者调用代码:
基于springBoot 的dubbo使用
boot-server
application.properties
boot-client
application.properties
2.Dubbo 常规配置说明
负载均衡
容错
异步调用
过滤器
面试问题
关于dubbo的提供者(provider)和消费者(custom)异常捕获的问题
会发现provider抛出的异常,在custom端并不能正确的捕获
三、解决方案
那么就好办了,既然不想provider和custom有耦合,
只需要在接口类中再定义自己的模块异常类来集成公共的BusinessException,比如UserException,
(1)、自定义业务异常类,继承公共业务异常类
那么就好办了,既然不想provider和custom有耦合,
只需要在接口类中再定义自己的模块异常类来集成公共的BusinessException,比如UserException,
(1)、自定义业务异常类,继承公共业务异常类
(2)、业务代码中抛出异常改成自定义异常
(3)、这个时候登录返回的就是正常的JSON提示了
0 条评论
下一页