1. Spring 基本概念

image.png

POJO: 基于 POJO 的轻量级和最小侵入性编程;
DI: 通过依赖注入和面向接口实现松耦合;
AOP: 基于切面和惯例进行声明式编程;
Template: 通过切面和模板减少样板式代码。

比如我们要写 JDBC 这种牵扯到大量样板式的代码。我们其实只关注我们的 sql 语句(也就是它要实现什么功能),我们可不想看到他是如何连接的。等我们写好核心之后再去用切面进行连接,断开等。

1.1. POJO

基本原理-0、Java相关名词

POJO 的全称是 Plain Old Java Object,简单又老的 Java 对象。这里的简单是相对来讲的。 EJB 2. x 的 Entity Beans 比较重量,需要实现 javax.ejb 的一些接口。而 POJO 就比较轻量,就是一个 Java 对象,不需要实现任何的接口。POJO 专指只有 private 属性以及 setter/getter/toString 的简单类,包括 DO/DTO/BO/VO 等。

所以 POJO 本质上也是可以方便沟通的术语。

有了 POJO 这个名字,相比框架里面各种的对象概念,就容易理解多了,所以这个概念被很广地使用开来。可以用 POJO 来解释 JavaBean 这个惯例 (约定): JavaBean 就是可以序列化的 POJO,并且有无参构造器,可以使用 getter/setter 来读写属性。%%
0737-🏡⭐️◼️POJO 是一个约定,约定为只有 private 属性以及 getter、setter、toString 方法的简单的类,相对于 EJB 的 Entity Beans 是简单轻量的。也是领域模型中 DO,DTO,VO,BO 的统称。慢慢变成一种术语,可以用来解释 JavaBeans 这个约定,就是序列化且有无参构造的 POJO◼️⭐️-point-202302020737%%

Spring 的非侵入编程模型意味着使用 POJO 这种类在 Spring 应用和非 Spring 应用中都可以发挥同样的作用。

1.2. 约定 (惯例)

约定优于配置
convention over configuration

1.3. 依赖注入和面向接口

就是使用聚合 + 构造导入 (或者 setter 导入) 的方式扩展
设计模式中大量使用这种方式

1.4. Spring 的核心

image.png
%%
0709-🏡⭐️◼️Spring 通过 IOC 实现依赖反转,由容器创建管理 Javabean 并维护 Javabean 之间清晰松散的耦合关系,解放程序员的精力放在业务逻辑编码上,同时通过 AOP 实现面向接口编程,可以无侵入的增强业务功能,进一步减少对业务代码的侵入性◼️⭐️-point-202302010709%%

1.5. Spring 的优缺点

1.5.1. 优点

  1. 方便解耦,简化开发
    Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给 Spring 管理。

  2. AOP 编程的支持
    Spring 提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。

  3. 声明式事务的支持
    只需要通过配置就可以完成对事务的管理,而无需手动编程。

  4. 方便程序的测试
    Spring 对 Junit4 支持,可以通过注解方便的测试 Spring 程序。

  5. 方便集成各种优秀框架
    Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:Struts、Hibernate、MyBatis 等)。

  6. 降低 JavaEE API 的使用难度
    Spring 对 JavaEE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等),都提供了封装,使这些 API 应用难度大大降低。

  7. Java 源码是经典学习范例
    Spring 的源码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。Spring 框架源码无疑是 Java 技术的最佳实践范例。

1.5.2. 缺点

  1. Spring 明明一个很轻量级的框架,却给人感觉大而全
  2. Spring 依赖反射,反射影响性能
  3. 使用门槛升高,入门 Spring 需要较长时间

1.6. Spring 的核心模块

^ob40ki
^hasxle
大约 20 几个模块,总共 1300 多个文件,这些组件被分别整合在:核心容器、AOP、Aspect、Instrumentation、Messing、DataAccess、Web、Test 等几大模块中
image.png

  1. spring core:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能
  2. spring beans:提供了 BeanFactory,是工厂模式的一个经典实现,Spring 将管理对象称为 Bean。
  3. spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框架式的对象访问方法。
  4. spring jdbc:提供了一个 JDBC 的抽象层,消除了烦琐的 JDBC 编码和数据库厂商特有的错误代码解析, 用于简化 JDBC。
  5. spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等
  6. spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。
  7. spring test:主要为测试提供支持的,支持使用 JUnit 或 TestNG 对 Spring 组件进行单元测试和集成测试。

2. IOC 相关

2.1. 什么是 IOC 容器

控制反转即 IOC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
Spring IOC 负责创建对象,管理对象(通过依赖注入(DI)),装配对象,配置对象,并且管理这些对象的整个生命周期。

2.2. IOC 的作用和意义

作用

  1. 管理对象的创建和依赖关系的维护
  2. 解耦,由容器去维护具体的对象
  3. 托管了类的生成过程,比如我们无需去关心类是如何完成代理的

 控制反转    控制了什么?
