Spring的IoC容器

前言:

本内容参考与《spring揭秘》一本挺老的书了,不过讲得知识比较透彻,顺着它的节奏只看比较有用的部分,还是蛮不错的。

IoC的基本概念

注意:这里指的不是spring的ioc容器,而是ioc的概念。
IoC(Inversion of Control) 控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统(Ioc Service Provider)内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
通俗的来讲:ioc就是就是将注入对象与被依赖对象之间进行解耦,本来注入对象是要自己手动创建被依赖对象的,但是ioc却将其用ioc service provider来进行注入,省去注入对象自己手动创建。 而ioc service provider可以是一段代码,也可以是一个ioc容器例如spring ioc容器。
依赖注入主要有三种实现方式:

  • 构造函数注入
    就是被注入对象可以通过其构造方法中声明依赖对象的参数列表,让外部(ioc容器)知道它需要哪些依赖对象。
    (对象被构造完之后,即进入就绪状态)
  • setter函数注入
    对于javaBean来说,通常会用setxxx()和getxxx()来访问对应的熟悉,这些setxxx方法统称为setter方法,所以当前对象只要为其依赖对象所对应属性添加setter方法,即可通过setter方法将对应的依赖对象设置到注入对象之中。
    (可以在对象构造完之后再注入)
  • 接口注入
    被注入对象如果想要IoC Service Provider为其注入依赖对象,就必须实现某个接口,这个接口提供一个方法,来为其注入依赖对象。IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。
    这个有点难理解。贴上书上的uml图。
    在这里插入图片描述
    FXNewsProvider为了让IoC Service Provider为其注入所依赖的IFXNewsListener,首先需要实现IFXNewsListenCallable接口,这个接口会声明一个injectNewsListener方法,该方法的参数就是所依赖对象的类型。对应的IoC Service Provider就可以通过这个接口方法将依赖对象注入到被注入对象的FXNewsProvider中。

Spring Ioc容器之BeanFactory

spring的IoC容器就是一个IoC Service Provider,但spring ioc容器远不止这个功能还包括aop支持、对象生命周期管理、线程管理、企业服务继承。
spring提供两种容器类型:BeanFactory和ApplicationContext。

我们先来了解BeanFactory。

BeanFactory:基础类的IoC容器,提供完整的IoC服务支持,没有特殊指定,默认采用延迟初始化策略。只有当客户端对象需要访问容器中某个受管理对象时,才对该对象进行初始化以及依赖注入操作。

BeanFactory只是一个接口,我们最终需要一个该接口的实现来进行实际的bean管理,DefaultlistableBeanFactory就是这么一个比较通用的实现类。

DefaultlistableBeanFactory

DefaultlistableBeanFactory还实现了BeanDefinitionRegstery接口,该接口才是在BeanFactory中担当bean注册管理的角色,基本上BeanFactory接口只定义如何访问容器内管理bean的方法。打个比方来说即:BeanDefinitionRegstery就像是图书馆的书架,所有书都是放在书架上的,但是你借书找的却是图书馆,即BeanFactory。
每个受管理的对象,在容器里面都有一个BeanDefinition实例与之相对应。该实例负责保存对象的所有必要信息(class类型、是否抽象类、构造方法参数以及其他属性)。当客户端向BeanFactory请求相应对象时,BeanFactory会通过这些信息返回一个可用对象实例。
在这里插入图片描述

bean的scope

配置中的bean定义可以看作是一个模版,容器会根据这个模版来构建对象,但是根据这个模版构建多少对象实例,又该让这些构造完的对象实例存活多久,则有容器根据bean定义的scope来定了

  • singleton:在spring的ioc容器中只存在一个实例,所有对该对象的引用将共享这个实例。该实例从容器启动,并且因为第一次请求而初始化后,就一直存在到容器销毁,
  • prototype:容器在接收到该类型对象的请求时候,会每次都重新生成一个新的对象实例给请求方,虽然这种类型的对象的实例化以及属性设置工作由容器负责,但当对象实例返回给请求方后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前对象的后继生命周期的管理工作。
  • request:其实可以看成是prototype的一种特例,生命周期是一个请求的生命周期
  • session:spring容器为每个独立的session创建属于他们自己的对象实例,跟request差不多啦,就是生命周期长一点。
  • global session:只要应用在基于portlet的web应用程序中才有意义,他会映射到porlet的global范围的session,如果在普通基于servlet的web应用中,视为session对待
3、插手容器的启动

