spring原理

mybatis与spring整合原理

我们会在spring配置文件中配置mybatis的MapperScannerConfigurer对象的ioc和di,di的时候我们传入的参数就是我们项目中mapper接口所在包,这个scanner实现了spring容器的一个后置接口,这个scanner会在spring容器初始化过程中会自动调用,在scanner方法中会先获取我们所有的mapper接口,并且为其生成对应的BeanDefinition对象,然后会将每个BeanDefinition对象的类型替换为mybatis的MapperFactoryBean类,MapperFactoryBean类实现了FactoryBean接口,而在其中的getObject方法中就实现了利用我们的mapper接口生成动态代理对象的代码,之后mybatis就将这个MapperFactoryBean对象交给spring容器进行管理,spring容器在后续的初始化过程中就会自动调用其中getObject方法创建并返回mapper的代理对象。所以我们能够在项目的service中直接注入mapper接口,当我们调用mapper接口的任何方法时,都会触发这个对象的invoke方法,在invoke方法中,就会去解析触发invoke的方法上通过注解编写的sql,或者在xml配置文件中编写的sql,将其发送到数据库进行具体操作

spring启动流程(Spring整合Servlet原理)

首先spring会通过我们在web.xml中配置的ContextLoadListener监听器监听创建servletContext对象的创建,servletContext对象是整个web项目共享的一个域对象,这个对象就相当于是项目本身,在这个监听器当中就会创建BeanFactory对象,BeanFactory是spring容器的顶级接口,spring容器在初始化过程中一般会使用ApplicationConetxt这个子接口,这个接口相比于BeanFactory接口,会定义更多的属性和方法,这里就会利用这个接口来创建spring容器,之后会读取我们配置的spring容器的配置文件,在配置文件中我们会定义很多bean标签,这些bean标签就相当于之后需要交给spring容器管理的对象,spring在读取到这些bean标签后,会将bean标签转化为BeanDefinition对象,这个对象会保存bean标签中配置的详细信息,之后就会刷新整个spring容器并且利用所有BeanDefinition对象初始化所有单例bean对象,然后再把这些bean对象放入到spring容器中,这一步就相当于完成了spring的ioc和di,之后会spring会把这个spring容器放到servletContext域对象当中,完成spring和servlet的整合,这时spring容器的初始化就已经完成。

(拓展:如果我们的项目采用的是原生springmvc的框架,在这里初始化的容器就相当于spring的父容器,而之后会继续创建spring的子容器,子容器的创建是在DispatcherServlet前端控制器创建时创建的,前端控制器实际就是一个servlet,默认是在第一次访问时创建,也可以配置项目启动时创建,但无论哪种创建方式,都是在ServletContext域对象创建后才创建的,所以一定是在父容器创建后才会创建子容器。在创建时会读取我们配置的springmvc配置文件中的内容,然后进行ioc和di,同时子容器创建时会获取父容器的引用,原因是父容器一般会存放Service层对象和Mapper层对象,而子容器中存放的是Controller层对象,在使用springmvc的三层结构时,我们需要在controller层注入service层的对象,也就是需要在子容器中注入父容器管理的对象,所以需要在子容器中通过父容器的引用进行注入,这也就是为什么可以在controller注入service对象,而不能在service对象中注入controller对象)

BeanDefinition

BeanDefinition是spring定义的,专门用于存放在我们在xml配置文件中通过bean标签,或者是java配置文件中通过bean注解所定义的,这些会放入spring容器中的对象所对应的类的信息及其属性的,因为还需要在BeanDefinition中存放类中的属性信息,所以BeanDefinition又继承了AttributAccessor接口,AttributAccessor中就定义了保存类中属性的各种方法。所以BeanDefinition对象和Bean对象就相当于类和类对象的关系。在创建完成BeanDefinition对象后,spring会将其通过BeanDefinitionRegistry中的方法将BeanDefinition对象放入一个spring容器中的map集合BeanDefinitionMap中。

如何在spring容器管理的对象中操作spring的一些底层组件?

使这个对象的类实现xxxAware接口,只要某类实现了xxAware接口,就能将对应的组件传递给对象,比如Aware接口的子接口包括了BeanNameAware、ClassLoaderAware、BeanFactoryAware、ApplicationContextAware,如果我们想在对象中操作spring容器,则可以实现BeanFactoryAware或者ApplicationContextAware,并实现setBeanFactoryAware或者setApplicationContextAware方法,这个方法就是个回调方法,在当前这个类对应的BeanDefinition对象创建并注册完成后,就会调用这个回调方法,将spring容器传递到方法中,我们就可以在方法中对组件进行操作

