不一样的视角来学习Spring源码之容器与Bean---上

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不一样的视角来学习Spring源码之容器与Bean---上相关的知识,希望对你有一定的参考价值。

Spring源码学习一之容器与Bean---上


1) 容器接口

  • BeanFactory 接口,典型功能有:

    • getBean
  • ApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:

    • 国际化
    • 通配符方式获取一组 Resource 资源
    • 整合 Environment 环境(能通过它获取各种来源的配置信息)
    • 事件发布与监听,实现组件之间的解耦

BeanFactory 与 ApplicationContext 的区别

  1. 到底什么是 BeanFactory

    • 它是 ApplicationContext 的父接口
    • 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory
  2. BeanFactory 能干点啥

    • 表面上只有 getBean
    • 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
    • 下面例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean

BeanFactory只是一个接口,我们需要的是它的实现类,BeanFactory默认实现类为DefaultListableBeanFactory


下面我们尝试反射调用该成员变量:

@SpringBootApplication
public class OneMain 
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException 
        ConfigurableApplicationContext context = SpringApplication.run(OneMain.class, args);

        //默认单例bean的注册中心
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //获取在beanFactory实例上singletonObjects属性集合
        Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
        //对Bean名称进行过滤,不然会有很多的单例bean
        map.entrySet().stream().filter(e->e.getKey().startsWith("dhy")).forEach(e->
            System.out.println(e.getKey()+" = "+e.getValue());
        );
    


3.ApplicationContext 比 BeanFactory 多点啥

  • ApplicationContext 组合并扩展了 BeanFactory 的功能
  • 国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听
  • 新学一种代码之间解耦途径,事件解耦

国际化

国际化文件均在 src/resources 目录下

messages.properties(空)

messages_en.properties

hi=Hello

messages_zh.properties

hi=你好

注意

  • ApplicationContext 中 MessageSource bean 的名字固定为 messageSource
  • 使用 SpringBoot 时,国际化文件名固定为 messages
  • 空的 messages.properties 也必须存在
@SpringBootApplication
public class OneMain 
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException 
        GenericApplicationContext context=new GenericApplicationContext();
         //自定义一个bean注册到容器中
        context.registerBean("messageSource", MessageSource.class, () -> 
            ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
            ms.setDefaultEncoding("utf-8");
            ms.setBasename("messages");
            return ms;
        );

        context.refresh();

        System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
        System.out.println(context.getMessage("hi", null, Locale.CHINESE));
    


资源获取

详细参考

@SpringBootApplication
public class OneMain 
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException 
        GenericApplicationContext context=new GenericApplicationContext();
        //寻找多个资源,底层使用ResourcePatternResolver
        Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
        for (int i = 0; i < resources.length; i++) 
            System.out.println(resources[i]);
        
        //查找单个资源--底层使用DefaultResourceLoader
        Resource resource = context.getResource("classpath:messages.properties");
        System.out.println(resource);
    


环境

@SpringBootApplication
public class OneMain 
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException 
        GenericApplicationContext context=new GenericApplicationContext();
        String java_home = context.getEnvironment().getProperty("java_home");
        System.out.println(java_home);
    


事件

自定义一个事件发布对象:

public class UEvent extends ApplicationEvent 
    public UEvent(Object source) 
        super(source);
    

发布事件:

@SpringBootApplication
public class OneMain 
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException 
        ConfigurableApplicationContext applicationContext = SpringApplication.run(OneMain.class, args);
        //我们只要获取到了context对象,就可以在应用程序执行期间发布事件
        applicationContext.publishEvent(new UEvent("你好,我是自定义事件"));
    

监听器:

@Component
public class UListener 
    @EventListener
    public void listener(UEvent event)
    
        System.out.println(event.getSource());
    


详细参考


2) 容器实现

Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考

  • DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转依赖注入功能,都是它来实现
  • ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
  • FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
  • XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
  • AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
  • AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
  • AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)
  • AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)

另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来


DefaultListableBeanFactory

使用演示:

@SpringBootApplication
public class OneMain 
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException 
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();
       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);
        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) 
            System.out.println(beanDefinitionNames[i]);
        
    

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


BeanFactory自己为什么不能解析bean里面的@Bean和 @Configuration注解呢?

为什么只有config被注册到了容器中,config里面的@Bean注解并没有被解析,显然BeanFactory并没有解析这些注解的能力,那么这些能力是who提供的呢?

public class OneMain 
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException 
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();
       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);

        //给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
        //这一步只是往容器中注册了一些后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //这一步才是让这些后置处理器开始工作
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
                .values().stream().forEach(
                        //执行bean工厂后置处理器
                        beanPostProcessor->beanPostProcessor.postProcessBeanFactory(beanFactory);
                            System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
                        );

        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) 
            System.out.println(beanDefinitionNames[i]);
        
    

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


Spring的BeanFactory原始功能并没有那么丰富,但是可以通过后置处理器来增强BeanFactory的功能


BeanFactory自己为什么不能从bean定义中扫描到@Autowired注解,并进行自动依赖注入呢?

上面添加的bean工厂的后置处理器是用来解析一些例如@Configuration,@Bean等注解


我们还需要让Bean的后置处理器开始工作,Bean的后置处理器一般工作在Bean的生命周期过程中

public class Bean2 
    @Autowired
    private Bean1 bean1;

    public Bean2() 
        System.out.println("Bean2构造中...");
    

    public Bean1 getBean1() 
        return bean1;
    

public class Bean1 
    public Bean1() 
        System.out.println("Bean1构造中...");
    


@SpringBootApplication
public class OneMain 
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException 
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();

       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);

        //给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
        //这一步只是往容器中注册了一些后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //这一步才是让这些后置处理器开始工作
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
                .values().stream().forEach(
                        //执行bean工厂后置处理器
                        beanPostProcessor->beanPostProcessor.postProcessBeanFactory(beanFactory);
                            System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
                        );

        //添加bean后置处理器---针对bean的生命周期各个阶段提供扩展
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) 
            System.out.println(beanDefinitionNames[i]);
        

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        System.out.println(beanFactory.getBean(Bean2.class).getBean1());
    

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


使用到bean才会去创建,这显然表明BeanFactory是懒加载的


BeanFactory设置预先实例化所有的Bean

@SpringBootApplication
public class OneMain 
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException 
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();

       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);

        //给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
        //这一步只是往容器中注册了一些后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //这一步才是让这些后置处理器开始工作
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
                .values().stream().forEach(
                        //执行bean工厂后置处理器
                        beanPostProcessor->beanPostProcessor.postProcessBeanFactory(beanFactory);
                            System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
                        );

        //添加bean后置处理器---针对bean的生命周期各个阶段提供扩展
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::以上是关于不一样的视角来学习Spring源码之容器与Bean---上的主要内容,如果未能解决你的问题,请参考以下文章

不一样的视角来学习Spring源码之AOP---下

spring源码-bean之加载-2

spring源码学习之路---IOC容器初始化要义之bean定义载入

重新学习Spring AOP 之源码分析

Spring核心技术之IOC容器:IOC容器与Bean简介

spring源码阅读-- 容器启动之创建bean