当前位置: 代码迷 >> 综合 >> spring基本使用(7)-Spring应用上下文刷新核心AbstractApplicationContext.Refresh()
  详细解决方案

spring基本使用(7)-Spring应用上下文刷新核心AbstractApplicationContext.Refresh()

热度:61   发布时间:2023-10-24 16:11:26.0

1、容器技术内幕-AbstractApplicationContext.Refresh()

     AbstractApplicationContext : 是ApplicationContext的抽象类,里面的refresh()方法是容器加载的入口。
     Refresh()方法的主要流程如下:   
      第1步:初始化BeanFactory。
                  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

      第2步:调用工厂后置处理器,之前生命周期中的BeanFactoryPostProcessor。     
                  invokeBeanFactoryPostProcessors()

      第3步:注册BeanPostProcessor,之前生命周期中的BeanPostProcessor在ApplicationContext加载的时候,在此处统一注册到                    BeanFactory中。
                  registerBeanPostProcessors();

      第4步:初始化消息源。
                  initMessageSource();

      第5步:初始化应用上下文事件广播器。
                  initApplicationEventMulticaster();

      第6步:初始化其他特殊的bean,由具体的子类实现。
                  onRefresh();

      第7步:注册事件监听器。
                   registerListeners();

      第8步:初始化所有单实例bean,延迟加载的除外,所有的bean都在此步骤实例化、属性设置包含依赖注入、初始化。
                   finishBeanFactoryInitialzation(beanFactory);

       第9步:完成容器刷新,并广播容器刷新事件。
                   finishRefresh();

2主要组件

       2.1BeanDefinition:用于描述一个java类

             org.springframework.beans.factory.config.BeanDefinition, 主要就是定义bean的一些属性,此类结构图如下:

                     

 

              Spring 通过BeanDefinitionReader读取配置信息的Resource,将其转化为容器的内部表示BeanDefinition,并且将这些                  BeanDefinition注册到BeanDefinitionRegistry中,BeanDefinitionRegistry就像是spring配置信息的内存数据库,后续操                作将直接从BeanDefinitionRegistry中读取配置信息。

 

       2.2BeanDefinition加载流程

               分为以下两步:

                      1、通过BeanDefinitionReader读取配置信息的Resource,通过解析,生成BeanDefinition,此处的BeanDefinition                                可能是个半成品,因为在bean的定义中,可能存在占位符变量应用外部属性文件中配置的属性,如${jdbc.url}等                                变量引用,这些变量在此处还没有被解析出来,因此此时的BeanDefinition可能是一个半成品。

                      2、利用BeanFactoryPostProcessor对半成品的BeanDefinition进行加工处理,将使用占位符应用的配置解析为最                                 终的实际值,这样这些半成品的BeanDefinition就被加工成为成品了。

        2.3、PropertyPlaceholderConfigurer项目配置载入 

                PropertyPlaceholderConfigurer, 此类能够将bean 配置的引用外部属性文件中的参数解析为实际值,                                              PropertyPlaceholderConfigurer此类也是实现了BeanFactoryPostProcessor接口,因而也是一个工厂后置处理器。像                    ${jdbc.url}就是使用此工厂后置处理器解析为外部属性文件中的实际值的。
                使用PropertyPlaceholderConfigurer:
                        1、直接定义<bean>:             
                                  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
                                        p:locations="classpath:application.properties,classpath:tapp.properties"
                                        p:fileEncoding="utf-8"
                                        p:placeholderPrefix="${"
                                        p:placeholderSuffix="}"
                                        p:order="1"/>

                        2、使用context命名空间简化上述配置如下:默认使用${变量名}
                                   
