深入解读Spring Framework IoC容器(第一弹:IoC容器和Bean概述)

Spring IoC容器和Bean概述

我们主要来看一下Spring框架实现控制反转(IoC)的原理。IoC也被称作依赖注入(DI)。他是一个对象定义依赖项的过程。当创建bean后,IoC容器再将这些依赖项注入进去。这个过程是反转的,因此得名控制反转 (IoC)。

org.springframework.beans和org.springframework.context包是Spring框架IoC容器的基础。BeanFactory接口提供了一个先进的配置机制能够管理任何类型的对象。ApplicationContext是BeanFactory的一个子接口。它增加了更方便的集成Spring的AOP功能、消息资源处理(国际化)、事件发布和特定的应用层,如在web应用层中使用的WebApplicationContext。

总之,BeanFactory提供了配置框架和基本功能,ApplicationContext则添加了更多的企业特定的功能。ApplicationContext是BeanFactory的一个完整的超集,我们一般用他来指代Spring IoC容器。

在Spring中,被Spring IoC容器管理的这些来自于应用主干的这些对象称作beans。bean是一个由Spring IoC容器进行实例化、装配和管理的对象。Beans以及他们之间的依赖关系(dependencies)是通过容器使用配置元数据(configuration metadata)反应出来的。

容器概述

org.springframework.context.ApplicationContext接口代表了Spring IoC容器,并且负责Beans的实例化、配置和装配。容器通过读取配置元数据获取对象实例化、配置和装配的方式。配置元数据可以用XML、Java注解或Java代码来描述。

Spring提供了几个即用的ApplicationContext接口的实现。通常我们用ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的实例。我们主要使用XML来定义配置元数据,但也可以使用Java注解或者代码作为元数据格式,只需要通过提供少量XML配置声明来支持这些额外的元数据格式。

配置元数据

在基于XML配置的元数据中,这些beans配置成一个个的<bean/>元素,这些<bean/>元素定义在顶级元素<beans/>的里面。如果是在Java配置中,通常在一个@Configuration注解的类中,在方法上使用@Bean注解。

我们看一下基于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">

    <!-- id属性是一个用来识别每一个独立bean定义的字符串。 -->
    <!-- class属性定义了bean的类型,这个属性需要使用bean类的全限定名称。 -->
    <!-- id属性的值可以被其他的bean对象引用。 -->
    <bean id="..." class="...">
        <!-- bean的相关设置 -->
    </bean>

    <bean id="..." class="...">
        <!-- bean的相关设置 -->
    </bean>

    <!-- 更多的bean定义 -->

</beans>

实例化容器

将一个或多个位置路径提供给ApplicationContext的构造方法就可以让容器加载配制元数据,可以从多种外部资源进行获取,例如文件系统、Java的CLASSPATH。例如:

ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});

这些xml就是刚才上面看到的xml配置元数据。

也可以使用应用上下文的构造方法从多个XML片段中加载bean的定义。比如上面的例子,构造方法可以接收多个Resource位置。或者在bean定义中使用一个或多个<import/>从其他的配置文件引入bean定义。例如:

<beans>
    <!-- 这里的路径是相对的 -->
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

使用容器

ApplicationContext能够维护注册不同beans和它们的依赖。通过使用T getBean(String name, Class<T> requiredType)方法可以取得这些beans的实例。

// 创建并配置beans
ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});

// 取得实例
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// 使用实例
List<String> userList = service.getUsernameList();

Bean概述

一个Spring IoC容器管理了很多的beans。这些beans通过我们提供给容器的配置元数据进行创建。在容器内,这些bean定义表示为BeanDefinition对象,它包含了如下的元数据:

  1. 包限定的类名: 通常是bean定义的实现类。
  2. Bean行为配置元素,这些状态指示bean在容器中的行为(范围,生命周期回调,等等)。
  3. bean工作需要引用的其他beans;这些引用也称为协作者或依赖者。
  4. 其他配置设置。
    这些元数据转换bean定义的一组属性,这些属性有:class,name,scope,constructor arguments,properties,autowiring mode,lazy-initialization mode,initialization method,destruction method。

bean的命名