UserService service=new UserService();   // 耦合度太高 、维护不方便
引入 Ioc   就将创建对象的控制权交给 Spring 的 Ioc.   以前由程序员自己控制对象创建, 现在交给 Spring 的 Ioc 去创建, 
如果要去使用对象需要通过 DI(依赖注入)@Autowired 自动注入 就可以使用对象 ;

意义
1. IOC 容器以最小的代价和最小的侵入性使松散耦合得以实现。
2. IOC 容器支持加载服务时的饿汉式初始化和懒加载。
%%
▶10.🏡⭐️◼️IOC 容器的意义◼️⭐️-point-20230226-2231%%

2.3. IOC 的实现机制

工厂模式 + 反射

2.4. 什么是 Spring 的依赖注入 (DI)?IOC 和 DI 的区别是什么

很多人把 IOC 和 DI 说成一个东西,笼统来说的话是没有问题的,但是本质上还是有所区别的,希望大家能够严谨一点,IOC 和 DI 是从不同的角度描述的同一件事,IOC 是从容器的角度描述,而 DI 是从应用程序的角度来描述,也可以这样说,IOC 是依赖倒置原则的设计思想,而 DI 是具体的实现方式

2.5. BeanDefinition 的作用

image.png

它主要负责存储 Bean 的定义信息,包括 beanClass、scope、lazyInit、dependsOn、autowireMode 等信息,决定 Bean 的生产方式。后续 BeanFactory 根据这些信息就行生产 Bean: 比如实例化 ,通过 class 进行反射进而得到实例对象 , 比如 lazy  则不会在 ioc 加载时创建 Bean。

举例:由 BeanDefinition 中 Object 类型的 beanClass 强转为 String,然后通过反射得到 Class 对象
image.png

2.6. BeanFactory 和 ApplicationContext 有什么区别

image.png
image.png

2.6.1. 答案 2

BeanFactory 和 ApplicationContext 是 Spring 的两大核心接口,都可以当做 Spring 的容器。其中 ApplicationContext 是 BeanFactory 的子接口。

2.6.1.1. 功能大小

  1. BeanFactory:是 Spring 里面最底层的接口,包含了各种 Bean 的定义,读取 bean 配置文档,管理 bean 的加载、实例化,控制 bean 的生命周期,维护 bean 之间的依赖关系。
  2. ApplicationContext 接口作为 BeanFactory 的派生,除了提供 BeanFactory 所具有的功能外,还提供了更完整的框架功能

    继承 MessageSource,因此支持国际化。
    统一的资源文件访问方式。
    提供在监听器中注册 bean 的事件。
    同时加载多个配置文件。
    载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的 web 层。

资源文件访问方式: https://www.cnblogs.com/kongbubihai/p/15915434.html

2.6.1.2. 加载方式

  1. BeanFactroy 采用的是延迟加载形式来注入 Bean 的,即只有在使用到某个 Bean 时 (调用 getBean()),才对该 Bean 进行加载实例化。这样,我们就不能发现一些存在的 Spring 的配置问题。如果 Bean 的某一个属性没有注入,BeanFacotry 加载后,直至第一次使用调 getBean 方法才会抛出异常。
  2. ApplicationContext,它是在容器启动时,一次性创建了所有的 Bean。这样,在容器启动时,我们就可以发现 Spring 中存在的配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext 启动后预载入所有的单实例 Bean,通过预载入单实例 bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

image.png

下面的 3 个绿色的,都是功能扩展接口。

看下面的隶属 ApplicationContext 粉红色的 “高级容器”,依赖着 “低级容器”,这里说的是依赖,不是继承哦。他依赖着 “低级容器” 的 getBean 功能。而高级容器有更多的功能:支持不同的信息源头,可以访问文件资源,支持应用事件(Observer 模式)

通常用户看到的就是 “高级容器”。 但 BeanFactory 也非常够用啦!

左边灰色区域的是 “低级容器”, 只负载加载 Bean,获取 Bean。容器其他的高级功能是没有的。例如上图画的 refresh 刷新 Bean 工厂所有配置。生命周期事件回调等。

2.7. BeanFactory 和 FactoryBean 有什么区别

  1. BeanFactory 是一个工厂,也就是一个容器,是来管理和生产 bean 的;
  2. Spring 中有两种类型的 bean,一种是普通 bean,另一种是工厂 bean,即 FactoryBean。工厂 bean 跟普通 bean 不同,其返回的对象不是指定类的一个实例,其返回的是该工厂 bean 的 getObject 方法所返回的对象。工厂 bean 必须实现 org.springframework.beans.factory.FactoryBean 接口。

image.png

2.8. 有哪些不同类型的依赖注入实现方式

依赖注入是时下最流行的 IOC 实现方式,依赖注入分为接口注入(Interface Injection),Setter 方法注入(Setter Injection)和构造器注入(Constructor Injection)三种方式。其中接口注入由于在灵活性和易用性比较差,现在从 Spring4 开始已被废弃。
构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
Setter 方法注入:Setter 方法注入是容器通过调用无参构造器或无参 static 工厂 方法实例化 bean 之后,调用该 bean 的 setter 方法,即实现了基于 setter 的依赖注入。

image.png

