当前位置: 首页 > news >正文

Spring前置准备(七)——DefaultListableBeanFactory

终于到了我们的DefaultListableBeanFactory(默认列表Bean工厂)的解析,首先先回顾一下,通过前面的了解我们对于ConfigurableApplicationContext有了清晰的理解,ConfigurableApplicationContext主要作用是对Beean工厂对象进行了部分功能的扩展,例如:资源解析,生命周期管理等等,并且保证了BeanFacotry功能的单一职责,那么这章我们将深入解析DefaultListableBeanFactory,还是老样子,先从接口或者类的上级看起:

在这里插入图片描述


AliasRegistry

AliasRegistry是 Spring 框架中的一个接口,它提供了管理对象别名的功能。在 Spring 的 Bean 管理场景中,别名可以为 Bean 提供额外的标识符,增加了配置的灵活性和可维护性。以下是关于AliasRegistry的详细介绍:

public interface AliasRegistry {/*** 注册bean的别名* Given a name, register an alias for it.* @param name the canonical name* @param alias the alias to be registered* @throws IllegalStateException if the alias is already in use* and may not be overridden*/void registerAlias(String name, String alias);/*** 移除别名* Remove the specified alias from this registry.* @param alias the alias to remove* @throws IllegalStateException if no such alias was found*/void removeAlias(String alias);/*** Determine whether the given name is defined as an alias* (as opposed to the name of an actually registered component).* @param name the name to check* @return whether the given name is an alias*/boolean isAlias(String name);/*** Return the aliases for the given name, if defined.* @param name the name to check for aliases* @return the aliases, or an empty array if none*/String[] getAliases(String name);}

1. 核心功能

  • 注册别名:通过registerAlias(String name, String alias)方法,可以为指定的对象(通常是Bean)注册一个别名。例如,在配置文件中,你可能有一个名为dataSource的Bean,通过registerAlias("dataSource", "myDataSource"),就为dataSource这个Bean注册了一个别名myDataSource。这样在后续获取Bean时,既可以使用dataSource,也可以使用myDataSource
  • 移除别名removeAlias(String alias)方法用于移除指定的别名。当某个别名不再需要时,可以调用这个方法将其从别名注册表中删除。
  • 检查别名isAlias(String name)方法用于判断给定的名称是否是一个别名。这在需要动态处理Bean名称,并且要区分是真实名称还是别名的场景中很有用。
  • 获取所有别名getAliases(String name)方法返回指定对象的所有别名。这在需要获取某个Bean的所有别名列表,以便进行统一处理或展示时非常方便。

2. 应用场景

  • 配置灵活性:在大型项目中,不同的模块可能习惯使用不同的名称来引用同一个Bean。通过别名机制,可以满足各个模块的需求,同时保持Bean的实际定义的一致性。例如,一个通用的日志记录Bean,在业务模块A中可能习惯使用别名logService,而在模块B中使用别名loggingBean,但实际上它们都指向同一个真实的Bean名称。
  • 简化配置与迁移:当需要对Bean进行重构或迁移时,别名可以减少对配置文件和代码的修改。比如,将一个Bean的名称从oldBeanName改为newBeanName,只需要在别名注册表中注册registerAlias("newBeanName", "oldBeanName"),而无需在所有引用该Bean的地方修改名称,降低了维护成本。
  • 兼容旧有配置:在项目升级或整合不同的配置源时,可能会存在旧的配置使用了特定的Bean名称。通过为新的Bean名称注册旧的别名,可以保持与旧配置的兼容性,确保系统平稳过渡。

3. 接口实现

AliasRegistry接口在Spring框架中有多个实现类,其中DefaultSingletonBeanRegistry是一个重要的实现类,它是Spring单例Bean注册和管理的核心类之一。DefaultSingletonBeanRegistry实现了AliasRegistry接口,使得在单例Bean的管理过程中可以方便地使用别名功能。以下是一个简单的代码示例展示如何使用AliasRegistry