BeanFactoryPostProcessor

spring容器初始化后置处理器,spring会在生成所有BeanDefinition后会自动调用

用于获取Spring容器中BeanDefinitionMap中保存的BeanDefinition对象,并可以针对这些BeanDefinition对象的信息进行自定义修改,之后会根据修改后的BeanDefinition对象创建Bean对象,在实现时先编写一个类实现BeanFactoryPostProcessor接口,并重写其中的postProcessBeanFactory,这个方法会被spring在完成BeanDefinitionMap的初始化后调用,其中会传入spring容器,我们可以通过容器的getBeanDefinition方法取得特定的BeanDefinition对象,然后我们就能针对这个beanDefinition对象的属性进行自定义修改,之后我们再在xml配置文件中配置一下这个实现BeanFactoryPostProcessor的类,最后spring容器中存放的对象就是我们修改beanDefinition后创建的对象

BeanDefinitionRegistryPostProcessor

上面接口的子接口,属于spring容器初始化后置处理器,spring会在生成所有BeanDefinition后会自动调用

BeanFactoryPostProcessor中的postProcessBeanFactory方法只能从BeanDefinitionMap中获取已有的BeanDefinition对象,并针对其进行修改,而BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的一个子接口,其中还新提供了一个postProcessBeanDefinitionRegistry方法,spring在调用这个方法时会传递BeanDefinitionRegistry对象,我们就能够在方法中通过这个registry对象向BeanDefinitionMap注册新的BeanDefinition对象,在之后进行spring容器的ioc和di时,就会出现这个对象

BeanPostProcessor

这个接口实现类会在spring容器中对象完成ioc和di才会调用,其中有两个方法,分别在bean实例显示的初始化之前和之后执行,在实现时直接编写类实现这个接口,并在xml中用bean标签配置就能自动生效,流程如下
在这里插入图片描述
bean实例的显示初始化有两种,这两种方法都是在BeanPostProcessor的前置处理执行完毕后、后置处理执行前执行的

  1. 让Bean实现InitializingBean接口,并重写afterPropertiesSet方法
  2. 在Bean中定义一个init方法,在xml配置文件中bean标签中使用init-method属性调用

BeanPostProcessor的应用(aop代理对象的创建):
我们可以利用BeanPostProcessor的前置处理方法为当前已经实例化的对象创建动态代理对象,这个动态代理对象中可以针对原实例对象的方法进行增强,之后这个对象就会取代之前的对象存放到spring容器中,比如我们可以利用cglib的方式在这里为已经完成ioc的di的实体对象创建代理对象,实现的时候先使用Enhancer对象的父类设置为原对象,再编写Enhancer对象的回调函数,在调用任何原对象的方法时,都会先调用这个回调方法,所以在回调方法中就能对原对象中的被调用方法进行增强

代码如下
在这里插入图片描述
在调用spring容器中名为user的任何方法就会先执行回调方法

spring中Bean的生命周期

在这里插入图片描述

spring启动时会扫描所有配置文件,并依次将Java配置文件中Bean注解或xml配置文件中的bean标签转换成BeanDefinition对象,然后会利用BeanDefinitionRegistry对象将所有BeanDefinition对象放入spring中的一个map集合,然后spring会检查我们项目中有没有实现了spring前置处理器接口的类,前置处理器接口BeanFactoryPostProcessor或者其子接口BeanDefinitionRegistryPostProcessor中的方法对map集合中的BeanDefinition对象进行修改,前者的方法只能针对map中已有的BeanDefinition对象进行修改,后者不仅可以修改还可以进行新增。(其实在Mybatis底层在为mapper接口生成代理对象时就是实现了后者的接口,根据mapper接口生成了对应的BeanDefinition对象)执行完成后就会从map集合中依次取出BeanDefinition对象,并根据BeanDefinition对象生成Bean对象,然后再进行Bean对象的依赖注入,完成Bean的初始化后spring会检查每个Bean对应的类有没有实现Aware接口,如果实现了Aware接口在这里就会将对应的Aware接口的组件传递给对应的Bean对象,其中可以对这个spring的底层组件进行操作,常见的Aware接口的子接口包括BeanNameAware、ApplicationContextAware、BeanFactoryAware等等。之后spring又会检查我们项目中有没有实现了spring后置处理器BeanPostProcessor的类,如果有的话就执行其中的方法,后置处理器中有两个方法,分别会在Bean对象的显示的初始化方法执行之前和之后执行,显示的初始化方法可以通过两种方式定义,分别是实现InitializingBean接口的方法和在bean标签中通过init-method属性指定特定的初始化方法。在这之前会调用后置处理器的第一个方法,这个方法当中会传入已经完成ioc和di的对象,在这个方法中我们可以针对已经创建完成的对象进行进一步封装,(利用cglib的继承的方式为对象生成动态代理对象,具体实现方法就是创建一个Enhancer对象,将传入的springbean对象的类设置为Enhancer类对象的父类,然后编写Enhancer对象的回调方法,这个回调方法就会在被代理的springbean对象的任何方法被调用前触发,所以我们可以在这个回调方法中针对springbean对象的某些特定方法进行增强),比如aop代理对象就是在这里进行创建的,在这里会先判断当前aop对象有没有实现接口,如果实现了接口就用jdk动态代理,如果没有实现接口就用cglib动态代理,如果我们在这里创建了原springbean对象的代理对象,这个代理对象就会取代原springbean对象被放入spring容器中。这个方法执行完成后就是执行显示的初始化方法,显示的初始化方法执行完成后再执行BeanPostProcessor中的第二个方法,这里也可以对springbean进行进一步加工和处理。到这里整个spring容器就已经初始化完毕,当spring容器关闭时,我们可以通过两种方法来配置springbean对象的销毁方法,第一种是实现DisposableBean接口,第二种是在bean标签上通过destroy属性指定销毁方法,这些方法就会在spring容器关闭前自动执行

