深度解析 Spring 源码:揭秘BeanFactory 之谜

2024-04-25 19:20

本文主要是介绍深度解析 Spring 源码:揭秘BeanFactory 之谜,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

文章目录

    • 一、认识BeanFactory
      • 1.1 BeanFactory的概述
      • 1.2 BeanFactory与 ApplicationContext的区别
    • 二、BeanFactory源码解读
      • 2.1 BeanFactory 接口
        • 2.1.1 getBean()
        • 2.1.2 containsBean()
        • 2.1.3 isSingleton()
      • 2.2 DefaultListableBeanFactory 类
        • 2.2.1 registerBeanDefinition()
        • 2.2.2 getBean()
    • 三、BeanFactory的使用场景与注意事项
      • 3.1 探讨BeanFactory在生命周期管理中的具体操作和控制逻辑,以及如何影响Bean的生命周期
      • 3.2 分析BeanFactory在依赖注入过程中的作用和实现方式
      • 3.3 探讨BeanFactory在AOP中的作用,以及如何实现切面的注入和切点的定义

一、认识BeanFactory

1.1 BeanFactory的概述

BeanFactory是Spring框架中的一个核心接口,它提供了一种灵活的方式来管理Bean,并实现了IoC(控制反转)和DI(依赖注入)等特性,为应用程序提供了灵活、可扩展的对象管理和配置机制。

BeanFactory的特性

  1. IoC容器: BeanFactory是Spring的IoC容器之一。IoC是一种设计模式,它将控制权从应用程序代码中转移到了外部容器中,由容器来管理和控制对象的创建、配置和生命周期。在Spring中,BeanFactory负责管理应用程序中的Java对象(称为Bean),并负责将它们装配到应用程序中。
  2. Bean的定义和注册: BeanFactory负责加载、解析和注册Bean的定义。Bean的定义通常包括Bean的类型、名称、作用域、依赖关系等信息。通过BeanFactory,我们可以将Bean的定义注册到容器中,以便在需要时进行实例化和使用。
  3. Bean的创建和管理: 一旦Bean的定义被注册到了BeanFactory中,BeanFactory就可以根据这些定义来创建和管理Bean的实例。BeanFactory负责实例化Bean,并处理它们的生命周期,包括初始化、依赖注入、属性设置等。
  4. 依赖注入(DI): BeanFactory实现了依赖注入的功能,它负责解析Bean之间的依赖关系,并将依赖的Bean实例注入到目标Bean中。这种依赖注入的方式使得对象之间的耦合度降低,提高了代码的灵活性和可测试性。
  5. 延迟初始化: BeanFactory支持延迟初始化,即只有在需要时才会实例化Bean。这种延迟加载的方式可以提高应用程序的性能和资源利用率,特别是在应用程序启动时加载大量的Bean时。
  6. 支持不同的Bean作用域: BeanFactory支持不同的Bean作用域,包括单例(singleton)、原型(prototype)、请求(request)、会话(session)等。通过配置不同的作用域,我们可以控制Bean的生命周期和共享方式。

1.2 BeanFactory与 ApplicationContext的区别

网上有些博主描述BeanFactory与 ApplicationContext是一样的 || 现在一般都用ApplicantContext代替BeanFactory 的说法,其实不一定准确。BeanFactory与 ApplicationContext大部分相同的原因是ApplicationContext是BeanFactory的子接口,所以存在些许区别;具体何时使用需要根据业务的需求合理性选择。

BeanFactory是一个轻量级的Bean容器,适用于资源有限的环境和对性能要求较高的场景;而ApplicationContext是一个功能更加丰富的应用上下文,适用于大多数的应用程序开发。

BeanFactory和ApplicationContext的区别

  1. 初始化时机:
    • BeanFactory在初始化时并不会实例化所有的Bean,而是在需要时才进行实例化。这样可以延迟加载,提高了应用程序的性能和资源利用率。
    • ApplicationContext在初始化时会预先实例化所有的单例Bean,并缓存这些实例。这样可以提高应用程序的启动速度,但也会消耗更多的内存。
  2. 功能扩展:
    • ApplicationContext是BeanFactory的子接口,它提供了更多的功能和扩展,如国际化支持、事件发布、AOP集成、资源加载、消息解析等。
    • BeanFactory相对简单,主要用于基本的Bean管理和依赖注入,不支持ApplicationContext提供的额外功能。
  3. 自动装配:
    • ApplicationContext支持自动装配(autowiring),可以根据类型、名称等条件自动注入依赖的Bean。
    • BeanFactory需要显式地配置依赖关系,不能自动装配Bean。
  4. 应用场景:
    • 如果应用程序对资源占用有较高的要求,且需要延迟加载Bean,则可以使用BeanFactory。
    • 如果应用程序需要更多的高级功能,如国际化、事件处理、AOP等,则建议使用ApplicationContext。

