spring 大致分为IOC和AOP,如下图:
IoC:通过容器去控制业务对象之间的依赖关系。控制权由应用代码中转到了外部容器,控制权的转移就是反转。控制权转移的意义是降低了类之间的耦合度。
创建对象的时候,向类里面的属性设置值。spring依赖注入主要是两种方式:
(1)使用set方法(常用)
(2)使用带参的构造函数
spring的IOC简单的理解大致就是如下过程:
其实最核心的就是IOC容器,就是用来存放Bean或者BeanDefintion信息的容器,我们一般会想到Map,用来做存储,常见的容器存储K‘、V值,如下:
这里的ObjectFacotry,主要用来解决循环依赖的,后续再细聊这一块。
继续说Bean的定义信息,常见的有xml,注解,properties,yaml等,我们需要把各种定义bean信息的格式文件统一读取,解析成BeanDefintion信息,那么就需要一个统一的接口,BeanDefinitionReader.,如:下图
查看源码,实现的子类:
除了实现BeanDefinitionReader接口以外,spring还有一些其他的BeanDefinitionReader,虽然没有实现BeanDefinitionReader,其功能大致都是一直的,只是原理不同罢了。如:AnnotatedBeanDefinitionReader,ConfigurationClassBeanDefinitionReader,JdbcBeanDefinitionReader, 这里不具体讲,后续单独讲。
说到这里有必要说一下,BeanFactory,
Bean工厂, 整个容器的根接口,也是整个容器的入口.
通过反射实例化对象,如下:
Constructor ctor= clazz.getConstructior();
Object obj =ctor.newInstance();
还有必要提一下:bean的作用域,scope,常用的singleton,prototype.
singleton是默认的作用域,当定义Bean时,如果没有指定scope配置项,Bean的作用域被默认为singleton。singleton属于单例模式,在整个系统上下文环境中,仅有一个Bean实例。也就是说,在整个系统上下文环境中,你通过Spring IOC获取的都是同一个实例。
当一个Bean的作用域被定义prototype时,意味着程序每次从IOC容器获取的Bean都是一个新的实例。因此,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
Spring IOC容器创建一个Bean实例时,可以为Bean指定实例的作用域,作用域包括singleton(单例模式)、prototype(原型模式)、request(HTTP请求)、session(会话)、global-session(全局会话)。
创建对象可以分为两个部分,实例化和初始化。
实例化,在堆中开辟一块内存空间,同时给对象的属性值赋默认值,比如基本属性,int 赋值为0,对象属性赋值为null.
初始化,给属性赋值,填充属性值,执行初始化方法,init-method.
在容器创建过程中,需要改变bean的定义信息怎么办?
比如:我们有时候配置数据库的连接信息,如下图:
在这里要说到一个知识点,幕后的操作者就是 Spring 中后置处理器(PostProcessor)
后置处理器分为两种:BeanFactoryPostProcessor(Bean 工厂后置处理器) 和 BeanPostProcessor(Bean 后置处理器)
BeanFactoryPostProcessor 接口里面只有一个 postProcessBeanFactory 方法,结合接口的注释和接口里的方法注释,可以得出,这个接口是一个 BeanFactory 钩子接口,实现该接口,可以通过 postProcessBeanFactory 方法在 BeanFactory 实例化后,Bean 实例化前调用该接口,对 BeanFactory 中的 BeanDefinition 或 BeanDefinition 的元数据进行修改。
BeanDefinitionRegistryPostProcessor
BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor,在 Bean 实例化和检测之前,实现了一个非常重要的功能,动态注册一个 BeanDefinition,除此之外,你还可以修改已注册的 BeanDefinition 信息。
BeanPostProcessor
BeanPostProcessor 是 Bean 后置处理器接口,里面包含两个方法:postProcessBeforeInitialization 和 postProcessAfterInitialization。前者在 Bean 实例化前调用,后者则是在实例化后。比如生成一个 Bean 的代理类,就是其子类做的。
别小看上面三个接口,Spring 绝大部分重要技术都是这三个接口的子类来实现,它们也是扩展接口,可以自定义类来实现它们,以增强 Spring 功能或者是扩展 Spring 功能,又或是第三方框架。
后续再细聊这一块。
所以大致的流程就是:定位配置文件,加载bean的信息,读取解析bean的信息,转化为:BeanDefinition,在中间过程中可以对BeanDefinition进行修改或者增强,实例化对象,填充属性,设置 Aware接口的属性,对bean初始化之前增强,执行init-method,再bean的初始化后增强,最后生成完整的对象,存入容器中,需要用的时候,从容器中获取即可。
这里要特别说明一下:Aware接口,
Aware.java是个没有定义任何方法的接口,拥有众多子接口,在spring源码中有多处都在使用这些子接口完成各种场景下的回调操作,当业务有需要时,我们只需创建类来实现相关接口,再声明为bean,就可以被spring容器主动回调;
比如:我们想要在一个类中获取ApplicationContext对象,怎么获取,定义一个类,实现ApplicationContextAware接口
实现setApplicationContext方法,定义一个私有ApplicationContext对象,在setApplicationContext方法中赋值即可,然后再定义一个getApplicationContext方法,提供对外使用。
需要获取容器中的其他属性或者对象,都可以通过实现Aware接口,让容器回调赋值获取。
容器中到底在哪里给我们调用我们定义的方法呢?看下源码:AbstractAutowireCapableBeanFactory类中的doCreateBean方法。
这里再看一个抽象类AbstractAutoProxyCreator,主要是用来实现AOP,创建动态代理对象的。
最后发现,就是cglib和jdk两种动态代理。
如果看源码,需要翻译,可以安装插件:如下图:
下面再继续聊一下bean的生命周期,可以先看下源码:BeanFactory
下面再提一个问题:如果 :在不同的阶段要处理不同的工作,应该怎么办?Spring里面是通过,观察者模式:监听器,监听事件,多播器(广播器)来实现的。AbstractApplicationContext的Refresh方法中,如下图:
如果看源码,可以注意一些关键的接口,如下:
这里可以看下Environment是什么时候创建的,
最后再来说说BeanFacotry和FactoryBean的区别,这里也是面试最喜欢问的
先来自定义一个类
如果需要获取工厂bean,需要加&符号
那到底在哪里调用了getObject()方法呢?
下面看下源码:从getBean方法开始找
找到doGetBean,中间有些步骤省去了