写点什么

Spring Boot 扩展:深入分析 IoC 容器

作者:Java你猿哥
  • 2023-04-28
    湖南
  • 本文字数:14045 字

    阅读完需:约 46 分钟

Spring IoC 容器是整个框架中最核心、最关键的一部分内容,也是许多面试考察中的重要内容。 今天我将从 Spring 接口源码入手,与大家一块学习下容器相关的内容。 今天的内容包括三部分,第一部分是 Spring 中 BeanFactory 接口及其子类(接口)的梳理;第二部分是对 ApplicationContext 接口及其子类实现的梳理; 最后一部分从应用的角度介绍容器的使用、配置、定制化等内容。

01-BeanFactory

官方文档中,对 BeanFactory 定义或介绍为:

The root interface for accessing a Spring bean container. This interface is implemented by objects that hold a number of bean definitions, each uniquely identified by a String name.

这个定义,简单、明确的指明了 BeanFactory 的功能,它负责管理 Bean,并通过 String 类型的名称来区分不同的 Bean。 BeanFactory 定义了 Spring 框架中 Bean 容器应当具备的最基本的能力。 这些能力可以分为如下几类:

  • 判断特定 Bean 是否存在容器中;

boolean containsBean(String name);
复制代码


  • 获取特定 Bean 实例或 ObjectProvider;


<T> T getBean(Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
复制代码
  • 获取特定 Bean 的类型;

Class<?> getType(String name) throws NoSuchBeanDefinitionException;Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
复制代码
  • 判断 Bean 的作用范围,Spring 中具有 Singleton / ProtoType 两种典型的作用范围;

boolean isPrototype(String name) throws NoSuchBeanDefinitionException;boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
复制代码
  • 判断 Bean 是否是特定类型;

boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
复制代码

01.1-盘点 BeanFactory 的众多子类(接口)

BeanFactory 接口的子类(接口),都在某些特定方面增强了它的功能,例如:

  • HierarchicalBeanFactory,定义了 BeanFactory 之间的分层关系。它扩展了是否包含 Bean 的语义,包含意味着自己包含或自己的父类包含。 为了区分这种不同,定义了 containsLocalBean 方法,即当前 BeanFactory 自身包不包含,而不考虑其父类是否包含。

BeanFactory getParentBeanFactory();boolean containsLocalBean(String name);
复制代码
  • ListableBeanFactory,在 BeanFactory 基础上,增加了可以迭代(或遍历)其中的 Bean 对象的方法。 常用的 API 包括:根据特定类型查询 Bean,根据特定注解查询 Bean,获得托管的 Bean 数量,获得所有 Bean 的名称等;

<T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException; <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException;Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;int getBeanDefinitionCount();String[] getBeanDefinitionNames();
复制代码
  • AutowireCapableBeanFactory,给容器增加了“自动装配”的能力。 它定义了两类比较常用的接口,第一类在 doCreateBean 中的 initializeBean 阶段使用,发生在分别发生在调用初始化方法之前和之后; 第二类,resolveXXX 方法,用来解析注入点。其中的 resolveDependency 在之前分析循环依赖的过程中,经常能够看到。

Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)			throws BeansException;Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)			throws BeansException;<T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;
复制代码
  • ConfigurableBeanFactory,继承了 HierarchicalBeanFactory,增加了一些配置化接口,例如 setXXX、registerXXX、addXXX 等。

  • ConfigurableListableBeanFactory 聚合了 ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory 接口,并且增加了分析、修改 BeanDefinition 的接口,以及预先实例化单例 Bean 的方法。 例如 ignoreDependencyType 和 ignoreDependencyInterface 方法(这个有什么用后面会有专门的分析),preInstantiateSingletons 等等。

  • AbstractBeanFactory 提供对 BeanFactory 的基础实现,并且实现了 ConfigurableBeanFactory 定义的全部方法。 AbstractBeanFactory#doGetBean 作为 BeanFactory#getBean 的具体实现,定义了从 BeanFactory 获取 Bean 的方法“模板”。

