一、前言

  写这篇博文的主要目的如下:

  • 通过相关类和接口分析IoC容器到底长什么样。
  • 阐述笔者对Spring上下文和容器的理解。
  • 介绍重要的类辅助理解SpringBoot的启动流程。

二、Spring IoC容器的设计

  看看下面这张图(摘自《Spring技术内幕》),IoC容器的设计分为两条线,

  1.   BeanFactory ==> HierarchicalBeanFactory ==>ConfigurableBeanFactory ,这条线可以理解成IoC容器的设计路线。
  2.   BeanFactory ==> ListableBeanFactory ==> ApplicationContext ==> ConfigurableApplicationContext ,这条可以成为Spring应用上下文的设计路线。

  为什么这样要分两条线呢,主要是将容器和上下文区分开来。因为在在Spring项目中,上下文对容器不仅是扩展的关系,更重要的是持有的关系,上下文是以属性的形式持有了容器,开发者可以通过上下文对象获取到容器。笔者十分倾向于将二者分开来理解。当然也可以将应用上下文理解成容器的高级表现形式。

2.1,IoC容器的设计线路

  BeanFactory定义了IoC容器的基本规范,包括getBean()按类型和按名称的获取Bean的方法。

   

  HierarchicalBeanFactory 在BeanFactory的基础上增加了getParentBeanFactory()方法,使BeanFactory具备了双亲IoC容器管理的功能。

  ConfigurableBeanFactory接口提供了配置BeanFactory的各种方法。比如setParentBeanFactory()方法,配置上面提到的双亲IoC容器,addBeanPostProcessor()方法,配置Bean后置处理器等。

  到这里先埋个包袱:到ConfigurableBeanFactory接口为止,IoC容器还没有具备作为“容器”最基本的功能,那就是能装东西。

2.2、应用上下文设计路线

  上面说了应用上下文是IoC容器的高级表现形式,ListableBeanFactory具备了操作BeanDefinition 的能力,比如getBeanDefinitionCount()方法,可以获取Bean的总数等。

  ApplicationContext 类那就厉害了,如下代码所示,实现了一大堆接口

 public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver
  • MessageSource,支持不同的信息源。具备支持国际化的实现,为开发多语言版本的应用提供服务。
  • ResourcePatternResolver,访问数据源。具备了从不同地方得到Bean定义资源的能力,比如:xml,java config,注解等等。
  • ApplicationEventPublisher,发布事件。使应用上下文具备了事件机制。事件机制为Bean声明周期的管理提供了便利。

  WebApplicationContext扩展了对web应用的支持。

  ConfigurableApplicationContext就更重要了,看过Spring源码的都知道一个重要的方法叫refresh,没错就是在这个接口中定义的。最重要的是扩展了配置上下文的功能,和控制上下文生命周期的能力等等。

