spring ioc容器结构初始化步骤以及bean生命周期和三种创建方式

 

Spring创建对象的三种方式

创建对象的三种方式:     

       1、采用默认的构造函数创建

       2、采用静态工厂方法

          1、写一个静态工厂方法类

               public class HelloWorldFactory {

                  public static HelloWorld getInstance(){

                     return new HelloWorld();

                  }

               }

          2、在spring的配置文件中进行声明

                <bean id="helloWorld2" class="com.itheima05.spring.bean.HelloWorldFactory"

                           factory-method="getInstance"></bean>

                告诉spring容器利用HelloWorldFactory类中的getInsatance静态方法产生对象

                但是具体的对象的创建的过程是由程序员来完成的

       3、实例工厂方法

          1、写一个实例工厂类

              public class HelloWorldFactory {

                  public HelloWorld getInstance(){

                     return new HelloWorld();

                  }

               }

          2、在spring的配置文件中

            1、<bean id="helloWorld3" class="com.itheima05.spring.bean.HelloWorldFactory2"></bean>

                   spring容器为HelloWorldFactory2创建对象

            2、<bean id="helloWorldFactory" factory-bean="helloWorld3" factory-method="getInstance"></bean>

                  告诉spring容器,利用helloWorld3对象调用getInstance方法

 

 

 

 

Bean的生命周期

1.Spring对Bean进行实例化(相当于程序中的new Xx())
2.Spring将值和Bean的引用注入进Bean对应的属性中
3.如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法(实现BeanNameAware主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的)
4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。(实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等)
5.如果Bean实现了ApplicationContextAwaer接口,Spring容器将调用setApplicationContext(ApplicationContext ctx)方法,把y应用上下文作为参数传入.(作用与BeanFactory类似都是为了获取Spring容器,不同的是Spring容器在调用setApplicationContext方法时会把它自己作为setApplicationContext 的参数传入,而Spring容器在调用setBeanDactory前需要程序员自己指定(注入)setBeanFactory里的参数BeanFactory )
6.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法(作用是在Bean实例创建成功后进行增强处理,如对Bean进行修改,增加某个功能)
7.如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。
8.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessAfterInitialization(后初始化)方法(作用与6的一样,只不过6是在Bean初始化前执行的,而这个是在Bean初始化后执行的,时机不同 )
9.经过以上的工作后,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁
10.如果Bean实现了DispostbleBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在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生命周期结束。

Spring容器,指的就是IoC容器,其初始化步骤为: 
1.创建容器对象,设置父容器、配置文件; 
2.容器预处理,主要是设置容器同步标识(标识当前容器是否激活); 
3.关闭旧容器,读取配置文件,将其普通bean的元数据BeanDefinition注册到beanFactory中;(bean这个时候并没有实例化) 
4.针对子类的或用户自定义的,执行postBeanPostProcessor方法,对BeanDefinition做后处理工作; 
5.注册BeanPostProcessor; 
6.初始化MessageResource; 
7.初始化事件传播器; 
8.子类自有容器初始化工作; 
9.注册监听器bean; 
10.实例化单例非延迟bean; 
11.发布容器生命周期事件。

 

Spring IoC容器结构

 

Spring IoC容器

IoC容器主要作用就是创建并管理Bean对象以及Bean属性注入。如何创建Bean对象?是通过读取Bean配置文件生成相应对象。这些配置文件格式可能多种多样,xml、properties等格式,所以需要将其转换(ResourceLoader/Resolver)成统一资源对象(Resource),存储的位置也不一样,可能是ClassPath,可能是FileSystem,也可能是URL路径,路径的不一样,说明的是应用环境可能不一样。获得Resource对象,还要将其转换成(BeanDefinitionReader)Spring内部对Bean的描述对象(BeanDefinition),然后,将其注册(BeanRegister)到容器中(BeanFactory),供以后转换成Bean对象使用。

所以,IoC容器包括了很多东西,但主要有以下六个组件:

1.资源组件:Resource,对资源文件的描述,不同资源文件如xml、properties文件等,格式不同,最终都将被ResourceLoader加载获得相应的Resource对象;

2.资源加载组件:ResourceLoader:加载xml、properties等各类格式文件,解析文件,并生成Resource对象。

3.Bean容器组件:BeanFactory体系:IoC容器的核心,其他组件都是为它工作的(但不是仅仅为其服务).

4.Bean注册组件:SingletonBeanRegister/AliasRegister:将BeanDefinition对象注册到BeanFactory(BeanDefinition Map)中去。

5.Bean描述组件:BeanDefinition体系,Spring内部对Bean描述的基本数据结构