protected <T> T doGetBean(        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)        throws BeansException {
String beanName = transformedBeanName(name); Object beanInstance;
// Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { /** 对于已经初始化过的单例,直接取出 */ beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); }
else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
/** * 如果当前容器中不包含 containsBeanDefinition(beanName) == false * 则尝试从父容器中取 * 根据父容器的类型,调用不同的接口 */ BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } /** * 当前容器中没有、父容器中也没找到,则尝试创建(实例化) */ if (!typeCheckOnly) { markBeanAsCreated(beanName); }
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate") .tag("beanName", name); try { if (requiredType != null) { beanCreation.tag("beanType", requiredType::toString); } /** * 取得 BeanName 对应的 BeanDefinition 对象 */ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args);
/** * 是否有依赖项,若有,则先创建依赖项 */ // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { /** 循环依赖的一种情况,发现后抛异常 */ if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { /** 最终又到 doGetBean */ getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } /** 所有的依赖都创建完毕后,创建当前 Bean */ // Create bean instance. if (mbd.isSingleton()) { /** 这个过程在之前分析创建 Bean 的过程时分析过 */ sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); /** 过程与创建单例 Bean 类似,不同在于前后的 beforeXXX 和 afterXXX */ prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { /** request、session、application 等类型的作用范围在这里处理,用户自定义的作用范围也在这里处理 */ String scopeName = mbd.getScope(); if (!StringUtils.hasLength(scopeName)) { throw new IllegalStateException("No scope name defined for bean '" + beanName + "'"); } Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { /** 与 prototype 类似 */ Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new ScopeNotActiveException(beanName, scopeName, ex); } } } catch (BeansException ex) { beanCreation.tag("exception", ex.getClass().toString()); beanCreation.tag("message", String.valueOf(ex.getMessage())); cleanupAfterBeanCreationFailure(beanName); throw ex; } finally { beanCreation.end(); } } /** 根据 requiredType 的情况,确定是否需要做类型转换 */ return adaptBeanInstance(name, beanInstance, requiredType);}
复制代码

AbstractBeanFactory 预留了三个抽象方法由子类实现,containsBeanDefinition、getBeanDefinition 和 createBean。

  • AbstractAutowireCapableBeanFactory 是 AbstractBeanFactory 的子类,它以模板方法的模式实现了 createBean 方法,并且将实例创建的过程封装在了 doCreateBean 方法中。 注:这也是我们之前反复提到的 Spring 创建 Bean 的过程(三个阶段)。 AbstractAutowireCapableBeanFactory 并未实现 containsBeanDefinition 和 getBeanDefinition 方法。

  • DefaultListableBeanFactory 是 AbstractAutowireCapableBeanFactory 的唯一实现类,且 ClassPathXmlApplicationContext、AnnotationConfigApplicationContext 等中使用的 BeanFactory 都是 DefaultListableBeanFactory 类型。 它定义了一个 Map<String, BeanDefinition> beanDefinitionMap 用来存储 BeanName 和 对应的 BeanDefinition,并基于此实现了 containsBeanDefinition、getBeanDefinition 方法。 它与前面介绍的接口之间的继承关系如下图所示:

  • ApplicationContext,是 BeanFactory 的众多子接口中最为人熟知的接口。 开发应用过程中,使用最多的也是 ApplicationContext 的实现类,例如 AnnotationConfigApplicationContext、ClassPathXmlApplicationContext 等等。

02-ApplicationContext

ApplicationContext 的接口定义如下:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,		MessageSource, ApplicationEventPublisher, ResourcePatternResolver { ... }
复制代码

从接口定义上看,ApplicationContext 继承了 ListableBeanFactory 和 HierarchicalBeanFactory,因此具备它们所具有的能力(这里需要注意的是 ApplicationContext 并没有重复实现 BeanFactory 的功能,而是将请求代理给内部持有的 BeanFactory)。 GenericApplicationContext 中就将对容器的请求,代理给了 DefaultListableBeanFactory 这一 BeanFactory 实现。 除此之外,ApplicationContext 继承了 EnvironmentCapable 表明它具有操纵 Environment 的能力(Environment getEnvironment();); 继承了 ResourcePatternResolver 表明它能够解析、加载资源文件(getResources、getResource); 继承了 MessageSource 表明它能够解析消息,处理参数化、国际化等; 继承了 ApplicationEventPublisher 表明它能够发布相关事件(publishEvent)。