import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.io.ClassPathResource;public class AliasRegistryExample {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) context.getDefaultListableBeanFactory();// 注册别名registry.registerAlias("dataSource", "myDataSource");// 加载Bean定义XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);reader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));// 检查别名boolean isAlias = registry.isAlias("myDataSource");System.out.println("Is myDataSource an alias? " + isAlias);// 获取所有别名String[] aliases = registry.getAliases("dataSource");for (String alias : aliases) {System.out.println("Alias for dataSource: " + alias);}context.refresh();context.close();}
}

在上述示例中,首先获取DefaultSingletonBeanRegistry实例,然后注册了一个别名,接着检查别名并获取所有别名,展示了AliasRegistry接口的基本使用方式。通过AliasRegistry,Spring框架为Bean的管理提供了更加灵活和便捷的方式。 DefaultSingletonBeanRegistry的继承了SimpleAliasRegistry,而SimpleAliasRegistry实现了AliasRegistry,在SimpleAliasRegistry中通过aliasMap这个集合保存了所有的别名和Bean名称的映射:

public class SimpleAliasRegistry implements AliasRegistry {/** Logger available to subclasses. */protected final Log logger = LogFactory.getLog(getClass());/*** 别名和bean名称映射的Map集合*/private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);@Overridepublic void registerAlias(String name, String alias) {Assert.hasText(name, "'name' must not be empty");Assert.hasText(alias, "'alias' must not be empty");synchronized (this.aliasMap) {// 如果别名本省就是Bean名称,则不需要设置别名,因为直接通过bean名称就能获取到Bean,没必要多次一举if (alias.equals(name)) {this.aliasMap.remove(alias);if (logger.isDebugEnabled()) {logger.debug("Alias definition '" + alias + "' ignored since it points to same name");}}else {// 通过别名获取bean名称String registeredName = this.aliasMap.get(alias);// 如果已经存在,则不进行重复放入if (registeredName != null) {if (registeredName.equals(name)) {// An existing alias - no need to re-registerreturn;}if (!allowAliasOverriding()) {throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +name + "': It is already registered for name '" + registeredName + "'.");}if (logger.isDebugEnabled()) {logger.debug("Overriding alias '" + alias + "' definition for registered name '" +registeredName + "' with new target name '" + name + "'");}}checkForAliasCircle(name, alias);this.aliasMap.put(alias, name);if (logger.isTraceEnabled()) {logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");}}}}// 代码省略..........
}

其中通过registerAlias方法我们可以看到这段代码:

this.aliasMap.put(alias, name);

通过这段代码我们可以看出,别名map集合的key是别名,val 是Bean的名称,以此来完成映射

别名的使用

在Spring的XML配置文件中,使用别名有以下两种常见方式:

  • 通过bean标签的name属性:在bean标签中,name属性可以用来指定一个或多个别名,多个别名之间可以用逗号、分号或空格隔开。示例如下:
<?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"><!-- 这里bookService是bean的id,service、service4、bookEbi是别名 --><bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl"><property name="bookDao" ref="bookDao"/></bean><bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl"/>
</beans>

在上述配置中,可以通过bookServiceserviceservice4bookEbi中的任意一个名称从Spring容器中获取BookServiceImpl类型的bean。

  • 通过alias标签:使用<alias>标签也可以为bean指定别名,name属性指定要设置别名的bean的名称,alias属性指定别名。示例如下:
<?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"><bean id="user" class="org.cjw.pojo.User" /><!-- 为user这个bean设置别名user1 --><alias name="user" alias="user1" />
</beans>

在这种情况下,既可以使用user,也可以使用user1从Spring容器中获取User类型的bean。

本来还有一个Bean工厂没说,但是我觉得大家只要学习过Spring都清楚,这里省略了


SingletonBeanRegistry

从某种意义上来讲这个接口是一种规范,其实现类必须按照该规范实现单例Bean的注册类,而其实现类DefaultSingletonBeanRegistry是单例Bean工厂的核心类,也是Spring单例Bean的核心类,这个类中的Map集合保存了Spring 框架的所有单例对象,关于DefaultSingletonBeanRegistry我觉得有必要单独开一章来讲,因为太过于重要,后面有机会会详细讲解


HierarchicalBeanFactory

之前已经介绍过了,主要作用是代码分层,详情请看专栏里这篇文章: Spring前置准备(六)——Spring 的核心接口HierarchicalBeanFactory


AutowireCapableBeanFactory

这个类和Spring框架的自动装配有关,AutowireCapableBeanFactory定义了自动注入的规范,它在Spring框架的依赖注入过程中起着关键作用,老办法,单开一章


ListableBeanFactory

