• bean的创建过程分析finishBeanFactoryInitialization01


    bean的创建过程分析-finishBeanFactoryInitialization-01

    我们都知道spring中有两个启动类, 分别是 ClassPathXmlApplicationContextFileSystemXmlApplicationContext

    通常我们实际使用比较多得启动类就是 ClassPathXmlApplicationContext。下面我们就来先简单的回忆一下该启动类启动的核心流程。

    0、前言概述

    我们先来看一下类的继承关系图 ,这里一定要知道 ClassPathXmlApplicationContext 构造方法中调用的refresh() 方法是父类AbstractXmlApplicationContext 类中的refresh() 方法。

    image-20220503134325821

    我们都知道,要想搞懂spring源码,必须要搞清楚refresh() 源码。下图就是展示了大致的一个如何走到refresh() 方法的流程

    image-20220503135441842

    然后今天我们主要讲解的就是refresh方法中执行的finishBeanFactoryInitialization(beanFactory) 这一步。

    1、finishBeanFactoryInitialization(beanFactory); 初始化bean讲解

    finishBeanFactoryInitialization 方法中,依然是隶属于 AbstractApplicationContext

    refresh()方法中一共执行了13个方法,今天我们具体讲讲 finishBeanFactoryInitialization(beanFactory); 这个方法 是如何实例化bean的过程。

    image-20220503142238591

    1.1、第一部分概述 - ConversionService 处理

    这部分,主要是向 spring 容器中添加 beanFactory 添加 一些类型转换服务, 具体的细节参见:

        // Initialize conversion service for this context.
    		// 为上下文初始化类型转换器
    		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
    				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
    			beanFactory.setConversionService(
    					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    		}
    

    Spring中类型转换服务ConversionService 以及 Converter 类型转换器

    1.2、第二部分概述 - EmbeddedValueResolver 处理

    这部分, 主要判断是否有 EmbeddedValueResolver 内嵌式值处理器,如果没有的话, 就添加该处理器

    实际上,我们在 执行 invokeBeanFactoryPostProcessor(beanFactory) 的时候, 就已经处理过这部分了, 所以一般是进不来的,更多的是起到安全性考虑的保护作用。

    
    if (!beanFactory.hasEmbeddedValueResolver()) {
    			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    		}
    

    image-20220506231338083

    image-20220506231531888

    1.3、第三部分概述-LoadTimeWeaverAware 处理

        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
    		// 尽早初始化loadTimeWeaverAware bean,以便尽早注册它们的转换器
    		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    		for (String weaverAwareName : weaverAwareNames) {
    			getBean(weaverAwareName);
    		}
    

    image-20220506232318248

    其实这部分中的 LoadTimeWeaverAware 主要是为了后面的面向切面服务的, 其实我们在prepareBeanFactory 环节中就已经添加了相应的织入执行器

    image-20220506233118349

    正如截图中的注解所言:

    • LoadTimeWeaverAware 主要是在AOP中进行使用的, 后面我们讨论AOP的时候在进行补充

    1.4、第四部分概述- TempClassLoader & freezeConfiguration

    image-20220506233443960

    这部分怎么理解呢?

    • 这部分主要是做实例化之前的禁止操作,实例化之前,我们不希望beanFactory中的bean定义信息BeanDefinition 再有修改。所以存在上述的一些操作

    • beanFactory.setTempClassLoader(null); 即 精致使用临时类加载器, 在进行类型匹配操作了,即不希望再有类的一些定义信息再加入到``beanFactory`了。

    • beanFactory.preInstantiateSingletons(); 即 冻结所有的BeanDefinition 定义, 说明注册的bean定义将不被修改或者任何进一步的处理。

      • 	public void freezeConfiguration() {
        		this.configurationFrozen = true;
        		this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
        	}
        
      • 这里主要做了两件事

        • 设置冻结的标志位
        • 将所有的冻结的BeanDefinitionNames 即bean定义信息的名称, 添加到frozenBeanDefinitionNames 字符串数组中去。

    1.5、第五部分概述- preInstantiateSingletons

    这个步骤非常重要, 核心作用就是预先实例化所有单例bean

        // 实例化剩下的单例对象
    		beanFactory.preInstantiateSingletons();
    

    具体讲解,我会单独拿出来说明。。。

    1.5.1、下面我们结合一个FactoryBean接口的使用,分析一下FactoryBeanBeanFactory 的区别

    • 实现一个Car 汽车类
    package com.qzk.QzkFactoryBeanDir;
    
    /**
     * @ClassName Car
     * @Description 汽车实现类
     * @Author qzk
     * @Date 2022/5/9 11:30 上午
     * @Version 1.0
     **/
    public class Car {
    
        /**
         * 名称
         */
        private String name;
    
        /**
         * 品牌
         */
        private String brand;
    
        /**
         * 速度
         */
        private Integer speed;
    
        public Car() {
        }
    
        public Car(String name, String brand, Integer speed) {
            this.name = name;
            this.brand = brand;
            this.speed = speed;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getBrand() {
            return brand;
        }
    
        public void setBrand(String brand) {
            this.brand = brand;
        }
    
        public Integer getSpeed() {
            return speed;
        }
    
        public void setSpeed(Integer speed) {
            this.speed = speed;
        }
    
        @Override
        public String toString() {
            return "Car{" +
                    "name='" + name + '\'' +
                    ", brand='" + brand + '\'' +
                    ", speed='" + speed + '\'' +
                    '}';
        }
    }
    
    
    • 实现一个继承FactoryBean接口的类,重写getObject() 方法,返回Car 对象。
    package com.qzk.QzkFactoryBeanDir;
    
    import org.springframework.beans.factory.FactoryBean;
    
    /**
     * @ClassName CarFactoryBean
     * @Description 实现了 FactoryBean 接口的 CarFactoryBean 汽车工厂
     * @Author qzk
     * @Date 2022/5/9 11:29 上午
     * @Version 1.0
     **/
    public class CarFactoryBean implements FactoryBean<Car> {
    
    
        private String carInfo;
    
        public CarFactoryBean(String carInfo) {
            this.carInfo = carInfo;
        }
    
        public String getCarInfo() {
            return carInfo;
        }
    
        public void setCarInfo(String carInfo) {
            this.carInfo = carInfo;
        }
    
        @Override
        public Car getObject() throws Exception {
            Car car = new Car();
            String[] splits = carInfo.split(",");
            car.setName(splits[0]);
            car.setBrand(splits[1]);
            car.setSpeed(Integer.valueOf(splits[2]));
            return car;
        }
    
        @Override
        public Class<?> getObjectType() {
            return Car.class;
        }
    
        @Override
        public boolean isSingleton() {
            return FactoryBean.super.isSingleton();
        }
    }
    
    
    • 编写xml配置文件 testFactoryBean.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--  直接创建car1 这个bean   -->
        <bean id="car1" class="com.qzk.QzkFactoryBeanDir.Car" >
            <constructor-arg name="brand" value="奔驰"></constructor-arg>
            <constructor-arg name="name" value="E300"></constructor-arg>
            <constructor-arg name="speed" value="200"></constructor-arg>
        </bean>
    
        <!-- 利用无参构造, 结合 property 属性赋值的方式创建bean   -->
        <bean id="car2" class="com.qzk.QzkFactoryBeanDir.CarFactoryBean" >
            <property name="carInfo" value="总裁,玛莎拉蒂,251"></property>
        </bean>
    
        <!--  利用有参构造 结合 constructor-arg 参数传递的方式创建bean -->
        <bean id="car3" class="com.qzk.QzkFactoryBeanDir.CarFactoryBean">
            <constructor-arg name="carInfo" value="卡宴,保时捷,250"></constructor-arg>
        </bean>
    
    </beans>
    
    • 编写测试启动类 TestFactoryBean
    package com.qzk.QzkFactoryBeanDir;
    
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * @ClassName TestFactoryBean
     * @Description 测试 实现了FactoryBean接口的测试类
     * @Author qzk
     * @Date 2022/5/9 11:51 上午
     * @Version 1.0
     **/
    public class TestFactoryBean {
    
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("testFactoryBean.xml");
            Car car = (Car) context.getBean("car1");
            System.out.println(car); // Car{name='E300', brand='奔驰', speed='200'}
            Car car2 = (Car)context.getBean("car2");
            System.out.println(car2); // Car{name='总裁', brand='玛莎拉蒂', speed='251'}
            CarFactoryBean  bean = (CarFactoryBean) context.getBean("&car2");
            System.out.println(bean); // com.qzk.QzkFactoryBeanDir.CarFactoryBean@1d29cf23
            CarFactoryBean carFactoryBean = context.getBean("&car2",CarFactoryBean.class);
            System.out.println(carFactoryBean); // com.qzk.QzkFactoryBeanDir.CarFactoryBean@1d29cf23
            Car car21 = context.getBean("car2", Car.class);
            System.out.println("car2 是单例模式, 所以再工厂bean中获取的car2 应该是同一个bean,所以相同:" +(car21 == car2));
            Car car3 = (Car) context.getBean("car3");
            System.out.println(car3);
            Object car31 = context.getBean("car3");
            System.out.println("car3 是原型模式,所以这边应该是不等的:"+ (car31 == car3));
    //
        }
    }
    
    

    image-20220509135451895

    1.5.2、源码分析beanFactory.preInstantiateSingletons()

    我们在上面的xml中分别定义个 car1 & car2 & car3,同样,spring 在读取的时候, 也会按照这个顺序依次去创建car

    1. 创建car1 普通bean 的执行流程

    image-20220509140343972

    大致的一个 bean创建流程 遵循如下如所示:

    image-20220509140750733

    在 执行doCreateBean 的过程中, 会根据 bean的类型, bean的定义信息(是否单例,是否原型, 是否懒加载, 是否初始化, 之类的信息, 做一些相关的处理工作,),并且会判断是否是工厂类, 以及是否是静态工厂,以及实例工厂等方式去执行不同的实例化步骤。

  • 相关阅读:
    【原】Go语言及Web框架Beego环境无脑搭建
    Treap学习笔记
    读书笔记--Head First PMP目录
    Linux操作系统常用命令合集——第四篇-文件系统权限操作(5个命令)
    Linux操作系统常用命令合集——第三篇-系统管理操作(25个命令)
    Linux操作系统常用命令合集——第二篇- 用户和组操作(15个命令)
    路由器与交换机配置——配置文件和系统映像备份与恢复
    路由器配置——密码重置
    交换机配置——端口安全
    路由器与交换机配置——交换机默认网关(实现跨网段telnet)
  • 原文地址:https://www.cnblogs.com/qianzhengkai/p/16257658.html
Copyright © 2020-2023  润新知