AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP 就是基于动态代理的,
有实现接口的对象,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,
而对于没有实现接口的对象, Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理
开发有两种方式,XML 和注解
Spring的AOP是对一个类的方法在不进行任何修改的前提下实现增强,这些方法我们给起了一个名字叫连接点
需要增强的方法我们给起了一个名字叫切入点
存放共性功能的方法,我们给起了个名字叫通知
通知和切入点之间的关系描述,我们给起了个名字叫切面
方法不能独立存在需要被写在一个类中,这个类我们也给起了个名字叫通知类
(1) 通常使用@Order 注解直接定义切面顺序
(2) 实现Ordered 接口重写 getOrder 方法。返回值越小优先级越高

执行前;当一个请求发来时先进服务器(Tomcat),在服务器中会有拦截器,过滤器啊,等这些功能走完之后,才真正的进入了框架中。
1.用户发来一个请求,首先进入的是前端控制器DispatcherServlet
2.前端控制器将(DispacherServlet)用户发来的请求发送给处理器映射器(HandlerMapping)
3.处理器映射器根据前端控制器发来的用户的请求找到对应符合的控制器(Handler),并且将其封装成处理器执行链,返回给前端控制器。
4.前端控制器DispatcherServlet调用处理适配器(HandlerAdapter)来调用的具体的控制器
5.控制器执行完成后,会返回一个ModelAndView对象给处理器适配器
6.处理器适配器将返回来的ModelAndView对象返回给前端控制器(到这里所有的业务处理过程就要完了,接下就是将结果以页面的的形式相应给用户)
7.前端控制器将返回回来的ModelAndView对象交给视图解析器(ViewResolver),视图解析器根据传过里的View对象解析成对应的页面对象,然后将页面对象和Model对象返回给前端控制器。
8.前端控制器再将返回回来的对象交给视图(View),视图根据传过来的Model对象再一次的对页面进行渲染,然后在返回给前端控制器。
9.前端控制器将完成的结果响应给浏览器,然后浏览器在展现给用户。

创建型模式,5 (将对象的创建与使用分离)⼯⼚/抽象⼯⼚/单例/建造者/原型模式
结构型模式,7 关注类和对象的组织 适配器/桥接模式/过滤器/组合/装饰器/外观/享元/代理模式
行为型模式,11 关注对象之间的相互交互 任链/命名/解释器/迭代器/中介者/备忘录/观察者/状态/策略/模板/访问者模式
接一(依)单,开里迪
工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
单例设计模式 : Spring 中的 Bean 默认都是单例的。
Spring 中 bean 的默认作用域就是 singleton(单例)的 ,这一类对象只能有一个实例
对于频繁使用的对象,可以省略创建对象所花费的时间
由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,
代理设计模式 : Spring AOP 功能的实现。
模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
包装器设计模式 - : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。

@Autowired是按照类型注入的,如果有多个实现类,@Qualifier来指定注入哪个名称的bean对象。

用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中
如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现
@Component 注解作用于类,而@Bean注解作用于方法。