  1. ListableBeanFactory让bean工厂有了批量处理bean的能力,是对整个bean工厂容器所在集合的整体批处理操作,
  2. 在文档注释中提到了一个词:“枚举(enumerate )”,在英文里这个单词的意思是 “逐个列出、遍历、清点” 的意思,我第一次看到这个词想到了枚举类,但是这里的枚举不是枚举类,
  3. 而是一次性列出所有 Bean,而不是像普通 BeanFactory 那样只能通过 getBean(“beanName”) 一个个查找
  4. 它下面的方法包括:containsBeanDefinition,getBeanDefinitionCount等等从方法名就可以看出,需要遍历集合才能获取结果,单个getBean方法是没有办法处理的

ConfigurableBeanFactory

ConfigurableBeanFactory是 Spring 框架中一个非常重要的接口,它扩展了BeanFactory接口,提供了更多用于配置和管理 Bean 工厂的功能。主要扩展了例如:

  • Bean定义的操作:允许对Bean定义进行操作,如注册、获取和移除Bean定义。通过这些操作,Spring容器能够灵活地管理Bean的创建规则。例如,registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法可以将一个新的Bean定义注册到Bean工厂中,使得容器能够根据这个定义来创建Bean实例。
  • 作用域管理:支持定义Bean的作用域。Spring框架中常见的Bean作用域包括单例(SCOPE_SINGLETON)和原型(SCOPE_PROTOTYPE)等。ConfigurableBeanFactory提供了设置和获取Bean作用域的方法,如void setScope(String name, String scope),可以动态地为指定的Bean设置作用域,满足不同的应用场景需求。
  • 依赖关系处理:它参与了Bean依赖关系的管理。例如,在解析Bean的依赖时,ConfigurableBeanFactory会根据Bean定义和容器的状态来确定如何满足这些依赖,确保Bean在创建时能够正确地获取其所需的其他Bean实例。

2. 与其他接口的关系

  • 继承结构ConfigurableBeanFactory继承自HierarchicalBeanFactorySingletonBeanRegistry接口。通过继承HierarchicalBeanFactory,它获得了处理父子Bean工厂关系的能力,使得Spring容器可以构建分层的Bean工厂结构,这在一些复杂的应用场景中非常有用,例如在Spring的Web应用中,可能存在根应用上下文和特定Servlet的上下文,它们之间通过父子Bean工厂关系进行管理。继承SingletonBeanRegistry则为其提供了管理单例Bean的基础功能,如注册、获取和移除单例Bean。
  • BeanFactory的关系BeanFactory是Spring框架中Bean管理的基础接口,定义了获取Bean实例等基本操作。ConfigurableBeanFactoryBeanFactory的基础上进行了扩展,提供了更多配置和管理Bean工厂的高级功能,是Spring容器在实际实现中用于精细控制Bean创建和管理的重要接口。

3. 应用场景