三、IoC容器的具体实现类 DefaultListableBeanFactory(重点)

  首先证明一点,为什么说DefaultListableBeanFactory类是具体的实现类呢?

  随便启动一个SpringBoot项目找到第25行代码(在SpringBoot的启动流程系列博文中有介绍)

 public ConfigurableApplicationContext run(String... args) {
//记录程序运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// ConfigurableApplicationContext Spring 的上下文
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//从META-INF/spring.factories中获取监听器
//1、获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//2、构造容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//处理需要忽略的Bean
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
///3、初始化容器
context = createApplicationContext();
//实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[]{ConfigurableApplicationContext.class}, context);
//4、刷新容器前的准备阶段
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//5、刷新容器
refreshContext(context);
//刷新容器后的扩展接口
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
} try {
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

  debug

  如2标注点所示IoC容器的真实面孔就是这个DefaultListableBeanFactory类了。当然他还有一个子类XmlBeanFactory,不过都已经被标注为弃用了(@Deprecated)在《Spring技术内幕》这本书里面也是着重的讲的这个类,可能当时作者是以SpringMVC项目来讲解的吧。XmlBeanFactory顾名思义就是提供了对xml配置方式的支持。呃。。。又勾起了用SpringMVC的痛苦回忆。

  言归正传,

  如下图,看看他的继承关系

   章节二中提到了很多的IoC容器系列,这样总结一下吧,俗话说一流企业做标准,二流企业做产品,章节二中的那一坨就是IoC容器的实现标准,本章节我们要总结的类DefaultListableBeanFactory就是IoC容器的具体产品。

  看见上图中那一堆接口和类是不是有点懵,没关系,咱们慢慢梳理一下。

3.1,作为IoC容器的基础设计路线

  这条线路在上一章节中已经梳理过了。只是多出了ConfigurableListableBeanFactory接口,ConfigurableListableBeanFactory接口主要是增加指定忽略类型和接口等

3.2、作为IoC容器的高级设计路线

  这条设计路线乍一看还是挺复杂的,的确是这样。

  1, BeanFactory ==> AutowireCapableBeanFactory ==> AbstractAutowireCapableBeanFactory ==> DefaultListableBeanFactory

  在这条线路中,AutowireCapableBeanFactory接口定义了自动注入bean(autowireBean()),创建bean(createBean()),初始化bean(initializeBean())方法等。那么真正实现这些方法的类便是AbstractAutowireCapableBeanFactory。

  AbstractAutowireCapableBeanFactory抽象类中实现了AutowireCapableBeanFactory接口定义的方法。在此基础上通过继承AbstractBeanFactory具备了操作Bean的能力。

  2, SingletonBeanRegistry ==> DefaultSingletonBeanRegistry ==> FactoryBeanRegistrySupport ==> AbstractBeanFactory ==> AutowireCapableBeanFactory ==> DefaultListableBeanFactory

  这条关系链有点长,在这条链中我们要关心的是SingletonBeanRegistry接口,顾名思义,这个接口是单例Bean的注册接口。当然也不止注册这么简单。如下图中所示,除了注册单例之外,还定义获取单例的方法。

  注意:为什么只有singleton的注册中心,而没有prototype类型的Bean的注册中心呢?因为单例Bean(singleton)是Spring帮我们创建的并维护的,原型Bean(prototype)是每次获取都会创建出来一个实例。本质是不同的。

  3, AliasRegistry ==> SimpleAliasRegistry ==> DefaultSingletonBeanRegistry ==> FactoryBeanRegistrySupport ==> AbstractBeanFactory ==> AutowireCapableBeanFactory ==> DefaultListableBeanFactory

  这条路线呢,主要是提供管理别称的能力。因为不是重点,在此就不详细分析了。

  4, AliasRegistry ==> BeanDefinitionRegistry ==> DefaultListableBeanFactory

  BeanDefinitionRegistry接口要重点说一下,该接口是BeanDefinition的注册中心。使DefaultListableBeanFactory具备操作BeanDefinition的能力。看一下它有什么方法。

  包括了注册,删除,获取BeanDefinition的方法。当然这只是个接口,这些方法的具体实现在DefaultListableBeanFactory中。

3.3、DefaultListableBeanFactory几个重要的父类和接口

3.3.1, AbstractBeanFactory 抽象类

  如上图所示,AbstractBeanFactory中实现了BeanFactory中定义的几个重要的方法。常用的注解 @Autowired @Resource(name = "xxx") 大家都知道一个是按类查找,一个是按名获取。具体实现这两个注解的方法就是上图中圈出来的几个方法。几个getBean()方法最终都进入了doGetBean()方法。doGetBean()方法是实际获得Bean的地方,也是触发依赖注入发生的地方。在SpringBoot启动流程总会对这个方法进行详细的介绍。

3.3.2, AbstractAutowireCapableBeanFactory 抽象类

  AbstractBeanFactory中实现了getBean()方法,AbstractAutowireCapableBeanFactory中实现了Bean的创建方法。

  当我们需要定义一个Bean通常会有这样写 @Bean(name = "test", initMethod = "init", destroyMethod = "destroy") 。AbstractAutowireCapableBeanFactory中完成了一个Bean从 create(createBean()) ==> createInstance(createBeanInstance()) ==> init(invokeInitMethods()) 的所有工作。所以这个抽象类的作用不言而喻。具体的创建过程,会在SpringBoot的启动流程中详细介绍。

3.3.3, DefaultSingletonBeanRegistry 让IoC容器拥有作为“容器”的能力

  其实我们经常说的Spring 容器,这个容器其实更多的是BeanFactory所代表的意义:Bean生产工厂。是滴,通常我们理解的容器应该是能装东西的,但是spring 容器不是代表代表水桶一样的东西,而是像富士康一样,生产东西的地方。比如我们需要一个Bean,我们只需要告诉spring,spring就会给我们。所以到目前为止我们还没有看到IoC作为“容器”的能力。以上言论纯属自己理解,不喜勿喷。

  DefaultSingletonBeanRegistry属性先贴出来

 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/**
* Cache of singleton objects: bean name --> bean instance
* 缓存 单例对象
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);//好习惯,创建ConcurrentHashMap,指定初始化因子,纵观Spring源码,创建HashMap,都有初始化因子。get /**
* Cache of singleton factories: bean name --> ObjectFactory
* 缓存 单例工厂
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /**
* Cache of early singleton objects: bean name --> bean instance
* 缓存 提前暴露的对象
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); /**
* Set of registered singletons, containing the bean names in registration order
* 已经注册的单例对象集合,按照注册顺序排序的,并且是不可重复的。
*/
private final Set<String> registeredSingletons = new LinkedHashSet<>(256); /**
* Names of beans that are currently in creation
*
*/
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16)); /**
* Names of beans currently excluded from in creation checks
*/
private final Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16)); /**
* List of suppressed Exceptions, available for associating related causes
*/
@Nullable
private Set<Exception> suppressedExceptions; /**
* Flag that indicates whether we're currently within destroySingletons
*/
private boolean singletonsCurrentlyInDestruction = false; /**
* Disposable bean instances: bean name --> disposable instance
* spring是作为一个注册中心的样子,在容器shutdown的时候,直接从这里面找到需要执行destory钩子的Bean
*/
private final Map<String, Object> disposableBeans = new LinkedHashMap<>(); /**
* Map between containing bean names: bean name --> Set of bean names that the bean contains
* 名称为name的bean,所持有的beans 的映射关系
*/
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16); /**
* Map between dependent bean names: bean name --> Set of dependent bean names
* 名称为name的bean与其所依赖的bean的映射关系
*/
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64); /**
* Map between depending bean names: bean name --> Set of bean names for the bean's dependencies
*/
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
}

  属性singletonObjects ,没错,就是这个东东,最终存储单例(singleton)Bean的地方,在SpringBoot启动流程中,会详细介绍存取的过程。上面说了原型(prototype)Bean是不需要缓存的,不解释了。到这里我们初步看到了IoC作为“容器”该有的样子。

  DefaultSingletonBeanRegistry上面提到的SingletonBeanRegistry接口的相关方法,并且增加了很多对单例的操作的方法。

