Spring Bean实例化的底层原理

Spring容器实例化Bean底层原理

  1. Spring容器在初始化时,会将xml中配置的<bean>的信息(并不是bean对象本身,而是其信息)封装成一个BeanDefinition对象

  2. 再创建一个名为beanDefinitionMap的Map集合用于存储容器内所有的BeanDefinition对象

  3. Spring框架对beanDefinitionMap进行遍历,并通过反射机制创建对应的Bean对象

  4. 创建一个名为singletonObjects的Map集合用于存储产生的Bean对象

  5. 当调用getBean方法时则从singletonObjects中获取对应的Bean并返回

Spring的后处理器

Spring的后处理器允许*第三方介入*到Bean的整个实例化流程中,方便实现动态注册BeanDefinition、修改BeanDefinition以及修改Bean的效果,是Spring对外开放的重要扩展点

  • 注册BeanDefinition:理解为往BeanDefinitionMap添加BeanDefinition的过程

本质:在Spring对Bean实例化的过程中,其是依靠遍历BeanDefinitionMap中的每一个BeanDefinition而进行相应的Bean实例化,而这里的思路就是通过操纵(添加、修改、删除等)往BeanDefinitionMap中添加的某个BeanDefinition,便可以修改其实例化的Bean

Spring主要的两种处理器:

  • BeanFactoryPostProcessor

    Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行
    BeanFactoryPostProcessor是一个接口,将实现了该接口的类交予Spring容器进行管理,则Spring会自动调用接口方法,用于实现对BeanDefinition注册和修改的功能(主要偏向于对注册Bean的功能)

@FunctionalInterface  
public interface BeanFactoryPostProcessor {  
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;  
}  

例子-不通过配置将Processor放入BeanDefinitionMap中:

前提:Processor并未注册

将MyBeanFactoryPostProcessor交予Spring容器管理

    <bean class="com.xiaowan.processor.MyBeanFactoryPostProcessor">  
    </bean>```  

```java  
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {  
    @Override  
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {  
        System.out.println("BeanFactoryPostProcessor启动");  

        //创建一个BeanDefinition  
        BeanDefinition beanDefinition = new RootBeanDefinition();  
        //BeanDefinition注入Processor类路径  
        beanDefinition.setBeanClassName("com.xiaowan.dao.impl.ProcessorImpl");  
        //DefaultListableBeanFactory继承了ConfigurableListableBeanFactory接口,功能更加强大,通过其registerBeanDefinition方法将新创建的BeanDefinition放入BeanDefinitionMap中  
        DefaultListableBeanFactory defaultBeanFactory = (DefaultListableBeanFactory) beanFactory;  
        defaultBeanFactory.registerBeanDefinition("processorDao",beanDefinition);  
    }  
}  

以上仅为案例,在实际开发中当需要通过此方法进行注册时,可以使用BeanFactoryPostProcessor的子接口BeandefinitionRegistryPostProcessor,其专门用于注册BeanDefinition

其中在BeandefinitionRegistryPostProcessor的实现类的postProcessBeanDefinitionRegistry方法中进行注册操作

  • BeanPostProcessor

    Bean后处理器,在Bean实例化之后,填充到单例池singletonObjects之前执行(中间会经历Bean的初始化过程,例如属性的填充、初始化init等等)
    BeanPostProcessor是一个接口,将实现了该接口的类交予Spring容器进行管理,则Spring会自动调用接口方法,实现对Bean对象的操作(主要偏向于对实初始化的Bean对象本身进行操作的功能)
    BeanPostProcessor中存在两个接口方法:

  • postProcessBeforeInitialization:before

  • postProcessAfterInitialization:after

    执行时机:

graph TB  
A(Bean实例化) --> B(postProcessBeforeInitialization)  
B --> C(属性设置)  
C --> D(init方法执行)  
D --> E(postProcessAfterInitialization);  

注解配置 实现的基本思路

大概思路,实际注解的实现非常复杂,实现思想与本讲解抽象起来差不多

在Bean工厂的后处理器中,创建一个解析器,对指定包的整个包中的类进行全方位代码扫描,获取使用了指定注解的类的相关信息(例如类名,类路径),存储到BeanDefinitionMap中,成功实现注解注册Bean