为什么需要三级缓存

当spring容器中两个对象的属性需要相互注入时,如果只有一个spring对象单例池则不可能完成,所以引入了二级缓存,在二级缓存的半成品池中会先存放一个对象的地址,然后再去初始化第二个对象,在第二个对象的属性注入时就从半成品池中取出第一个对象的地址进行注入,但如果当这两个对象互相注入的是在spring后置处理器BeanPostProcessor中创建的代理对象时,比如出现了aop代理对象的循环依赖,二级缓存就不能实现,因为二级缓存中存放的是提前暴露的还未完成di的对象,而aop代理对象正常的创建时间应该在ioc和di已经完全完成之后。所以这里就引入了三级缓存工厂对象池来解决两个代理对象之间相互注入的问题

三级缓存的原理

一级缓存单例池,存放完全完成ioc和di的对象
二级缓存半成品池,存放提前暴露的还未完成di的对象
三级缓存工厂对象池,存放aop对象对应的工厂对象

aop处理器类会实现BeanPostProcessor接口,所以aop代理对象的正常创建时间是在显示的初始化方法之后才会创建,但如果出现了两个aop代理对象的循环依赖,这里就需要用到spring的第三级缓存,即工厂对象池。spring在初始化时会针对每个aop代理对象首先创建一个工厂对象,工厂对象中会有个方法用于提前创建代理对象,然后spring会把这个工厂对象放入三级缓存的工厂对象池中。比如在项目中如果存在两个aop对象A和B,这两个对象会分别对应一个工厂对象池中的工厂对象,如果A和B出现了循环依赖,假设spring先针对A进行初始化,当在注入B代理对象时会发现B还没有创建,并且spring发现B也是aop对象时,就会在这里调用三级缓存中存放的A的工厂对象,利用工厂对象提前为A创建代理对象,然后将A提前创建的aop代理对象放入半成品池中,再去初始化B对象,在B对象的初始化过程中就会一路正常进行,先是实例化B对象,对B对象属性进行注入,在注入A的代理对象时直接从半成品池中取出之前提前创建的A的代理对象即可,然后调用显示的初始化方法,最后在spring后置处理方法中为B创建代理对象,所以B的aop代理对象的创建过程是完全正常的,不需要提前为B创建aop代理对象。创建完B代理对象后就将B放入一级缓存单例池中,然后再将单例池中B对象注入放在半成品池的A代理对象中,然后再调用A代理对象的显示初始化方法和后置处理方法,在后置处理方法中就会发现当前A对象的aop代理对象已经在进行di的过程中通过调用三级缓存工厂池中的工厂类方法提前创建过,所以此时在A的后置处理方法中无需再次创建,就直接完成A代理对象的初始化,放入单例池中,完成aop对象的循环依赖过程

流程图,上面一张图是aop对象的正常创建流程,下面一张是两个aop对象出现循环依赖时的创建流程
在这里插入图片描述

动态代理:
mybatis的mapper:BeanDefinitionRegistryPostProcessor+FactoryBean,在前置处理器中将mapper接口->BeanDefinition对象->mapperFactoryBean对象
aop:BeanPostProcessor,后置处理器中判断是否实现接口选择jdk或cglib
feign:FactoryBean
dubbo:调用方的service接口

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值