ApplicationContext 自身只定义了少数几个方法,例如 getId、getApplicationName、getDisplayName、getAutowireCapableBeanFactory 等。 ApplicationContext 的直接子接口包括两个:ConfigurableApplicationContext 和 WebApplicationContext。


  • ConfigurableApplicationContext 在 ApplicationContext 基础上,针对 getXXX 方法增加了对应的或相关的 setXXX 方法,例如 setId、setParent、addApplicationListener、addBeanFactoryPostProcessor 等,使得容器可配置(正如它的名字一样)。 而且,它还继承了 LifeCycle 接口,定义了 close、refresh、isActive 等方法。其中的 refresh 方法需要特别关注。 它是一个启动方法,会根据配置信息加载或刷新 Bean 容器。配置信息可以是 Java 注解类、XML 文件等。 而且 refresh 方法还有一个限制,就是要么配置中的全部 Bean 被成功加载,要么所有的 Bean 都不加载。 另外一个需要额外关注的方法是 getBeanFactory 方法,通过它可以推断出 ApplicationContext 是通过代理方式,即内部包含一个 BeanFactory 方式,处理对 IoC 容器请求的。

  • WebApplicationContext 是 Web 环境中常用的容器,它关联了 ServletContext,并扩展了容器中 Bean 的作用范围(request/session/application 等)。

  • AbstractApplicationContext 是 ConfigurableApplicationContext 接口的基本实现。特别地,它实现了 ConfigurableApplicationContext#refresh 方法。 这个方法在后面会详细介绍。

  • AbstractRefreshableApplicationContext 继承了 AbstractApplicationContext,并实现了 refreshBeanFactory 方法,定义了容器刷新的流程。 而且 ApplicationContext 与 BeanFactory 的关系,也在 AbstractRefreshableApplicationContext 中体现,即定义了属性 private volatile DefaultListableBeanFactory beanFactory。

  • GenericApplicationContext 是 AbstractApplicationContext 的另一个实现。 开发过程中常用的几种 ApplicationContext 类型,它们与 AbstractApplicationContext 及它的子类 AbstractRefreshableApplicationContext 和 GenericApplicationContext 的关系如下图所示:


接下来,我来完成前面留下的一个任务,即与大家一块学习下 AbstractApplicationContext 对 ConfigurableApplicationContext#refresh 方法的实现。


@Overridepublic void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
/** * 主要是设置 active/closed 状态位,startDate 统计信息, * 初始化 PropertySource、验证 required property 等 */ // Prepare this context for refreshing. prepareRefresh();
/** * 刷新内部的 BeanFactory,实际是 DefaultListableBeanFactory 对象。 * 如果存在旧的 BeanFactory,则关闭后,重新创建、配置并加载 BeanDefinition */ // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/** * 对 BeanFactory 进行预处理,包括: * 1. 设置类加载器 * 2. 设置 BeanPostProcessor:ApplicationContextAwareProcessor、ApplicationListenerDetector 和 LoadTimeWeaverAwareProcessor 等。 * 3. 注入 environment、systemProperties 等对象 * 这里是官网对比 ApplicationContext 与 BeanFactory 优劣时的一项内容 “Automatic BeanPostProcessor registration” * ApplicationContext 相比 BeanFactory 会做更多的事,开发时更推荐使用 ApplicationContext,除非你有非用 BeanFactory 不可的理由。 */ // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory);
try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); /** * 记录启动时间 */ StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory);
/** * 注册 BeanPostProcessor */ // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); beanPostProcess.end();
// Initialize message source for this context. initMessageSource();
// Initialize event multicaster for this context. initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses. onRefresh();
// Check for listener beans and register them. registerListeners();
/** * 这里会触发容器预实例化所有的单例 Bean 对象 */ // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); /** * 容器的初始化完成,通知其他关注容器事件的模块,容器初始化(刷新)完成 */ // Last step: publish corresponding event. finishRefresh(); } catch (BeansException 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(); contextRefresh.end(); } }}
复制代码


接下来,我会特别介绍下 ApplicationContextAwareProcessor 这个 BeanPostProcessor 实现。 它与 AbstractAutowireCapableBeanFactory 关系比较密切。 AbstractAutowireCapableBeanFactory 中定义了两个集合,ignoredDependencyTypes 和 ignoredDependencyInterfaces,并实现了 ConfigurableListableBeanFactory#ignoreDependencyType 和 ConfigurableListableBeanFactory#ignoreDependencyInterface 两个方法,分别向两个集合中添加元素。 这两个集合仅在 AbstractAutowireCapableBeanFactory#isExcludedFromDependencyCheck 方法中使用,主要作用是判断某个特定的 property 是否应当在依赖检查中被排除。

protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {    return (AutowireUtils.isExcludedFromDependencyCheck(pd) ||    /** 忽略 CGLIB 定义的 property */            this.ignoredDependencyTypes.contains(pd.getPropertyType()) || /** 忽略 ignoredDependencyTypes 集合中包含的类型 */            AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces)); /** 如果 property 的设置方法在 ignoredDependencyInterfaces 中有定义,则忽略 */}
复制代码