二、BeanFactory源码解读

由于源码过长,仅仅展示部分源码截图分析,想要深入了解的读者可以自行结合源码解读分析,这里不做过多描述,仅和BeanFactory有关,Bean的生命周期在后续博文会有所提及,本篇不做概述。

2.1 BeanFactory 接口

本文仅解读BeanFactory接口的主要方法,其它方法读者可自行结合源码解读。BeanFactory 接口主要的方法包括 getBean(String name)containsBean(String name)isSingleton(String name) 等。

在这里插入图片描述

2.1.1 getBean()

根据给定的条件获取一个Bean对象。

在这里插入图片描述

在这里插入图片描述

2.1.2 containsBean()

检查是否存在给定名称的Bean 。

在这里插入图片描述

2.1.3 isSingleton()

用于检查给定名称的Bean是否是单例的,它考虑了单例对象缓存、父级Bean工厂以及Bean定义等因素。

在这里插入图片描述

2.2 DefaultListableBeanFactory 类

BeanFactory 接口有多个实现类,其中最重要的是 DefaultListableBeanFactory 和 XmlBeanFactory。前者是 Spring 默认的 BeanFactory 实现类,后者是从 XML 文件加载 bean 配置信息的 BeanFactory 实现类。本文只分析DefaultListableBeanFactory 类,想要了解XmlBeanFactory类可以自行结合源码分析。

DefaultListableBeanFactory 类是 BeanFactory 接口的默认实现,负责管理 bean 的注册、解析、依赖注入等工作。

2.2.1 registerBeanDefinition()

用于注册Bean定义。

在这里插入图片描述

在这里插入图片描述

2.2.2 getBean()

获取指定类型的 Bean。

在这里插入图片描述

三、BeanFactory的使用场景与注意事项

3.1 探讨BeanFactory在生命周期管理中的具体操作和控制逻辑,以及如何影响Bean的生命周期

BeanFactory负责管理Bean的生命周期,其具体操作和控制逻辑

  1. Bean的加载与实例化: 当Spring容器启动时,BeanFactory会根据配置文件或注解等方式加载Bean的定义。在需要时,BeanFactory会根据这些定义实例化Bean对象。
  2. 依赖注入: 在实例化Bean时,BeanFactory会检查Bean之间的依赖关系,并自动注入依赖的Bean实例。这可以通过构造函数注入、属性注入或者方法注入来实现(正常情况下来说,不能使用注解自动注入,上文1.2 - 3有解释[先预判一下你们的预判,我相信有细心的读者看完下面的Demo,就会来说怎么博主使用了@Autowired,请看Demo下的ps有解释哈])。
  3. 初始化回调: 在Bean实例化完成后,BeanFactory会调用Bean的初始化回调方法。这些方法可以通过注解(如@PostConstruct)或接口(如InitializingBean接口)来指定,用于执行一些初始化操作。
  4. 销毁回调: 当Spring容器关闭时,BeanFactory会调用Bean的销毁回调方法。这些方法可以通过注解(如@PreDestroy)或接口(如DisposableBean接口)来指定,用于执行一些资源释放或清理操作。
  5. 作用域管理: BeanFactory支持不同的Bean作用域,如单例(singleton)、原型(prototype)等。根据配置,BeanFactory会管理和控制不同作用域下Bean的生命周期。

使用BeanFactory管理Bean的生命周期,接口实现Demo

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;/*** 实现了 InitializingBean 和 DisposableBean 接口,分别用于在Bean初始化和销毁时执行特定的逻辑
*/
@Component
class MyBean implements InitializingBean, DisposableBean {private String message;public MyBean() {System.out.println("Bean实例化");}/*** 通过 @Autowired 注解的 setMessage 方法进行注入*/@Autowiredpublic void setMessage(String message) {this.message = message;}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Bean初始化: " + message);}@Overridepublic void destroy() throws Exception {System.out.println("Bean销毁");}
}/*** 实现了 BeanFactoryPostProcessor 接口,用于在BeanFactory标准初始化之后修改应用程序上下文的内部bean工厂*/
@Component
class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {/*** BeanFactory 后置处理器是在 BeanFactory 标准初始化之后、在所有其他 bean 被实例化之前执行的* 意味着,在容器启动时,首先会初始化 BeanFactory,然后才会执行任何 BeanFactory 后置处理器的逻辑,包括自定义的 BeanFactory 后置处理器*/@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {System.out.println("自定义BeanFactory后置处理器");}
}public class Demo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Demo.class);MyBean myBean = context.getBean(MyBean.class);myBean.setMessage("Hello, Spring!");context.close();}
}/*** 输出结果:* 自定义BeanFactory后置处理器Bean实例化Bean初始化: Hello, Spring!Bean销毁* 原因:* 1、AnnotationConfigApplicationContext 会创建并初始化 BeanFactory,然后执行所有的 BeanFactoryPostProcessor,所以首先输出 “自定义BeanFactory后置处理器”。
2、Spring 容器会创建 MyBean 的实例,并输出 “Bean实例化”。
3、在 MyBean 实例化之后,Spring 容器会通过 @Autowired 注解注入 message 属性,并调用 afterPropertiesSet() 方法,输出 “Bean初始化: Hello, Spring!”。
4、当调用 context.close() 方法时,Spring 容器会销毁 MyBean 的实例,并调用 destroy() 方法,输出 “Bean销毁”。*/

