拉钩笔记_模块二

spring架构

Spring是⼀个分层⾮常清晰并且依赖关系、职责定位⾮常明确的轻量级框架,主要包括⼏个⼤模块:数据处理模块、Web模块、AOP(Aspect Oriented Programming)/Aspects模块、Core Container模块和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了⼀个令⼈愉悦的融合了现有解决⽅案的零侵⼊的轻量级框架。


拉钩笔记_模块二_第1张图片
spring架构
  • Spring核⼼容器(Core Container)容器是Spring框架最核⼼的部分,它管理着Spring应⽤中bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼容器之上。

  • ⾯向切⾯编程(AOP)/Aspects Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应⽤系统中开发切⾯的基础,与DI⼀样,AOP可以帮助应⽤对象解耦。

  • 数据访问与集成(Data Access/Integration)Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外,Spring AOP为数据访问提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、MyBatis等。该模块由JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。

  • Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。

  • Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测试。 通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。

IOC

1.什么是IOC

IoC Inversion of Control (控制反转/反转控制),注意它是⼀个技术思想,不是⼀个技术实现描述的事情:Java开发领域对象的创建,管理的问题

  • 传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象
  • IoC思想下开发⽅式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可

我们丧失了⼀个权利(创建、管理对象的权利),得到了⼀个福利(不⽤考虑对象的创建、管理等⼀系列事情)

为什么叫做控制反转?

  • 控制:指的是对象创建(实例化、管理)的权利
  • 反转:控制权交给外部环境了(spring框架、IoC容器)


    拉钩笔记_模块二_第2张图片
    什么是ioc

2.IOC解决了什么问题

IoC解决对象之间的耦合问题


拉钩笔记_模块二_第3张图片
对象耦合关系

AOP

1.什么是AOP

  • AOP: Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程
    AOP是OOP的延续,从OOP说起
  • OOP三⼤特征:封装、继承和多态
    OOP是⼀种垂直继承体系


    拉钩笔记_模块二_第4张图片
    OOP纵向继承体系

    OOP编程思想可以解决⼤多数的代码重复问题,但是有⼀些情况是处理不了的,⽐如下⾯的在顶级⽗类
    Animal中的多个⽅法中相同位置出现了重复代码,OOP就解决不了


    拉钩笔记_模块二_第5张图片
    重复代码

2.AOP在解决什么问题,如何解决

  • 在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复
  • 「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑
    「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯,有⼀个
    ⾯的概念在⾥⾯

1.Spring的生命周期

我们在使用Spring的时候,会创建一个main方法,然后在class上添加@SpringBootApplication注解。在main方法中调用SpringApplication的run方法,之后容器便会启动。这时我们需要将注意关注到SpringApplication这个类方法上。

SpringApplication

初始化SpringApplication

SpringApplication在初始化时会在构造函数中调用initialize方法,此方法中主要做了两件事。

  • 加载设置初始化器对象。

  • 加载设置监听器。

注:初始化器与监听器的加载方式都是采用的Spring SPI扩展,通过读取META-INF下的spring.factories文件中的值进行初始化。

拉钩笔记_模块二_第6张图片
image.jpeg

这里初始化的监听器与初始化器如下:

拉钩笔记_模块二_第7张图片
image.jpeg

创建生命周期监听器

SpringApplication初始化完毕后,接下来会调用对应的run方法启动SpringBoot。首先,我们先看到run方法中的这么一句调用:

拉钩笔记_模块二_第8张图片
image.jpeg

此方法中,同样做了如下两件事:

  • 初始化SpringApplicationRunListeners,将SpringApplicationRunListener作为参数初始化

  • 获取SpringApplicationRunListener同样采用SPI方式获取

拉钩笔记_模块二_第9张图片
image.jpeg

这里初始化的SpringApplicationRunListener对象如下:

image.jpeg

EventPublishingRunListener对象在初始化的时候,会将SpringApplication作为对象传递进来。然后创建SimpleApplicationEventMulticaster对象并且将SpringApplication中的监听器传递进去。

拉钩笔记_模块二_第10张图片
image.jpeg

生命周期Starting

掉用starting后,会遍历SpringApplicationRunListener中所有的listeners,并调用对应的starting方法。如下所示:

image.jpeg

其中根据之前的逻辑,我们可知这个listeners中只有一个EventPublishingRunListener实例对象。EventPublishingRunListener中的starting方法如下所示:

拉钩笔记_模块二_第11张图片
image.jpeg