最佳实践
两种依赖方式都可以使用,构造器注入和 Setter 方法注入。最好的解决方案是用构造器参数实现强制依赖,即组合的方式;setter 方法实现可选依赖,即聚合的方式。

2.9. Spring 中配置 (注册)Bean 的方式⭐️🔴

Spring-1、基本原理

2.10. BeanDefinition 的加载过程

3. 生命周期相关

3.1. Spring 框架中 bean 的生命周期⭐️🔴⭐️🔴

^fkjjz0

3.1.1. BD 相关

Spring-8、BeanDefinition

3.1.2. 整体流程

image.png

  1. Spring 对 bean 进行实例化;
  2. Spring 将值和 bean 的引用注入到 bean 对应的属性中;
  3. 如果 bean 实现了 BeanNameAware 接口,Spring 将 bean 的 ID 传递给 setBean-Name() 方法;
  4. 如果 bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanFactory() 方法,BeanFactory 容器实例传入;
  5. 如果 bean 实现了 ApplicationContextAware 接口,Spring 将调用 setApplicationContext() 方法,将 bean 所在的应用上下文的引用传入进来;
  6. 如果 bean 实现了 BeanPostProcessor 接口,Spring 将调用它们的 postProcessBeforeInitialization() 方法;
  7. 如果 bean 实现了 InitializingBean 接口,Spring 将调用它们的 after-PropertiesSet() 方法。类似地,如果 bean 使用 initmethod 声明了初始化方法,该方法也会被调用;
  8. 如果 bean 实现了 BeanPostProcessor 接口,Spring 将调用它们的 post-ProcessAfterInitialization() 方法;

此时,bean 已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
如果 bean 实现了 DisposableBean 接口,Spring 将调用它的 destroy() 接口方法。同样,如果 bean 使用 destroy-method 声明了销毁方法,该方法也会被调用。

3.1.3. 后置处理器相关⭐️🔴⭐️🔴

^n9drb6
%%
▶10.🏡⭐️◼️【🌈费曼无敌🌈⭐️第一步⭐️】◼️⭐️-point-20230321-2309%%
❕ ^jyw4v5

image.png
%%
1056-🏡⭐️◼️记忆方式 ?🔜MSTM📝 AutowiredAnnotationBPP/CommonAnnotationBPP=MI; AbstractAutoproxyCreator=SB◼️⭐️-point-202302081056%%

  1. ==doCreateBean 之前==,InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation()
    ①可以提前返回一个代理对象,而终止 bean 的创建给 BeanPostProcessors 一个机会来返回代理来替代真正的实例,应用实例化前的前置处理器,用户自定义动态代理的方式,针对于当前的被代理类需要经过标准的代理流程来创建对象。如果使用该扩展点,可以直接返回代理对象。
    Spring-3、AOP实现原理-@EnableAspectJAutoProxy

  2. ==在实例化之前==,即 createBeanInstance 之前还可以利用 SmartInstantiationAwareBeanPostProcessordetermineCandidateConstructors 方法来指定构造函数


  3. ①在实例化之后属性赋值之前,在 MergedBeanDefinitionPostProcessor 的子实现接口 AutowiredAnnotationBeanPostProcessorpostProcessMergedBeanDefinition 方法中,解析@Autowired @Value 转换为 InjectionMetadata 并缓存在 injectionMetadataCache

Spring-10、@Autowired

注意:@Lazy 是 BD 属性,不是 Bean 属性,所以没有预解析的逻辑,只有 BeanDefinition 的解析逻辑,在第 5 大步中,不在 9 大后置处理器中。
Spring-9、@Lazy

②在实例化之后属性赋值之前,在 MergedBeanDefinitionPostProcessor 的子实现接口 InitDestroyAnnotationBeanPostProcessor 的子类 CommonAnnotationBeanPostProcessorpostProcessMergedBeanDefinition 方法中,处理@PostConstruct 和@PreDestroy 注解,调用父类 InitDestroyAnnotationBeanPostProcessorfindLifecycleMetadata 方法构建 lifecycleMetadata 并缓存在 lifecycleMetadataCache
Spring-11、@PostConstruct

  1. 实例化之后属性赋值之前,将 SmartInstantiationAwareBeanPostProcessor 的子实现接口 AbstractAutoProxyCreatorgetEarlyBeanReference 方法作为钩子函数放入三级缓存,待到依赖方真正赋值时,调用 singletonFactory.getObject() 就会返回被依赖方的动态代理对象,从而解决了 AOP 的循环依赖问题 ❕%%
    1925-🏡⭐️◼️AbstractAutoProxyCreator 的作用原理 ?🔜MSTM📝 实例化之后属性赋值之前,将 AbstractAutoProxyCreator 的 getEarlyReferenceBean 方法作为钩子函数放入三级缓存,在依赖方属性填充时会获取到三级缓存并执行 singletonFactory. getObject,届时会调用 getEarlyReferenceBean,如果有 aop 的 BPP,就会返回一个动态代理对象◼️⭐️-point-202302051925%%

    Spring-2、IOC
  2. applyPropertyValues 真正赋值之前,与 1 相同的,InstantiationAwareBeanPostProcessorpostProcessAfterInstantiation,如果实现改接口重写该方法,可以跳过属性赋值的步骤

  3. applyPropertyValues 真正赋值之前,与 3-①相对应的,InstantiationAwareBeanPostProcessor 的子实现接口 AutowiredAnnotationBeanPostProcessorpostProcessProperties 方法中完成 bean 中@Autowired,@Inject,@Value 注解的解析并注入
    Spring-10、@Autowired

    Spring-9、@Lazy @Lazy 的赋值是在这里

  1. invokeInitMethods 初始化之前,与 3-②相对应的,BeanPostProcessor 的子类 InitDestroyAnnotationBeanPostProcessor 会调用 postProcessBeforeInitialization 方法,此时 @PostConstruct 注解的方法会被调用。确切的说应该是在 afterPropertiesSet 之前调用,这与 xml 方式配置的方式不同,后者是在 afterPropertiesSet 之后,执行 invokeInitMethods 之前调用Spring-11、@PostConstruct

  1. 初始化完成之后,如果有用到,则会调用 BeanPostProcessor 的子实现类 AbstractAutoProxyCreatorpostProcessAfterInitialization() 方法来生成动态代理