isExcludedFromDependencyCheck 方法被两个地方调用:

  1. AbstractAutowireCapableBeanFactory#unsatisfiedNonSimpleProperties,在 populateBean 阶段调用 AbstractAutowireCapableBeanFactory#autowireByName 和 AbstractAutowireCapableBeanFactory#autowireByType 是被调用。

  2. AbstractAutowireCapableBeanFactory#filterPropertyDescriptorsForDependencyCheck,在 populateBean 阶段被调用。

通过上面的介绍可以得出,这两个集合在填充类属性(即解析依赖并注入依赖)时使用。

ApplicationContextAwareProcessor 是一个 BeanPostProcessor,简单来说就是 Bean 容器在创建、实例化一个托管 Bean 时,会在一定的时机调用其中的回调方法。 ApplicationContextAwareProcessor 负责处理多个 Aware 接口,在 Bean 实例化后,调用它的 setXXX 方法:

  • EnvironmentAware#setEnvironment

  • EmbeddedValueResolverAware#setEmbeddedValueResolver

  • ResourceLoaderAware#setResourceLoader

  • ApplicationEventPublisherAware#setApplicationEventPublisher

  • MessageSourceAware#setMessageSource

  • ApplicationStartupAware#setApplicationStartup

  • ApplicationContextAware#setApplicationContext

03-如何创建并使用 IoC 容器?

基于 Spring IoC 容器构建的应用一般包括两部分,第一部分是业务对象,一般是指托管在容器中的 Java 类; 第二部分是配置元信息,这部分信息告诉 Spring IoC 容器如何实例化、配置、装配托管的 Bean。 配置元信息可以有多种形式:

  • XML 文件,最经典的配置模式,也是 Spring 最早支持的格式。

  • Java 注解或配置文件,是 Spring 从 2.5、3.0 版本逐渐支持的格式,应该也是目前最流行的形式。

  • Groovy 配置文件。

如无特殊说明,本节中涉及的 IoC 容器指 ApplicationContext 接口的不同实现。

03.1-容器的实例化和使用

/** 使用 XML 作为配置文件 */final ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
/** 使用 Java 类作为配置文件 */final ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
/** 使用 Groovy 文件作为配置文件 */final ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
复制代码


/** 使用容器 */final Object foo = context.getBean("foo");
复制代码

03.2-盘点使用 Java 类作为配置文件时常用的注解

本节中,我将介绍使用 Java 类做为配置文件时常用的注解。 相信大部分人对这部分内容都比较熟悉,因而我不会过多的展开。你也可以选择跳过本节的内容。

@Autowired 是最常见的一个注解,它可以标注在构造器方法、setter 方法、其他方法,甚至可以直接注解在对象属性上。

AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata

@Autowired 标注的地方,可以称之为依赖注入点(injection points)。 而且,默认是通过类型来进行注入的。注意看下面的示例:

@Autowiredprivate Bar[] bars;
复制代码

会收集容器中所有的 Bar 类型的 Bean,组合成数组,注入到 bars 中。 上述机制,除了数组类型,同样适用于 Set<Bar>、Map<String, Bar> 类型。

默认情况下,@Autowired 定义的注入点,默认是必需依赖;如果依赖非必需(或可选的),则可以通过 @Autowired(required = false)。 必需依赖和非必需依赖的区别在于,当依赖注入时,容器中找不到必需依赖时会抛异常,导致容器退出;而非必需依赖找不到时,只会忽略。 非必需依赖的另一种方式(Java 8 及以上版本),是通过 Optional 类,例如:


@Autowiredpublic void setBar(Optional<Bar> bar) {  }
复制代码

另外一种方式是 JSR-305 定义的 @Nullable:

 @Autowiredpublic void setBar(@Nullable Bar bar) {  }
复制代码

@Resource 与 @Autowired 作用一样,不同的是它由 JSR-250 定义,并且定义的注入点是按照 BeanName 注入的,而非按照类型。 @Inject 类似,由 JSR-330 定义,默认按照类型注入。


按照类型注入时,会遇到同类型多个候选对象的情况。 @Primary 的作用是在自动装配时,从众多候选者中指定一个优先级最高的候选者。 @Qualifier 的作用类似,用在自动装配时,不过能够提供粒度更细的控制。


前面提到的注解,注入的都是其他 Bean,即依赖类型为复合类型。 如果要注入的值是简单类型,例如字符串,可以使用 @Value。 它的数据来源可以是 Environment、SystemProperties 等中的变量,也可以是 @PropertySource 指定的外部配置文件中的变量。 而且,@Value 支持 SpEL 表达式。


