• 《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。

  • 相关阅读:
    人事管理系统案例
    匹马行天下之思维决定高度篇——教你如何爱上“编程妹子”
    抽象类和接口详解
    Servlet教程详解
    Java匹马行天下之J2EE框架开发——Spring—>用IDEA开发Spring程序(01)
    Java匹马行天下之J2EE框架开发——Spring—>Spring框架知多少
    匹马行天下之思维决定高度篇——编程“价”更高
    编程自学网站分享——网络老师金旭亮(很不错哦,自学的建议看看)
    匹马行天下之思维决定高度篇——大学再努力,培训机构做兄弟
    怀念Galois
  • 原文地址:https://www.cnblogs.com/IvySue/p/6484599.html
Copyright © 2020-2023  润新知