%%
0720-🏡⭐️◼️解析切面的地方 ?🔜MSTM📝 第一个 PP,InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation 中,解析切面并缓存所有通知到 advisorsCache 中◼️⭐️-point-202302120720%%

3.1.4. 生命周期回调相关

3.1.4.1. 初始化之前执行的生命周期的回调

image.png

真正初始化之前执行的生命周期回调%%
2116-🏡⭐️◼️扩展点 7 中的 3 个都在哪里 ?🔜MSTM📝 ApplicationContextAwareBPP、ImportAwareBPP、InitDestoryAnnotationBPP 都是在真正初始方法执行之前执行◼️⭐️-point-202302052116%%

image.png
%%
2237-🏡⭐️◼️invokeAwareMethods 方法有个重要点 ?🔜MSTM📝 就是对于实现 BeanFactoryAware 接口的 BPP,会设置 BeanFactory,在这个地方,AOP 设置了重要的属性 advisorRetrievalHelper、aspectJAdvisorsBuilder、aspectJAdvisorFactory,用于在后面实例化之前解析切面缓存通知◼️⭐️-point-20230216-2237%%

3.1.4.2. 初始化之后的生命周期的回调

image.png

3.2. 解释不同方式的自动装配,spring 自动装配 bean 有哪些方式?

3.2.1. XML

在 spring 中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用 autowire 来配置自动装载模式。
在 Spring 框架 xml 配置中共有 5 种自动装配:

  1. no:默认的方式是不进行自动装配的,通过手工设置 ref 属性来进行装配 bean。
  2. byName:通过 bean 的名称进行自动装配,如果一个 bean 的 property 与另一 bean 的 name 相同,就进行自动装配。
  3. byType:通过参数的数据类型进行自动装配。
  4. constructor:利用构造函数进行装配,并且构造函数的参数通过 byType 进行装配。

3.2.2. 注解

3.2.2.1. @Autowired 与@Qualifier

  1. @Autowired 是根据类型自动装配的,加上@Qualifier 则可以根据 byName 的方式自动装配。
  2. @Qualifier 不能单独使用。

3.2.2.2. @Autowired 与@Resource 异同

  1. @Autowired 与@Resource 都可以用来装配 bean。都可以写在字段上,或写在 setter 方法上。
  2. @Autowired 默认按类型装配(属于 Spring 规范),默认情况下必须要求依赖对象必须存在,如果要允许 null 值,可以设置它的 required 属性为 false,如:@Autowired(required=false)。如果我们想使用名称装配可以结合@Qualifier 注解进行使用
  3. @Resource(属于 J2EE 复返),默认按照名称进行装配,名称可以通过 name 属性进行指定。如果没有指定 name 属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在 setter 方法上默认取属性名进行装配。当找不到与名称匹配的 bean 时才按照类型进行装配。但是需要注意的是,如果 name 属性一旦指定,就只会按照名称进行装配。
  4. 它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired 先 byType,@Resource 先 byName。

PS: Autowired 根据类型装配的原因:
!Spring-10、@Autowired

https://blog.csdn.net/King_weng/article/details/122057561

3.3. 哪些是重要的 bean 生命周期方法 能否重载

有两个重要的 bean 生命周期方法,第一个是 setup , 它是在容器加载 bean 的时候被调用。第二个方法是 teardown 它是在容器卸载类的时候被调用。
bean 标签有两个重要的属性(init-method 和 destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct 和@PreDestroy)。

Spring-1、基本原理

3.4. 使用@Autowired 注解自动装配的过程是怎样的⭐️🔴

^ov9bn5

Spring-10、@Autowired

3.5. @PostConstruct 原理⭐️🔴⭐️🔴

^ry6di8

Spring-11、@PostConstruct

3.6. 如何在 Spring 创建完所有 bean 之后做扩展