例--自定义注解MyComponent完成Spring托管配置

MyComponent注解的声明:

@Target(ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)  
public @interface MyComponent {  
    String value();  
}  

Bean工厂的后处理器--BeanDefinitionRegistryPostProcessor的实现类

public class MyComponentBeanFactoryProcessor implements BeanDefinitionRegistryPostProcessor {  
    @Override  
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {  
        //通过扫描工具扫描指定包及其子包下的所有类,收集使用@MyComponent的注解的类  
        Map<String, Class> myComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation("com.xiaowan");  
        //遍历Map,组装BeanDefinition注册  
        myComponentAnnotationMap.forEach((beanName,clazz)-> {  
            //获得beanClassName  
            String beanClassName = clazz.getName();  
            //创建BeanDefinition  
            BeanDefinition beanDefinition = new RootBeanDefinition();  
            beanDefinition.setBeanClassName(beanClassName);  
            //注册  
            beanDefinitionRegistry.registerBeanDefinition(beanName,beanDefinition);  
        });  

    }  

    @Override  
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {  

    }  
}  

将BeanDefinitionRegistryPostProcessor交予spring托管

    <bean class="com.xiaowan.test.MyComponentBeanFactoryProcessor">  
    </bean>  

使用注解

@MyComponent("otherBean")  
public class OtherBean {  
}  

自定义注解配置成功实现

解析器具体代码

public class BaseClassScanUtils {  

    //设置资源规则  
    private static final String RESOURCE_PATTERN = "/**/*.class";  

    public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {  

        //创建容器存储使用了指定注解的Bean字节码对象  
        Map<String, Class> annotationClassMap = new HashMap<String, Class>();  

        //spring工具类,可以获取指定路径下的全部类  
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();  
        try {  
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +  
                    ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;  
            Resource[] resources = resourcePatternResolver.getResources(pattern);  
            //MetadataReader 的工厂类  
            MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);  
            for (Resource resource : resources) {  
                //用于读取类信息  
                MetadataReader reader = refractory.getMetadataReader(resource);  
                //扫描到的class  
                String classname = reader.getClassMetadata().getClassName();  
                Class<?> clazz = Class.forName(classname);  
                //判断是否属于指定的注解类型  
                if(clazz.isAnnotationPresent(MyComponent.class)){  
                    //获得注解对象  
                    MyComponent annotation = clazz.getAnnotation(MyComponent.class);  
                    //获得属value属性值  
                    String beanName = annotation.value();  
                    //判断是否为""  
                    if(beanName!=null&&!beanName.equals("")){  
                        //存储到Map中去  
                        annotationClassMap.put(beanName,clazz);  
                        continue;  
                    }  

                    //如果没有为"",那就把当前类的类名作为beanName  
                    annotationClassMap.put(clazz.getSimpleName(),clazz);  

                }  
            }  
        } catch (Exception exception) {  
        }  

        return annotationClassMap;  
    }  

}  
Spring流程图Spring Bean的生命周期

即Bean的实例化(通过反射创建出对象)-> Bean的初始化(Bean成为完整对象)-> 存储到单例池中

三个阶段:

graph LR  
A[Bean的实例化阶段]-->B[Bean的初始化阶段]-->C[Bean的完成阶段]  
  • Bean的实例化阶段
graph LR  
A[Spring框架对BeanDefinition的信息进行判断]-->B{"是否是singleton的"}  
A-->C{"是否延迟加载"}  
A-->D{"是否是FactoryBean"}  
A-->E{"......"}  
F[将一个普通的singleton的Bean通过反射进行实例化]  
B-->F  
C-->F  
D-->F  
E-->F  
  • Bean的初始化阶段

    实例化阶段得到的Bean为半成品,还需进行加工

graph TB  
A[填充Bean实例的属性]-->B[执行部分Aware接口方法]  
-->C[执行BeanPostProcessor方法]-->D[执行InitializingBean接口的初始化方法]  
-->E[执行自定义初始化init方法]-->F[......]  

该阶段是Spring最具有技术含量和复杂度的阶段

例如:

Aop增强功能

Spring注解功能

