【Spring技术实战】带你正视一下Spring祖容器之BeanFactory的原理与功能分析

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【Spring技术实战】带你正视一下Spring祖容器之BeanFactory的原理与功能分析

BeanFactory


BeanFactory是Spring bean容器的根接口,BeanFactory作为应用集中配置管理的地方,极大简便应用开发,这样开发人员可以集中与业务,这边定义了一系列的接口,通过这些接口的学习,可以大致了解BeanFactory体系各接口如何分工合作.



BeanFactory类的介绍


BeanFactory是Spring实现依赖注入的核心接口,提供应用的统一配置注册功能,实现业务开发解偶。使用getBean可以代替单例,原型设计模式.


BeanFactory里注释进行详细分析.


BeanFactory的介绍说明


The root interface for accessing a Spring bean container. This is the basic client view of a bean container; further interfaces such as {@link ListableBeanFactory} and {@link org.springframework.beans.factory.config.ConfigurableBeanFactory} are available for specific purposes


  • 访问一个Spring bean容器的根接口。这是一个bean容器的基本客户端视图; 进一步的接口,如 ListableBeanFactory 和  org.springframework.beans.factory.config.ConfigurableBeanFactory}可用于特殊目的。



BeanFactory的作用范围


This interface is implemented by objects that hold a number of bean definitions, each uniquely identified by a String name. Depending on the bean definition, the factory will return either an independent instance of a contained object (the Prototype design pattern), or a single shared instance (a superior alternative to the Singleton design pattern, in which the instance is a singleton in the scope of the factory). Which type of instance will be returned depends on the bean factory configuration: the API is the same. Since Spring 2.0, further scopes are available depending on the concrete application context (e.g. "request" and "session" scopes in a web environment).



  • 此接口由持有一些bean定义的对象来实现,每个bean由String字符串唯一标识。根据bean定义,工厂将返回一个独立对象实例(原型设计模式),或者一个单个共享实例(Singleton设计模式的优雅代替实现,其中该实例是一个factory范围内的单例)。
  • 实例的哪种类型将被返回依赖于bean工厂配置:即使API是一样的。从Spring2.0开始,作用域扩展到根据具体的应用上下文,如web环境的request,session。



BeanFactory的作用职能


The point of this approach is that the BeanFactory is a central registry of application components, and centralizes configuration of application components (no more do individual objects need to read properties files, for example). See chapters 4 and 11 of "Expert One-on-One J2EE Design and Development" for a discussion of the benefits of this approach.



  • 这种方案的关键是,BeanFactory的是应用程序组件注册的中心,同时集中应用程序组件的配置(程序模块不再需要读取诸如properties的配置文件)。这种设计的更多好处讨论详见的<J2EE设计开发编程指南>第4和第11章.


Note that it is generally better to rely on Dependency Injection ("push" configuration) to configure application objects through setters or constructors, rather than use any form of "pull" configuration like a BeanFactory lookup. Spring's Dependency Injection functionality is implemented using this BeanFactory interface and its subinterfaces.


  • 相比诸如 BeanFactory 中查找的pull配置方式,通过setters或者构造方法,依赖注入的方式配置应用对象更好,Spring的依赖注入功能就是通过实现BeanFactory和其子接口实现的.


Normally a BeanFactory will load bean definitions stored in a configuration source (such as an XML document), and use the {@code org.springframework.beans} package to configure the beans. However, an implementation could simply return Java objects it creates as necessary directly in Java code. There are no constraints on how the definitions could be stored: LDAP, RDBMS, XML, properties file, etc. Implementations are encouraged to support references amongst beans (Dependency Injection).


  • 通常,一个BeanFactory会从配置源(如XML文件)中加载bean对象模型,并使用{@code org.springframework.beans}包解析bean。然而,实现可以简单地返回Java代码直接新建的Java对象。这里没有限制bean 定义文件的格式:LDAP,RDBMS,XML.实现类欢迎支持应用而非bean(依赖注入)


In contrast to the methods in {@link ListableBeanFactory}, all of the operations in this interface will also check parent factories if this is a {@link HierarchicalBeanFactory}. If a bean is not found in this factory instance, the immediate parent factory will be asked. Beans in this factory instance are supposed to override beans of the same name in any parent factory.

  • 对比{@ListableBeanFactory}中的方法,如果这是一个{@link HierarchicalBeanFactory},这个接口的全部实现都会查找父工厂,如果在这个工厂实例找不到bean,去直接父工厂查找。factory实例中的bean会覆盖父factory实例中的同名bean。



Bean factory implementations should support the standard bean lifecycle interfaces as far as possible.


  • 实现类应该尽量支持标准bean的生命周期接口.全套的初始化方法。
