深度理解spring——BeanFactory的实现

BeanFactory

Spring之BeanFactory

什么是BeanFactory

BeanFactory是SpringApplication类的父类接口

BeanFactory才是Spring的核心容器,主要的SpringApplication类都组合了他的功能。

通过Ctrl+alt+u可以看到

可以从图片中看到SpringApplication是继承了BeanFactory接口对BeanFactory进行了功能性的扩展。

比如SpringApplication获取一个bean的对象

复制代码
       run.getBean(BreadRollMallServer.class);

他实际上是通过组合了BeanFactory实现找个获取对象的功能,可以看出来SpringApplication是间接的调用了BeanFactory的功能

ApplicationContext相对BeanFactory实现的功能性扩展

1. MessageSource

MessageSource 是 Spring 框架里用于消息解析与国际化的核心接口。借助它,你能够依据不同的语言环境获取对应的文本消息,从而实现国际化支持。像错误消息、提示信息这类文本内容,就可以通过 MessageSource 进行管理。

2. ResourcePatternResolver

ResourcePatternResolver 接口可用来解析资源路径,并且支持使用通配符来匹配多个资源。它是 ResourceLoader 的扩展,能够依据给定的资源路径模式查找多个资源。在加载配置文件、静态资源等场景中会经常用到。

3. ApplicationEventPublisher

ApplicationEventPublisher 接口提供了发布应用事件的功能。在 Spring 应用里,事件机制是一种重要的设计模式,允许组件之间以松耦合的方式进行通信。通过 ApplicationEventPublisher,你可以发布自定义的应用事件,其他组件可以监听这些事件并做出响应。

应用场景:用户注册时候的发短信发邮件。

4. EnvironmentCapable

EnvironmentCapable 接口表明一个对象具备获取 Environment 对象的能力。Environment 对象封装了应用程序运行时的环境信息,包含系统属性、环境变量、配置文件属性等。借助 Environment,你可以方便地获取和管理这些属性。

通用ApplicationContext实践实现BeanFactory

复制代码
public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // bean 的定义(class(类型), scope(单例or多例), 初始化方法, 销毁方法)
		// BeanDefinitionBuilder..xxx.xxx.xxx.getBeanDefinition()
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //(beanName,xxx)将bean注册到bean工厂中
        beanFactory.registerBeanDefinition("config", beanDefinition);

        // 【重点】给 BeanFactory 添加一些常用的后处理器(对BeanFactory的扩展) 
        //将后处理添加到bean工厂
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        // BeanFactory 后处理器主要功能,补充了一些 bean 定义
        // beanFactory.getBeansOfType 根据类型获取多个bean
        //BeanFactoryPostProcessor.class拿到bean工厂的所有后处理器,对bean工厂做出扩展就可以解析@bean注解等。
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
        	//拿到bean工厂的所有后处理器并且执行
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);//执行BeanFactory后置处理器
        });
        
       // 添加 BeanPostProcessor  也就是添加bean的后处理器
       //bean后处理器 针对bean的生命周期各个阶段进行扩展,例如@Autowire @Resouse
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }


    static class Bean1 {
        private static final Logger log = LoggerFactory.getLogger(Bean1.class);
        @Autowired
        private Bean2 bean2;
    }

    static class Bean2 {
        private static final Logger log = LoggerFactory.getLogger(Bean2.class);
    }
}

通过这个案例,你可以看到如何使用 DefaultListableBeanFactory 创建和管理 Bean,以及如何使用后置处理器在 Bean 的生命周期中执行自定义逻辑。

区别对比

  1. 作用时机
    BeanFactoryPostProcessor:在 BeanFactory 完成 Bean 定义的加载之后,但在 Bean 实例化之前执行。也就是说,它处理的是 Bean 的定义信息,而不是 Bean 实例。
    BeanPostProcessor:在 Bean 实例化之后,初始化前后执行。它作用于已经创建好的 Bean 实例。
  2. 作用对象
    BeanFactoryPostProcessor:作用于 BeanFactory 本身,主要用于修改 BeanFactory 中 Bean 的定义信息,例如修改 Bean 的属性值、作用域等。
    BeanPostProcessor:作用于具体的 Bean 实例,允许开发者在 Bean 初始化前后对其进行定制化处理。
  3. 功能侧重点
    BeanFactoryPostProcessor:侧重于对 Bean 定义的全局修改和扩展,例如动态添加或修改 Bean 定义,调整 Bean 的配置等。
    BeanPostProcessor:侧重于对 Bean 实例的个性化处理,例如对 Bean 进行增强、验证、日志记录等。

