Spring

█ Spring

spring = IOC + AOP + TX跟事务相关的控制

█ 1.IOC

○ 1.1 IoC 容器初始化过程

○ 1.2 依赖注入的实现方法

○ 1.3 依赖注入的相关注解

○ 1.4 依赖注入的过程

○ 1.5 Bean 的生命周期

○ 1.6 Bean 的作用范围

○ 1.7 通过 XML 方式创建 Bean

○ 1.8 通过 注解 创建 Bean

○ 1.9 通过注解 配置文件

○ 1.10 BeanFactory、FactoryBean 和 ApplicationContext 的区别?

█ 2.AOP:

AOP的关键单元是切面(关注点),像日志或者事务这些分散的切面(横切关注点——可以影响到整个应用的关注点)应该被尽量地集中到一个地方以方便管理(事务管理、权限、日志、安全)。而AOP就是作用于这些横切关注点,来让代码在当下和将来都变得容易维护。

一般AOP通过如下方式进行使用:

1)使用AspectJ 注解风格

2)使用Spring XML 配置风格

○ 2.1 AOP中关注点 和 横切关注点?

关注点: 解决特定业务问题的方法。比如库存管理、航运管理、用户管理等。

横切关注点: 贯穿整个应用程序的关注点。像事务管理、权限、日志、安全等。

○ 2.2 连接点(Joint Point)和切入点(Point cut)

连接点 是程序执行的一个点。

一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。

举例来说,所有定义在你的 EmpoyeeManager 接口中的方法都可以被认为是一个连接点,如果你在这些方法上使用横切关注点的话。


切入点 是一个匹配连接点的断言或者表达式。

Advice 与切入点表达式相关联,并在切入点匹配的任何连接点处运行

(比如,表达式 execution(* EmployeeManager.getEmployeeById(...)) 可以匹配 EmployeeManager 接口的 getEmployeeById() )。由切入点表达式匹配的连接点的概念是 AOP 的核心。Spring 默认使用 AspectJ 切入点表达式语言

○ 2.3 引介(Introduction)

为这些对象提供接口的实现

引介让一个切面可以声明 被通知的对象实现了(任何他们没有真正实现)的额外接口

使用 @DeclareParaents 注解来生成一个引介。

○ 2.4 织入(weaving)

织入是将 切面 与 外部的应用类型或者类 连接起来 以创建通知对象(adviced object)的过程。

这可以在编译时(比如使用 AspectJ 编译器)、加载时或者运行时完成。

Spring AOP 跟其他纯 Java AOP 框架一样,只在运行时执行织入。

在协议上,AspectJ 框架支持编译时和加载时织入。

AspectJ 编译时织入是通过一个叫做 ajc 特殊的 AspectJ 编译器完成的。

后编译时织入:
它可以将切面织入到 Java 源码文件中,然后输出织入后的二进制 class 文件。

它也可以将切面织入你的编译后的 class 文件或者 Jar 文件。

○ 2.5 AOP有哪些可用的实现?

基于Java的主要AOP实现有:
AspectJ
Spring AOP
JBoss AOP

○ 2.6 AOP的常用注解 通知的类型:

图片

spring4/5对于AOP的执行顺序是不同的

从spring boot1升级到 spring boot2,实际上是 spring4升级到 spring5

spring4 正常执行:@Before @After @AfterReturning

异常执行:@Before @After @AfterThrowing

环绕通知 前---@Before--业务逻辑 --- 环绕通知 后 --- @After--- @AfterReturning

环绕通知 前---@Before--业务逻辑 --- @After--- @AfterThrowing
spring5

环绕通知 前 ---@Before--业务逻辑 ---@AfterReturning --- @After  ---环绕通知 后

环绕通知 前 ---@Before--业务逻辑 ---@AfterThrowing --- @After

○ 2.7 Spring AOP 代理

设计模式

代替他实现功能。

Spring AOP是基于代理实现的

AOP 代理是一个由 AOP 框架创建的用于在运行时实现切面协议的对象

Spring AOP默认为 AOP 代理使用标准的 JDK 动态代理。这使得任何接口(或者接口的集合)可以被代理。

Spring AOP 也可以使用 CGLIB 代理。这对代理类而不是接口是必须的。

如果业务对象没有实现任何接口那么默认使用CGLIB。



Spring AOP的设计原理 主要的分代理的创建代理的调用两部分,

1)代理的创建

创建代理工厂:拦截器数组,目标对象接口数组,目标对象。

创建代理工厂时,默认会在拦截器数组尾部再增加一个默认拦截器 —— 用于最终的调用目标方法。

当调用 getProxy 方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib)。

注意:创建代理对象时,同时会创建一个外层拦截器,这个拦截器就是 Spring 内核的拦截器,用于控制整个 AOP 的流程。


2)代理的调用

当对代理对象进行调用时,就会触发外层拦截器。

外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式。

当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法,最后返回。

█ 3.Spring循环依赖

多个bean之间相互依赖,形成了一个闭环。

通常,如果问Spring容器内部如何解决循环依赖,一定是指默认的单例Bean 中,属性相互引用的场景

○ 3.1 两种注入方式对循环依赖的影响

构造 set 注解

AB循环依赖问题 只要A的注入方式 是 setter且singleton,就不会有循环依赖问题

图片

构造注入 无法解决循环依赖,无限嵌套

○ 3.2 spring 三级缓存

○ ○ 3.1.1 加上Spring容器xml循环依赖

xml bean

默认的单例singleton的场景是支持循环依赖的,不报错

原型Prototype 不支持 报错 BeanCurrentlyInCreationException


Spring内部通过三级缓存来解决循环依赖

DefaultSingletonBeanRegistry

里面有三个Map
图片

一级缓存(也叫 单例池)singletonObjects

用于存放完全初始化好的 bean


第二级缓存:earlySngletonObjects

存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 */


第三级缓存: singletonTactories

存放 bean 工厂对象,用于解决循环依赖

只有单例的bean会通过三级缓存提前暴露 来解决循环依赖的问题。

而非单例的bean,每次从 容器中 获取都是一个新的 对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。

○ 3.2.1 三级缓存的流程:

在bean里面 get啥的最终都是调用的 doxxx底层

流程是

A里有B,B里有A
getSingleton 获得bean, 

不能获得,就创建bean对象 doCreateBean,

之后添加到三级缓存中,(表示是一个KV键值对,k=A,v=lamda)  

属性注入populateBean,如果需要B还要再来一遍,如果又到了A,A已经在三级缓存中了,

然后让B获得A的早期引用 getBean(A) doGetBean(beanA...),

然后从三级缓存移到二级,返回A的原始对象,

帮助B完成实例化和初始化 initializeBean addSingleton, getObjectForBeanInstance。  

最后添加都池里addSingleton ,A也是,最后添加到一级缓存

图片

图片

图片

图片

○ 3.3 多例的情况下,循环依赖问题为什么无法解决?

非单例的bean,每次从 容器中 获取都是一个新的 对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。

○ 3.4 如何检测是否存在循环依赖

1)A 创建过程中需要 B,于是 A 将自己放到三级缓里面 ,去实例

2)B 实例化的时候需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了!

3)然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A

4)B 顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)

5)然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,完成创建,并将自己放到一级缓存里面
如此一来便解决了循环依赖的问题

posted @ 2021-03-31 08:33  千面鬼手大人  阅读(253)  评论(0编辑  收藏  举报
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css