美文网首页java 知识点
Spring Bean的解析和加载详细解释

Spring Bean的解析和加载详细解释

作者: Tim在路上 | 来源:发表于2019-10-21 17:09 被阅读0次

    spring分为以下几个部分:

    1. Core Container

    Core Container 包含 Core,Beans,Context,Expression Language 模块
    Core 和 Beans 提供 IOC 和 依赖注入特性.
    Context 提供了类似JNDI注册器的框架, ApplicationContext 接口是Context的关键
    EL 用于运行时的查询和操作

    beans 的解析

    1. 读取Beans

    spring自定义资源加载类

    Resource resource=new ClassPathResource( "beanFactoryTest .xml ”), 
    InputStream inputStream=resource.getinputStream (); 
    

    而资源加载类的底层也是调用字节流的加载,这样可以加载jar中配置文件

    if (this . clazz 1= null ) { 
        is = this clazz getRes urceAsStream(this path)
    }else { 
        is= this classLoader . getResourceAsStream(this . path} ; 
    }
    

    Resources 完成对xml的封装后,将其交给 XMLBeanDefinitionReader

    然后 XMLBeanFactory通过调用this.reader.loadBeanDefinitions(resource); 来进行加载Bean

    注意:在调用加载资源文件前会先调用 ignore BeanNameAware 接口, BeanNameAware 接口是什么呢? 我们一般用的Bean都是无知觉的,不知道自己在那个工厂中,或者自己的代号,通过实现BeanNameAware接口,接口中就一个方法setBeanName(). A中有属性B, 那么当Spring在获取A的Beans时候,B还没有初始化,Spring会初始化B,但是B实现BeanNameAware接口,就会忽略再进行初始化.
    1. 首先对Resource进行EncodedResource封装, EncodedReasource 作用设置编码属性
      然后使用 SAX 读取 XML 得到Document

    2. 根据Document 注册 解析标签

    得到root节点,然后首先处理Profile属性的使用 profile 可以表面使用 dev 还是 production test等,这个特性用来配置生产环境和测试环境.

    spring-xml中配置两种Bean声明

    <bean id=” test” class=” test TestBean ” />
    
    <tx :annotation-driven/> 
    

    spring中默认的标签有四个:

    import alias bean beans

    • bean 标签解析
    1. 提取元素中的id以及name属性
    2. 进一步解析其他属性封装 GenericBeanDefinition中,没有name的生成name

    spring的配置信息主要以map形式进行保存

    1. 然后对保存在实体中属性进行解析
    <bean id = myTestBea class= "bean.MyTestBean">
        <meta key=” testStr " value= ” aaaaaaaa ” />
    </bean>
    
    1. 解析子元素meta construct, property,qualifier等
      • AbstractBeanDefinition 属性

    GenericBeanDefinition 只是子类实现,大部分保存在 AbstractBeanDefinition 中

      • 注册解析的 Bean Definition

    解析Bean进行注册主要分为 通过beanName进行注册,通过别名进行注册两种方式

    (1) 通过BeanName进行注册

    beanDefinitionMap 是全局变量存在并发访问.

    beanDefinitionMap beanName作为key, beanDefinition 作为Value

    这里会判断如果已经注册同时设置不允许覆盖,则不进行覆盖,否则覆盖 ,清除之前Bean缓存

    (2) 通过别名alias进行注册

    如果alias与beanName相同不记录alias,使用beanName走1

    如果aliasName已经使用并指向另一个beanName进行设置,避免 A->B 存在 , A -> C -> B进行循环检测

    注册alias

      • 通知监听器注册完成
    • alias 标签解析

    在对bean进行定义,除了使用 id外,还可以使用别名alias,给bean提供多个名称

    <bean id= ”testBean” class= ”com.test” />
    

    可以直接当前位置加别名

    <bean id= ” testBean ” name= ” testBean , tescBean 2 ” c lass= ” com test " />
    

    同时可以在别处定义bean别名

    <alias name="testBean" alias="testBear,testBean2" /〉
    

    和beanName一样都是将别名和beanName组成一对进行注册

    • import 标签解析
    <import resource=” customerContext.xml ” />
    <import resource=” systemContext.xml ” />
    

    使用import导入不同模块的配置文件

    递归调用解析程序

    • 嵌入式beans标签解析

    bean 的加载

    1. 转换为对应的beanName

    传入参数可能为alias或者FactoryBean ,返回别名指向的最终beanName

    1. 尝试从缓存中加载单例
    ** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
    
    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
    
    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
    

    spring 中有三级缓存,singletonFactories : 单例对象工厂的cache
    earlySingletonObjects :提前暴光的单例对象的Cache
    singletonObjects:单例对象的cache

    spring为了避免循环依赖注入,采用三级缓存,在创建bean中不等bean创建完成就将创建bean的ObjectFactory提前曝光到缓存中,下一个bean需要依赖这个bean就可以直接使用ObjectFactory
    1. bean 的实例化

    如果得到的原始状态,就需要对bean进行实例化.
    通过工厂获得bean,还未返回bean中factory-method 返回的bean

    原型模式需要依赖检测 , 在单例情况下才会尝试解决循环依赖

    1. 如果缓存中没有数据,同时容器中有父类工厂,且不为null,通过父类工厂加载bean
    2. 根据不同scope进行创建bean
    • FactoryBean 的使用

    一般情况下通过配置bean的class的反射来实例化bean, 某些情况bean实例比较复杂,spring可以通过实现FactoryBean接口,定制Bean的实例化逻辑

    如果要获取工厂的Bean 可以使用 getBean("&car")

    • 缓存中获取单例bean

    单例在spring的同一个容器中只会被创建一次,后序再获取bean直接从单利的缓存中获取bean

    singletomObjects: 用于保存BeanName 和 创建bean 实例之间的关系

    singletonFactories: 用于保存 BeanName 和 Bean工厂之间的关系

    earlySingletonObjects :也是保存 BeanName 和创建 bean 实例之间的关系,与
    singletonO ects 的不同之处在于,当一个单 bean 被放到这里面后,那么当 bean
    在创建过程中,就可以通过 getBean 方法获取到了,其目的是用来检测循环引用

    registeredSingletons :用来保存当前所有巳注册的 bean

    spring中的循环依赖

    (1). 构造器的循环依赖

    通过构造器注入构成的循环依赖,此依赖无法解决,抛出异常

    <bean id=”testA” class="com.bean.TestA”>
    <constructor- arg index="0" ref="testB" /〉
    </bean> 
    <bean id=”testB” class=” com.bean.TestB”> 
    <constructor arg index ="0" ref="testC" /〉
    </bean> 
    <bean id=”tesi::C ” class=” com.bean.TestC”> 
    <constructor- arg index="0" ref="testA" /〉
    </bean> 
    

    spring在创建bean的时候会去 "当前正在创建Bean池" ,查找是否正在创建,如果发现依赖正在创建 报循环依赖

    (2). setter循环依赖

    通过提前暴露一个单例工厂方法,从而使其他 bean 能引用到bean

    addSingletonFactory (beanName , new 0:0] ectFactory() { 
        public Object getobject () throws BeansException { 
            return getEarlyBeanReference(beanName,mbd,bean) ; 
        }
    });
    

    (3). prototype 范围的依赖处理

    对于“prototype ”作用域 bean, Spring 容器无法完成依赖注入,因为 Spring 容器不进行缓
    存“prototype ”作用域的 bean ,因此无法提前暴露一个创建中的 bean.

    1. 创建Bean

    (1) 如果是单例需要首先清除缓存
    (2) 实例化Bean ,将BeanDefinition 转换为BeanWrapper
    如果工厂使用工厂,有构造函数使用构造,否则默认
    (3) MergedBeanDefinitionPostProcessor 应用
    bean合并后处理, Autowired 注解通过此方法实现预解析
    (4) 依赖处理,属性填充
    (5) 注册DisposableBean
    (6) 完成并返回

    相关文章

      网友评论

        本文标题:Spring Bean的解析和加载详细解释

        本文链接:https://www.haomeiwen.com/subject/ucztvctx.html