JSR-250 还定义了 Bean 的生命周期回调 @PostConstruct 和 @PreDestroy,作用类似于 XML 配置中的 "init-method" 和 "destroy-method"。


Spring 中,Bean 的默认作用范围是 Singleton,可以通过 @Scope 修改它的实际作用范围。


@Component 及它的子类型 @Service、@Controller、@Repository、@Configuration 被称之为 "Stereotype Annotations"(样板注解)。 Spring 中提供了自动扫描或检测的机制,来发现上述样板注解标注的类,并创建对应的 Bean 对象。这个机制称为 "Component Scan"(组件扫描)。 组件扫描通过 @ComponentScan 来配置,作用与 XML 配置中的 context:component-scan 一样。 组件扫描识别到的类,创建 Bean 对象时如何来生成 BeanName 是由 BeanNameGenerator 来完成的。 在 AnnotationConfigApplicationContext 中,默认使用的是 AnnotationBeanNameGenerator。


JSR-330 中定义了 @Named、@ManagedBean,与 @Component 作用一样。


@Bean 注解一般可以用在 @Configuration 或其他 @Component 类中,用来定义 Bean。


03.3-盘点 Spring IoC 容器预留给用户的扩展点一般来说,用户不需要通过继承 ApplicationContext 实现的方式扩展容器的功能。 Spring IoC 容器在设计的时候,预留了扩展点给用户,以便用户实现特有的业务功能。 接下来,我将盘点一下 Spring 预留的扩展点。


首先,最为人熟知或者最常见的扩展点是 BeanPostProccessor 接口。 它共定义了两个方法:

public interface BeanPostProcessor {	@Nullable	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {		return bean;	}
@Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }
复制代码

这两个方法的调用时机发生在 AbstractAutowireCapableBeanFactory#initializeBean 中,即 Spring 创建 Bean 实例过程三阶段中的最后一个阶段。

Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}
try { invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) { }if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}
复制代码

AppliationContext 实现类会自动的查找容器中所有 BeanPostProcessor 实现,并将它们注册到 BeanFactory 中。

// org.springframework.context.support.AbstractApplicationContext#refreshregisterBeanPostProcessors(beanFactory);
// org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessorsBeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);priorityOrderedPostProcessors.add(pp);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
复制代码

另外一个扩展点是 BeanFactoryPostProcessor 接口,它只定义了一个方法:

public interface BeanFactoryPostProcessor {	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;}
复制代码

调用时机发生在 ApplicationContext 容器刷新时,即 AbstractApplicationContext#refresh 中。

// org.springframework.context.support.AbstractApplicationContext#refreshinvokeBeanFactoryPostProcessors(beanFactory);
// org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessorsPostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
复制代码


PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors 中,会先执行 BeanDefinitionRegistryPostProcessor(它是 BeanFactoryPostProcessor 的一个子类),再执行其他的 BeanFactoryPostProcessor。 ConfigurationClassPostProcessor 是 BeanDefinitionRegistryPostProcessor 的一个实现类,负责处理 @Configuration 标注的类。

再举个例子,PropertySourcesPlaceholderConfigurer 也实现了 BeanFactoryPostProcessor 接口,负责处理 BeanDefinition 中的“${...} ”占位符。 BeanFactoryPostProcessor 的调用时机发生在所有的单例 Bean 被创建之前,而且它操作的对象是 BeanDefinition。 所以,如果你有在 Spring 创建 Bean 之前做某些特定逻辑的需求,可以考虑实现自己的 BeanFactoryPostProcessor。

最后一个扩展点是 FactoryBean 接口,即工厂 Bean。 这个接口其实比较容易理解,它里面最重要的方法就是 FactoryBean#getObject 方法。 当通过 "&" + "beanName" 方式从容器中获取 Bean 时,获得的是 FactoryBean 对象(若有)。 如果仅通过 "beanName" 从容器中获取,当存在 BeanFactory 对象时,会调用它的 getObject 方法来创建一个对象。 当某个 Bean 的创建过程非常复杂时,可以通过自定义 FactoryBean 的方式来完成 Bean 的创建。

希望今天的内容能对你有所帮助。


用户头像

Java你猿哥

关注

一只在编程路上渐行渐远的程序猿 2023-03-09 加入

关注我,了解更多Java、架构、Spring等知识

评论

发布
暂无评论
Spring Boot扩展:深入分析 IoC 容器_Java_Java你猿哥_InfoQ写作社区