awakeBird Back-end Dev Engineer

Spring(一)IoC

2019-01-17

对Spring IoC相关的知识做点笔记,涉及内容有Ioc概念、Spring Ioc容器、注入方式、容器启动方式以及Bean的生命周期。本篇不涉及源码的解读,只是介绍相关接口的功能和继承关系以及其实现类,大部分源于王福强的《Spring揭秘》一书,少部分源自其他博文。

IoC

控制反转Inversion of Control是一种思想,也叫依赖注入Dependency Injection,简单来说就是将所有的被注入对象和依赖对象交由IoC Service Provider来管理,控制对象也由被注入对象变为了IoC Service Provider。

一般依赖注入的方式:

  • 构造器注入:即将被依赖对象作为被注入对象的构造器参数
  • setter方法注入:为被依赖对象提供setter方法
  • 接口注入:需要被注入对象实现一个接口,在接口提供的方法中将被依赖对象作为参数注入。(已经不再使用)

IoC Service Provider

IoC Service Provider的定义很宽泛,凡是能够实现将依赖对象和被注入对象进行绑定的角色都可以成为IoC Service Provider,它可以是一段代码、一组类、Ioc框架或者像Spring这样的IoC容器。它的职责主要由两个:

  • 业务对象的构建管理
  • 业务对象的依赖绑定

IoC Service Provider需要一些东西来记录对象之间的绑定关系,称为注册对象管理信息,这些信息可以是以下几种:

  • 直接编码
  • 配置文件
  • 元数据:这部分稍微解释下,是指直接通过注解或其他方式标明依赖对象,而绑定关系则由其他类实现,这些类就成为被注入对象的元数据

BeanFactory

Spring的Ioc容器是一个提供IoC支持的轻量级容器,除了IoC外其还提供了诸如AOP等其他支持,本篇不进行展开,只关注其IoC部分。Spring提供了两种容器类型:

  • BeanFactory
  • ApplicationContext

ApplicationContext间接继承自BeanFactory,除了BeanFactory外其还继承了其他接口,实现了更高级的功能。两者最大的不同之处在于BeanFactory容器中管理的对象采用延迟初始化的策略,在被请求到时才会初始化一个对象实例,而ApplicationContext在容器启动之后会初始化所有的对象实例并完成绑定,这一特性导致在两个容器中Bean的生命周期有所不同,在下面介绍时会标注出来。

下面介绍BeanFactory相关功能的实现接口,ApplicationContext特有的功能接口将在之后列出。

主要顶层接口

  • BeanFactory:定义了访问容器内管理的相关Bean实例的查询方法。
  • BeanDefinition:每个受管理的对象,容器内都有一个BeanDefinition的实例,负责保存对象的所有必要信息,包括class类型、是否是抽象类、构造方法及参数、其他属性。其主要有两个实现类,RootBeanDefinitionChildBeanDefinition
  • BeanDefinitionRegistry:与BeanDefinition为组合关系,抽象了Bean的注册管理逻辑。

BeanFactory的实现类都会同时实现BeanFactory接口和BeanDefinitionRegistry接口。

容器启动