6.Bean构造组件:BeanDefinitionReader体系,读取Resource并将其数据转换成一个个BeanDefinition对象。

这里组件的定义是相对于IoC容器而言,一般这里的组件设计包括一组接口、一组抽象类、一组实现类、异常类、工具类组成,至于为什么要这么设计,可以参考下设计模式的七大原则。

这些组件并不是互相独立的,而是相互联系形成一个极其复杂的类关系网,下图摘取的组件类关系图只是摘取比较纯粹(比较高层)的那一部分(比如,就ApplicationContext这种比较中层的对象而言,既是ResourceLoader组件,也是BeanFactory组件一部分,还有其他的,越底层其成分越复杂,功能对应的也越具体,其实质就是一个抽象到具体的过程,但这种抽象不是纯粹的抽象到具体,更掺杂有多种抽象的结合。另外,摘取的图来源Spring 3.2.7)。

下面依次对各个组件做详细说明:

1.资源组件,Resource体系,信息的载体,如各类型的文件,二进制流数据都是资源,是Spring内部对资源的一种统一描述,整个体系类图网如下:(图中名称前面 I 代表是接口,C表示实体类,C中左上角有四分之一扇形绿色的表示是抽象类,虚线表示实现,实线表示继承,下同)

每一个实体类代表一种资源类型,比如ClassPathResource表示类加载路径上的资源,FileSystemResource表示文件系统上的资源,UrlResource则是表示网络资源。这是Spring对资源访问策略的一种实现,更加详细内容可以参考:Spring资源文件剖析和策略模式应用(李刚)

总的来说,Java有自己的对资源访问策略的实现,比如Path,File,URI/URL类等,但有缺点或有不足,Spring就自己弄了一套,可以粗略的认为Spring在Java基础上封装了一层(就像UrlResource,其源码内部就是通过URI/URL实现的),增加了一些功能,更加方便点。

2.资源加载组件,ResourceLoader/Resolver体系,负责资源的加载,这里的资源指的是xml、properties等文件资源,返回一个对应类型的Resource对象。

同Resource对应,不同的实现类加载不同的Resource,返回对应的Resource对象,针对这一部分,现在有兴趣的可以参考:深入Spring IoC源码之ResourceLoader(上善若水)

3.Bean描述组件,BeanDefinition体系,是对bean对象描述的基本数据结构。

4.Bean构造组件,BeanDefinitionReader体系,将Resource对象,转换成BeanDefinition对象,就是将内部资源数据转换成Spring Bean描述数据。其实这种读取数据转换成内部对象的,不仅仅是Spring专有的,比如:Dom4j解析器

SAXReader reader = new SAXReader(); Document doc = reader.read(ur.getFile());//ur是一个URLResource对象

严格来说,都是Reader体系吧,就是将统一资源数据对象读取转换成相应内部对象。

5.Bean注册组件,将BeanDefinition对象注册到BeanFactory中去。

6.Bean容器组件,整个IoC容器核心,所谓Bean容器,就是这里装着Bean对象以及所需要的各种数据。其中BeanFactory是纯粹的Bean容器,用来存储描述Bean,无关其他环境,而像ApplicationContext,也是Bean容器,但它和应用环境息息相关,所以被称为应用上下文(环境)更恰当,从图中也能看出来,ApplicationContext不仅有着BeanFactory“血统”,同时也继承了EnvironmentCapable、MessageSource、ApplicationEventPublisher,即扩展了其许多额外功能,而其实现类则是和具体应用相关了,比如ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、WebApplicationContext,从字面上就不难理解其作用。BeanFactory和ApplicationContext的详细区别,可以自行查询。

这里下面这句代码为例子,描述整个工作流程。

ApplicationContext context = new ClassPathXmlApplicationContext(path);

 

0.新建ApplicationContext容器对象(并没有初始化);

1.ResourceLoader加载并解析资源文件 ———> 获得Resource对象;

2.BeanDefinitionReader读取Resource对象,将其读到的bean元素数据封装到BeanDefinition组件中;

3.BeanDefinitionRegister将所有的BeanDefinition注册到BeanFactory中(BeanDefinition是容器内部Bean的基本数据结构,BeanFactory维持着一个BeanDefinition Map);

4.容器初始化开始,容器初始化工作由AbstractApplicationContext提供的refresh()方法完成,具体初始化步骤可以参考下一节:Spring IoC容器初始化。

整个流程大体概括如下:

至此,context作为应用环境上下文功能完成,程序此后就可以通过context的geBean(String beanName)方法获得path文件声明定义的bean对象。

 

阅读更多 登录后自动展开
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页