zoukankan      html  css  js  c++  java
  • 《Spring》(八)---- IoC容器及Bean的生命周期

      Spring的IoC容器会以某种方式加载配置信息,然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统。实现以上功能,分为两个阶段:容器启动阶段和Bean实例化阶段。而且Spring的IoC容器在每个阶段都加入了相应的扩展点,以便根据具体场景的需要加入自定义的扩展逻辑。

      1 容器启动阶段

      首先会通过某种途径加载配置信息,大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的配置信息进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDifinitionRegistry,这样容器启动工作就完成了。

      该阶段所作工作是准备性的,重点更加侧重于对象管理信息的收集,以及一些验证性的和辅助性的工作。

      2 Bean实例化阶段

      现在所有的bean定义信息都已经注册到了BeanDefinitionRegistry中,当某个请求方通过容器的getBean方法明确地请求某个对象,或者因依赖关系容器需要隐式地调用getBean方法时,就会触发bean实例化。

      在这一阶段,容器会先检查所请求的对象之前是否已经初始化,如果没有,会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当该对象装配完毕之后,容器会立即将其返回请求方使用。

      3 干预容器启器

      Spring提供了一种叫做BeanFactoryPostProcessor的容器扩展机制,允许在容器实例化对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。这就相当于在容器实现的第一阶段最后加入一道工序,让我们对最终的BeanDefinition做一些额外的操作,比如修改其中bean定义的某些属性,为bean定义增加其他信息等。

      可以通过两种方式来应用BeanFactoryPostProcessor, 分别针对基本的IoC容器BeanFactory和较为先进的容器ApplicationContext.

      对于BeanFactory来说,我们需要用手动方式应用所有的BeanFactoryPostProcessor:

    ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("..."));
    
    PropertyPlaceholderConfigurer propertyPostProcessor = new PropertyPlaceholderConfigurer();
    
    propertyPostProcessor.setLocation(new ClassPathResource("..."));
    propertyPostProcessor.postProcessBeanFactory(beanFactory);

      对于ApplicationContext来说,它会自动识别配置文件中的BeanFactoryPostProcessor并应用它,所以仅需要在XML中将这些BeanFacotryPostProcessor简单配置一下即可。

    <beans>
        <bean    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations"> 
           <list>
            <value>conf/jdbc.properties</value>
            <value>conf/mail.properties</value> </list> 
        </property>
        </bean>
    ... 
    </beans>

    Spring提供的几个BeanFactoryPostProcessor

    • PropertyPlaceholderConfigurer

       PropertyPlaceholderConfigurer允许在XML配置文件中使用占位符,并将这些占位符所代表的资源单独配置到简单的properties文件中来加载。以数据源的配置为例:

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="url">
          <value>${jdbc.url}</value>
      </property>
      <property name="driverClassName">
        <value>${jdbc.driver}</value>
      </property>
      <property name="username">
        <value>${jdbc.username}</value>
      </property>
      <property name="password">
        <value>${jdbc.password}</value>
      </property>

       <property name="maxActive">
        <value>100</value>
       </property>

    < /bean> 

    jdbc.properties文件如下: 

    jdbc.url=jdbc:mysql://server/MAIN?useUnicode=true&characterEncoding=ms932& 
    failOverReadOnly=false&roundRobinLoadBalance=true
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.username=your username
    jdbc.password=your password

    原理:

      当BeanFactory在第一阶段加载完成所有配置信息时,BeanFactory中保存的对象的属性信息还只是以占位符的形式存在,如${jdbc.url}。当PropertyPlaceholderConfigurer作为BeanFactoryPostProcessor被应用时,它会使用properties配置文件中的配置信息来替换相应BeanDefinition中占位符表示的属性值。这样,当进入容器实现的第二个阶段实例化bean时,bean定义中的属性值就是最终替换完成后的了。

      PropertyPlaceholderConfigurer不仅会从其配置的properties文件中加载配置项,同时还会检查System类中的Properties. PropertyPlaceholderConfigurer提供了SYSTEM_PROPERTIES_MODE_FALLBACK/SYSTEM_PROPERTIES_MODE_NEVER/SYSTEM_PROPERTIES_MODE_OVERRIDE三种模式,默认采用FALLBACK,即如果properties文件中找不到相应配置项,则到System的properties中查找。

    • PropertyOverrideConfigurer

      PropertyOverrideConfigurer可以对容器中配置的任何你想处理的bean定义的property信息进行覆盖替换。比如前一个例子中的dataSource,maxActive值为100, 如果想把这个值覆盖掉,改成200,就可以在一个properties文件中配置:

    dataSource.maxActive=200

      所以如果要对容器中某些bean的property信息进行覆盖,需要按照如下规则提供一个PropertyOverrideConfigurer使用的配置文件:

    beanName.propertyName=value

    也就是说,properties文件中的键是以XML中配置的bean定义的beanName为标志开始的(通常就是id指定的值),后面跟着相应被覆盖的property的名称。如下是PropertyOverridConfigurer在XML中的配置信息:

    <bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
      <property name="location" value="pool-adjustment.properties"/>
    </bean>

    当容器中配置的多个PropertyOverrideConfigurer对同一个bean定义的同一个property值进行处理的时候,最后一个会生效。

      4 Bean的生命周期

     转载自 http://www.jianshu.com/p/3944792a5fff

    • ApplicationContext Bean生命周期

    1. Bean的实例化

      首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化。

      容器在内部实现的时候,采用“策略模式”来决定采用何种方式初始化bean实例。通常,可以通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。默认情况下,容器内部采用CglibSubclassingInstantiationStartegy。容器只要根据相应bean定义的BeanDefinition取得实例化信息,结合CglibSubclassingInstantiationStartegy以及不同的bean定义类型,就可以返回实例化完成的对象实例。但不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。这个BeanWrapper的实现类BeanWrapperImpl是对某个bean进行包裹,然后对包裹后的bean进行操作,比如设置或获取bean的相应属性值。

    2. 设置对象属性

      BeanWrapper继承了PropertyAccessor接口,可以以同一的方式对对象属性进行访问,同时又继承了PropertyEditorRegistry和TypeConverter接口,然后BeanWrapper就可以很方便地对bean注入属性了。

    3. 如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该bean的id,此时该Bean就获得了自己在配置文件中的id。

    4. 如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory.

    5. 如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext, 这样该Bean就获得了自己所在的ApplicationContext.

    6. 如果有一个Bean实现了BeanPostProcessor接口,并将该接口配置到配置文件中,则会调用该接口的postProcessBeforeInitialization()方法。

    7.如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法。

    8. 如果Bean配置了init-method方法,则会执行init-method配置的方法。

    9. 如果有一个Bean实现了BeanPostProcessor接口,并将该接口配置到配置文件中,则会调用该接口的postProcessAfterInitialization方法。

    10.经过9之后,就可以正式使用该Bean了,对于scope为singleton的Bean, Spring IoC容器会缓存一份该Bean的实例,而对于scope为prototype的Bean, 每次被调用都回new一个对象,而且生命周期也交给调用方管理了,不再是Spring容器进行管理了。

    11. 容器关闭后,如果Bean实现了DisposableBean接口,则会调用该接口的destroy()方法。

    12. 如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean生命周期结束。

    示例

    我们定义了一个Person类,该类实现了BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean五个接口,并且在applicationContext.xml文件中配置了该Bean的id为person1,并且配置了init-method和destroy-method,为该Bean配置了属性name为jack的值,然后定义了一个MyBeanPostProcessor方法,该方法实现了BeanPostProcessor接口,且在applicationContext.xml文件中配置了该方法的Bean

    Person.class

    package com.ivy.beanlifecycle;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    public class Person implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean{
    
        private String name;
        
        public Person() {
            System.out.println("Person constructor");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
            System.out.println("setter() invoked");
        }
        
        public void myInit() {
            System.out.println("myInit() invoked");
        }
        
        public void myDestroy() {
            System.out.println("myDestroy() invoked");
        }
    
        @Override
        public void setBeanName(String beanName) {
            System.out.println("setBeanName() invoked, beanName : " + beanName);
            
        }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            // TODO Auto-generated method stub
            System.out.println("setBeanFactory() invoked, beanFactory : " + beanFactory);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext)
                throws BeansException {
            System.out.println("setApplicationContext() invoked");
            
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("afterPropertiesSet() invoked");
            
        }
    
        @Override
        public void destroy() throws Exception {
            System.out.println("destroy() invoked");
            
        }
        
        public String toString() {
            return "Person[name=" + name +"]";
        }
    }

    MyBeanPostProcessor.class

    package com.ivy.beanlifecycle;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyBeanPostProcessor implements BeanPostProcessor{
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName)
                throws BeansException {
            // TODO Auto-generated method stub
            System.out.println("postProcessAfterInitialization() invoked, beanName : " + beanName);
            return bean;
        }
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName)
                throws BeansException {
            System.out.println("postProcessBeforeInitialization() invoked, beanName : " + beanName);
            return bean;
        }
    
    }

    applicationContext.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">
                
        <bean id="person1" class="com.ivy.beanlifecycle.Person" init-method="myInit" destroy-method="myDestroy">
            <property name="name" value="ivy"></property>
        </bean>
        
        <bean id="myPostProcessor" class="com.ivy.beanlifecycle.MyBeanPostProcessor"></bean>
    </beans>

    PersonServiceApplicationContextTest.class

    package com.ivy.beanlifecycle;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class PersonServiceApplicationContextTest{
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            System.out.println("start init ioc container");
            ApplicationContext aContext = new ClassPathXmlApplicationContext("applicationContext.xml");
            System.out.println("end loading xml");
            Person person = (Person)aContext.getBean("person1");
            System.out.println(person);
            System.out.println("close container");
            ((ClassPathXmlApplicationContext)aContext).close();
        }
    
    }

    运行结果:

    start init ioc container
    log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
    log4j:WARN Please initialize the log4j system properly.
    Person constructor
    setter() invoked
    setBeanName() invoked, beanName : person1
    setBeanFactory() invoked, beanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@5fa12d: defining beans [person1,myPostProcessor]; root of factory hierarchy
    setApplicationContext() invoked
    postProcessBeforeInitialization() invoked, beanName : person1
    afterPropertiesSet() invoked
    myInit() invoked
    postProcessAfterInitialization() invoked, beanName : person1
    end loading xml
    Person[name=ivy]
    close container
    destroy() invoked
    myDestroy() invoked
    

      可以看出,在加载xml的时候ApplicationContext就实例化了所有的bean

    • BeanFactory Bean生命周期

     BeanFactoty容器中, Bean的生命周期如上图所示,与ApplicationContext相比,有如下几点不同:

    1. BeanFactory容器中,不会调用ApplicationContextAware接口的setApplicationContext()方法

    2. BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法不会自动调用,必须自己通过代码手动注册

    3. BeanFactory容器启动的时候,不会去实例化所有bean,包括所有scope为singleton且非延迟加载的bean也是一样,而是在调用的时候去实例化。

    还是以上边Person为示例

    PersonServiceBeanFactoryTest.class

    package com.ivy.beanlifecycle;
    
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    
    public class PersonServiceBeanFactoryTest {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            System.out.println("start init ioc container");
            ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
            System.out.println("end loading xml");
            beanFactory.addBeanPostProcessor(new MyBeanPostProcessor());
            Person person = (Person)beanFactory.getBean("person1");
            System.out.println(person);
            System.out.println("close container");
            beanFactory.destroySingletons();
        }
    }

    运行结果:

    start init ioc container
    log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
    log4j:WARN Please initialize the log4j system properly.
    end loading xml
    Person constructor
    setter() invoked
    setBeanName() invoked, beanName : person1
    setBeanFactory() invoked, beanFactory : org.springframework.beans.factory.xml.XmlBeanFactory@ccd65d: defining beans [person1,myPostProcessor]; root of factory hierarchy
    postProcessBeforeInitialization() invoked, beanName : person1
    afterPropertiesSet() invoked
    myInit() invoked
    postProcessAfterInitialization() invoked, beanName : person1
    Person[name=ivy]
    close container
    destroy() invoked
    

      可以看出,end loading xml之后才实例化的person。

  • 相关阅读:
    不可或缺 Windows Native (15)
    不可或缺 Windows Native (14)
    不可或缺 Windows Native (13)
    不可或缺 Windows Native (12)
    不可或缺 Windows Native (11)
    不可或缺 Windows Native (10)
    不可或缺 Windows Native (9)
    不可或缺 Windows Native (8)
    不可或缺 Windows Native (7)
    不可或缺 Windows Native (6)
  • 原文地址:https://www.cnblogs.com/IvySue/p/6484599.html
Copyright © 2011-2022 走看看