public interface BeanFactory {
    /**
     * 用于区分是否直接获取FactoryBean实例.
     * bean以&开头表示获取FactoryBean实例.否则获取created的实例.For example, if the bean named
     * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
     * will return the factory, not the instance returned by the factory.
     */
    String FACTORY_BEAN_PREFIX = "&";
    /**
     * 返回一个原型或者单例实例.
     * 抢单例,原型设计模式的饭碗
     * 可以根据别名查找,也可以去父容器实例查找
     */
    Object getBean(String name) throws BeansException;
    /**
     * 加个类型
     */
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    /**
     * 根据类型获取bean实例.可以是接口或子类,但不能是{@code null}.
     * {@link ListableBeanFactory}也可以使用类型转化为name进行查找.更多bean集合的操作可以看
     * ListableBeanFactory和BeanFactoryUtils
     */
    <T> T getBean(Class<T> requiredType) throws BeansException;
    /**
     * 多了构造方法,工厂方法的参数
     */
    Object getBean(String name, Object... args) throws BeansException;
    /**
     * 判断是否包含bean(包括别名,父容器)
     * 陷阱出现:这边不管类是否抽象类,懒加载,是否在容器范围内,只要符合都返回true,所以这边true,不一定能从getBean获取实例
     */
    boolean containsBean(String name);
    /**
     * 是否单例
     */
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    /**
     * 是否原型
     */
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    /**
     * 是否有跟name匹配类型的bean
     */
    boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;
    /**
     * 根据bean name获取类型
     */
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    /**
     * 获取别名
     */
    String[] getAliases(String name);
}
复制代码



接口里定义了一个变量String FACTORY_BEAN_PREFIX = "&": 用来区分是获取FactoryBean还是FactoryBean的createBean创建的实例,如果&开始则获取FactoryBean;否则获取createBean创建的实例.


  • Object getBean(String name) throws BeansException; 可以用别名查找哦
  • T getBean(String name, Class requiredType) throws BeansException;
  • T getBean(Class requiredType) throws BeansException; 这边的类型可以是接口或者子类,但不能是null
  • Object getBean(String name, Object... args) throws BeansException;
  • 判断是否包含bean.陷阱出现:这边不管类是否抽象类,懒加载,是否在容器范围内,只要符合都返回true,所以这边true,不一定能从getBean获取实例
  • boolean containsBean(String name);
  • 单例,原型,bean类型的判断
  • boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

 - boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

 - boolean isTypeMatch(String name, Class<?> targetType) throws


NoSuchBeanDefinitionException;


  • 获取bean 的类型,别名  


Class<?> getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);



BeanFactory的架构模型机制


BeanFactory是Spring bean容器的根接口,提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean别名的api。


  • AutowireCapableBeanFactory 添加集成其他框架功能,如果集成WebWork则可以使用Spring对Actions等进行管理.


  • HierarchicalBeanFactory:提供父容器的访问功能
  • ConfigurableBeanFactory:如名,提供factory的配置功能。
  • ConfigurableListableBeanFactory:集大成者,提供解析,修改bean定义,并与初始化单例.
  • ListableBeanFactory 提供容器内bean实例的枚举功能,这边不会考虑父容器内的实例.


image.png



AutowireCapableBeanFactory


在BeanFactory基础上实现对已存在实例的管理.


  • 可以使用这个接口集成其它框架,捆绑并填充并不由Spring管理生命周期并已存在的实例.像集成其他框架机制。


  • 一般应用开发者不会使用这个接口,所以像ApplicationContext这样的外观实现类不会实现这个接口,如果真手痒痒可以通过ApplicationContext的getAutowireCapableBeanFactory接口获取。



这边定义了5种自动装配策略:


  • 不注入:AUTOWIRE_NO
  • 使用bean name策略装配:AUTOWIRE_BY_NAME
  • 使用类型装配策略:AUTOWIRE_BY_TYPE
  • 使用构造器装配策略:AUTOWIRE_CONSTRUCTOR
  • 自动装配策略:AUTOWIRE_AUTODETECT


这边的自动策略是先尝试构造器,然后才是byType,应该是跟xml配置文件中的装配策略对应。



创建和填充外部bean实例的典型方法

<T> T createBean(Class<T> beanClass) throws BeansException;
// 使用autowireBeanProperties装配属性
void autowireBean(Object existingBean) throws BeansException; 
// 自动装配属性,填充属性值,使用诸如setBeanName,setBeanFactory这样的工厂回调填充属性,最好还要调用post processor
Object configureBean(Object existingBean, String beanName) throws BeansException; 
Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException;
复制代码



