第一小节主要介绍容器的核心概念,第二小节介绍容器的操作,配置,创建,使用。是下文的引言。
1、核心概念
第一小节介绍了三个容器的核心概念。IOC,Bean,IOC container。
1. 问题1:什么是IOC?
答:官网中IOC定义的原文如下:It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean
这段话包含的三个重点要素
- When(何时):we creates the beans(在创建bean的过程中)
- Process(过程):we define their dependencies(在定义bean的依赖关系)。
- Ways(方式):constructor arguments(构造器参数),arguments to a factory method(工厂方法的参数),set properties(set方法方式)。
我理解这里的依赖关系是指在创建过程中需要的必要对象或属性,或者是某些必备的前置条件。例如我们在创建数据库对象时,你必须首先加载驱动,它是前置条件。你必须提供用户名,密码,服务器IP,端口号等等,这些都是数据库对象的必要属性。
数据库对象本身还有许多非必要属性,假设对象创建完成之后,希望测试一下连接,此时需要提供测试的SQL语句,以及尝试次数,等待时间,这些依赖不是发生在创建过程中的。当然我们也可以把这些属性都在创建时一次性指定,而在现实基本都是这样操作的,不存在按需配置的说法。
从理论角度上,可以指定任何依赖,不一定只是对象的必要属性和必备条件。
从程序角度上,注入只发生在创建过程中,所有的依赖关系都转换为构造器参数,属性,工厂方法参数三种代码形式。
2. 问题2:什么是Bean?
答:官网中bean的定义如下:A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container
这段话包含两个要素
- A bean is an object,bean是Java语言中的任何一个对象。Spring配置文件中的bean标签本质就是建立它与类之间的关系,标签内部属性与Java对象属性之间的关系。所以在配置bean时,一定熟悉与之关联的对象。
- IOC container的作用:它用来管理,创建,配置bean。
3. 问题3:什么是IOC container?
答:官网中IOC container的原文如下:The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans. The container gets itsinstructions on what objects to instantiate, configure, and assemble by reading configuration metadata
这段话包含三个要素
- ApplicationContext interface represents the spring IOC container,它(applicationContext)就是IOC container的抽象。所以理解ApplicationContext的职责很重要。
- 它的作用:管理,创建,配置bean,之前已提过。
- configuration metadata:配置信息
在学习IOC容器时,也可以将其分为三个方面,
- ApplicationContext的类结构,研究它的API。
- 了解bean的创建过程,或者说生命周期。IOC如何管理bean。如何去配置bean。
- 配置元数据,之后的章节会分别介绍XML方式,Java based注解方式,Spring的注解方式,以及混合方式。
2、核心对象
父接口:
- EnvironmentCapable:该接口提供获取环境的相关信息,常见的就是操作系统和JVM的信息。
- listableBeanFactory:该接口提供了一些很实用的方法,例如根据类型查询bean的数量,判断bean是否存在,bean的总数等等。
- HierarchicalBeanFactory:该接口提供了bean的层次结构。
- messageResource:实现国际化功能的接口
- applicationEventPublisher:发布事件,目前没使用过。
- ResourcePatternResolver:它的父接口是ResourceLoader,主要功能是加载资源文件。
实现类:ConfigurableApplicationContext那部分没有画全,它与webApplicationContext的结构类似,也是分为三种AbstractRefreshable,Generic,static。
- AnnotationConfig,Groovy,Xml分别对应三种方式,分别是注解,Groovy配置文件,XML配置文件。Groovy略。
- FileSystemApplicationContext对应磁盘上的配置文件,不过前三种方式已经适用90%以上的场景。它的使用频率不是很高,了解即可。
3、操作容器
3.1 配置
本小节采用XML方式,配置元数据需要写一份spring的配置文件。在编写之前首先需要了解schema。
编写spring配置文件需要两步:
- 引入schema,并指定schema对应的xsd文件。它会从官网请求xsd文件,断网时,引入schema失败,spring配置文件也完全失效。遇到这种情况时,将XSD文件提前下载保存到本地,然后在编译器中配置XSD。在spring相关的jar包中都有对应的xsd文件。
- 使用schema中的标签,标签的格式为namespace:tagName,当schema为默认值时,无需指定namespace,例如context:component-scan。默认情况下都是使用beans作为默认schema。
在实践时,拷贝一份已存在的配置文件更方便。学习时,单个引入schema,并学习schema下的标签效果会更好。
3.2 创建
创建IOC容器,抽象到程序角度,本质是创建ApplicationContext对象。它有三种方式
- 配置文件方式:配置文件可以是本地文件,也可以是项目的classpath。可以只有一份,也可以有多份。
示例代码如下:
//**********************************配置文件方式*******************************// // 多个XML文件, Application context = new ClassPathXmlApplicationContext(“config1.xml”,”config2.xml”); // 从文件系统中加载,参数为文件路径,或者是File对象 Application fileSystemContext = new FileSystemApplicationContext(“文件路径”); // 一份配置文件,引入其他配置文件,例如在config1.xml中引入 <import resource=”config1_test.xml”> <import resource=”config1_product.xml”>
2. 注解方式:带有@Configuration注解的类,可以是一到多个。
示例代码如下:
//**********************************注解方式*******************************// // 单个Config配置文件,类上有@Configuration注解 ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class) // 多个类,通常类上有@Configuration注解,也可以是@Component,@Service,@Controller,@Repository。 ApplicationContext ctx = new AnnotationConfigApplicationContext(Test.class, Test2.class);
3.3 使用
使用IOC容器,可以抽象为调用applicationContext对象的API。官网中1.2.3小节只介绍了如何获取容器中的bean。
获取过程有两步操作。
- 第一步:根据id,name等相关的key,在容器中找到bean,如果bean还未创建,则触发bean的创建过程,并将找到的bean返回。
- 第二步:将bean映射为Java对象,这也是第二个参数的作用。如果不指定,有可能会报类型转换异常。