1.基础知识

Spring有两个核心功能,分别是ioc和aop,其中ioc是控制反转,aop是切面编程。

在ioc中,还有一个名次叫DI,也就是依赖注入。嗯,好像IOC和DI是指同一个,好像又感觉他俩不是同一个。

具体的区别是:IOC是DI的原理。依赖注入是向某个类或方法注入一个值,其中所用到的原理就是控制反转。

所以说到操作层面的时候用DI,原理层的是说IOC,下文亦同。

对于DI最新使用方法,一般都是Java注解去标识。但是用这种方式去看源码,好像不太适合。用XML的方式,根据一个demo来进行源码的阅读,比较适合。

2.demo代码

BeanService:

public interface BeanService {
String getName();
}

BeanServiceImpl:

public class BeanServiceImpl implements BeanService {
public String getName() {
return "----------------------------------------:I am Bean";
}
}

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd <!--ioc源码-->
<bean id="beanService" class="com.example.demo.ershi.IocSource.BeanServiceImpl"/> </beans>

test代码:

public class BeanTest {
public static void main(String[] args) {
// 加载xml配置
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContextF.xml"); // IOC获取Bean
BeanService beanService = context.getBean(BeanService.class); System.out.println(beanService.getName());
}
}

测试结果:

22:01:07.069 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanService'
22:01:07.069 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'beanService'
22:01:07.069 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'beanService' to allow for resolving potential circular references
22:01:07.069 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'beanService'
22:01:07.070 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@67205a84]
22:01:07.070 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
22:01:07.071 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
22:01:07.072 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'beanService'
----------------------------------------:I am Bean

3.源码阅读

3.1 Bean的含义

前置先解释下这个Bean的含义,因为会贯穿整个流程。
通俗地讲,Bean就是IOC的容器。如上面的例子,将BeanService注册到Spring里,那么BeanService就是Spring的里面的一个Bean。Demo里面context.getName()就是从Spring中取出这个Bean,完成控制反转的。
所以我们的重点就是要看看Spring到底是怎么生成管理这些Bean的。

3.2 ClassPathXmlApplicationContext

启动类中,加载配置的ClassPathXmlApplicationContext肯定就是完成IOC的核心。不知道它到底是怎么做的,怎么入手呢?
先来看看它的类
先分析下这个类图:

  1. ClassPathXmlApplicationContext类是AbstractApplicationContext抽象类的子类
  2. AbstractApplicationContext类是ApplicaionContext接口的实现。
  3. ApplicaionContext接口集合了非常多的内容,其中和IOC比较相关的就是ListableBeanFactory接口和HierarchicalBeanFactory接口
  4. ListableBeanFactory接口和HierarchicalBeanFactory接口是继承BeanFactory

从此分析可以看出,ClassPathXmlApplicationContext是什么,了解下ApplicaionContext;它怎么和IOC有关,要了解BeanFactory
所以后面我们先来看看ApplicaionContextBeanFactory

3.3 ApplicationContext

/**
*提供应用程序配置的中央接口
*当应用程序运行时,这是只读的,但如果实现支持,则可以重新加载
*用于访问应用程序组件的Bean工厂方法
*以通用方式加载文件资源的能力
*拥有向注册侦听器发布事件的能力
*解析消息的能力,支持国际化
*为了让BeanFactory拥有生命周期,实现了一些类
*/
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver { /**
* 返回application context唯一的id
String getId(); /**
*返回上下文中已经部署的应用程序的名称
*/
String getApplicationName(); /**
* 返回一个展示名称
*/
String getDisplayName(); /**
* 程序第一次加载时返回时间.
*/
long getStartupDate(); /**
* 返回父应用程序
*/
ApplicationContext getParent(); /**
* Expose AutowireCapableBeanFactory functionality for this context.
*/
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException; }

ApplicationContext

从该接口的注解描述可知,ApplicationContext是整个项目的配置,Spring项目在启动或运行的时候都需要依赖到它。

其中Bean管理相关的则是ListableBeanFactoryHierarchicalBeanFactory

3.4 BeanFactory

ListableBeanFactoryHierarchicalBeanFactory都是继承BeanFactory的。