在bean的生命周期进行细粒度控制的专门方法

Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
 // 会执行bean完整的初始化,包括BeanPostProcessors和initializeBean
Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;
void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
Object initializeBean(Object existingBean, String beanName) throws BeansException;
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;
Object resolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException;
复制代码



HierarchicalBeanFactory


提供父容器的访问功能.至于父容器的设置,需要找ConfigurableBeanFactory的setParentBeanFactory(接口把设置跟获取给拆开了!).



获取父容器 bean factory

BeanFactory getParentBeanFactory();


判断当前容器是否保护bean

boolean containsLocalBean(String name);



ListableBeanFactory


获取bean时,Spring 鼓励使用这个接口定义的api,其他的4个接口都是不鼓励使用的.


提供容器中bean迭代的功能,不再需要一个个bean地查找,比如可以一次获取全部的bean,根据类型获取bean.在看SpringMVC时,扫描包路径下的具体实现策略就是使用的这种方式(那边使用的是BeanFactoryUtils封装的api).


如果同时实现了HierarchicalBeanFactory,返回值不会考虑父类BeanFactory,只考虑当前factory定义的类.当然也可以使用BeanFactoryUtils辅助类来查找祖先工厂中的类.


  • 这个接口中的方法只会考虑本factory定义的bean.
  • 这些方法会忽略ConfigurableBeanFactory的registerSingleton注册的单例bean(getBeanNamesOfType和getBeansOfType是例外,一样会考虑手动注册的单例).
  • 当然BeanFactory的getBean一样可以透明访问这些特殊bean.当然在典型情况下,所有的bean都是由external bean定义,所以应用不需要顾虑这些差别.


注意:getBeanDefinitionCount和containsBeanDefinition的实现方法因为效率比较低,还是少用为好.



暴力获取全部bean的属性:


  • boolean containsBeanDefinition(String beanName);  //是否包含bean
  • int getBeanDefinitionCount(); // 当前factory中定义的bean数量
  • String[] getBeanDefinitionNames(); // 获取当前工厂中定义的所有bean 的name



根据bean的类型获取bean


这边的方法仅检查顶级bean.它不会检查嵌套的bean.FactoryBean创建的bean会匹配为FactoryBean而不是原始类型.


一样不会考虑父factory中的bean,非要用可以通过BeanFactoryUtils中的beanNamesForTypeIncludingAncestors.


这个版本的getBeanNamesForType会匹配所有类型的bean,包括单例,原型,FactoryBean.返回的bean names会根据backend 配置的进行排序.


  • String[] getBeanNamesForType(Class<?> type); // 获取给定类型的bean names(包括子类),通过bean 定义或者FactoryBean的getObjectType判断.
  • String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
  • Map<String, T> getBeansOfType(Class type) throws BeansException; // 如果保护懒加载的类,FactoryBean初始化的类和工厂方法初始化的类会被初始化.就是说执行这个方法会执行对应的初始化.
  • Map<String, T> getBeansOfType(Class type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException;



查找使用注解的类


  • Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;


查找一个类上的注解,如果找不到,父类,接口使用注解也算.





相关文章
|
2月前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
73 6
|
2月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
50 1
|
4月前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
665 6
|
3月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
3月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
70 0
|
2天前
|
Ubuntu API 网络虚拟化
ubuntu22 编译安装docker,和docker容器方式安装 deepseek
本脚本适用于Ubuntu 22.04,主要功能包括编译安装Docker和安装DeepSeek模型。首先通过Apt源配置安装Docker,确保网络稳定(建议使用VPN)。接着下载并配置Docker二进制文件,创建Docker用户组并设置守护进程。随后拉取Debian 12镜像,安装系统必备工具,配置Ollama模型管理器,并最终部署和运行DeepSeek模型,提供API接口进行交互测试。
66 14
|
2月前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
294 78
|
1月前
|
Ubuntu NoSQL Linux
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
152 6
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
|
2月前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
|
2月前
|
Ubuntu Linux 开发工具
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
Docker 是一个开源的容器化平台,允许开发者将应用程序及其依赖项打包成标准化单元(容器),确保在任何支持 Docker 的操作系统上一致运行。容器共享主机内核,提供轻量级、高效的执行环境。本文介绍如何在 Ubuntu 上安装 Docker,并通过简单步骤验证安装成功。后续文章将探讨使用 Docker 部署开源项目。优雅草央千澈 源、安装 Docker 包、验证安装 - 适用场景:开发、测试、生产环境 通过以上步骤,您可以在 Ubuntu 系统上成功安装并运行 Docker,为后续的应用部署打下基础。
95 5
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