3.3.4, DefaultListableBeanFactory (重点)

  上面我们从IoC容器的宏观设计角度阐述了DefaultListableBeanFactory作为IoC容器的具体实现的设计思想。在这里来分析一下这个类本身的设计。

  首先看看该类的属性

 public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
/**
* Map from serialized id to factory instance
* 缓存 序列化ID到 DefaultListableBeanFactory 实例的映射
*/
private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
new ConcurrentHashMap<>(8); /**
* Optional id for this factory, for serialization purposes
*/
@Nullable
private String serializationId; /**
* Whether to allow re-registration of a different definition with the same name
*/
private boolean allowBeanDefinitionOverriding = true; /**
* Whether to allow eager class loading even for lazy-init beans
*/
private boolean allowEagerClassLoading = true; /**
* Optional OrderComparator for dependency Lists and arrays
*/
@Nullable
private Comparator<Object> dependencyComparator; /**
* Resolver to use for checking if a bean definition is an autowire candidate
* 被用来解决去校验一个BeanDefinition是不是自动装载的候选人
*/
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver(); /**
* Map from dependency type to corresponding autowired value
* 缓存 类型对应的自动装载的Bean
*/
private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16); /**
* Map of bean definition objects, keyed by bean name
* 缓存 beanName到BeanDefinition的映射关系
*/
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); /**
* Map of singleton and non-singleton bean names, keyed by dependency type
* 缓存 类型 和 beanName的映射关系
*/
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64); /**
* Map of singleton-only bean names, keyed by dependency type
* 缓存 类型 和 单例Bean names的映射
*/
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64); /**
* List of bean definition names, in registration order
* 缓存 beanDefinition name的list
*/
private volatile List<String> beanDefinitionNames = new ArrayList<>(256); /**
* List of names of manually registered singletons, in registration order
*/
private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16); /**
* Cached array of bean definition names in case of frozen configuration
*/
@Nullable
private volatile String[] frozenBeanDefinitionNames; /**
* Whether bean definition metadata may be cached for all beans
*/
private volatile boolean configurationFrozen = false;
}

  在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用。 DefaultListableBeanFactory作为一个功能完整的容器具备了除以上父类所具有功能外,还加入了对BeanDefinition的管理和维护。从上面的代码可以看到一个重要的属性:beanDefinitionMap。beanDefinitionMap缓存了Bean name到 BeanDefinition的映射。到这里是不是发现了IoC容器另外一个作为“容器”的能力。在我的理解范围内,IoC容器作为“容器”真正装的两个最总要的能力算是总结完了,一个是装单例(Singleton)Bean,一个是装BeanDefinition。