<context:property-placeholder ignore-unresolvable="true"
                                           location="classpath:application.properties"
                                           file-encoding="utf-8"
                                           order="1" />
                       属性引用方式:
                                Xml 中获取配置方式:  <property name="maxIdle" value="${redis.maxIdle}"/>

                                注解中获取配置方式:
                                         @Component()
                                          public class Car {

                                                @Value("${jdbcRrl}")
                                                private String jdbcurl;
                                          }

 

       2.4<util:properties/> util命名空间项目配置载入

               配置方式:
                     <util:properties id="meta" location="classpath:config/metainfo.properties" />          

               属性引用方式:
                      Xml 中获取配置方式:  <property name="maxIdle" value="#{meta['redis.maxIdle']}"/>

               注解中获取配置方式:
                     @Component()
                     public class Car {

                          @Value("#{meta['redis.maxIdle']}")
                          private String jdbcurl;
                     }

 

     2.5、属性文件自身引用

            如下:比如在application.properties文件中
                       dbName=aaa
                       dbUrl=jdbc:mysql://localhost:3306/${dbname}  此处就是属性文件中自身引用,参数名称字符中不能引用!!!

            使用小技巧:如果一个属性值太长,可以使用“\”将属性划分为多行如:
                                 desc : aaa\
                                            bbb\
                                            ccc
                                 就等价于desc=aaabbbccc


             引用bean的属性值:
                        xml 中引用格式: 使用#{beanName.属性名称} 来取值。
                                    <bean id="boss" class="com.wzy.springstudy.propertyEditor.Boss" p:name="马云" />
                                    <bean id="boss1" class="com.wzy.springstudy.propertyEditor.Boss" p:name="#{boss.name}"/>

                       注解中引用格式:@Value("#{beanName.name}")。
                                    @Component
                                    public class Car {

                                            @Value("#{boss.name}")
                                            private String name;
                                    }

 

 

     2.6、InstantiationStrategy:  bean的实例化策略

              org.springframework.beans.factory.support.InstantiationStrategy 主要负责根据BeanDefinition对象创建一个Bean实                例,此接口类图如下:

                                        

                SimpleInstantiationStrategy 是最常用的实例化策略,该策略利用bean实现类的默认构造函数、有参数构造函数、或工                    厂方法创建bena实例。

                CglibSubclassingInstantiationStrategy 扩展了SimpleInstantiationStrategy ,为需要进行方法注入(如前面提到的                          lookup、replace方法注入)的bean提供支持,它是利用CGLIB类库为bean动态生成子类,在子类中生成方法注入的逻                    辑,然后使用这个动态生成的子类创建bean的实例。

                总结:InstantiationStrategy仅负责bean的实例化工作,相当于执行Java中的new 对象的功能,它并不会参与bean属性                            的设置工作。所以InstantiationStrategy返回的beanshi里实际上只是一个半成品的bean实例,属性填充的工作由                            BeanWrapper来完成。

 

     2.7BeanWrapper:bean包装器

              org.springframework.beans.BeanWrapper ,相当于一个代理器,spring 委托BeanWrapper完成bean的属性填充工作,              在bean被InstantiationStrategy实例化之后,容器主程序将bean实例通过BeanWrapper包装起来,这个包装是通过                        BeanWrapper实现类BeanWrapperImpl的setWrappedInstance方法来完成的。

            BeanWrapper 类图:

                                    

            实现类只有一个就是BeanWrapperImpl, 从类图来看BeanWrapper的实现类

            BeanWrapperImpl有三重身份:

                                             1、Bean包装器。

                                             2、属性访问器。

                                             3、属性编辑器注册表。

            属性填充流程:

                        从BeanDefinitionRegistry中获取BeanDefinition,然后获取到bean的propertyValue,然后使用PropertyEditor进                            行转换得到bean的属性值。

 

      2.8、属性编辑器                  

                      spring的属性编辑器也是使用Java的属性编辑器,我们只需要配置
                   org.springframework.beans.factory.config.CustomEditorConfigurer此配置即可。

 

             1、自定义属性编辑器方式一

                   步骤1:定义自己的属性编辑器,直接继承PropertyEditorSupport 重写setAsText(String text)方法。

                   步骤2:将自定义的属性编辑器配置到spring的CustomEditorConfigurer即可。

                   案例如下:

                          public class Boss {private String name;private Car car;}public class Car {private String name;private String price;}public class CustomCarEditor extends PropertyEditorSupport {@Overridepublic void setAsText(String text) throws IllegalArgumentException {String[] strings = text.split(",");Car car = new Car();car.setName(strings[0] );car.setPrice(strings[1]);//调用父类的setValue()方法设置转换后的属性值。setValue(car);}}<bean id="boss" class="com.wzy.springstudy.propertyEditor.Boss" p:name="马云" p:car="迈巴赫,1100w"/><!--用户属性编辑器配置--><bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"><property name="customEditors"><map>//Key 表示需要属性编辑的类型,value 表示使用的编辑器。<entry key="com.wzy.springstudy.propertyEditor.Car"value="com.wzy.springstudy.propertyEditor.CustomCarEditor"/></map></property></bean>

 

            2、自定义属性编辑器方式二

                      使用JavaBean规范,就是在相同的包路径下面找ClassName+Edior的类,如果找到了就直接使用此属性编辑器,                         无需配置org.springframework.beans.factory.config.CustomEditorConfigurer
                 如:com.wzy.springstudy.propertyEditor.Car
                        com.wzy.springstudy.propertyEditor.CarEditor 就是JavaBean的规范,因此CarEditor 就是Car类型的属性编辑器。
                        在spring中配置依旧如下:
                              <bean id="boss" class="com.wzy.springstudy.propertyEditor.Boss" p:name="马云" p:car="迈巴                                                                                                                                                                                           赫,1100w"/>

  相关解决方案