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

AliasRegistry
AliasRegistry是 Spring 框架中的一个接口,它提供了管理对象别名的功能。在 Spring 的 Bean 管理场景中,别名可以为 Bean 提供额外的标识符,增加了配置的灵活性和可维护性。以下是关于AliasRegistry的详细介绍:
java
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:
java
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名称的映射:
java
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);
@Override
public 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-register
return;
}
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方法我们可以看到这段代码:
java
this.aliasMap.put(alias, name);
通过这段代码我们可以看出,别名map集合的key是别名,val 是Bean的名称,以此来完成映射
别名的使用
在Spring的XML配置文件中,使用别名有以下两种常见方式:
- 通过
bean标签的name属性 :在bean标签中,name属性可以用来指定一个或多个别名,多个别名之间可以用逗号、分号或空格隔开。示例如下:
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">
<!-- 这里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>
在上述配置中,可以通过bookService、service、service4或bookEbi中的任意一个名称从Spring容器中获取BookServiceImpl类型的bean。
- 通过
alias标签 :使用<alias>标签也可以为bean指定别名,name属性指定要设置别名的bean的名称,alias属性指定别名。示例如下:
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">
<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
- ListableBeanFactory让bean工厂有了批量处理bean的能力,是对整个bean工厂容器所在集合的整体批处理操作,
- 在文档注释中提到了一个词:"枚举(enumerate )",在英文里这个单词的意思是 "逐个列出、遍历、清点" 的意思,我第一次看到这个词想到了枚举类,但是这里的枚举不是枚举类,
- 而是一次性列出所有 Bean,而不是像普通 BeanFactory 那样只能通过 getBean("beanName") 一个个查找,
- 它下面的方法包括: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继承自HierarchicalBeanFactory和SingletonBeanRegistry接口。通过继承HierarchicalBeanFactory,它获得了处理父子Bean工厂关系的能力,使得Spring容器可以构建分层的Bean工厂结构,这在一些复杂的应用场景中非常有用,例如在Spring的Web应用中,可能存在根应用上下文和特定Servlet的上下文,它们之间通过父子Bean工厂关系进行管理。继承SingletonBeanRegistry则为其提供了管理单例Bean的基础功能,如注册、获取和移除单例Bean。 - 与
BeanFactory的关系 :BeanFactory是Spring框架中Bean管理的基础接口,定义了获取Bean实例等基本操作。ConfigurableBeanFactory在BeanFactory的基础上进行了扩展,提供了更多配置和管理Bean工厂的高级功能,是Spring容器在实际实现中用于精细控制Bean创建和管理的重要接口。
3. 应用场景
- 自定义Bean创建逻辑 :在开发自定义框架或对Spring容器进行扩展时,开发人员可能需要自定义Bean的创建逻辑。
ConfigurableBeanFactory提供的方法允许在Bean创建前、创建过程中和创建后进行干预。例如,可以通过自定义的BeanPostProcessor结合ConfigurableBeanFactory来对Bean进行特殊的初始化或增强操作。 - 动态Bean注册与管理 :在一些动态配置或插件化的应用场景中,可能需要在运行时动态注册、修改或移除Bean定义。
ConfigurableBeanFactory提供的相关方法使得这种动态管理成为可能。比如,在一个支持插件扩展的应用中,新的插件可以在运行时向Spring容器注册新的Bean定义,ConfigurableBeanFactory能够很好地支持这种操作。 - 资源管理与优化 :通过对Bean作用域的管理,
ConfigurableBeanFactory有助于优化应用的资源使用。例如,对于资源消耗较大的Bean,可以将其设置为单例作用域,确保在容器中只有一个实例,避免重复创建带来的资源浪费;而对于一些每次使用都需要全新状态的Bean,则可以设置为原型作用域。
4. 示例代码
以下是一个简单的示例,展示如何使用ConfigurableBeanFactory的部分功能:
java
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 {
@Override
public String toString() {
return "This is MyBean";
}
}
在上述示例中,首先获取ConfigurableBeanFactory实例,然后注册了一个MyBean的Bean定义,并将其作用域设置为原型,最后从容器中获取MyBean实例并打印。通过这个示例,可以直观地了解ConfigurableBeanFactory在Bean管理中的基本使用方式。
总结
DefaultListableBeanFactory 是Spring框架中一个核心的类,它实现了 ConfigurableListableBeanFactory 接口,而 ConfigurableListableBeanFactory 又继承了多个重要接口,如 ConfigurableBeanFactory、ListableBeanFactory 等。以下从多个方面来介绍 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。例如,如果一个BeanA依赖于BeanB,DefaultListableBeanFactory会先创建或获取BeanB的实例,然后将其注入到BeanA中。这种依赖注入的处理机制是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:
java
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();
// 定义一个BeanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(MyBean.class).getBeanDefinition();
// 注册BeanDefinition
factory.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 创建了一个 MyBean 的 BeanDefinition,并将其注册到 DefaultListableBeanFactory 中。最后通过 getBean 方法获取 MyBean 的实例并调用其方法。这个示例简单演示了 DefaultListableBeanFactory 的基本使用流程。