Spring容器
Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周 期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Be ans,我们将在下一章中进行讨论。
通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML,Ja va 注释或 Java 代码来表示。下图是 Spring 如何工作的高级视图。 Spring IoC 容器利用 Java 的 POJO 类和 配置元数据来生成完全配置和可执行的系统或应用程序。
Spring容器分为BeanFactory容器和ApplicationContext容器。
Sping 的 BeanFactory 容器
这是一个最简单的容器,它主要的功能是为依赖注入 (DI) 提供支持,这个容器接口在 org.springframework.beans.factory.BeanFactor 中被定义。 BeanFactory 和相关的接口,比如,BeanFactoryAware、 Dispo sableBean、InitializingBean,仍旧保留在 Spring 中,主要目的是向后兼容已经存在的和那些 Spring 整合在 一起的第三方框架。
在 Spring 中,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从 一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
在资源宝贵的移动设备或者基于 applet 的应用当中, BeanFactory 会被优先选择。否则,一般使用的是 Applic ationContext,除非你有更好的理由选择 BeanFactory。
Spring ApplicationContext 容器
Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bea n,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性 文件从解析文本信息和将事件传递给所指定的监听器。这个容器在org.springframework.context.Application Context interface 接口中定义。
ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationConte xt 会被推荐使用。BeanFactory 仍然可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。
最常被使用的 ApplicationContext 接口实现:
FileSystemXmlApplicationContext
:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供 给构造器 XML 文件的完整路径ClassPathXmlApplicationContext
:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提 供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中 搜索 bean 配置文件。WebXmlApplicationContext
:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 be an。
Bean
被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化,组装,并通 过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的,例如,已经在先前章节看到 的,在 XML 的表单中的 定义。
bean 定义包含称为配置元数据的信息,下述容器也需要知道配置元数据:
- 如何创建一个 bean
- bean 的生命周期的详细信息
- bean 的依赖关系
上述所有的配置元数据转换成一组构成每个 bean 定义的下列属性。
属性 | 描述 |
---|---|
class | 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。 |
name | 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。 |
scope | 这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域 的章节中进行讨论。 |
constructor-arg | 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。 |
properties | 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。 |
autowiring mode | 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。 |
lazy-initialization mode | 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创 建一个 bean 实例。 |
initialization 方法 | 在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的 生命周期章节中进行讨论。 |
destruction 方法 | 当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章 节中进行讨论。 |
Spring 配置元数据
Spring IoC 容器完全由实际编写的配置元数据的格式解耦。有下面三个重要的方法把配置元数据提供给Spring容器:
- 基于 XML 的配置文件。
- 基于注解的配置
- 基于Java的配置
Bean作用域
当在 Spring 中定义一个 时,你必须声明该 bean 的作用域的选项。例如,为了强制 Spring 在每次需要时都产 生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需 要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为 singleton。
Spring 框架支持以下五个作用域,如果你使用 web-aware ApplicationContext 时,其中三个是可用的。
作用域 | 描述 |
---|---|
singleton | 该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中的一个单一实例(默认)。 |
prototype | 该作用域将单一 bean 的定义限制在任意数量的对象实例。 |
request | 该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationCo ntext 的上下文中有效。 |
session | 该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationCo ntext的上下文中有效。 |
global-session | 该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring Applicatio nContext 的上下文中有效。 |
1 | <!-- A bean definition with singleton scope --> |
Bean生命周期
在Spring基础中已经介绍过,bean在Spring容器的生命周期为:
为了定义安装和拆卸一个 bean,我们只要声明带有 init-method 和/或 destroy-method 参数的 。init-metho d 属性指定一个方法,在实例化 bean 时,立即调用该方法。同样,destroy-method 指定一个方法,只有从容 器中移除 bean 之后,才能调用该方法。
1 | package com.tutorialspoint; |
1 | package com.tutorialspoint; |
1 |
|
默认的初始化和销毁方法
如果你有太多具有相同名称的初始化或者销毁方法的 Bean,那么你不需要在每一个 bean 上声明初始化方法和销毁方法。框架使用 元素中的 default-init-method 和 default-destroy-method 属性提供了灵活地配置这种情况,如下所示:
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
另一种方式是实现接口进行回调。
1 | public class ExampleBean implements InitializingBean { |
1 | public class ExampleBean implements DisposableBean { |
Bean后置处理器
Bean定义继承
依赖注入
每个基于应用程序的 java 都有几个对象,这些对象一起工作来呈现出终端用户所看到的工作的应用程序。当编写 一个复杂的 Java 应用程序时,应用程序类应该尽可能独立于其他 Java 类来增加这些类重用的可能性,并且在 做单元测试时,测试独立于其他类的独立性。依赖注入(或有时称为布线)有助于把这些类粘合在一起,同时保 持他们独立。
依赖注入主要有两种方式:
- 构造器注入
- setter注入
当对象与它们的依赖关系被提供时,解耦效果非常明显。对象不查找它的依赖关系,也不 知道依赖关系的位置或类,而这一切都由 Spring 框架控制的。
Spring 基于构造函数的依赖注入
当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖。
1 | package x.y; |
1 | <beans> |
果你使用 type 属性显式的指定了构造函数参数的类型,容器也可以使用与简单类型匹配的类型。例如:
1 | <beans> |
Spring 基于setter的依赖注入
当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数,基于设值函数的 DI 就完成了。
1 | package com.tutorialspoint; |
1 | package com.tutorialspoint; |
1 | package com.tutorialspoint; |
1 |
|
注入内部Beans
Java内部类是在其他类的范围内被定义的,同理,inner beans是在其他bean的范围内定义 的 bean。因此元素内元素被称为内部bean,如下所示。
1 |
|
注入集合
Spring提供了四种类型的集合的配置元素,如下所示:
元素 | 描述 |
---|---|
它有助于连线,如注入一列值,允许重复。 | |
它有助于连线一组值,但不能重复。 | |
它可以用来注入名称-值对的集合,其中名称和值可以是任何类型。 | |
它可以用来注入名称-值对的集合,其中名称和值都是字符串类型。 |
两种情况:(a)传递集合中直接的值 (b)传递一个bean的引用作为集合的元素。
1 | package com.tutorialspoint; |
1 | package com.tutorialspoint; |
1 |
|
注入Bean引用到集合
下面的 Bean 定义将帮助你理解如何注入 bean 的引用作为集合的元素。甚至你可以将引用和值混合在一起:
1 |
|
注入 null 和空字符串的值
如果你需要传递一个空字符串作为值,那么你可以传递它,如下所示:
1 | <bean id="..." class="exampleBean"> |
前面的例子相当于 Java 代码:exampleBean.setEmail("")
。 如果你需要传递一个 NULL 值,那么你可以传递它,如下所示:
1 | <bean id="..." class="exampleBean"> |
前面的例子相当于 Java 代码:exampleBean.setEmail(null)
。
Beans自动装配
在前面的例子中,我们手动声明了每一个需要装配的bean的依赖关系。当需要装配的bean非常多时,工作量是很大的。
Spring容器可以在不使用和元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于Spring的应用程序的XML配置的数量。
自动装配模式
下列自动装配模式,它们可用于指示 Spring 容器为来使用自动装配进行依赖注入。你可以使用 元素的 autowire 属性为一个 bean 定义指定自动装配模式。
模式 | 描述 | |
---|---|---|
no | 这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你 不用为了连线做特殊的事。在依赖注入章节你已经看到这个了。 | |
byName | 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性 设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名 称的 beans 的属性进行连接。 | |
byType | 由属性数据类型自动装配。Spring容器看到在XML 配置文件中 bean 的自动装配 的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名 称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致 | 命的异常将会被抛出。 |
constructor|类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造|函数参数类型的 bean,则一个致命错误将会发生。|
|autodetect |Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。|
可以使用 byType 或者 constructor 自动装配模式来连接数组和其他类型的集合。
当自动装配始终在同一个项目中使用时,它的效果最好。如果通常不使用自动装配,它可能会使开发人员混淆的 使用它来连接只有一个或两个 bean 定义。不过,自动装配可以显著减少需要指定的属性或构造器参数,但你应 该在使用它们之前考虑到自动装配的局限性和缺点。
限制 | 描述 |
---|---|
重写的可能性 | 你可以使用总是重写自动装配的 |
原始数据类型 | 你不能自动装配所谓的简单类型包括基本类型,字符串和类。 |
混乱的本质 | 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。 |
Spring 自动装配 ‘byName’
这种模式由属性名称指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 auto-wire 属性设 置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹 配项,它将注入这些 beans,否则,它将抛出异常。
例如,在配置文件中,如果一个 bean 定义设置为自动装配 byName,并且它包含 spellChecker 属性(即,它 有一个 setSpellChecker(...)
方法),那么 Spring 就会查找定义名为 spellChecker
的 bean,并且用它来设 置这个属性。你仍然可以使用 <property>
标签连接其余的属性。
1 | package com.tutorialspoint; |
1 | package com.tutorialspoint; |
1 | package com.tutorialspoint; |
下面是在正常情况下的配置文件 Beans.xml 文件:
1 | <<?xml version="1.0" encoding="UTF-8"?> |
如果你要使用自动装配 “byName”,那么你的 XML 配置文件将成为如下:
1 |
|
Spring 自动装配 ‘byType’
这种模式由属性类型指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设 置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的 属性。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
例如,在配置文件中,如果一个 bean 定义设置为自动装配 byType,并且它包含 SpellChecker 类型的 spellC hecker 属性,那么 Spring 就会查找定义名为 SpellChecker 的 bean,并且用它来设置这个属性。你仍然可以 使用
1 |
|
Spring 由构造函数自动装配
这种模式与 byType 非常相似,但它应用于构造器参数。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个 进行匹配和连线。如果找到匹配项,它会注入这些 bean,否则,它会抛出异常。
例如,在配置文件中,如果一个 bean 定义设置为通过构造函数自动装配,而且它有一个带有 SpellChecker 类 型的参数之一的构造函数,那么 Spring 就会查找定义名为 SpellChecker 的 bean,并用它来设置构造函数的参 数。你仍然可以使用
1 |
|
基于注解的配置
从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个bean装配,你可以使用相关 类,方法或字段声明的注解,将 bean 配置移动到组件类本身。
在 XML 注入之前进行注解注入,因此后者的配置将通过两种方式的属性装配被前者重写。
注解装配在默认情况下在 Spring 容器中不打开。因此,在可以使用基于注解的连线之前,我们将需要在我们的 Spring 配置文件中启用它。所以如果你想在 Spring 应用程序中使用的任何注解,可以考虑到下面的配置文件。
1 |
|
一旦被配置后,你就可以开始注解你的代码,表明 Spring 应该自动连接值到属性,方法和构造函数。几个重要的注解:
注解 | 描述 |
---|---|
@Required | @Required注解应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中(不能自动匹配,也不能不配置),否则容器就会抛出一个 BeanInitializationException 异常。 |
@Autowired | 应用到 bean 属性的 setter方法,非setter方法,构造函数和属性。 |
@Qualifier | 通过指定确切的将被连线的bean,@Autowired 和 @Qualifier 注解可以用来删除混乱。 |
JSR-250 Annotations | Spring 支持 JSR-250 的基础的注解,其中包括了 @Resource,@PostConstru ct 和 @PreDestroy 注解。 |
Spring @Autowired注解
@Autowired注解对在哪里和如何完成自动连接提供了更多的细微的控制。
Setter 方法中的 @Autowired
你可以在 XML 文件中的 setter 方法中使用 @Autowired 注释来除去 元素。当 Spring遇到一个在 setter 方法 中使用的 @Autowired 注释,它会在方法中视图执行 byType 自动连接。
1 | package com.tutorialspoint; |
1 |
|
属性中的 @Autowired
你可以在属性中使用 @Autowired 注释来除去 setter 方法。当时使用 为自动连接属性传递的时候,Spring 会 将这些传递过来的值或者引用自动分配给那些属性。所以利用在属性中 @Autowired 的用法,你的 TextEdit or.java 文件将变成如下所示:
1 | package com.tutorialspoint; |
构造函数中的 @Autowired
也可以在构造函数中使用 @Autowired。一个构造函数 @Autowired 说明当创建 bean 时,即使在 XML 文 件中没有使用 元素配置 bean ,构造函数也会被自动连接。
1 | package com.tutorialspoint; |
1 |
|
@Autowired 的(required=false)选项
默认情况下,@Autowired 注释意味着依赖是必须的,它类似于 @Required 注释,然而,你可以使用 @Auto wired 的 (required=false) 选项关闭默认行为。
1 | package com.tutorialspoint; |
Spring @Qualifier 注释
可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行 装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装 配来消除混乱。下面显示的是使用 @Qualifier 注释的一个示例。
1 | package com.tutorialspoint; |
1 | package com.tutorialspoint; |
1 |
|
Spring JSR-250 注释
Spring还使用基于 JSR-250 注释,它包括 @PostConstruct, @PreDestroy 和 @Resource 注释。因为你已经有了其他的选择,尽管这些注释并不是真正所需要的,但是关于它们仍然让我给出一个简短的介绍。
@PostConstruct 和 @PreDestroy 注释:
为了定义一个 bean 的安装和卸载,我们使用 init-method 和/或 destroy-method 参数简单的声明一下 。init-method 属性指定了一个方法,该方法在 bean 的实例化阶段会立即被调用。同样地,destroy-method 指定了一个方法,该方法只在一个 bean 从容器中删除之前被调用。
你可以使用 @PostConstruct 注释作为初始化回调函数的一个替代,@PreDestroy 注释作为销毁回调函数的一个替代。
@Resource 注释:
可以在字段中或者 setter 方法中使用 @Resource 注释,它和在 Java EE 5 中的运作是一样的。@Resource 注释使用一个 ‘name’ 属性,该属性以一个 bean 名称的形式被注入。你可以说,它遵循 by-name 自动连接语义,如下面的示例所示:
1 | package com.tutorialspoint; |
如果没有明确地指定一个 ‘name’,默认名称源于字段名或者 setter 方法。在字段的情况下,它使用的是字段名;在一个 setter 方法情况下,它使用的是 bean 属性名称。
基于Java的配置
我们已经学习了如何使用 XML 配置文件来配置 Spring bean。如果你熟悉使用 XML 配置,那么我会说,不需要再学习如何进行基于 Java 的配置是,因为你要达到相同的结果,可以使用其他可用的配置。
基于Java的配置选项,可以使你在不用配置XML的情况下编写大多数的Spring,是一种更贴近java语法的配置。
@Configuration 和 @Bean 注解
带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。最简单可行的 @Configuration 类如下所示:
1 | package com.tutorialspoint; |
上面的代码将等同于下面的 XML 配置:
1 | <beans> |
在这里,带有 @Bean 注解的方法名称作为 bean 的 ID,它创建并返回实际的 bean。你的配置类可以声明多个 @Bean。一旦定义了配置类,你就可以使用 AnnotationConfigApplicationContext 来加载并把他们提供给 Spring 容器,如下所示:
1 | public static void main(String[] args) { |
你可以加载各种配置类,如下所示:
1 | public static void main(String[] args) { |
注入 Bean 的依赖性
当 @Beans 依赖对方时,表达这种依赖性非常简单,只要有一个 bean 方法调用另一个,如下所示:
1 | package com.tutorialspoint; |
需要注意的是,在Spring的Java配置中,通过声明方法引用一个Bean并不等同于调用该方法。如果真的这样,每次调用都将得到该Bean的一个新实例。
通过@Bean注解方法,会告知Spring我们希望该方法定义的Bean要被注册进Spring的应用上下文中。因此在其他Bean的声明方法中应用这个方法时,Spring都会拦截该方法的调用,并尝试在应用上下文中查找该Bean,而不是让方法创建一个新的实例。
@Import 注解:
@import 注解允许从另一个配置类中加载 @Bean 定义。考虑 ConfigA 类,如下所示:
1 |
|
你可以在另一个 Bean 声明中导入上述 Bean 声明,如下所示:
1 |
|
现在,当实例化上下文时,不需要同时指定 ConfigA.class 和 ConfigB.class,只有 ConfigB 类需要提供,如下所示:
1 | public static void main(String[] args) { |
生命周期回调
@Bean 注解支持指定任意的初始化和销毁的回调方法,就像在 bean 元素中 Spring 的 XML 的初始化方法和销毁方法的属性:
1 | public class Foo { |
1 |
|
指定 Bean 的范围:
默认范围是单实例,但是你可以重写带有 @Scope 注解的该方法,如下所示:
1 |
|
参考Spring教程。