  • 自定义Bean创建逻辑:在开发自定义框架或对Spring容器进行扩展时,开发人员可能需要自定义Bean的创建逻辑。ConfigurableBeanFactory提供的方法允许在Bean创建前、创建过程中和创建后进行干预。例如,可以通过自定义的BeanPostProcessor结合ConfigurableBeanFactory来对Bean进行特殊的初始化或增强操作。
  • 动态Bean注册与管理:在一些动态配置或插件化的应用场景中,可能需要在运行时动态注册、修改或移除Bean定义。ConfigurableBeanFactory提供的相关方法使得这种动态管理成为可能。比如,在一个支持插件扩展的应用中,新的插件可以在运行时向Spring容器注册新的Bean定义,ConfigurableBeanFactory能够很好地支持这种操作。
  • 资源管理与优化:通过对Bean作用域的管理,ConfigurableBeanFactory有助于优化应用的资源使用。例如,对于资源消耗较大的Bean,可以将其设置为单例作用域,确保在容器中只有一个实例,避免重复创建带来的资源浪费;而对于一些每次使用都需要全新状态的Bean,则可以设置为原型作用域。

4. 示例代码

以下是一个简单的示例,展示如何使用ConfigurableBeanFactory的部分功能:

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class ConfigurableBeanFactoryExample {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();ConfigurableBeanFactory beanFactory = context.getBeanFactory();// 注册一个Bean定义BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(MyBean.class).getBeanDefinition();beanFactory.registerBeanDefinition("myBean", beanDefinition);// 设置Bean的作用域为原型beanFactory.setScope("myBean", ConfigurableBeanFactory.SCOPE_PROTOTYPE);context.refresh();// 获取Bean实例MyBean myBean = (MyBean) beanFactory.getBean("myBean");System.out.println(myBean);context.close();}
}class MyBean {@Overridepublic String toString() {return "This is MyBean";}
}

在上述示例中,首先获取ConfigurableBeanFactory实例,然后注册了一个MyBean的Bean定义,并将其作用域设置为原型,最后从容器中获取MyBean实例并打印。通过这个示例,可以直观地了解ConfigurableBeanFactory在Bean管理中的基本使用方式。


总结

DefaultListableBeanFactory 是Spring框架中一个核心的类,它实现了 ConfigurableListableBeanFactory 接口,而 ConfigurableListableBeanFactory 又继承了多个重要接口,如 ConfigurableBeanFactoryListableBeanFactory 等。以下从多个方面来介绍 DefaultListableBeanFactory

1. 功能特性

  • Bean定义的管理:它负责管理Bean定义,提供了注册、获取和查询Bean定义的功能。例如,可以使用 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法向工厂中注册一个新的Bean定义。同时,通过 getBeanDefinition(String beanName) 方法能够获取指定名称的Bean定义信息,包括Bean的类、作用域、依赖关系等。这使得Spring容器能够根据这些定义来创建和管理Bean实例。
  • Bean实例的创建与获取DefaultListableBeanFactory 具备创建和获取Bean实例的能力。当调用 getBean 方法时,它会根据Bean定义以及相关的依赖解析策略来创建Bean实例。对于单例Bean,它会在内部维护一个缓存,确保整个容器中只有一个实例;对于原型Bean,则每次都会创建一个新的实例。例如,在 doGetBean 方法中,会根据Bean的作用域(单例或原型等)以及依赖关系来决定如何创建和返回Bean实例。
  • 依赖注入处理:在创建Bean实例的过程中,DefaultListableBeanFactory 会处理Bean之间的依赖关系,完成依赖注入。它通过解析Bean定义中的依赖信息,递归地获取和注入依赖的Bean。例如,如果一个Bean A 依赖于Bean BDefaultListableBeanFactory 会先创建或获取Bean B 的实例,然后将其注入到Bean A 中。这种依赖注入的处理机制是Spring框架实现解耦和控制反转(IoC)的关键。
  • 作用域支持:支持多种Bean作用域,如单例(SCOPE_SINGLETON)、原型(SCOPE_PROTOTYPE)等。通过 setScope(String name, String scope) 方法可以为指定的Bean设置作用域,并且在创建和管理Bean实例时,会根据设置的作用域来采取相应的策略。例如,对于单例作用域的Bean,会在容器启动时或首次请求时创建,并缓存起来供后续使用;对于原型作用域的Bean,每次请求都会创建新的实例。

2. 在Spring容器中的角色