ps: AnnotationConfigApplicationContextBeanFactory 接口的一个具体实现,同时也是 ApplicationContext 接口的一个子接口,它提供了对 @Autowired 注解的支持。

3.2 分析BeanFactory在依赖注入过程中的作用和实现方式

BeanFactory 能够通过读取配置文件或者注解等方式,将 bean 实例化并管理起来,并且解决它们之间的依赖关系,确保各个 bean 能够正确初始化和销毁。

使用 Spring 的 BeanFactory 实现依赖注入Demo:

  1. 创建一个简单的Java类作为bean。
/*** message 属性和相应的 setter 和 getter 方法*/
public class MyBean {private String message;public void setMessage(String message) {this.message = message;}public String getMessage() {return message;}
}
  1. 创建一个接口来定义BeanFactory的行为。
public interface MyBeanFactory {MyBean getMyBean();
}
  1. 实现这个接口来创建一个简单的BeanFactory。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class MyBeanFactoryImpl implements MyBeanFactory {private final MyBean myBean;/*** 构造函数注入来获取MyBean实例,并将其保存在BeanFactory中*/@Autowiredpublic MyBeanFactoryImpl(MyBean myBean) {this.myBean = myBean;}@Overridepublic MyBean getMyBean() {return myBean;}
}
  1. 创建一个简单的应用程序来演示BeanFactory的使用。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** MyDemo 类使用了 @Component 注解,表明它是一个Spring的组件,并且在其中注入了 MyBeanFactory 的实例。Spring在启动时会扫描这个类,并创建它的实例,并通过构造函数注入的方式将 MyBeanFactory 的实例传递给 MyDemo 类*/
@Component
public class MyDemo {private final MyBeanFactory myBeanFactory;/*** 通过构造函数注入的方式获取了 MyBeanFactory 的实例*/@Autowiredpublic MyDemo(MyBeanFactory myBeanFactory) {this.myBeanFactory = myBeanFactory;}public void run() {// 获取 MyBean 实例,并设置消息内容并输出MyBean myBean = myBeanFactory.getMyBean();myBean.setMessage("Hello, BeanFactory!");System.out.println(myBean.getMessage());}
}

3.3 探讨BeanFactory在AOP中的作用,以及如何实现切面的注入和切点的定义

当在 AOP(面向切面编程)中使用 BeanFactory 时,它的作用主要是负责管理切面(Aspect)以及它们所需的各种对象(通常是通知(Advice)和切点(Pointcut))。

实现 AOP,并实现切面的注入和切点的定义Demo:

  1. 定义切面(Aspect)。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;/** * 定义切面 MyAspect,并在someMethod()方法执行之前打印一条日志*/
@Aspect
@Component
public class MyAspect {@Before("execution(* com.example.MyBean.someMethod())")public void beforeSomeMethod() {System.out.println("Before executing someMethod()");}
}
  1. 定义切点(Pointcut)。

切点是一个表达式,它决定了切面将会在哪些连接点(方法调用、方法执行、异常处理等)被触发。在这里,将切点定义为 MyBean 类中的 someMethod() 方法。

execution(* com.example.MyBean.someMethod())
  1. 实现切面的注入。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class MyBeanFactoryImpl implements MyBeanFactory {private final MyBean myBean;/*** 注入了MyBean类的实例,这个实例可能会被切面拦截*/@Autowiredpublic MyBeanFactoryImpl(MyBean myBean) {this.myBean = myBean;}@Overridepublic MyBean getMyBean() {return myBean;}
}
  1. 启用AOP。

要启用 Spring 的 AOP 功能,需要在配置中启用 @EnableAspectJAutoProxy 注解。

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@EnableAspectJAutoProxy
public class AppConfig {// 其他配置...
}

人生在勤,不索何获

这篇关于深度解析 Spring 源码:揭秘BeanFactory 之谜的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/935566

相关文章

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain