The IoC Container 1.1-1.3

1.1. Spring IoC Container and Beans介绍

任何一个面向对象的系统实现,都是通过多个不同的对象相互组合并作用下使系统运行起来。而IoC的核心功能,简单来说,就是不需要人为的显示的去构造对象,Spring IoC Container帮助我们构造和管理程序中的对象,而我们只需要定义好其中的依赖关系即可。依赖关系即A对象中的私有变量为B对象,A包含B的关系。被Spring IoC Container管理的对象,称之为bean。

The org.springframework.beans and org.springframework.context packages are the basis for Spring Framework’s IoC container.

The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory

1.2. Container Overview

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans

The following diagram shows a high-level view of how Spring works. Your application classes are combined with configuration metadata so that, after the ApplicationContext is created and initialized, you have a fully configured and executable system or application.
从这张图可以看出,通过元数据配置(xml形式或者java注解形式),再加上我们应用所定义的类本身,通过这两种信息结合起来初始化ApplicationContex,初始化完成后,你就拥有了一个可用的系统。ApplicationContext帮助我们完成了系统中所有对象的初始化以及组装工作,系统能正常运行的背后是所有对象正常协同工作的结果。


Spring Container

最后,说明下,ApplicationContext是BeanFactory接口的子类,所以ApplicationContext就是IoC Container的编程实现,因为它拥有BeanFactory的功能,而BeanFactory顾名思义,就是生产bean的工厂。

1.2.1. Configuration Metadata

<?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">

    <bean id="..." class="...">   
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

The id attribute is a string that identifies the individual bean definition.
The class attribute defines the type of the bean and uses the fully qualified classname.

上面为Spring官方给出的xml配置示例,此处给出的示例仅仅列出了如何定义一个bean,bean中所包含的依赖对象的如何在xml中定义请参考官方1.4 Dependencies。xml中的<bean/>元素等价于使用如下注解:

Java configuration typically uses @Bean-annotated methods within a @Configuration class.

1.2.2. Instantiating a Container

Container的实例化,下面是xml配置元数据方式的Container实例化,一句代码直接搞掂(再重复下,ApplicationContext大家理解为Container即可):

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

services.xml的服务层对象(service layer objects)的配置:

<?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">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>

daos.xml数据访问层对象(data access objects)的配置:

<?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">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

service.xml中引用了daos.xml中的bean定义,这块比较简单,观察两个xml就可以知道其间的关系。描述了类的全路径、对象以及依赖对象间关系,通过这个xml的定义,ApplicationContext就可以帮助我们实例化一个完整可用的petStore对象出来。

(组合多个xml的配置元数据)Composing XML-based Configuration Metadata

使用import可以引用其他xml中定义的元素。通常来讲,一个系统包含多个xml配置是合理的,每个xml代表一个模块,这样的话减少了xml文件的大小,逻辑根据模块的不同分散到不同的xml文件中,维护起来更轻松。

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

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

针对以上组合xml配置,需要注意的点:

  1. services.xml,messageSource.xml,themeSource.xml是从外部导入的xml配置
  2. 三个xml的路径都是相对路径,相对于当前定义文件的路径。
  3. /resources/themeSource.xml最前面的斜杠会被忽略掉,所以Spring官方建议不要加斜杠,因为加了也没用,仍然是相对路径,免得引起歧义
  4. 被导入的xml配置一定要是合法的(符合Spring XML的Schema),这个没啥好说的
  5. 不建议使用../这种相对路径。更不建议使用classpath:../ 这种表示方法,这样会先找到最近的classpath所在路径,在往上找父目录,因为classpath找到是最近的目录,所以很可能出现位置找错的情况,所以请一定避免这种引用方式。
  6. 不建议直接在xml配置中使用绝对路径,如file:C:/config/services.xml or classpath:/config/services.xml。因为这样会使项目跟环境产生耦合,应该避免这种情况。更好的使用方式是使用"${…​}"占位符来替代绝对路径,然后启动java进程时通过java系统属性将绝对路径传递进去。

1.2.3. Using the Container

初始化Container,即初始化ApplicationContext后,使用context.getBean方法获取对应的bean的实例。一般来说,我们会在Spring应用中让ApplicationContext负责管理所有的常驻内存的实例:比如service/repository/datasource等。

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

下面示例提供实例化Container的另外一种方式。先使用XmlBeanDefinitionReader读取相应配置到context中,再执行refresh进行Container的实例化。这个唯一的好处是因为Spring还支持groovy脚本的配置方式,不仅限于xml配置方式,这样,如果一个系统同时拥有xml和groovy两种方式的混合配置,可以通过该方式初始化,后面会给出示例。

GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();

groovy和xml混合的配置的初始化(对groovy配置方式感兴趣的可以参考官方文档),可以看到services由groovy进行配置,daos由xml进行配置:

GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy");
XmlBeanDefinitionReader(context).loadBeanDefinitions( "daos.xml");
context.refresh();

这里的示例使用了getBean方法取出对象,但实际使用中Spring基本不需要这么使用,而是直接通过自动装配方式注入(@Autowire等注解),这个在后面会讲。

1.3. Bean Overview

Spring IoC container管理的bean的数量没有任何限制,可以是一个,也可以是多个。
Container内部,bean的定义会被表示为BeanDefinition对象,BeanDefinition包含以下信息:

*A package-qualified class name: typically, the actual implementation class of the bean being defined.
*Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).
*References to other beans that are needed for the bean to do its work. These references are also called collaborators or dependencies.
*Other configuration settings to set in the newly created object — for example, the size limit of the pool or the number of connections to use in a bean that manages a connection pool.

总体来说,BeanDefinition需要包含这些信息:Class,Name,Scope,Constructor arguments,Properties,Autowiring mode,Lazy initialization mode,Initialization method,Destruction method。从这些信息的描述就可以看出来为啥需要这些信息,有些还不清楚的比如Autowiring mode后面再看到底是什么用处。从这些信息可以知道,必然是使用java的反射机制来对实例进行初始化的,反射需要的信息来源就是BeanDefinition。

ApplicationContext还支持两个方法用来在运行时注册实例和BeanDefinition实例,registerSingleton(..) registerBeanDefinition(..)。但是,官方并不推荐,因为并发异常或者先后顺序初始化、覆盖等问题,一般还是通过xml配置等方式进行初始化的。

1.3.1. Naming Beans

传统惯例:驼峰式命名。如果未在配置中给name赋值,那么Spring会自动为对象生成名称,生成规则如下:

following the rules described earlier: essentially, taking the simple class name and turning its initial character to lower-case. However, in the (unusual) special case when there is more than one character and both the first and second characters are upper case, the original casing gets preserved. These are the same rules as defined by java.beans.Introspector.decapitalize (which Spring uses here).

别名(一般还是处于子系统模块划分的考虑,可以在子系统中可以避免命名冲突),子系统A使用别名subsystemA-dataSource,子系统B使用subsystemB-dataSource,包含了子系统A和子系统B的上层系统使用myApp-dataSource,但实际是指向同一个对象myApp-dataSource:

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

1.3.2. Instantiating Beans

bean的初始化可以使用两种方式:

  1. 构造函数
  2. 工厂函数(比较少用)

两种方式都可以通过xml配置进行定义。

内部类名的指定方式:

For example, if you have a class called SomeThing in the com.example package, and this SomeThing class has a static nested class called OtherThing, the value of the class attribute on a bean definition would be com.example.SomeThing$OtherThing.

Instantiation with a Constructor(下面两种定义使用默认无参构造函数实例化对象):

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

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

后面章节会介绍带参数的构造函数实例化的xml配置方式。

Instantiation with a Static Factory Method:

<bean id="clientService" class="examples.ClientService"
    factory-method="createInstance"/>

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}

Instantiation by Using an Instance Factory Method:

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容