  • 基础Bean工厂DefaultListableBeanFactory 是Spring容器的基础实现之一,为其他高级容器(如 ApplicationContext)提供了底层的Bean管理功能。ApplicationContext 通常会包含一个 DefaultListableBeanFactory 实例,通过委托的方式,将大部分Bean管理的操作委托给 DefaultListableBeanFactory 来完成。例如,ClassPathXmlApplicationContext 在初始化时,会创建一个 DefaultListableBeanFactory 实例,并使用它来加载和解析XML配置文件中的Bean定义。
  • 灵活的扩展点:由于 DefaultListableBeanFactory 实现了多个接口,它为开发人员提供了丰富的扩展点。开发人员可以通过继承 DefaultListableBeanFactory 或实现相关接口,来定制Bean的创建、依赖注入等行为。例如,可以自定义一个 BeanPostProcessor,并注册到 DefaultListableBeanFactory 中,在Bean初始化前后执行自定义的逻辑,从而对Bean进行增强或修改。

3. 示例代码

以下是一个简单的示例,展示如何使用 DefaultListableBeanFactory 来注册和获取Bean:

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;public class DefaultListableBeanFactoryExample {public static void main(String[] args) {// 创建DefaultListableBeanFactory实例DefaultListableBeanFactory factory = new DefaultListableBeanFactory();// 定义一个BeanDefinitionAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(MyBean.class).getBeanDefinition();// 注册BeanDefinitionfactory.registerBeanDefinition("myBean", beanDefinition);// 获取Bean实例MyBean myBean = (MyBean) factory.getBean("myBean");myBean.doSomething();}
}class MyBean {public void doSomething() {System.out.println("MyBean is doing something.");}
}

在上述示例中,首先创建了一个 DefaultListableBeanFactory 实例,然后使用 BeanDefinitionBuilder 创建了一个 MyBeanBeanDefinition,并将其注册到 DefaultListableBeanFactory 中。最后通过 getBean 方法获取 MyBean 的实例并调用其方法。这个示例简单演示了 DefaultListableBeanFactory 的基本使用流程。

http://www.dtcms.com/a/457570.html

相关文章:

  • Linux 进程间通信——System V
  • 【Spring Boot】自定义starter
  • 微网站建设网络温州大军建设有限公司网站
  • 残差特征蒸馏网络(RFDN)探索札记:轻量化图像超分的突破
  • 一般做网站什么价格可以做公众号的网站
  • 优选算法---字符串
  • 任丘网站建设资料查询网站怎么做
  • 华为OD机试C卷 - 流量波峰 - 暴力搜索 - (Java C++ JavaScript Python)
  • 使用CSS3动画属性实现斜线动画 -- 弧线动画 -- 波纹动画 -- 点绕圆旋转动画 -- 浮动动画
  • 打工人日报#20251008
  • 手机网站触摸版萧山中兴建设有限公司网站
  • Python游戏开发入门:从零开始制作贪吃蛇小游戏
  • kanass入门到实战(11) - Kanass如何有效集成sward文档
  • 尚硅谷SpringBoot3零基础教程,课程介绍,笔记01
  • 51网站统计德州网站建设的公司
  • C++23 高级编程 Professional C++, Sixth Edition(一)
  • Verilog和FPGA的自学笔记3——仿真文件Testbench的编写
  • 记录gitee的使用
  • 动态业务流程的案例管理标准(CMMN)
  • 广东门户网站建设哪个网站有适合小学生做的题
  • .NET周刊【9月第4期 2025-09-28】
  • 一级a做爰片365网站天门建设局官方网站
  • 电子商城网站制作广东网站营销seo费用
  • HarmonyOS应用开发 - 无受限权限保存资源到媒体库
  • 网上书店电子商务网站建设企业网站模板下载psd格式
  • 京东手机项目:手机受欢迎的影响因素分析
  • linux zgrep命令介绍
  • 成都著名网站建设公司php 抓取 wordpress 文字内容
  • 高性能Go协程库ants实战指南(二)
  • [Android] 【最新更新】电子书/小说下载APP 遇见云书V3.2.0