总结:

1.bean工厂不会主动调用beanFactory后处理器

2.bean不会主动添加后处理器

3.不会主动初始化单例

4.不会解析BeanFactory

BeanFactory后处理器排序让谁优先执行

第一种实现通过xml等方式实现的bean注入原理

通过xml注入bean容器

复制代码
  // ⬇️1.最为经典的容器,基于classpath 下 xml 格式的配置文件来创建
    public void testClassPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring_bean.xml");
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
第二种基于磁盘路径下 xml 格式的配置文件来创建
复制代码
        //基于磁盘路径下 xml 格式的配置文件来创建
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        System.out.println("读取之前");
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println("读取之后");
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);;
        reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\spring_bean.xml"));
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
第三种较为经典的容器,基于java配置类来创建

较为经典的容器,基于java配置类来创建,通过这个配置类创建会帮我们创建后处理器了来解析@Bean注解

复制代码
   // ⬇️3.较为经典的容器,基于java配置类来创建
    public void testAnnotationConfigApplicationContext() {
        // 会自动加上5个后处理器
        // org.springframework.context.annotation.internalConfigurationAnnotationProcessor
        // org.springframework.context.annotation.internalAutowiredAnnotationProcessor
        // org.springframework.context.annotation.internalCommonAnnotationProcessor
        // org.springframework.context.event.internalEventListenerProcessor
        // org.springframework.context.event.internalEventListenerFactory
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
// 单元测试的过程中如果要解析一些Spring注解,比如@Configuration的时候不要把相关类定义到写单元测试类的内部类,会读取不到
@Configuration
class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public Bean2 bean2(Bean1 bean1) {
        Bean2 bean2 = new Bean2();
        bean2.setBean1(bean1);
        return bean2;
    }
}

class Bean1 {

}

class Bean2 {
    private Bean1 bean1;

    public Bean1 getBean1() {
        return bean1;
    }

    public void setBean1(Bean1 bean1) {
        this.bean1 = bean1;
    }
}
第四种较为经典的容器,基于java配置类来创建,并且还可以用于web环境
复制代码
   // 模拟了 springboot web项目内嵌Tomcat的工作原理
    public void testAnnotationConfigServletWebServerApplicationContext() throws IOException {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        // 防止程序终止
        System.in.read();
    }
    
@Configuration
class WebConfig {
    @Bean
    // 1. WebServer工厂
    public ServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

    @Bean
    // 2. web项目必备的DispatcherServlet
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    @Bean
    // 3. 将DispatcherServlet注册到WebServer上
    public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }

    @Bean("/hello")
    public Controller controller1() {
        return (request, response) -> {
            response.getWriter().println("hello");
            return null;
        };
    }
}
相关推荐
BillKu10 分钟前
Java后端检查空条件查询
java·开发语言
涡能增压发动积13 分钟前
一起来学 Langgraph [第一节]
后端
jackson凌14 分钟前
【Java学习笔记】String类(重点)
java·笔记·学习
ruokkk33 分钟前
重启Eureka集群中的节点,对已经注册的服务有什么影响
后端
刘白Live37 分钟前
【Java】谈一谈浅克隆和深克隆
java
一线大码39 分钟前
项目中怎么确定线程池的大小
java·后端
要加油哦~42 分钟前
vue · 插槽 | $slots:访问所有命名插槽内容 | 插槽的使用:子组件和父组件如何书写?
java·前端·javascript
LNin43 分钟前
Spring AI 自定义数据库持久化的ChatMemory
后端
crud1 小时前
Spring Boot 3 整合 Swagger:打造现代化 API 文档系统(附完整代码 + 高级配置 + 最佳实践)
java·spring boot·swagger
天天摸鱼的java工程师1 小时前
从被测试小姐姐追着怼到运维小哥点赞:我在项目管理系统的 MySQL 优化实战
java·后端·mysql