容器启动阶段需要做下面几件事:

  • 1.通过某种途径加载Configuration MetaData(可以理解为Resource
  • 2.从Configuration MetaData中进行分析和解析,将解析到的信息映射到BeanDefinition上,需要依赖相关的工具类(BeanDefinitionReader的实现类)
  • 3.将BeanDefinition注册到容器中

容器启动阶段有一个主要接口BeanFactoryPostProcessor:它会在容器实例化相应对象之前对BeanDefinition信息做最后的修改,相当于启动阶段的最后一步。

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

对于BeanFactory容器,需要手动调用BeanFactoryPostProcessorpostProcessBeanFactory()方法将其应用到容器,对于ApplicationContext容器,则会自动识别配置文件中的BeanFactoryPostProcessor类并应用它。

spring提供了下面几个实现类:

  • PropertyPlaceholderConfigurer:允许我们用占位符${}在properties文件中加载一些变量,有三个模式,可以通过setSystemPropertiesMode来变更,默认为SYSTEM_PROPERTIES_ MODE_FALLBACK,如果找不到所需要的属性会在System Properties中寻找(即启动命令中指定的property)
      <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
          <property name="locations">
              <list>
                  <value>properties_path</value>
              </list>
          </property>
      </bean>
    
  • PropertyOverrideConfigurer:对容器中已定义的bean的属性进行替换,通过配置文件
  • CustomEditorConfigurer:配置从字符串到具体对象转换过程中的信息,如日期等

Bean的作用域

Bean是有作用域的,超出作用域的Bean将被销毁,对应配置文件中bean的scope属性。非Web容器中,Bean有两种作用域

  • singleton:在spring中只存在一个bean实例,初始化后将一直存活到容器退出,与容器几乎相同的寿命。
  • prototype:每次收到bean实例请求的时候都会重新生成一个新的实例对象,生成之后返回给请求方,容器即不再拥有对象的引用,请求方自己来负责对象实例生命周期的管理。

对于Web容器,还新增了requestsessionglobal session三种作用域,这里不做展开。

Bean的实例化

容器启动之后仅仅拥有对象的BeanDefinition来保存实例化对象需要的信息,Bean的实例化只有在请求BeanFactorygetBean()方法之后才进行,当然这一步可能是应用程序显式调用,也可能是由容器隐式调用的。(ApplicationContext容器就是在容器启动之后在refresh()方法中隐式调用注册对象的getBean()方法)。

不同于一般对象,Spring给其管理的对象以统一的生命周期,不再是「new完被使用,脱离作用域后被回收」的命运。Bean的生命周期如下所示:

1.实例化

容器采用策略模式实例化对象,这一步只是进行简单的初始化,并未进行依赖注入。返回BeanWarpper对象而非对象实例,为了方便第二步的属性设置。

  • InstantiationStrategy接口定义了实例化策略,CglibSubclassingInstantiationStrategy是其默认的实现类,采用CGLIB动态字节码生成功能。

2.设置属性及依赖

根据BeanDefinition中保存的信息对第一步返回的BeanWrapper对象调用setPropertyValue()方法设置属性和依赖对象,即IoC注入。

  • BeanWrapper接口间接继承了PropertyAccessorPropertyEditorRegistryTypeConverter,提供了一系列类型转换和设置对象属性的方法。

3.检查Aware接口并设置相关依赖

容器会检查实例对象是否实现了相关Aware接口,如果是,会为对象注入相关的依赖。对于BeanFactory容器有下面三个Aware接口:

  • BeanNameAware:将配置文件中bean的name属性注入实例对象
  • BeanClassLoaderAware:将bean的ClassLoader注入实例对象,默认为org.springframework.util.ClassUtils类的Classloader
  • BeanFactoryAware:将bean的BeanFactory容器引用注入实例对象

对于ApplicationContext容器,则新增了下面几个Aware方法,都会将容器自身引用注入到对象实例,不同于上面几个方法的实现机理,下面的方法是通过BeanPostProcessor方式实现的,之后会提到。

  • ResourceLoaderAware
  • ApplicationEventPublisherAware
  • MessageSourceAware
  • ApplicationContextAware

4.BeanPostProcessor

为实例注册一个处理器,与之前的BeanFactoryPostProcessor接口类似,对于BeanFactory容器,需要手动调用容器的addBeanPostProcessor()方法将其实现类应用到容器,而ApplicationContext则可以自动识别配置文件中配置的BeanPostProcessor的实现类并将其应用到容器。

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

BeanPostProcessor接口定义了两个方法,分别在下面要提到的InitilizationBeaninit-method的之前和之后进行,称作前置处理后置处理。这两个接口将Bean实例作为参数传入,因此可以对Bean的属性进行任意操作。

  • ApplicationContextAwareProcessor就是其中一个实现类,其postProcessBeforeInitialization()方法会检查并设置Aware依赖。
      private void invokeAwareInterfaces(Object bean) {
          if (bean instanceof Aware) {
              if (bean instanceof ResourceLoaderAware) {
                  ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
              }
              if (bean instanceof ApplicationEventPublisherAware) {
                  ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
              }
              if (bean instanceof MessageSourceAware) {
                  ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
              }
              if (bean instanceof ApplicationContextAware) {
                  ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
              }
          }
      }
    

5.InitializationBean和init-method

在前置处理结束之后容器会检查对象是否实现了InitializationBean接口,这个接口只定义了一个方法afterPropertiesSet(),由于没有传入Bean实例,因此只能进行一些额外的逻辑而不能对Bean对象属性进行操作。

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

这个接口对业务对象有侵入性,spring为配置文件中的bean提供了一个init-method属性,可以为对象设置初始化方法,作用与afterPropertiesSet()类似。

6.DisposableBean与destory-method

在Bean实例被销毁之前会检查对象是否实现了DisposableBean接口,同初始化时类似,spring为bean添加了destory-method属性,为对象提供了自定义销毁的逻辑。需要注意的是,如果是prototype作用域下的实例,则无法执行自毁逻辑。

public interface DisposableBean {
    void destroy() throws Exception;
}

容器需要明确指定这些自定义销毁的逻辑在什么时候被执行,否则实例将不会执行这些自毁逻辑而直接被销毁。

  • 对于BeanFactory容器,需要手动执行容器的destroySingletons()方法销毁容器中管理的所有singleton类型的对象实例。
  • 对于ApplicationContext容器,则需要调用容器的registerShutdownHook()方法执行bean的自毁逻辑,该方法会调用Runtime类的addShutdownHook()方法调用相应bean的销毁逻辑。

基于注解的注入

spring2.5之后提供了基于注解的注入方式,实际上还是采用BeanPostProcessor的方式,容器应用了特定的BeanPostProcessor,就可以使用相应的注解实现注入。

AutowiredAnnotationBeanPostProcessor

  • Autowired:按照类型匹配,可以实现属性、构造器或者方法的自动注入
  • Qualifier:当容器内找到多个相同类型的实例时,需要用Qualifier指定bean的id或者name属性

CommonAnnotationBeanPostProcessor

  • Resource:基于BeanName的查找方式实现自动注入
  • PostConstruct:类似于init-methodBeanInitialization
  • PostDestroy:类似于destroy-methodDisposableBean

上面两个BeanPostProcessor指定了容器中Bean的依赖关系,配置文件中的<context:annotation-config/>可以实现上面两个BeanPostProcessor的注册应用,不仅如此,它还同时应用了PersistenceAnnotationBeanPostProcessorRequiredAnnotationBeanPostProcessor

要实现Bean的自动装载,将其从配置文件中消失,则需要依赖@Component等注解,通过classpath-scanning功能实现,在容器启动时,它会从某一顶层(base package)开始扫描,提取相应注解的类的BeanDefinition信息,构建相应的BeanDefinition并将其添加进容器。不但如此,它还会将上面提到的两种BeanPostProcessor应用到容器中,满足依赖注入的需求。

<context:component-scan base-package="org.mypackage"/>

ApplicationContext

相比于BeanFactory,ApplicationContext进一步扩展了基本容器的功能外(包括对BeanFactoryPostProcessorBeanPostProcessor及其他特殊类型bean的自动识别,容器启动后自动初始化实例bean),还提供了其他更为先进的功能。ApplicationContext常用的实现类主要有下面三个:

  • FileSystemXmlApplicationContext:默认从文件系统加载bean及相关资源。
  • ClassPathXmlApplicationContext:默认从classpath加载bean及相关资源。
  • XmlWebApplicationContext:基于Web应用程序。

下面是ApplicationContext特有的功能,这里不做展开

  • 统一资源定位
  • 国际化信息支持
  • 容器内事件发布
  • 多模块加载的简化

(End)

参考资料:


Similar Posts

下一篇 JMM小结

Comments