Spring源码(三)-Bean工厂-BeanFactory

简介: DefaultListableBeanFactory 上篇就使用到了 DefaultListableBeanFactory类中的registerBeanDefinition方法将 `BeanDefinition` 缓存在了 `beanDefinitionMap` Map中。`DefaultListableBeanFactory` 是Spring中最为核心的 `BeanFactory`。
日积月累,水滴石穿 😄

DefaultListableBeanFactory

上篇就使用到了 DefaultListableBeanFactory类中的registerBeanDefinition方法将 BeanDefinition 缓存在了 beanDefinitionMap Map中。DefaultListableBeanFactory 是Spring中最为核心的 BeanFactory。先来看一下它的继承关系图。
继承关系图

看到这个图可能会吓一大跳,DefaultListableBeanFactory 实现了这么多接口,不过这也代表着它拥有很多功能,我们先来分析每个接口的作用吧!

    1. AliasRegistry:支持别名功能,一个 beanName 可以对应多个别名
    1. BeanDefinitionRegistry:可以注册、保存、移除、获取某个BeanDefinition
    1. SimpleAliasRegistry:它是一个类,实现了AliasRegistry接口中所定义的功能,支持别名功能,使用 map 作为 alias 的缓存
    1. SingletonBeanRegistry:可以直接注册、获取某个单例 Bean
    1. BeanFactory:Bean工厂,可以根据某个 bean 的名字、或类型、或别名获取某个 Bean 对象
    1. ListableBeanFactory:在 BeanFactory 的基础上,增加了其他功能,可以获取所有 BeanDefinitionbeanNames,可以根据某个类型获取对应的 beanNames,可以根据某个类型获取{类型:对应的Bean}的映射关系
    1. HierarchicalBeanFactory:在 BeanFactory 的基础上,添加了获取父 BeanFactory 的功能
    1. DefaultSingletonBeanRegistry:它是一个类,实现了 SingletonBeanRegistry 接口,拥有了直接注册、获取某个单例Bean的功能,还继承了 SimpleAliasRegistry,支持别名功能
    1. ConfigurableBeanFactory:在 HierarchicalBeanFactorySingletonBeanRegistry 的基础上,添加了设置父 BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、设置SpringEL表达式解析器(表示该 BeanFactory 可以解析EL表达式)、设置类型转化服务(表示该 `BeanFactory 可以进行类型转化)、可以添加 BeanPostProcessor(表示该 BeanFactory 支持Bean的后置处理器),可以合并 BeanDefinition,可以销毁某个 Bean 等功能
    1. FactoryBeanRegistrySupport:支持了FactoryBean的功能
    1. AutowireCapableBeanFactory:直接继承了 BeanFactory,在 BeanFactory 的基础上,支持在创建Bean 的过程中能对 Bean 进行自动装配
    1. AbstractBeanFactory:实现了 ConfigurableBeanFactory 接口,继承了FactoryBeanRegistrySupport,这个 BeanFactory的功能已经很全面了,但是不能自动装配和获取beanNames
    1. ConfigurableListableBeanFactory:继承了 ListableBeanFactoryAutowireCapableBeanFactoryConfigurableBeanFactory
    1. AbstractAutowireCapableBeanFactory:继承了 AbstractBeanFactory,实现了AutowireCapableBeanFactory,拥有了自动装配的功能
    1. DefaultListableBeanFactory:继承了 AbstractAutowireCapableBeanFactory,实现了ConfigurableListableBeanFactory 接口和 BeanDefinitionRegistry接口,所以DefaultListableBeanFactory的功能很强大
    1. XmlBeanFactory:XmlBeanFactory 类继承自DefaultListableBeanFacotry类,使用了自定义的XML 读取器 XmlBeanDefinitionReader,主要使用 reader 属性对资源文件进行读取和注册

通过以上分析,我们可以知道,通过DefaultListableBeanFactory我们可以做很多事情,比如:

    public static void main(String[] args) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition.setBeanClass(User.class);

        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        // 注册BeanDefinition
        factory.registerBeanDefinition("user", beanDefinition);
        // 注册别名
        factory.registerAlias("user", "user1");
        // 添加BeanPostProcessor
        factory.addBeanPostProcessor(new GongjBeanPostProcessor());
        // 获取别名获取Bean对象
        System.out.println(factory.getBean("user1"));
        // 根据类型获取beanNames
        String[] beanNamesForType = factory.getBeanNamesForType(User.class);
        for (String s : beanNamesForType) {
            System.out.println(s);
        }
    }
  • GongjBeanPostProcessor
public class GongjBeanPostProcessor implements BeanPostProcessor {
    //bean初始化方法调用前被调用
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean初始化方法调用前被调用");
        return null;
    }
    //bean初始化方法调用后被调用
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean初始化方法调用后被调用");
        return null;
    }
}

BeanPostProcessorSpring IOC 容器给我们提供的一个扩展接口,该接口就不在本文中阐述。


容器概览

Spring 中的 Ioc 容器,我们可以大致上分为两种:

  • BeanFactory
  • ApplicationContext

BeanFactory

BeanFactory 是最最基础的 IoC 容器,它提供了一个 IoC 容器所需的基本功能。
BeanFactory 默认采用延迟初始化策略,即当容器启动时,并未完成 Bean 的初始化,只有当调用到该 Bean 的实例时,才会完成其初始化操作,并进行依赖注入。

如下代码:

XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
User user = factory.getBean(User.class);

当第一行代码执行的时候,beans.xml 中配置的 User 对象并未进行初始化,只有当第二行 getBean 方法调用时,User 对象才进行了初始化操作。

这样设计的好处是容器启动速度快,因为要做的事情比较少。接下来看看BeanFactory的示例:

public interface BeanFactory {  

    //FactoryBean的前缀
    //有&符号:返回工厂本身
   //无&符号:返回工厂创建的实例
    String FACTORY_BEAN_PREFIX = "&";

    //返回指定bean的一个实例,如果没有找到指定bean,该方法会抛出异常
    Object getBean(String name) throws BeansException;

    //返回以给定名称注册的bean实例,并转换为给定class类型
    //如果没有找到指定bean、或者类型转换失败,该方法会抛出异常
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    // 通过name获取Bean, 通过第二个参数可以给对象赋值,这种方式需要 指定Bean的作用域为 prototype
    Object getBean(String name, Object... args) throws BeansException;

    // 根据指定Class类型查找Bean
    // 如果没有找到指定bean、或者找到多个类型,该方法会抛出异常
    <T> T getBean(Class<T> requiredType) throws BeansException;

    //通过类型查找Bean,通过第二个参数可以给对象赋值,需要 指定Bean的作用域为 prototype
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    // 获取bean的提供者
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

    // 是否包含指定名字的bean
    boolean containsBean(String name);

    // 指定名称的bean是否为单例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    // 指定名称的bean是否为原型
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    // 指定名称的bean是否和指定的类型匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    // 获取指定名字的bean的类型
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//获取指定名字的bean的类型,如果该 Bean 的类型为 FactoryBean,第二个参数就有作用,当 参数值为 false 时,FactoryBean不会被创建。
    @Nullable
    Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

    // 获取指定名字的bean的所有别名
    String[] getAliases(String name);
}

实例

  public static void main(String[] args) {
        ClassPathResource resource = new ClassPathResource("spring-config.xml");
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions(resource);
        //获取别名获取Bean对象
        System.out.println("person1 = " + factory.getBean("person"));
        System.out.println("person2 = " +factory.getBean("person", Person.class));
        System.out.println("person3 = " + factory.getBean("person", "gongj", "shanghai"));
        System.out.println("person4 = " + factory.getBean(Person.class));
        System.out.println("person4 = " + factory.getBean(Person.class,"gognj"));

        System.out.println("getBeanProvider = " + factory.getBeanProvider(Person.class));

        System.out.println("containsBean = " + factory.containsBean("person"));
        System.out.println("isSingleton = " + factory.isSingleton("person"));
        System.out.println("isPrototype = " + factory.isPrototype("person"));
        System.out.println("isPrototype = " + factory.isTypeMatch("person",Person.class));
        System.out.println("getType = " + factory.getType("person",false));
        System.out.println("getType = " + factory.getType("p",false));

        String[] people = factory.getAliases("person");
        for (String s : people) {
            System.out.println("getAliases = " + s);
        }
        // 根据类型获取beanNames
        String[] beanNamesForType = factory.getBeanNamesForType(User.class);
        for (String s : beanNamesForType) {
            System.out.println(s);
        }
    }
  • xml
<bean id="person" class="com.gongj.bean.Person" scope="prototype"></bean>
<alias name="person" alias="person2"/>
<bean id="p" class="com.gongj.bean.PersonFactory"></bean>
  • Person
public class Person {

    private String name;
    private String address;

    public Person(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public Person(String name) {
        this.name = name;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
  • PersonFactory
public class PersonFactory implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

ApplicationContext

ApplicationContext 是在 BeanFactory 的基础上实现的,BeanFactory 的功能它都有,算是一种高级容器。ApplicationContextBeanFactory 的基础上提供了事件发布、国际化等功能。同时,ApplicationContextBeanFactory 还有一个很大的不同。在于 ApplicationContext 在容器启动时,就会完成所有单例非懒加载 Bean 的初始化,这也就以为着容器启动时间较长,并且对系统资源要求也较高。

如下一段代码:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

当这段代码执行时,beans.xml 中配置的 User 对象就会完成初始化。
ApplicationContext是个接口,可以把它理解为一个特殊的BeanFactory,通常建议比BeanFactory优先 。

  • ApplicationContext 不等于 BeanFactoryApplicationContext 只是继承了BeanFactory, 拥有了Bean 工厂的功能,同时,ApplicationContext 还继承了其他接口,比如:
  • HierarchicalBeanFactory:拥有获取父 BeanFactory 的功能
  • ListableBeanFactory:拥有获取 beanNames 的功能
  • ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
  • EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
  • ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
  • MessageSource:拥有国际化功能

image.png

ApplicationContext 还有两个比较重要的实现类:

  • AnnotationConfigApplicationContext
  • ClassPathXmlApplicationContext

AnnotationConfigApplicationContext

image.png

  • ConfigurableApplicationContext:继承了 ApplicationContext 接口,增加了添加事件监听器、添加 BeanFactoryPostProcessor、设置 Environment,获取 ConfigurableListableBeanFactory 等功能
  • AbstractApplicationContext:实现了 ConfigurableApplicationContext 接口,继承 DefaultResourceLoader
  • GenericApplicationContext:继承了 AbstractApplicationContext,实现了 BeanDefinitionRegistry 接口,拥有了所有 ApplicationContext 的功能,并且可以注册 BeanDefinition,注意这个类中有一个属性是(DefaultListableBeanFactory beanFactory)
  • AnnotationConfigRegistry:可以单独注册某个类为 BeanDefinition(可以处理该类上的@Configuration注解,可以处理@Bean注解)。
  • AnnotationConfigApplicationContext:继承了GenericApplicationContext,实现了AnnotationConfigRegistry接口,拥有了以上所有的功能

ClassPathXmlApplicationContext

image.png

它也是继承了AbstractApplicationContext,但是相对于AnnotationConfigApplicationContext 而言,功能没有AnnotationConfigApplicationContext 强大,比如不能注册 BeanDefinition

参考文献

Spring 源码系列之容器概览

相关文章
|
16天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
45 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
23天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
22天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
45 9
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
120 5
|
7月前
|
安全 Java 应用服务中间件
阿里技术官架构使用总结:Spring+MyBatis源码+Tomcat架构解析等
分享Java技术文以及学习经验也有一段时间了,实际上作为程序员,我们都清楚学习的重要性,毕竟时代在发展,互联网之下,稍有一些落后可能就会被淘汰掉,因此我们需要不断去审视自己,通过学习来让自己得到相应的提升。
|
7月前
|
Java 关系型数据库 数据库连接
Spring源码解析--深入Spring事务原理
本文将带领大家领略Spring事务的风采,Spring事务是我们在日常开发中经常会遇到的,也是各种大小面试中的高频题,希望通过本文,能让大家对Spring事务有个深入的了解,无论开发还是面试,都不会让Spring事务成为拦路虎。
100 1
|
2月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
2月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
137 9