spring提供一种叫做BeanFactoryPostProcessor的容器扩展机制。该机制允许我们在容器实例化对象之前,对注册到容器的Beandefinition所保存的信息做相应的修改。
我们最熟悉的莫过于PropertyPlaceholderConfigurer。实现了BeanFactoryPostProcessor接口,主要用于将环节配置的properties与业务对象相关配置的xml进行分离。

<!--配置spring文件的时候分离常量,propertyConfigurer用于读取配置文件-->
    <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:datasource.properties</value>
            </list>
        </property>
        <property name="fileEncoding" value="utf-8"/>
    </bean>

    <!--配置dbcp连接池-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${db.driverClassName}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
        <!-- 连接池启动时的初始值 -->
        <

还有就是针对特定对象类型的PropertyEditor实现,例如日期转换类咯
继承PropertyEditorSupport来书写自己的转换规则,然后将其注入到spring容器中,容器会自动识别并应用(仅限于ApplicationContext容器)

Spring Ioc容器之ApplicationContext

ApplicationContext是比BeanFactory更为先进的ioc容器实现。扩展了基本容器的功能。BeanFactory的基本功能它都基本含有。启动的时候就完成了所有bean的初始化。

统一资源加载策略

spring提供了Resource和ResourceLoader接口来进行资源抽象和加载策略。

Resource

主要有5种具体实现累

  • ByteArrayResource:将字节数组提供的数据作为一种资源进行封装。
  • ClassPathResource:该实现从java应用好处呢工序的classpath中加载具体资源来进行封装。
  • FileSystemResource:可以通过文件护照url形式对类型资源进行访问
  • UrlResource:内部委托url进行具体资源操作
  • InputStreamResource:将给定的InputStream视为一种资源的resource实现累,较为少用。
public interface Resource extends InputStreamSource {
    boolean exists();

    boolean isReadable();

    boolean isOpen();

    URL getURL() throws IOException;

    URI getURI() throws IOException;

    File getFile() throws IOException;

    long contentLength() throws IOException;

    long lastModified() throws IOException;

    Resource createRelative(String var1) throws IOException;

    String getFilename();

    String getDescription();
}

Resource 接口是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。Resource 接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源访问逻辑。

ResourceLoader

资源有了,如何查找和定位这些资源就是ResourceLoader的作用了。

public interface ResourceLoader {
    String CLASSPATH_URL_PREFIX = "classpath:";

    Resource getResource(String var1);

    ClassLoader getClassLoader();
}

翻了翻源码 他们的关系是这样的,在ResourceLoader子类中重写getResource根据策略模式 选择不用的读取方式来返回一个Resource。

public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        if (location.startsWith("classpath:")) {
            return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());
        } else {
            try {
                URL url = new URL(location);
                return new UrlResource(url);
            } catch (MalformedURLException var3) {
                return this.getResourceByPath(location);
            }
        }
    }

ResourceLoader:该接口实现类的实例可以获得一个 Resource 实例。
在 ResourceLoader 接口里有如下方法:
Resource getResource(String location):该接口仅包含这个方法,该方法用于返回一个 Resource 实例。
ApplicationContext 的实现类都继承ResourcePatternResolver(用于批量查找的ResourceLoader子类)等于间接实现了ResourceLoader 接口,而该接口中的getresource又返回一个Resource实例,因此 ApplicationContext 可用于直接获取 Resource 实例,这也是为什么容器就可以处理Resource的原因。

容器功能实现的各个阶段
1、容器启动阶段

容器启动,首先会通过某个途径来加Configuration MetaData(元数据)。)代码注入、配置文件注入)。容器需要依赖某些工具类(BeandefinitionReader)对加载的Configuration MetaData进行解析分析,并且将分析后的信息编组成相应的Beandefinition,最后把这些保存了bean定义的必要信息的Beandefinition,注册到BeandefinitionRegistry。这样容器的启动工作就完成了。

2、Bean实例化阶段

经过第一阶段,现在所有的bean信息都通过Beandefinition的方式注册到了BeandefinitionRegistry中,当某个请求通过容器的getbean方法明确请求某个对象,或者因依赖关系容器需要隐式的调用getbean方法时,就会触发第二阶段的活动。
该阶段,容器会检查所请求对象之前是否已经初始化,如果没有,则会根据注册。的Beandefinition所提供的信息来实例化被请求对象,为期注入依赖。当对象装配王弼,容器会立即将其返回请求方使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值