3.6.1. 基于 SmartInitializingSingleton 接口

在创建所有单例 Bean 的方法中: 

1 finishBeanFactoryInitialization(beanFactory);
SmartInitializingSingleton 接口是在所有的 Bean 实例化完成以后,Spring 回调的方法, 
所以这里也是一个扩展点,可以在单例 bean 全部完成实例化以后做处理。
image.png

3.6.2. 基于 Spring 事件监听

生命周期的最后一步是 finishRefresh();,这里面中有一个方法是 publishEvent
所以这里也可以进行扩展,监听 ContextRefreshedEvent 事件 。
image.png

3.7. Spring 容器启动时,为什么先加载 BeanFactoryPostProcess

  1. 因为 BeanDefinition 会在 ioc 容器加载的先注册, 而 BeanFactoryPostProcess 就是在所有的 BeanDefinition 注册完后做扩展的,所以要先加载 BeanFactoryPostProcess
  2. 解析配置类的组件  它就实现 BeanFactoryPostProcess, 所以要先去加载 BeanFactoryPostProcess

4. BeanDefinition 相关

4.1. BeanDefinition 的注册顺序

1. @Configuration   
2. @Component 
3. @Import—类
4. @Bean
5. @Import—ImportBeanDefinitionRegistrar

详细版
image.png

5. 线程相关

5.1. Spring 如何处理线程并发问题⭐️🔴

^yqybkq
在一般情况下,只有无状态的 Bean 才可以在多线程环境下共享,在 Spring 中,绝大部分 Bean 都可以声明为 singleton 作用域,因为 Spring 对一些 Bean 中非线程安全状态采用 ThreadLocal 进行处理,解决线程安全问题。

==ThreadLocal== 和 ==线程同步机制==都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队而 ThreadLocal 采用了“空间换时间”的方式

ThreadLocal 会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal 提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进 ThreadLocal

6. 设计模式相关

6.1. Spring 中用到了哪些设计模式⭐️🔴⭐️🔴

  1. 单例模式:Bean 默认为单例模式 [[内功心法专题-设计模式-3、单例模式]] ^4jhz9m
  2. 工厂模式:BeanFactory 就是简单工厂模式的体现,用来创建对象的实例 [[内功心法专题-设计模式-4、工厂模式]]
  3. 原型模式: [[内功心法专题-设计模式-5、原型模式]]
  4. 建造者模式: [[内功心法专题-设计模式-6、建造者模式]]
  5. 代理模式:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码生成技术;设计模式-7、代理模式
  6. 适配器模式: 设计模式-8、适配器模式
  7. 装饰者模式: [[内功心法专题-设计模式-9、装饰者模式]]
  8. 桥接模式: 设计模式-10、桥接模式
  9. 组合模式:[[内功心法专题-设计模式-12、组合模式]]
  10. 享元模式:[[内功心法专题-设计模式-13、享元模式]]
  11. 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate [[内功心法专题-设计模式-14、模板方法模式]]
  12. 策略模式:[[内功心法专题-设计模式-15、策略模式]]
  13. 命令模式:[[内功心法专题-设计模式-16、命令模式]]
  14. 责任链模式:[[内功心法专题-设计模式-17、责任链模式]]
  15. 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如 Spring 中 listener 的实现 ApplicationListener [[内功心法专题-设计模式-19、观察者模式]]
  16. 迭代器模式:[[内功心法专题-设计模式-21、迭代器模式]]
  17. 解释器模式:[[内功心法专题-设计模式-24、解释器模式]]
    %%
    2129-🏡⭐️◼️Spring 中用到了哪些设计模式 ?🔜MSTM📝 23 种设计模式中用到了 17 种,其中抽象工厂模式、外观模式、状态模式、中介者模式、访问者模式、备忘录模式 6 种没有使用到◼️⭐️-point-202302052129%%

6.2. 单例 bean 的优势

  1. 减少了新生成实例的消耗。新生成实例消耗包括两方面:第一,spring 会通过反射或者 cglib 来生成 bean 实例这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法。 提供服务器内存的利用率 ,减少服务器内存消耗  
  2. 减少 jvm 垃圾回收由于不会给每个请求都新生成 bean 实例,所以自然回收的对象少了。
  3. 可以快速获取到 bean 因为单例的获取 bean 操作除了第一次生成之外其余的都是从缓存里获取的所以很快。❕%%
    2135-🏡⭐️◼️单例 bean 有哪些好处 ?🔜MSTM📝 1. 减少了生成新实例的性能消耗,包括两部分:Spring 通过反射或者 cglib 方式生成对象的性能消耗、为对象分配内存的性能消耗。2. 减少 JVM 的 GC 和 STW。3. 可以快速获得 bean ◼️⭐️-point-202302052135%%

7. 注解相关

7.1. Spring 有哪几种配置方式

 这里有三种重要的方法给 Spring 容器提供配置元数据。

1. XML 配置文件。  spring 诞生
spring.xml     <bean>

2. 基于注解的配置。  Spring2.5+
spring.xml  + @Component  @Autowired