这里我们可以看到首先,封装了一个ApplicationStartedEvent事件,然后作为参数执行multicastEvent方法。如下所示:

拉钩笔记_模块二_第12张图片
image.jpeg
拉钩笔记_模块二_第13张图片
image.jpeg

进入到multicastEvent方法后,主要做了如下几点操作

  • 根据ApplicationEvent获取对应所有的listener,这里的listener就是之前SpringApplication中在初始化时通过SPI加载的listener。

  • 遍历执行对应listener中的onApplication(Event)方法。

其他生命周期

其他生命周期的调用,与starting一样,通过构建一个Event事件,然后调用EventPublishingRunListener中的对应方法,在此方法中或找到Application中的listener执行对应的逻辑操作。如图所示:

拉钩笔记_模块二_第14张图片
image.jpeg

所以SpringApplicarion的生命周期有如下几步:

拉钩笔记_模块二_第15张图片
image.jpeg

2.BeanFactory

基础概念

BeanFactory是IOC容器对象的顶级接口,提供了IOC容器最基本的形态,给具体的IOC实现提供了规范。但是因为BeanFactory这个接口比较原始,无法支持Spring的很多其他插件,所以由这个接口派生出了ApplicationContext接口,通常建议比BeanFactory优先。
类图如下所示


拉钩笔记_模块二_第16张图片
image.jpeg

Spring代码中创建

Spring在SpringApplication.run方法中调用createApplicationContext()获取ApplicationContext实例对象。默认实例对象为AnnotationConfigEmbeddedWebApplicationContext。如下所示:

拉钩笔记_模块二_第17张图片
image.jpeg
拉钩笔记_模块二_第18张图片
image.jpeg

在AnnotationConfigEmbeddedWebApplicationContext的父类GenericApplicationContext中,当进行初始化逻辑时,会默认创建一个BeanFactory实例,这个BeanFactory用来初始化IOC容器中的bean对象。

拉钩笔记_模块二_第19张图片
image.jpeg

在Spring中ApplicationContext主要子类的装饰

AnnotationConfigEmbeddedWebApplicationContext

用来读取注解类,主要是@Configuration、@Component、injext相关注解。允许逐个注册类(将类名指定为配置位置)以及类路径扫描(将基本包指定为配置位置)

EmbeddedWebApplicationContext

WebApplicationContext实例,主要用于Servlet相关bean的创建。能够通过EmbeddedServletContainerFactory工厂类引导创建相关属性。在这个实例中,会根据EmbeddedServletContainerFactory创建一个EmbeddedServletContainer实例作为容器的上下文,

另外,上下文中定义的任何bean都将自动注册到嵌入式Servlet容器中。对于单个Servlet bean,将使用“/”映射。如果找到多个Servlet bean,那么小写的bean名称将用作映射前缀。任何名为“dispatcherServlet”的Servlet将始终映射到“/”。Filter bean将映射到所有url('/*')。

GenericApplicationContext

包含DefaultListableBeanFactory实例且不采用特定bean定义格式实现的ApplicationContext实例。实现BeanDefinitionRegistry接口,以便允读取任何的bean定义。

AbstractApplicationContext

实现ApplicationContext的抽象类,主要使用模板模式,提供大量的模板方法供子类实现,其中的refresh()方法最为重要,会初始化所有的bean对象也是初始化bean的调用入口,会直接在SpringApplication中调用。

3.BeanDefinition

基础概念
BeanDefinition接口定义了一套规范,这套规范用来描述一个bean的实例,Spring通过扫描@Configuration、@Component等注解生成BeanDefinition实例保存在beanFactory中的beanDefinitionMap中,通过bean name进行索引。
当容器需要初始化某一个bean时,会优先通过bean name去beanDefinitionMap中读取对应的BeanDefinition实例,然后根据属性信息进行bean的创建。
具体类图如下所示:


拉钩笔记_模块二_第20张图片
image.jpeg

Spring扫描创建BeanDefinition过程

在Spring中,我们可以使用XML、注解、SPI的方式去向容器中注入一个bean实例对象。其中XML与SPI都是采用配置文件的方式直接指定bean的class进行加载,这里我们只说明注解模式下的扫描加载方式。

在上一节中,我们了解到了BeanFactory的作用是用来创建bean实例的工厂方法,自然BeanDefinition的创建也离不开BeanFactory的影子。首先我们还是先找到创建ApplicationContext的位置(BeanFactory的派生类)。如下图所示:

拉钩笔记_模块二_第21张图片
image.jpeg

我们之前了解到,在①处我们创建了ApplicationContext。但是此时的ApplicationContext中并没有各个类的BeanDefinition信息,而具体生成是在②处完成。我们进入refreshContext方法后,发现这个方法执行了((AbstractApplicationContext) applicationContext).refresh();进行刷新,而在这个刷新逻辑中会对所有的bean进行IOC注入。如下图所示:

拉钩笔记_模块二_第22张图片
image.jpeg
拉钩笔记_模块二_第23张图片
image.jpeg

而SpringBoot对BeanDefinition的扫描在上图的invokeBeanFactoryPostProcessors方法中执行。我们暂时先直接跳到具体的加载逻辑。如下所示:

拉钩笔记_模块二_第24张图片
image.jpeg

这里,分三步加载了所有BeanDefinition实例

1.扫描加载所有实现了PriorityOrdered接口的class,目标类为ConfigurationClassPostProcessor其中我们通常使用的@Configuration、@Component等注解都是在此扫描生成BeanDefinition。

2.扫描加载所有实现了Ordered接口的class,主要是三方接入的扫描,比如baidu的disconf

3.扫描加载所有其他的class,如SpringSecuret相关

4.BeanFactoryPostProcessor

基本概念

BeanFactoryPostProcessor为一个接口类,里面仅提供了一个postProcessBeanFactory的方法,入参为BeanFactory的派生类ConfigurableListableBeanFactory。

具体如下所示:

拉钩笔记_模块二_第25张图片
image.jpeg

根据里面的入参,我们可知此接口提供了对BeanFactory的自定义操作方式,我们可以通过操作BeanFactory自定义注入相关的BeanDefinition实例,自定义进行bean的注入操作。一般可以作为接入其他组件使用。

SpringBoot调用位置

BeanFactoryPostProcessor在SpringBoot生命周期的refresh中调用如下图所示:

拉钩笔记_模块二_第26张图片
image.jpeg
拉钩笔记_模块二_第27张图片
image.jpeg
拉钩笔记_模块二_第28张图片
image.jpeg

SpringBoot中大多数BeanFactoryPostProcessor实例会从BeanFactory中获取通过getBean方法获取,所以我们在定义BeanFactoryPostProcessor对象进行注入时,只需要将此class声明为Bean进行注入即可,SpringBoot会在执行Bean初始化前调用BeanFactoryPostProcessor对象。

5.BeanPostProcessor

基本概念

BeanPostProcessor同样也是一个接口,但是与BeanFactoryPostProcessor不同的是,他们在Bean生命周期中执行的位置,接口执行的意义都是不一样的。BeanPostProcessor接口有两个方法,分别是postProcessBeforeInitialization与postProcessAfterInitialization,入参同样都是Object bean与String beanName。

具体如下:

拉钩笔记_模块二_第29张图片
image.jpeg

此接口为一个钩子接口,在IOC容器初始化Bean的时候调用,两个方法分别在Bean初始化时与Bean初始化完成进行调用。用来在Bean的生命周期中进行自定义处理。

有一点需要注意的是BeanPostProcessor是注册在BeanFactory上的,并不会与某一个class进行绑定,所以在入参中会传递进来每一个正在进行初始化或者已经初始化完毕的Bean对象与beanName,我们需要通过此信息与我们想要进行操作的Bean进行匹配,然后执行。

SpringBoot调用位置

同样BeanPostProcessor任然是在refresh方法中进行调用,具体调用路径如下:

拉钩笔记_模块二_第30张图片
image.jpeg
image.jpeg
拉钩笔记_模块二_第31张图片
image.jpeg

与BeanFactoryPostProcessor不同的是,在refresh中只对BeanPostProcessor进行了注册,并没有调用调用逻辑,而且这里同样也是通过BeanFactory的getBean方法获取具体的实例,所以我们同样在实现此实例时只用将其声明为Bean即可。

具体的执行位置如下:

拉钩笔记_模块二_第32张图片
image.jpeg
拉钩笔记_模块二_第33张图片
image.jpeg
拉钩笔记_模块二_第34张图片
image.jpeg
拉钩笔记_模块二_第35张图片
image.jpeg
拉钩笔记_模块二_第36张图片
image.jpeg
拉钩笔记_模块二_第37张图片
image.jpeg
拉钩笔记_模块二_第38张图片
image.jpeg

你可能感兴趣的:(拉钩笔记_模块二)