3.3.5, BeanDefinition

  Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及他们之间的相互依赖关系。BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。我么都知道在计算机世界里,所有的功能都是建立在通过数据对现实进行抽象的基础上的。IoC容器是用来管理对象依赖关系的,对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。这些BeanDefinition就像是容器里装的水,有了这些基本数据,容器才能发挥作用。简单一句话来说,BeanDefinition就是Bean的元数据,BeanDefinition存放了对Bean的基本描述,包括Bean拥有什么属性,方法,Bean的位置等等Bean的各种信息。IoC容器可以通过BeanDefinition生成Bean。

  BeanDefinition究竟长什么样呢?

  在同第三章debug的地方一样,点开beanFactory,然后查看beanDefinitionMap属性。

  OK,BeanDefinition就是长这样了。具体怎么通过它生成Bean,在SpringBoot启动流程中会详细介绍。

四、SpringBoot web工程中的上下文 AnnotationConfigServletWebServerApplicationContext

  在SpringBoot工程中,应用类型分为三种,如下代码所示。

 public enum WebApplicationType {
/**
* 应用程序不是web应用,也不应该用web服务器去启动
*/
NONE,
/**
* 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
*/
SERVLET,
/**
* 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。
*/
REACTIVE
}

  对应三种应用类型,SpringBoot项目有三种对应的应用上下文,我们以web工程为例,即其上下文为AnnotationConfigServletWebServerApplicationContext

 public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

  我们先看一下AnnotationConfigServletWebServerApplicationContext的设计。 

  在2.2中已经介绍了ApplicationContext的设计

  关于AnnotationConfigServletWebServerApplicationContext详细的设计路线在这里就不像DefaultListableBeanFactory容器那么详细的去讲解了。在第二章说过,应用上下文可以理解成IoC容器的高级表现形式,拿上图和DefaultListableBeanFactory的继承关系图,不难发现,应用上下文确实是在IoC容器的基础上丰富了一些高级功能。在第二章中,我们还说过应用上下文对IoC容器是持有的关系。继续看第二章debug的截图,context就是AnnotationConfigServletWebServerApplicationContext的神秘面孔,他的一个属性beanFactory就是IoC容器(DefaultListableBeanFactory)。所以他们之间是持有,和扩展的关系。

  接下来看GenericApplicationContext类

 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