Bead的循环引用问题等等

  • Bean的完成阶段

    初始化完成后,Bean成功编程Spring Bean,并存入单例池

Spring Bean的初始化阶段
  • Bean实例的属性填充

    在Beandefinition中propertyValues用于存储Bean实体的注入信息

    不同数据类型的属性填充方式:

    • 注入普通属性、String、int或存储基本类型的集合

      通过set方法的反射设置

    • 注入单向对象引用属性 (单向:即A类需要注入B类,但B类不需要注入A类)

      以下情况的前提是容器中载入了引用对象

      • 当容器中存在引用对象时,从容器中getBean()获取后通过set方法反射设置

      • 当容器中不存在引用对象时(即当前Bean实例已被创建时所需注入Bean实例还未被创建),则先创建被注入对象Bean实例(完成整个生命周期后),再进行注入

    • 注入双向对象引用属性 (双向:即A类需要注入B类,且B类也需要注入A类)

      循环依赖/循环引用 (重点原理)

      多个实体之间相互依赖并形成闭环

graph LR
A[BeanA] --> B[BeanB]
B-->A
原先单向引用对象方法应用与双向引用对象场景场景:

注意点:

  • 该图中,所检验的容器中是否存在Bean实例的真正含义是:是否能在单例池(SingletonObjects)中查找到该类,而这里的实例化Bean实例仅仅只是开辟内存空间存储半成品,还无法存入单例池中,即不能作为通过条件

  • 以上过程不断循环,使得内存占用越来越多,并且无法解决问题

开拓思路:

当实例化BeanA后,进入其引用类的判断,接着实例化BeanB,再次进入其引用类的判断,回到BeanA的实例化...

为了使得死循环拥有终点:

这里将实例化好的BeanA存入一个Map集合中(由于单例池只存储完整的Bean,这里使用一个新的池),当进行BeanB的实例化以及对应注入类时,先从单例池进行注入类的查询,当查询无果时,再从这个新池子进行注入类的查询,查询到不完整的BeanA后,先将不完整的BeanA注入至BeanB终结死循环,后续再对BeanA进行相应的操作使BeanA也完整化

以上思想即为三级缓存

三级缓存

为了可以存储完整Bean实例半成品Bean实例解决循环引用问题,Spring框架创建出DefaultSingletonBeanRegistry类(DefaultListableBeanFactory的上四级父类)提供了三个池子实现三级缓存

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
    ......
}
  • singletonObjects 一级缓存

    最终存储单例Bean成品的容器(实例化和初始化都完成的Bean)

  • earlySingletonObjects 二级缓存

    早期Bean单例池,缓存半成品对象且当前对象已经被其他对象引用

  • singletonFactories 三级缓存

    单例Bean的工厂池,缓存半成品对象且对象未被引用

    使用时通过工厂创建Bean

ObjectFactory:用于实现延迟依赖查找(区别于延迟加载等)

查找底层:依赖于beanFactory.getBean()

延迟底层:依赖于DependencyObjectProvider,只有当我们调用ObjectFactory.getBean()方法才会对所需依赖进行处理

场景引入:

  1. BeanA进行实例化后,Spring框架依靠半成品的BeanA创建一个对应的ObjectFactory对象(包装一层,当实际需要半成品对象时,调用ObjectFactory对象的getObject方法返回对象实例用于使用)并放入三级缓存中(singletonFactories)

  2. 进入BeanA注入类的查询(在三个池子中依次进行进行查询),并未查询到BeanB后,创建出BeanB的半成品对象,并同BeanA半成品对象的方式创建对应的ObjectFactory对象其放入三级缓存中

  3. 在三个池子中依次查询BeanB的注入类BeanA,在三级缓存中查询到BeanA,将BeanA注入至BeanB,与此同时,将原先的BeanA从三级缓存中移除,并将BeanA实例放入二级缓存中

  4. 注入所需注入类后的BeanB执行其他生命周期过程,最终成为一个完成的Bean对象,并存入一级缓存中,同时删除二三级缓存

  5. BeanA注入完成的BeanB,执行其他声明周期过程,最终成为完整的Bean,存入一级缓存中,并删除二三级缓存

图解:

后续对Bean的许多重要操作基本都依赖本过程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值