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

发布于:2024-04-25 ⋅ 阅读:(20) ⋅ 点赞:(0)

在这里插入图片描述

一、认识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 方法进行注入
     */
    @Autowired
    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Bean初始化: " + message);
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Bean销毁");
    }
}

/**
 * 实现了 BeanFactoryPostProcessor 接口,用于在BeanFactory标准初始化之后修改应用程序上下文的内部bean工厂
 */
@Component
class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    /**
     * BeanFactory 后置处理器是在 BeanFactory 标准初始化之后、在所有其他 bean 被实例化之前执行的
     * 意味着,在容器启动时,首先会初始化 BeanFactory,然后才会执行任何 BeanFactory 后置处理器的逻辑,包括自定义的 BeanFactory 后置处理器
     */
    @Override
    public 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中
     */
    @Autowired
    public MyBeanFactoryImpl(MyBean myBean) {
        this.myBean = myBean;
    }

    @Override
    public 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 的实例
     */
    @Autowired
    public 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类的实例,这个实例可能会被切面拦截
     */
    @Autowired
    public MyBeanFactoryImpl(MyBean myBean) {
        this.myBean = myBean;
    }

    @Override
    public 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 {
    // 其他配置...
}

人生在勤,不索何获