/**
*用于访问SpringBean容器的顶级接口
*根据bean的定义,工厂将返回包含对象的独立实例或者单例的对象
*HierarchicalBeanFactory是一个分层的Bean,如果实现了这个接口,所有方法都会经过父类的工厂。
* ListableBeanFactory这个接口是要实现预先加载Bean的配置,生成好实例,直接管理Bean的实例,而不是来一个请求,生成一个。
*/
public interface BeanFactory { /**
* 用来获得实例的引用,并且区分FactoryBean区分。
* 如果使用bean的名字myJndiObject获取FactoryBean,返回的是一个工厂,而不是工厂的实例;如果需要获得工厂实例,需要转义。
*/
String FACTORY_BEAN_PREFIX = "&"; /**
* 根据bean的名称,获取指定的bean实例,该实例可以是共享的,也可以是独立的.
*/
Object getBean(String name) throws BeansException; /**
* 根据bean的名称,获取指定的bean实例,该实例可以是共享的,也可以是独立的.并且增加了一个类型的检验。
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; /**
* 根据给定类型返回匹配的bean实例.
*/
<T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; /**
* 检查spring的bean容器中是否包含有该bean
*/
boolean containsBean(String name); /**
* 判断bean的作用域是否是singleton
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException; /**
* 判断bena的作用域是否是prototype
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /**
* 检查给定名称的bean是否和指定类型匹配.更确却的说是通过检查给定的bean,返回指定类型的目标对象
*/
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException; /**
* 获取给定名称的bean的class类型
*/
Class<?> getType(String name) throws NoSuchBeanDefinitionException; /**
* 获取给定bean名称的别名,如果根据别名检索,将会获得原始bean名称。
*
String[] getAliases(String name); } BeanFactory

BeanFactory

3.5初始化IOC容器

ClassPathXmlApplicationContext的构造函数看,最核心的就是refresh()函数,其他只是设一些值。
而这个refresh()是调用父类AbstractApplicationContext中的refresh()
根据它的注解可知它是加载刷新了整个context,并且加载所有Bean定义和创建对应的单例。

ClassPathXmlApplicationContext中refresh()方法:

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

AbstractApplicationContext中refresh()方法:

public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
//启动监控标识,并且同步此对象,防止同一时间有多个
//线程加载
synchronized(this.startupShutdownMonitor) {
//初始化一些上下文参数
this.prepareRefresh();
//创建一个BeanFactory,进去后只有两个方法this.refreshBeanFactory();
//return this.getBeanFactory();
//具体的实现在它的父类AbstractRefreshableApplicationContext中 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//为 此beanFactory初始化一些组件,比如:ClassLoadder等等
this.prepareBeanFactory(beanFactory); try {
//获取容器级别的后处理器,允许上下文的子类中对
//beanFactory进行后处理,在应用上下文内部beanFactory
//初始化之后可以修改beanFactory,此时所有的BeanDefinittions都
//已经被加载,但未被实例化,具体的实现在AbstractRefreshableWebApplicationContext
this.postProcessBeanFactory(beanFactory);
/**在装配完成配置后执行这些后处理器,这里涉及到一些接口
我们在开发时可以实现这些接口扩展功能,例如:
InstantiationAwareBeanPostProcessor包含两个方法
一个是在实例化前调用,一个在实例化后,初始化前调用
可以用来做特殊作用,例如代理等等
DestructionAwareBeanPostProcessor在销毁前调用
*/
this.invokeBeanFactoryPostProcessors(beanFactory);
/**
把所有的bean的后处理器排序,在bean实例化后调用
*/
this.registerBeanPostProcessors(beanFactory);
//初始化国际化信息资源
this.initMessageSource();
//初始化事件多播组件,Event触发时由Multicaster
//通知ApplicationListener
this.initApplicationEventMulticaster();
//空方法由子类扩展,可以在实例化bean之前
//做一些ApplicationContext相关的操作
this.onRefresh();
//注册事件监听器
this.registerListeners();
//单例模式的bean实例 化,初始化等等完成
this.finishBeanFactoryInitialization(beanFactory);
//applicationContext刷新完成后的处理,
//例如生命周期监听器的回调,广播通知等
this.finishRefresh();
} catch (BeansException var9) {
if(this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
//如果加载失败,则清理环境相关的信息
this.destroyBeans();
//把applicationContext的active设置成false
this.cancelRefresh(var9);
throw var9;
} finally {
//清理一些缓存
this.resetCommonCaches();
} }
}

refresh()

里面有许多步骤,重点看下obtainFreshBeanFactory()(重新获取一个BeanFactory)。

它里面有个核心的方法refreshBeanFactory()

obtainFreshBeanFactory:

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}

obtainFreshBeanFactory

AbstractRefreshableApplicationContext.refreshBeanFactory():
    @Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
//如果存在就销毁重建
destroyBeans();
closeBeanFactory();
}
try { //创建一个DefaultListableBeanFactory作为Bean的
//管理工厂类
DefaultListableBeanFactory beanFactory = createBeanFactory();
//加载自定义的beanFactory
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory); //加载beanDefinition,关系这个类加载的东西比较多
//可以单分析
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

refreshBeanFactory

如果已有BeanFactory,先删除所有Bean,然后关闭BeanFactory。
然后创建一个新的ListableBeanFactory,上面说到这个工厂里会预先加载所有的Bean。
最后核心的就是loadBeanDefinitions(beanFactory),它是加载Bean的定义。实现交给了子类。

AbstractXmlApplicationContext.loadBeanDefinitions方法:

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}

loadBeanDefinitions

最后一行调用的方法:

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}

loadBeanDefinitions

用的是XmlBeanDefinitionReader直接读配置文件加载Bean Definition(Bean定义)到BeanFactory。它里面一步步把xml的配置文件拆解读取,把一个个Bean Definition加载到BeanFactory里。
至此,已经有用一个加载好Bean Definition的BeanFactory了。

3.6 依赖注入

回到启动类中,看看怎么从context中获取bean的。

// IOC获取Bean
BeanService beanService = context.getBean(BeanService.class);

是根据类去拿bean的,当然也可以根据id。

其对应的源码实现,在DefaultListableBeanFactory中,上文有说到对应的BeanFactory选型。

是根据类去拿bean的,当然也可以根据id。
其对应的源码实现,在DefaultListableBeanFactory中,上文有说到对应的BeanFactory选型。

DefaultListableBeanFactory.getBean()

    @Override
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
BeanFactory parent = getParentBeanFactory();
if (parent != null) {
return parent.getBean(requiredType, args);
}
throw new NoSuchBeanDefinitionException(requiredType);
}

getBean

NamedBeanHolder是里面包含一个实例化的对象,和bean的名字。resolveNamedBean()是怎么拿出Bean的关键。
一步步Debug,可以看到,它是遍历BeanFactory里面维护的beanDefinitionNames和manualSingletonNames成员变量,找出命中的beanName返回。

resolveNamedBean(requiredType,args)➡️getBeanNamesForType(requiredType)➡️getBeanNamesForType(type,true,true)➡️doGetBeanNamesForType(ResolvableType.forRawClass(type),includeNonSingletons, allowEagerInit)

DefaultListableBeanFactory.doGetBeanNamesForType():

    private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
List<String> result = new ArrayList<String>(); // Check all bean definitions.
for (String beanName : this.beanDefinitionNames) {
// Only consider bean as eligible if the bean name
// is not defined as alias for some other bean.
if (!isAlias(beanName)) {
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Only check bean definition if it is complete.
if (!mbd.isAbstract() && (allowEagerInit ||
((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
// In case of FactoryBean, match object created by FactoryBean.
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
boolean matchFound =
(allowEagerInit || !isFactoryBean ||
(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
(includeNonSingletons ||
(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
isTypeMatch(beanName, type);
if (!matchFound && isFactoryBean) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
}
if (matchFound) {
result.add(beanName);
}
}
}
catch (CannotLoadBeanClassException ex) {
if (allowEagerInit) {
throw ex;
}
// Probably contains a placeholder: let's ignore it for type matching purposes.
if (this.logger.isDebugEnabled()) {
this.logger.debug("Ignoring bean class loading failure for bean '" + beanName + "'", ex);
}
onSuppressedException(ex);
}
catch (BeanDefinitionStoreException ex) {
if (allowEagerInit) {
throw ex;
}
// Probably contains a placeholder: let's ignore it for type matching purposes.
if (this.logger.isDebugEnabled()) {
this.logger.debug("Ignoring unresolvable metadata in bean definition '" + beanName + "'", ex);
}
onSuppressedException(ex);
}
}
} // Check manually registered singletons too.
for (String beanName : this.manualSingletonNames) {
try {
// In case of FactoryBean, match object created by FactoryBean.
if (isFactoryBean(beanName)) {
if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
result.add(beanName);
// Match found for this bean: do not match FactoryBean itself anymore.
continue;
}
// In case of FactoryBean, try to match FactoryBean itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
}
// Match raw bean instance (might be raw FactoryBean).
if (isTypeMatch(beanName, type)) {
result.add(beanName);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Shouldn't happen - probably a result of circular reference resolution...
if (logger.isDebugEnabled()) {
logger.debug("Failed to check manually registered singleton with name '" + beanName + "'", ex);
}
}
} return StringUtils.toStringArray(result);
}

doGetBeanNamesForType

然后拿着这个beanName去找具体的bean实例。这里的代码比较长,在AbstractBeanFactory里面的doGetBean()中实现。
大意是先尝试去找手动添加bean的单例工厂里找有没有对应的实例,没有的话就往父类beanFactory里面找,最后没有的话就生成一个。
spring中一个bean是如何加载和如何注入大致如此。

参考:零基础带你看Spring源码——IOC控制反转

参考:Spring bean的生命周期

Spring源码——IOC控制反转的更多相关文章

  1. 零基础带你看Spring源码——IOC控制反转

    本章开始来学习下Spring的源码,看看Spring框架最核心.最常用的功能是怎么实现的. 网上介绍Spring,说源码的文章,大多数都是生搬硬推,都是直接看来的观点换个描述就放出来.这并不能说有问题 ...

  2. Spring源码-IOC部分-容器初始化过程【2】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  3. Spring学习之Ioc控制反转(1)

    开始之前: 1. 本博文为原创,转载请注明出处 2. 作者非计算机科班出身,如有错误,请多指正 ---------------------------------------------------- ...

  4. Spring学习之Ioc控制反转(2)

    开始之前: 1. 本博文为原创,转载请注明出处 2. 作者非计算机科班出身,如有错误,请多指正 ---------------------------------------------------- ...

  5. Spring框架之IOC(控制反转)

    [TOC] 第一章Spring框架简介 IOC(控制反转)和AOP(面向方面编程)作为Spring框架的两个核心,很好地实现了解耦合.所以,简单来说,Spring是一个轻量级的控制反转(IoC)和面向 ...

  6. Spring框架中IoC(控制反转)的原理(转)

    原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...

  7. Spring源码-IOC部分-Spring是如何解决Bean循环依赖的【6】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  8. Spring源码-IOC部分-容器简介【1】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  9. Spring源码-IOC部分-Xml Bean解析注册过程【3】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

随机推荐

  1. Openresty与Tengine

    Tengine官方网站:http://tengine.taobao.org/index_cn.html OpenResty官方网站:http://openresty.org/ Openresty和Te ...

  2. phpmyadmin上在某数据库里创建函数

    改那个PHP项目加了个功能,本地MYSQL里添加了一个函数了,现在要转到服务器上,然后服务器上的MYSQL只能通过PHPMYADMIN网页操作的,在网页上SQL里输入创建函数的语句, 结果报错了:th ...

  3. C++ Multimap运用实例—查找元素

    C++ Multimap运用实例—查找元素 #include <map> #include <iostream> #include <algorithm> #inc ...

  4. Flutter中极光推送的使用----jpush_flutter

    原文地址:https://www.cnblogs.com/niceyoo/p/11095994.html 1.申请极光账号和建立应用 极光推送的官方网址为:https://www.jiguang.cn ...

  5. async异步流程控制神器

    async https://www.npmjs.com/package/async Async is a utility module which provides straight-forward, ...

  6. 双写mq后碰到没有消费问题记录

    上周双写mq后碰到遇到个问题,mq双写的一台机器有produce,另一台一直没有,但是有的那台机器没有消费者,导致另一个服务 一直没有可以消费的mq.原因是 mq在双写初始化配置的时候两个类文件重复了 ...

  7. 【NPDP笔记】第三章 新产品流程

      3.1 产品开发,风险与汇报的过程,开发实践和流程提升成功率 管控新产品失败的风险,随着成本增加,风险降低 知识能改改进决策,降低风险,决策框架 识别问题与机会 收集信息 组织记录,组织员工 外部 ...

  8. 微服务, 架构, 服务治理, 链路跟踪, 服务发现, 流量控制, Service Mesh

    微服务, 架构, 服务治理, 链路跟踪, 服务发现, 流量控制, Service Mesh 微服务架构   本文将介绍微服务架构和相关的组件,介绍他们是什么以及为什么要使用微服务架构和这些组件.本文侧 ...

  9. Linux虚拟环境配置(安装python包,连接至jupyter notebook)

    在Linux虚拟环境下安装python包 方法一:使用下载包 由于实验室下载速度较慢,因此采用传输下载包的形式安装包. 首先导入python包至指定文件夹(任意文件夹,记住地址即可)并解压. 进入虚拟 ...

  10. vim设定Tab缩进长度

    在Linux系统中,vim是一款非常好用的文本编辑器,那么,如何在Linux下的vim编辑器设定Tab的缩进长度呢? Linux系统下,vim编辑器Tab键的默认长度为8个空格,在vim中可以通过修改 ...