spring揭秘读书笔记----spring的ioc容器之BeanFactory

spring的ioc容器是一种特殊的Ioc Service Provider(ioc服务提供者),如果把普通的ioc容器认为是工厂模式(其实很相似),那spring的ioc容器只是让这个工厂的功能更强大服务更全面,它们之间存在一定的交集。
 
Spring提供两种容器类型:BeanFactory和ApplicationContext
BeanFatory:基础类型IoC容器,提供完整的IoC服务支持。默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管理对象的时候,才对该受管对象进行初始化以及依赖注入操作。相对来说,容器启动初期速度较快,所需要的资源有限。
ApplicationContext:基于BeanFactory,相对比较高级的容器实现。相比BeanFactory要求更多的系统资源,启动时候较长,但是拥有事件发布、国际化等高级特性。
 作为Spring提供的基本的Ioc容器,BeanFactory可以完成作为Ioc Service Provider的所有职责,包括业务对象的注册和对象建依赖关系的绑定。
BeanFactory就像一个汽车生产厂,你只需告诉它你需要哪些零件,然后在生产线的终点取得成品汽车就可以了。所以对于客户端来说,与BeanFactory打交道很简单。来看下源码:
public interface BeanFactory {

    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

    boolean containsBean(String name);

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;

    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    String[] getAliases(String name);

}

我们应用的时候只用关心如何取得组装完成的对象的接口,即getBean()方法,这里提供了不同的参数以满足各种情况。所有对象之间的依赖关系就由BeanFactory来做了,那么如何做呢?

首先,通过xml告诉BeanFactory对象之间的依赖关系:

<beans>
    <bean id="djNewsProvider" class=".../FXNewsProvider"></bean>
   ...
</beans>

然后BeanFactory就可以按照配置文件来注册对象和绑定依赖了:

BeanFactory container = new XmlBeanFactory(new ClassPatnResource("配置文件路径"));
FXNewsProvider djNewsProvider  = (FXNewsProvider)container.getBean("djNewsProvider"); 
djNewsProvider.getAndPersistNews();

BeanFactory的三种对象注册与依赖绑定方式:

1.直接编码

BeanFactory接口只定义如何访问容器内管理的Bean的方法,还需要具体实现类来负责Bean的注册及管理工作。

DefaultListableBeanFactory间接地实现了BeanFactory和BeanDefinitionRegistry接口。它们之间的关系如下:

打个比方,BeanDefinitionRegistry就像图书馆的书架,所有的书是放在书架上的。虽然你还书或者借书都是跟图书馆(也就是BeanFactory,或许BookFactory可能更好些)打交道,但书架才是图书馆存放各类图书的地方。所以,书架相对于图书馆来说,就是它的“BookDefinitionRegistry”。

每一个受管的对象,在容器中都会有一个BeanDefinition的实例(instance)与之相对应,该BeanDefinition的实例负责保存对象的所有必要信息,包括其对应的对象的class类型、是否是抽象类、构造方法参数以及其他属性等。当客户端向BeanFactory请求相应对象的时候,BeanFactory会通过这些信息为客户端返回一个完备可用的对象实例。

2.外部配置文件(两种格式:Properties和xml)

这种方式还是由BeanDefinitionRegistory负责管理注册的bean,不过解析文件、装配BeanDefinition等工作都由BeanDefinitionReader实现类完成。模拟xml配置文件流程:

public static void main(String[] args)
{
    DefaultListableBeanFactory beanRegistry = new    DefaultListableBeanFactory();
    BeanFactory container = (BeanFactory)bindViaXMLFile(beanRegistry);
    FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
    newsProvider.getAndPersistNews(); 
}

public static BeanFactory bindViaXMLFile(BeanDefinitionRegistry registry)
{
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
    reader.loadBeanDefinitions("classpath:../news-config.xml");
   return(BeanFactory)registry;
  // 或者直接 
  //return new XmlBeanFactory(new ClassPathResource("../news-config.xml"));
}

3.注解方式

@Component
public class FXNewsProvider{
    @Autowired
    private IFXNewsListener newsListener;
    @Autowired
    private IFXNewsPersister newPersistener;
    public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister)
  {
      this.newsListener = newsListner;
      this.newPersistener = newsPersister;
  }
  ...
}    

@Autowired是这里的主角,它的存在将告知Spring容器需要为当前对象注入哪些依赖对象。而@Component则是配合Spring 2.5中新的classpath-scanning功能使用的。现在我们只要再向Spring的配置文件中增加一个“触发器”,使用@Autowired和@Component标注的类就能获得依赖对象的注入了。需要配置文件:

<context:component-scan base-package="cn.spring21.project.base.package"/></beans>

<context:component-scan/>会到指定的包(package)下面扫描标注有@Component的类,如果找到,则将它们添加到容器进行管理,并根据它们所标注的@Autowired为这些类注入符合条件的依赖对象。

然后就可以像之前加载配置文件一样的方式执行当前应用程序了:

public static void main(String[] args){
    ApplicationContext ctx = new ClassPathXmlApplicationContext("配置文件路径");
    FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("FXNewsProvider");4.3 
    newsProvider.getAndPersistNews();
}

整体来看spring Ioc容器
spring的Ioc容器的作用是以某种方式加载配置信息,然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统。这个过程可以分为两个阶段:容器启动和Bean实例化。Spring的IoC容器在实现的时候,充分运用了这两个实现阶段的不同特点,在每个阶段都加入了相应的容器扩展点,以便我们可以根据具体场景的需要加入自定义的扩展逻辑。

1. 容器启动阶段
容器启动伊始,首先会通过某种途径加载Configuration MetaData。除了代码方式比较直接,在大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工作就完成了。

总地来说,该阶段所做的工作可以认为是准备性的,重点更加侧重于对象管理信息的收集。当然,一些验证性或者辅助性的工作也可以在这个阶段完成。

2. Bean实例化阶段
经过第一阶段,现在所有的bean定义信息都通过BeanDefinition的方式注册到了BeanDefinitionRegistry中。当某个请求方通过容器的getBean方法明确地请求某个对象,或者因依赖关系容器需要隐式地调用getBean方法时,就会触发第二阶段的活动。该阶段,容器会首先检查所请求的对象之前是否已经初始化。如果没有,则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当该对象装配完毕之后,容器会立即将其返回请求方使用。如果说第一阶段只是根据图纸装配生产线的话,那么第二阶段就是使用装配好的生产线来生产具体的产品了。

总结:本节主要介绍了spring的两种容器类型,然后又介绍了最基本的BeanFactory的三种对象注册和依赖绑定方式,其实和一般的Ioc Service Provider支持的三种方式一样。

posted @ 2017-07-31 17:00  鹿丸不会多项式  阅读(263)  评论(0编辑  收藏  举报