private final DefaultListableBeanFactory beanFactory;
...
}

  第一行赫然定义了beanFactory属性,正是DefaultListableBeanFactory对象。

  关于上下文还有另外一个最重要的方法refresh,上文中说道该方法是在ConfigurableApplicationContext接口中定义的,那么在哪实现的该方法呢?

  看AbstractApplicationContext类。

 @Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//刷新上下文环境
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
//这里是在子类中启动 refreshBeanFactory() 的地方
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
//准备bean工厂,以便在此上下文中使用
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
//设置 beanFactory 的后置处理
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
//调用 BeanFactory 的后处理器,这些处理器是在Bean 定义中向容器注册的
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
//注册Bean的后处理器,在Bean创建过程中调用
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
//对上下文中的消息源进行初始化
initMessageSource(); // Initialize event multicaster for this context.
//初始化上下文中的事件机制
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
//初始化其他特殊的Bean
onRefresh(); // Check for listener beans and register them.
//检查监听Bean并且将这些监听Bean向容器注册
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
//实例化所有的(non-lazy-init)单件
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
//发布容器事件,结束Refresh过程
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

  OK,应用上下文就介绍到这里。

五、IoC容器的初始化过程

  在这里我们先口述一下IoC容器的初始化过程吧,源码分析,请移步SpringBoot启动流程分析。

  简单来说IoC容器的初始化过程是由前面介绍的refresh()方法启动的,这个方法标志着IoC容器的正式启动。具体来说,这个启动包括三个过程

 BeanDefinition的Resource定位
BeanDefinition的载入
向IoC容器注册BeanDefinition

  1、第一个过程:Resource定位

  这个定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。对于这些BeanDefinition的存在形式,可以是通过像SpringMVC中的xml定义的Bean,也可以是像在类路径中的Bean定义信息,比如使用@Component等注解定义的。这个过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样。

  结合SpringBoot说一下这个过程,对于SpringBoot,我们都知道他的包扫描是从主类所在的包开始扫描的,那这个定位的过程在SpringBoot中具体是这样的,在refresh容器之前(prepareContext()方法中),会先将主类解析成BeanDefinition,然后在refresh方法中并且是扫描Bean之前,解析主类的BeanDefinition获取basePackage的路径。这样就完成了定位的过程。(先不讨论SpringBoot中指定扫描包路径和自动装配)

  2、第二个过程:BeanDefinition的载入

  这个载入过程是把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。

  在SpringBoot中,上面我们说到通过主类找到了basePackage,SpringBoot会将该路径拼接成:classpath*:org/springframework/boot/demo/**/*.class这样的形式,然后一个叫做PathMatchingResourcePatternResolver的类会将该路径下所有的.class文件都加载进来,然后遍历判断是不是有@Component注解,如果有的话,就是我们要装载的BeanDefinition。大致过程就是这样的了。

  注意:@Configuration,@Controller,@Service等注解底层都是@Component注解,只不过包装了一层罢了。

  3、第三个过程:注册BeanDefinition

  这个过程通过调用上文提到的BeanDefinitionRegister接口的实现来完成。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过上文的分析,我们可以看到,在IoC容器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。比如DefaultListableBeanFactory 中的beanDefinitionMap属性。

六、IoC容器的依赖注入

  上面对IoC容器的初始化过程进行了详细的介绍,这个过程完成的主要的工作是在IoC容器中建立BeanDefinition数据映射。在此过程中并没有看到IoC容器对Bean的依赖关系进行注入。依赖注入是Spring实现“控制反转”的重要一环。Spring将依赖关系交给IoC容器来完成。

  依赖控制反转的实现有很多种方式。在Spring中,IoC容器是实现这个模式的载体,它可以在对象生成或者初始化时直接将数据注入到对象中,也可以通过将对象注入到对象数据域中的方式来注入对方法调用的依赖。这种依赖注入是可以递归的,对象被逐层注入。

  原创不易,转载请注明出处。

  如有错误的地方还请留言指正。

参考文献:

  《Spring技术内幕--深入解析Spring框架与设计原理(第二版)》