JAVA里面主要有ReentrantLock ,synchronized,Lock三种
Synchronized 的锁升级流程是这样:无锁 ----> 偏向锁 ----> 轻量级锁----> 锁自旋 ----> 重量级锁
可重入锁:当前线程获取到A锁,在获取之后尝试再次获取A锁是可以直接拿到的
java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是可重入锁
不可重入:当前线程获取到A锁,在获取之后尝试再次获取A锁,无法获取到的,因为A锁被当前线
程占用着,需要等待自己释放锁再获取锁。
乐观锁:为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了。适合于读操作多的,乐观思想就是认为:当前环境读数据的多,写数据的少,并发读多,并发写少。
悲观锁:
Java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是悲观锁。
一个共享数据加了悲观锁,那线程每次想操作这个数据前都会假设其他线程也可能会操作这个数据,所以每次操作前都会上锁,这样其他线程想操作这个数据拿不到锁只能阻塞了。
synchronized 引入了大量的优化如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销
Java中提供的synchronized只能是非公平锁。
Java中提供的ReentrantLock,ReentrantReadWriteLock可以实现公平锁和非公平锁
公平锁:线程A获取到了锁资源,线程B没有拿到,线程B去排队,线程C来了,锁被A持有,同时线
程B在排队。直接排到B的后面,等待B拿到锁资源或者是B取消后,才可以尝试去竞争锁资源。
非公平锁:线程A获取到了锁资源,线程B没有拿到,线程B去排队,线程C来了,先尝试竞争
Java中提供的synchronized、ReentrantLock是互斥锁。
Java中提供的ReentrantReadWriteLock,有互斥锁也有共享锁。
互斥锁:同一时间点,只会有一个线程持有者当前互斥锁。
共享锁:同一时间点,当前共享锁可以被多个线程同时持有。
知乎连接讲得很好:

偏向锁:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现这个线程 ID
是自己的就表示没有竞争,不用重新 CAS


要避免死锁问题:
(1)功能区别
Synchronized是java语言的关键字,
ReentrantLock 是JDK1.5之后提供的API层面的互斥锁,需要lock和unlock()方法配合try/finally代码块来完成。
相对于 synchronized 它具备如下特点
不同:
相同:

一个线程对共享变量的修改,另外一个线程能够立刻看到,称为变量的可见性。
读取 volatile 修饰的变量时,线程将不会从所在CPU的缓存,而是直接从系统的主存中读取变量值,读取 volatile 修饰的变量时,线程将不会从所在CPU的缓存,而是直接从系统的主存中读取变量值
不能保障原子性
原子性是拒绝多线程交叉操作的,对于volitile i;**i++**操作,就不能保证它的原子性
文本链接:
为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题,不同的线程之间不会相互干扰
线程隔离:




优先选择:实现Runnable 接口的
1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况,代码和线程独立。
实现多个 Thread共用一个Runnable



里面包含了很多可能会被问到的JVM的知识
对象的内存分布:视频讲解

根据JVM参数开头可以区分参数类型,共三类:“-”、“-X”、“-XX”,
标准参数(-):所有的JVM实现都必须实现这些参数的功能,而且向后兼容;
非标准参数(-X):默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;
非Stable参数(-XX):此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用;
堆:
-Xms 初始堆大小,-Xmx 最大堆大小
-XX:NewSize=n 设置年轻代大小-XX:NewRatio=n 设置年轻代和年老代的比值。
栈:
-Xss:栈空间大小,栈是线程独占的,所以是一个线程使用栈空间的大小。



系统加载 Class 类型的文件主要三步:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。
加载
3 件事情:
通过全类名获取定义此类的二进制字节流。
将字节流所代表的静态存储结构转换为方法区的运行时数据结构。
在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口。
验证
确保 Class 文件的字节流中包含的信息符合《Java 虚拟机规范》的全部约束要求,
保证这些信息被当作代码运行后不会危害虚拟机自身的安全。(文件格式验证,元数据验证,字节码验证,符号引用验证)
准备
为 static 变量分配空间,设置默认值
如果 static 变量是 final 的基本类型,以及字符串常量,那么编译阶段值就确定了,赋值在准备阶
段完成
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,也就是得到类或者字段、方法在内存中的指针或者偏移量。
初始化阶段
初始化即调用 ()V ,虚拟机会保证这个类的『构造方法』的线程安全
会触发初始化的情况:
(1)当遇到 new 、 getstatic、putstatic 或 invokestatic ,这 4 条直接码指令
new (创建一个类的实例对象)。
getstatic(程序访问类的静态变量)。
putstatic(给类的静态变量赋值)。
invokestatic (调用类的静态方法)。
(2)反射 Class.forName
(3)子类初始化,如果父类还没初始化,会引发
(4)main 方法所在的类,总会被首先初始化
(5)子类访问父类的静态变量,只会触发父类的初始化

类加载器:
JVM 中内置了三个重要的类加载器:
BootstrapClassLoader(启动类加载器),ExtensionClassLoader(扩展类加载器),AppClassLoader(应用程序类加载器)
除了 BootstrapClassLoader 是 JVM 自身的一部分之外,由 C++ 实现,过getParent()获取其父 ClassLoader,为null
其他所有的类加载器都是在 JVM 外部实现的

双亲委派:
执行流程:
(1)在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载(每个父类加载器都会走一遍这个流程)。
(2) 进行类加载的时候,它首先进行类加载的时候,调用父加载器 loadClass()方法来加载类,调用父加载器 loadClass()方法来加载类。
(3)只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载(调用自己的 findClass() 方法来加载类)。
(4)如果子类加载器也无法加载这个类,那么它会抛出一个 ClassNotFoundException 异常。


原子性:
synchronized( 对象 ) {
要作为原子操作代码
}
可见性:
main 线程对 run 变量的修改对于 t 线程不可见,它保证的是在多个线程之间,一个线程对 volatile 变量的修改对另一
个线程可见, 不能保证原子性,仅用在一个写线程,多个读线程的情况
synchronized 语句块既可以保证代码块的原子性,也同时保证代码块内变量的可见性。但缺点是
synchronized是属于重量级操作,性能相对更低
有序性-指令重排
在 Java 中,volatile 关键字除了可以保证变量的可见性,还有一个重要的作用就是防止 JVM 的指令重排序。
满老师讲的单例模式-指令重排添加链接描述


当一个线程想要访问某个对象的 synchronized 代码块,首先需要获取该对象的 Monitor。如果锁的计数器为 0 则表示锁可以被获取,否则,该 Monitor 已经被其他线程持有,则当前线程将会被阻塞,直至 Monitor 变为可用状态。

JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法
修饰普通方法:作用于当前对象实例,进入同步代码前要获得当前对象实例的锁
synchronized 修饰静态方法,相比普通方法,ACC_SYNCHRONIZED,只是多了个 ACC_STATIC 标识,
修饰静态方法:作用于当前类,进入同步代码前要获得当前类对象的锁
操作系统工具
top:显示系统整体资源使用情况
vmstat:监控内存和CPU
iostat:监控IO使用
netstat:监控网络使用
jmap可以查看当前Java进程的内存占用,把内存快照dump出来,jmap -heap pid
jstack可以查看线程运行情况,是否有死锁,cpu过高问题, jstack -l pid 生成虚拟机当前时刻的线程快照,deadlock:死锁,blocked:线程被阻塞,
jstat命令来查看垃圾回收的情况,full gc比价频繁,那么就得进行调优

摘抄于这篇知乎
step1:通过top命令各个进程的资源占用情况。
step2:通过打印出进程的所有线程信息和进程pid查看gc情况,jstat -gcutil pid 1000
step3:通过jmap查看是哪个对象占用了内存。
step4:使用visualVM对dump文件进行离线分析,找到占用内存高的对象,从代码和业务场景中定位具体问题。


一开始,客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口,处于 LISTEN状态
第一次握手:
客户端会随机初始化序号(client_isn)置于TCP中,TCP报文首部的同步位SYN=1.向服务器发送连接请求报文段,这时客户端进入SYN_SENT(同步已发送)状态,等待服务器的确认
第二次握手:
服务端回复客户端发送的TCP连接请求报文,服务端也随机初始化自己的序号(server_isn),填入 TCP 首部的「序号」字段中, TCP 首部的「确认应答号」字段填入 client_isn + 1, SYN 和 ACK 标志位置为 1,服务端处于SYN-RCVD 状态 。
第三次握手:
客户端收到服务端报文后,回应最后一个应答报文,
TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 **server_isn + 1 **,客户端处于== ESTABLISHED ==状态。服务端收到客户端的应答报文后,也进入 ESTABLISHED 状态
原因一:避免历史连接
我们考虑一个场景,客户端先发送了 SYN(seq = 90)报文,然后客户端宕机了,重启后,发送了 SYN(seq = 100)报文
正常的三次握手,服务端在第二次握手,ack 确认号实际是90+1,因为旧的报文比新的先返回给客户端,客户端收到此 ACK 报文时,发现自己期望收到的确认号应该是 101,而不是 91,于是就会回 RST 报文。就会释放连接。
在两次握手的情况下,服务端在收到 SYN 报文后,就进入 ESTABLISHED 状态,只有等到客户端判断到此次连接为历史连接,那么就会回 RST 报文来断开连接。
「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。

F12 TC 服务端:clc
客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入FIN_WAIT_1 状态。
服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSE_WAIT状态。
客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
服务端收到了 ACK 应答报文后,就进入了 CLOSE 状态,至此服务端已经完成连接的关闭。
客户端在经过 2MSL 一段时间后,自动进入CLOSE 状态,至此客户端也完成连接的关闭。


应表会 传网 数物
应 ------传网–网
根据叶子节点的内容,索引类型分为主键索引和非主键索引。
主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。
非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。
区别 :

从性能和存储空间方面考量,自增主键往往是更合理的选择。
覆盖索引:如果一个索引包含(或者说覆盖)所有需要查询的字段的值,不做回表操作,我们就称之为“覆盖索引”
最左匹配原则:在通过联合索引检索数据时,从索引中最左边的列开始,一直向右匹配,如果遇到范围查询(>、<、between、like等),就停止后边的匹配。
(或者比如直接使用第三个位置上的索引而忽略第一二个位置上的索引时,则会进行全表查询)
索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。

索引的出现其实就是为了提高数据查询的效率。

-树的查询时间跟树的高度有关,B+树是一棵多路搜索树可以降低树的高度,提高查找效率,为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。
一条SQL更新语句 :
当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到redo log(粉板)里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面(先写日志,再写磁盘)

依赖redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
redo log :
binlog:
undo log:
from -> on -> join -> where -> group by -> having -> count(聚合函数 avg、max、min、sum)
-> select -> distinct -> union -> order by -> limit
limit — 随着查询偏移的增大,查询时间急剧增加。
优化1-- 通过id:id是连续递增的,利用索引据查询的页数和查询的记录数可以算出查询的id的范围
优化2-- 子查询 :首先定位偏移量的id,根据获取的id值向后查询
当Explain 与 SQL语句一起使用时,MySQL 会显示来自优化器关于SQL执行的信息。
select子句或者操作表的顺序,id的值越大,代表优先级越高,越先执行
表示 select 查询的类型,主要是用于区分各种复杂的查询,例如:普通查询、联合查询、子查询等。
type:查询使用了何种类型,它在 SQL优化中是一个非常重要的指标,以下性能从好到坏依次是:
system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
index:Index 与ALL 其实都是读全表,区别在于index是遍历索引树读取,而ALL是从硬盘中读取。


接口:

区别:


如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,代理类需要实现InvocationHandler接口,通过proxy类创建代理对象,并且会调用InvocationHandler接口的invoke方法来处理具体的逻辑。
而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,会调用MethodInterceptor接口的intercept方法来处理具体的逻辑。
Java的反射是指在程序运行时,对于任意一个类,都可以获取到这个类的所有属性和方法,并能够对其进行操作。
应用场景了解么?





Redis是一种存储key-value的内存型数据库,它的key都是字符串类型,value支持存储5种类型的数据:String(字符串类型)、List(列表类型)、Hash(哈希表类型、即key-value类型)、Set(无序集合类型,元素不可重复)、Zset(有序集合类型,元素不可重复)。


分布式系统下,不同的服务/客户端通常运行在独立的 JVM 进程上。如果多个 JVM 进程共享同一份资源的话,使用本地锁就没办法实现资源的互斥访问了。