3. 基于 java 的配置。 JavaConfig  Spring3.0+
@Configuration   @Bean   ….

image.png

7.2. JavaConfig 与 XML 方式 Spring 配置的不同⭐️🔴

^b5hhm9

Spring-8、BeanDefinition

image.png

7.2.1. JavaConfig

image.png

https://www.processon.com/diagraming/63dd8eb4b59543238fa3a6a5

7.2.1.1. 注册核心 BPP

  1. new AnnotationConfigApplicationContext(MainConfig.class) 中的 this 方法中 AnnotatedBeanDefinitionReader 为工厂注册 6 大内置核心组件,其中 ConfigurationClassPostProcessor 是用来处理配置类的
  2. 刷新第5步:invokeBeanFactoryPostProcessors()
    getBean ConfigurationClassPostProcessor

7.2.1.2. 读取配置类 (注册配置类)

new AnnotationConfigApplicationContext(MainConfig.class) 中的 register(componentClasses) 方法中使用 AnnotatedBeanDefinitionReader 注册配置类

image.png

7.2.1.3. 解析配置类 (扫描注册引入类)

执行 ConfigurationClassPostProcessorpostProcessor.postProcessBeanDefinitionRegistry() 方法,把【配置类中的】所有 bean 的定义信息导入进来。由 ConfigurationClassParser 解析每一个配置类

核心方法,将完全填充好的 ConfigurationClass 实例转化为 BeanDefinition 注册入 IOC 容器 this.reader.loadBeanDefinitions(configClasses)

7.2.2. XML

https://www.processon.com/diagraming/63ddb70fc12afe0cadb59d2c
image.png

7.2.2.1. 读取配置文件

7.2.2.1.1. XmlBeanDefinitionReader
7.2.2.1.2. DefaultDocumentLoader

此处获取 xml 文件的 document 对象,这个解析过程是由 documentLoader 完成的,最终开始将 resource 读取成一个 document 文档
image.png

7.2.2.2. 解析配置类

7.2.2.2.1. DefaultBeanDefinitionDocumentReader

image.png

7.3. Component Controller Repository Service

  1. @Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
  2. @Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。
  3. @Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
  4. @Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。

7.4. @Import 可以有几种用法 (注册 BD 方法)

Spring-1、基本原理

4 种
1. 直接指定类 (如果配置类会按配置类正常解析、  如果是个普通类就会解析成 Bean)
2. 通过实现 ImportSelector 接口 可以一次性注册多个,返回一个 string[]  每一个值就是类的完整类路径
a. 通过实现 DeferredImportSelector 接口可以一次性注册多个,返回一个 string[]  每一个值就是类的完整类路径
i. 区别:DeferredImportSelector 顺序靠后
3. 通过实现 ImportBeanDefinitionRegistrar 接口可以一次性注册多个,通过 BeanDefinitionRegistry 来动态注册 BeanDefinition

7.5. @Configuration 的作用解析原理

@Configuration 用来代替 xml 配置方式 spring.xml 配置文件 bean 的吗?不全对
因为没有@Configuration 也是可以配置@Bean

7.5.1. @Configuration 加与不加有什么区别