Spring IoC容器与应用上下文的设计与实现的更多相关文章

  1. 【Spring】非Spring IOC容器下获取Spring IOC上下文的环境

    前言 在Spring Web项目中,有些特殊的时候需要在非Spring IOC容器下获取Spring IOC容器的上下文环境,比如获取某个bean. 版本说明 声明POM文件,指定需引入的JAR. & ...

  2. 对Spring IoC容器实现的结构分析

    本文的目标:从实现的角度来认识SpringIoC容器. 观察的角度:从外部接口,内部实现,组成部分,执行过程四个方面来认识SpringIoC容器. 本文的风格:首先列出SpringIoC的外部接口及内 ...

  3. 解读Spring Ioc容器设计图

    在Spring Ioc容器的设计中,有俩个主要的容器系列:一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器最基本的功能:另外一个是ApplicationContext应用上下 ...

  4. Spring IOC容器的实现原理

    1 概述 1.1 依赖反转模式 在Java中,一个复杂的功能一般都需要由两个或者两个以上的类通过彼此合作来实现业务逻辑的,这使得每个对象都需要与其合作的对象的引用.如果这个获取依赖对象的过程需要自己去 ...

  5. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  6. Spring IOC容器启动流程源码解析(一)——容器概念详解及源码初探

    目录 1. 前言 1.1 IOC容器到底是什么 1.2 BeanFactory和ApplicationContext的联系以及区别 1.3 解读IOC容器启动流程的意义 1.4 如何有效的阅读源码 2 ...

  7. 详解Spring IoC容器

    一.Spring IoC容器概述 1.依赖反转(依赖注入):依赖对象的获得被反转了. 如果合作对象的引用或依赖关系的管理由具体对象来完成,会导致代码的高度耦合和可测试性的降低,这对复杂的面向对象系统的 ...

  8. Spring ——Spring IoC容器详解(图示)

    1.1 Spring IoC容器 从昨天的例子当中我们已经知道spring IoC容器的作用,它可以容纳我们所开发的各种Bean.并且我们可以从中获取各种发布在Spring IoC容器里的Bean,并 ...

  9. Spring IoC容器的初始化过程

    Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...

随机推荐

  1. 新闻实时分析系统-Hadoop2.X分布式集群部署

    (一)hadoop2.x版本下载及安装 Hadoop 版本选择目前主要基于三个厂商(国外)如下所示: 1.基于Apache厂商的最原始的hadoop版本, 所有发行版均基于这个版本进行改进. 2.基于 ...

  2. nginx一:安装和基本应用

    Nginx: 目录 Nginx:... 1 NGINX简介和安装... 1 网站... 1 全称... 2 二次开发版... 2 Nginx的特性... 2 基本功能... 2 web服务相关的功能. ...

  3. vue通过控制boolean值来决定是否添加class类名

    vue通过控制boolean值来决定是否添加class类名

  4. 二叉查找树的平衡(DSW算法)

    树适合于表示某些领域的层次结构(比如Linux的文件目录结构),使用树进行查找比使用链表快的多,理想情况下树的查找复杂度O(log(N)),而链表为O(N),但理想情况指的是什么情况呢?一般指树是完全 ...

  5. android clipChildren 的使用与遇到的困难

    案例 在一次我写画板模块的时候,布局比较普通,但是需要子元素溢出父元素.其中一小块布局如下所示: 红色部分需要溢出,这个时候我想到了clipChildren. clipChildren 就是说我可以不 ...

  6. 天了噜,为什么外链css要放在头部,js要放在尾部?

    我们最开始学前端的时候都会看到教程在处理外部css,js的时候会将css放在header中,js放在body的最后.为什么要这样子处理,今天参考一些资料好好分析下. 为什么外链css为什么要放头部? ...

  7. 炸裂函数explode

    ateral viem 表的关联只能和explode使用 hive> select explode(info) from t_map where id=1; hive> create ta ...

  8. Spring(Bean)6

    生命周期构造 --->set--->Bean init前后执行 (新创建 类) public class MyBeanPostProcesser implements BeanPostPr ...

  9. 痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU启动那些事(2)- Boot配置(ISP_Pin, OTP)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是恩智浦i.MX RTxxx系列MCU的Boot配置. 在上一篇文章 Boot简介 里痞子衡为大家介绍了Boot基本原理以及i.MXRTx ...

  10. 点击查看大图滑动预览(h5,pc通用)

    点击预览大图并滑动观看,支持手机端和pc端,具体功能如下图: 一. touchTouch 的js和css 以及jquery依赖库 <link rel="stylesheet" ...