每个bean都有一个或多个标识符,这些bean的标识符在它所在的容器中必须唯一。一个bean通常只有一个标识符,但如果它有一个以上的id标识符,多余的标识符将被认为是别名。

在基于xml的配置元数据中,你可以使用id或name属性来指定bean的标识符。如果想给bean添加其他的别名,可以通过name属性来指定这些别名,可以使用逗号、分号或者空格来分割这些别名。 这里需要特别注意的是,在Spring3.1版本以前,id属性被定义成xsd:ID类型,可以通过xml规则限制唯一, 在Spring3.1以及以后的版本中,id被定义成了xsd:string类型,id属性不在通过XML解析器限制为唯一,而是通过容器强制限制为唯一。

bean的id和name不是必须的。如果没有明确的name或者id,容器会给bean生成一个唯一的名字。不提供名称的原因和内部beans和自动装配有关。

Bean命名约定

Spring约定使用标准的Java实例字段名称命名beans。也就是说bean名字由一个小写字母开头,后面采用驼峰式命名规则。 如:accountService、userDao、loginController等等。

Bean的别名

我们可以在当前位置为在别处定义的bean引入别名。在XML配置文件中,可以通过<alias/>元素来完成bean别名的定义,例如:
<alias name="fromName" alias="toName"/>
这在某些情况下很有用。例如,在子系统A中通过名字subsystemA-dataSource配置的数据源。在子系统B中可能通过名字subsystemB-dataSource来引用。当两个子系统构成主应用的时候,主应用可能通过名字myApp-dataSource引用数据源,要将这三个名字引用同一个对象,可以将下面的别名定义添加到应用配置中:

<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />

实例化beans

bean定义是用来创建对象的配方(recipe),当需要一个bean的时候,容器查看配方并且根据bean定义封装的配置元数据创建一个实际的对象。

如果使用基于XML的配置,可以在<bean/>元素通过class属性来指定对象的类型。这个class属性,就是BeanDefinition实例中的一个Class属性。这个class属性通常是必须的,使用Class属性的两种方式:
1. 通常情况下,直接通过反射调用构造方法来创建bean,和在Java代码中使用new有点像。
2. 通过静态工厂方法创建。通过调用静态方法返回对象的类型可能和Class一样,也可能完全不一样。

如果想配置使用静态的内部类,就必须用内部类的二进制名称。
例如,在com.example包下有个Foo类,这里类里面有个静态的内部类Bar,这种情况下bean定义的class属性应该是com.example.Foo$Bar。注意,使用$字符来分割外部类和内部类的名称。

通过构造函数实例化

当使用构造方法来创建bean的时候,这个类不需要实现任何特定的接口或者以特定的方式进行编码。但是可能需要一个默认无参的构造方法。

Spring IoC 容器可以管理几乎所有你想让它管理的类,不限于管理POJO。不过一般我们都使用POJO(一个默认无参的构造方法和setter,getter方法)。

当使用基于XML的元数据配置文件,可以这样来指定bean类:

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

使用静态工厂方法实例化

当采用静态工厂方法创建bean时,除了需要指定class属性外,还需要通过factory-method属性来指定创建bean实例的工厂方法。Spring将调用此方法返回实例对象。

下面的bean定义展示了如何通过工厂方法来创建bean实例。

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    // 在此例中,createInstance()必须是一个static方法
    public static ClientService createInstance() {
        return clientService;
    }
}

使用实例工厂方法实例化

与通过静态工厂方法实例化类似,通过调用工厂实例的非静态方法进行实例化。使用这种方式时,class属性必须为空,而factory-bean属性必须指定为当前容器中包含工厂方法的bean的名称,而该工厂bean的工厂方法本身必须通过factory-method属性来设定。

<!-- 工厂bean,包含createInstance()方法 -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- 其他要注入的依赖项 -->
</bean>

<!-- 通过工厂bean创建的bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();
    private DefaultServiceLocator() {}

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

一个工厂类也可以有多个工厂方法:

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- 其他要注入的依赖项 -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();
    private static AccountService accountService = new AccountServiceImpl();

    private DefaultServiceLocator() {}

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值