作用:
加了@Configuration 会为配置类创建 cglib 动态代理(保证配置类@Bean 方法调用 Bean 的单例),@Bean 方法的调用就会通过容器 getBean 进行获取
原理:

  1. 创建 Spring 上下文的时候会在 this() 方法中注册一个解析配置的处理器 ConfigurationClassPostProcessor(它实现了 BeanFactoryPostProcessor 和
    BeanDefinitionRegistryPostProcessor)
  2. 在 refresh () 方法第 5 步中,调用 invokeBeanFactoryPostProcessor,就会去调用
    ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry 进行解析配置(解析配置类就是去解析并注册各种注解的 BeanDefinition,比如@Bean @Configuration @Import @Component … 把这些注解的 BeanDefinition 解析并放入 BeanDefinitionMap 中
  3. 调用 invokeBeanFactoryPostProcessors,还会调用 ConfigurationClassPostProcessor.postProcessBeanFactory,在里面会调用 enhanceConfigurationClasses(beanFactory) 方法来创建 cglib 动态代理

7.6. @Bean 之间的方法调用是怎么保证单例的

https://www.bilibili.com/video/BV1t44y1C73F?p=45&spm_id_from=pageDriver&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204

( @Configuration 加与不加的区别是什么?)
1.如果希望@bean 的方法返回是对象是单例  需要在类上面加上@Configuration,
2.Spring 会在 invokeBeanFactoryPostProcessor  通过内置 BeanFactoryPostProcessor 中会 CGLib 生成动态代理代理
3.当@Bean 方法进行互调时, 则会通过 CGLIB 进行增强,通过调用的方法名作为 bean 的名称去 ioc 容器中获取,进而保证了@Bean 方法的单例

image.png

image.png
image.png

8. AOP 相关

8.1. 是什么做什么

AOP(Aspect-Oriented Programming),一般称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

可用于权限认证、日志、事务处理等。

AOP、OOP 在字面上虽然非常类似,但却是面向不同领域的两种设计思想。OOP(面向对象编程) 针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。 而 AOP 作为面向对象的一种补充,则是针对业务处理过程中的切面进行提取, 已达到业务代码和公共行为代码之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

8.2. 各种概念关系

  1. AOP 实现的关键在于代理模式,AOP 代理主要分为静态代理和动态代理。静态代理的代表为 AspectJ;动态代理则以 Spring AOP 为代表。Spring-3、AOP实现原理-@EnableAspectJAutoProxy
  2. Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理
  3. Objenesis 是 CGLIB 的增强,不需要构造函数就可以代理

直接使用注入的代理类的属性是不行的
当我们@Autowired 注入了代理类,如果想当然的直接使用被注入对象的属性,则一定会报空指针,而我们正常情况为什么没有发生问题呢,原因是我们一般都是调用该对象的方法,而不是直接使用其中的属性的,而调用其中的方法,则代理类处理完切面任务之后,会进入到真正的对象,而真正的对象里的各个属性都是有值的,不会发生空指针。

https://blog.csdn.net/qq_30095631/article/details/108086616

8.3. 解释一下 Spring AOP 里面的几个名词

(1)连接点(Join point): 指定就是被增强的业务方法
(2)切面(Aspect):  在 Spring Aop 指定就是“切面类” ,切面类管理着切点、通知
(3)切点(Pointcut):  由他决定哪些方法需要增强、哪些不需要增强,  结合切点表达式进行实现
(4)通知(Advice):     就是需要增加到业务方法中的公共代码,通知有很多种类型分别可以在需要增加的业务方法
不同位置进行执行(前置通知、后置通知、异常通知、返回通知、环绕通知)
(5)目标对象(Target Object):  指定是增强的对象
(6)织入(Weaving) :  spring aop 用的织入方式:动态代理。  就是为目标对象创建动态代理的过程就叫织入。

8.4. Spring 通知有哪些类型?

在 AOP 术语中,在的某个特定的连接点上执行的动作
Spring 切面可以应用 5 种类型的通知:
1. 前置通知(Before):在目标方法被调用之前调用通知功能;
2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
3. 返回通知(After-returning ):在目标方法成功执行之后调用通知;
4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

执行顺序:

1、正常执行:
@Around > @Before ­­­>方法 > @After ­­­­> @AfterReturning ­­­ > @Around
2、异常执行:
@Around > @Before­­­>方法 > @After­­­­ > @AfterThrowing­­­

Spring 在5.2.7 之后就改变的 advice 的执行顺序

1、正常执行:
@Around > @Before ­­­>方法 ­­­­> @AfterReturning ­­­> @After > @Around
2、异常执行:
@Around > @Before­­­>方法­­­­ > @AfterThrowing­­­ > @After

8.5. Spring AOP 与 AspectJ AOP 有什么区别?

8.5.1. 关系

当在 Spring 中要使用@Aspect、@Before 等这些注解的时候, 就需要添加 AspectJ 相关依赖

image.png

Spring Aop 提供了 AspectJ 的支持,但只用到的 AspectJ 的切点解析和匹配。 @Aspect、@Before 等这些注解都是由 AspectJ 发明的
AOP 实现的关键在于代理模式,AOP 代理主要分为静态代理和动态代理。静态代理的代表为 AspectJ;动态代理则以 Spring AOP 为代表。

8.5.2. 区别

(1)AspectJ 是静态代理的增强,所谓静态代理,就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强,他会在编译阶段将 AspectJ(切面) 织入到 Java 字节码中,运行的时候就是增强之后的 AOP 对象。
(2)Spring AOP 使用的动态代理,它基于动态代理来实现。默认地,如果使用接口的,用 JDK 提供的动态代理实现,如果没有接口,使用 CGLIB 实现。

8.6. JDK 动态代理和 CGLIB 动态代理的区别

Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理:

  1. JDK 动态代理只提供接口的代理,不支持类的代理
    JDK 会在运行时为目标类生成一个动态代理类 $proxy*.class  . 
     该代理类是实现了目标类接口, 并且代理类会实现接口所有的方法增强代码。 
    调用时通过代理类先去调用处理类进行增强,再通过反射的方式进行调用目标方法。从而实现 AOP
  2. 如果代理类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。
    CGLIB 的底层是通过 ASM 在运行时动态的生成目标类的一个子类。(还有其他相关类,主要是为增强调用时效率)会生成多个 ,并且会重写父类所有的方法增强代码,调用时先通过代理类进行增强,再直接调用父类对应的方法进行调用目标方法。从而实现AOP。
    CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。CGLIB 除了生成目标子类代理类,还有一个 FastClass(路由类),可以(但不是必须)让本类方法调用进行增强,而不会像 jdk 代理那样本类方法调用增强会失效,但是为了 AOP 整体一致性,这个特性并未对外使用
  3. 在老版本 CGLIB 的速度是 JDK 速度的 10 倍左右, 但是实际上 JDK 的速度在版本升级的时候每次都提高很多性能,而 CGLIB 仍止步不前。在对 JDK 动态代理与 CGlib 动态代理的代码实验中看,1W 次执行下,JDK7 及 8 的动态代理性能比 CGlib 要好 20% 左右。

8.7. JavaConfig 方式如何启用 AOP? 如何强制使用 cglib

%%
▶3.🏡⭐️◼️【🌈费曼无敌🌈⭐️第一步⭐️】◼️⭐️-point-20230323-0744%%
❕ ^sn8xbz

1
2
3
 @EnableAspectJAutoProxy
(proxyTargetClass = true//强制CGLIB
(exposeProxy = true) 在线程中暴露代理对象@EnableAspectJAutoProxy

^lcbyzk

  1. Spring 5.x 中 AOP 默认依旧使用 JDK 动态代理。
  2. SpringBoot 2.x 开始,为了解决使用 JDK 动态代理可能导致的类型转化异常而默认使用 CGLIB。目标类没有实现接口或者接收代理类的类型不是共同父接口就会导致类型转换异常。
  3. 在 SpringBoot 2.x 中,如果需要默认使用 JDK 动态代理可以通过配置项 spring.aop.proxy-target-class=false 来进行修改,proxyTargetClass 配置已无效。
    %%
    1211-🏡⭐️◼️强制开启 CGLIB 代理的方法 ?🔜MSTM📝 增加配置参数 proxy-target-class=”true”◼️⭐️-point-202302091211%%
Spring-3、AOP实现原理-@EnableAspectJAutoProxy

对比记忆:Spring-8、BeanDefinition
%%
0653-🏡⭐️◼️CGLIB 相关的 2 个配置 ?🔜MSTM📝 都是 proxy 开头,一个用于 AOP 中指定使用 cglib 作为动态代理的生成方式:proxytargetclass。一个用于方法 bean 是否需要 full 模式,即是否需要生成一个 cglib 动态代理:proxybeanmethod。◼️⭐️-point-202302100653%%

^1k8ap0

8.8. AOP 失效 or 如何在同一个 service 中使用传播行为

%%
▶1.🏡⭐️◼️【🌈费曼无敌🌈⭐️第一步⭐️】◼️⭐️-point-20230608-1708%%
❕ ^tzh2qj

解决方式:必须走代理, 重新拿到代理对象再次执行方法才能进行增强

1. 在本类中自动注入当前的 bean
2. 设置暴露当前代理对象到本地线程, 可以通过 AopContext.currentProxy() 拿到当前正在调用的动态代理对象

1
2
@EnableAspectJAutoProxy(exposeProxy=true)
SpuInfoService proxy = (SpuInfoService) AopContext.currentProxy();

8.9. 介绍 AOP 有几种实现方式

  1. Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的,想看源码的同学可以从这里起步。
  2. Spring 2.0 schema-based 配置:Spring 2.0 以后使用 XML 的方式来配置,使用命名空间 
  3. Spring 2.0 @AspectJ 配置:使用注解的方式来配置最方便的,还有,这里虽然叫做 @AspectJ,但是这个和 AspectJ 其实没啥关系。
  4. AspectJ  方式,这种方式其实和 Spring 没有关系,采用 AspectJ 进行动态织入的方式实现 AOP,需要用 AspectJ 单独编译。

8.10. Spring 的 AOP 是在哪里创建的动态代理?

1. 正常的 Bean 会在 Bean 的生命周期的‘初始化’后, 通过 BeanPostProcessor.postProcessAfterInitialization 创建 aop 的动态代理
2. 还有一种特殊情况: 循环依赖的 Bean 会在 Bean 的生命周期‘属性注入’时存在的循环依赖的情况下, 也会为循环依赖的 Bean
通过 MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition 创建 aop

8.11. Spring 的 Aop 的完整实现流程?

^f9oy7j
Aop 的实现大致分为三大步:JavaConfig
当@EnableAspectJAutoProxy 会通过@Import 注册一个 BeanPostProcessor 处理 AOP

  1. 解析切面: 在 Bean 创建之前的第一个 Bean 后置处理器会去解析切面(解析切面中通知、切点,一个通知就会解析成一个 advisor(通知、切点)) 
    image.png

  2. 创建动态代理:正常的 Bean 初始化后调用 BeanPostProcessor  拿到之前缓存的 advisor ,再通过 advisor 中 pointcut  判断当前 Bean 是否被切点表达式匹配,如果匹配,就会为 Bean 创建动态代理(创建方式:1.jdk 动态代理、 2.cglib)。

image.png

  1. 调用:拿到动态代理对象,调用方法就会判断当前方法是否增强的方法,就会通过调用链的方式依次去执行通知
    image.png

9. 事务相关

10. 参考与感谢

10.1. 图灵徐庶

https://www.bilibili.com/video/BV1mf4y1c7cV/?spm_id_from=..search-card.all.click&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
[[Spring全家桶面试题—图灵徐庶.pdf]]
https://www.processon.com/view/link/5f5075c763768959e2d109df#map ^b7goye

Spring-3、AOP实现原理-@EnableAspectJAutoProxy