索引
1.基本Bean配置
1.1Bean容器
1.2Bean工厂-BeanFactory
1.3应用上下文-ApplicationContext
1.4Bean生命周期
1.5Bean的创建
1.5.1通过构造函数注入
1.5.2通过setter方法注入
1.5.3注入集合
1.5.4注入空值
1.6自动装配
1.6.1四种自动装配类型
1.6.2默认自动装配
1.7控制Bean创建
1.7.1Bean范围
1.7.2利用工厂方法创建Bean
1.7.3初始化和销毁Bean
2.高级Bean装配
2.1Bean继承
2.1.1抽象共同属性
2.2方法注入
2.2.1基本方法替换
2.2.2获取器注入(getter方法注入)
2.3向非Spring Bean注入
2.4注册自定义属性编辑器
2.5使用Spring的特殊Bean
2.5.1后处理Bean(BeanPostProcessor)
2.5.2BeanFactory的后处理(BeanFactoryProcessor)
2.5.3配置属性外在化——PropertyPlaceholderConfiguror
2.5.4国际化
2.5.5程序事件解耦
2.6让Bean知道更多
2.6.1让Bean知道自己的名称
2.6.2让Bean知道自己所在的容器
2.7脚本化Bean
1.基本Bean配置
1.1Bean容器
有两种类型的Bean容器,Bean工厂(BeanFactory)和应用上下文(ApplicationContext)。BeanFactory延迟载入所有的Bean,直到getBean()方法被调用时Bean才被创建。而ApplicationContext启动后预载入所有的单实例Bean。
1.2 Bean工厂-BeanFactory
最常使用的BeanFactory的实现是org.springframework.beans.factory.xml.XmlBeanFactory。要创建XmlBeanFactory,需要传递一个org.springframework.core.io.Resource实例给构造函数。Resource类的作用是为工厂提供xml文件。
BeanFactory实例的创建:
1.1 Resource的各种实现类
Resource实现 目的
org.springframework.core.io.ByteArray.ByteArrayResource |
由一组字节给定的资源 |
org.springframework.core.io.ClassPathResource |
从classpath提取的资源 |
org.springframework.core.io.DescriptiveResource |
定义包含资源描述符但是实际没有可读资源的资源 |
org.springframework.core.io.FileSystemResource |
从文件系统提取的资源 |
org.springframework.core.io.InputStreamResource |
从输入流提取的资源 |
org.springframework.web.portlet.context.PortletContextResource |
在portlet上下文中的资源 |
org.springframework.web.context.support.ServletContextResource |
在servlet上下文中的资源 |
org.springframework.core.io.UrlResource |
从给定URL提取的资源 |
1.3 应用上下文-ApplicationContext
ApplicationContext实现了BeanFactory接口,因此,ApplicationContext包含了BeanFactory所有的功能,并提供了其他更多的功能:
- 应用上下文提供了文本信息解析工具,包括对国际化(I18N)的支持
- 应用上下文提供了载入文件资源的通用方法,如载入图片
- 应用上下文可以向注册为监听器的Bean发送事件
ApplicationContext有三个经常使用的实现:
- ClassPathXmlApplicationContext——从类路径中的XML文件载入上下文定义信息,把上下文定义文件当成类路径资源。
- FileSystemXmlApplicationContext——从文件系统中的XML文件载入上下文定义信息
- XmlWebApplicationContext——从Web系统中的XML文件载入上下文定义信息
ApplicationContext实例的创建示例:
1.4Bean的生命周期
2.1Bean生命周期明细
步骤 |
说明 |
1.实例化 |
Spring实例化Bean |
2.设置属性 |
Spring注入Bean的属性 |
3.设置Bean名称 |
如果Bean实现了BeanNameAware接口,Spring传递Bean工厂给setBeanFactory() |
4.预处理(在初始化之前) |
如果有多个BeanPostProcessor,Spring将调用postProcessBeforeInitialization()方法 |
5.初始化Bean |
如果Bean实现InitializingBean,其afterPropertiesSet()方法将被调用。如果Bean声明了自定义的初始化方法,那么将调用指定的初始化方法 |
6.预处理(在初始化之后) |
如果有多个BeanPostProcessor,Spring将调用postProcessAfterInitialization()方法 |
7.Bean已经准备好 |
此时Bean已经准备好,可以使用,并且将一直保留在Bean工厂中,直到不再需要它 |
8.销毁Bean |
如果Bean实现了DisposableBean,将调用destroy()方法 如果Bean有自定义的销毁方法,将调用指定的方法 |
1.5 Bean的创建
1.5.1 通过构造函数注入
1.5.2 通过setter方法注入
注入内部Bean:
1.5.3 注入集合
Spring通过value属性配置基本类型,通过ref属性配置已经声明的bean,但value和ref只有在你的属性是单一的时候才有效。当属性是复数(也就是集合)时,Spring提供了4种类型的集合配置元素来配置:
集合元素 用途
<list> |
装配一列值,允许有重复值,可以与任意类型java.util.Collection或数组的属性交换 |
<set> |
装配值集,确保无重复值,可以与任意类型java.util.Collection中的属性交换 |
<map> |
装配键值对的集合,键和值可以是任意类型 |
<props> |
装配键值对的集合,键和值都是String类型 |
1.5.3.1 <list>和<set>配置示例:
1.5.3.4 <map>配置示例:
可用的键值属性
属性 目的
key |
指定map项的键为String |
key-ref |
指定map项的键为Spring上下文中Bean的引用 |
value |
指定map项的值为String |
value-ref |
指定map项的值为Spring上下文中Bean的引用 |
1.5.3.5 <props>配置示例:
1.5.4 装配空值
1.6 自动装配
在<bean>中添加autowire属性,为bean增加自动装配功能
1.6.1 四种自动装配类型
- byName——试图在容器中寻找和需要自动装配的属性名相同的Bean(或ID)。如果没有找到相符的Bean,这个属性就没有被装配上。
- byType——试图在容器中寻找一个与需要自动配置的属性类型相同的Bean。如果没有找到相符的Bean,这个属性就没有被装配。如果找到超过一个相符的Bean,会抛出org.springframework.beans.factory.UnsatisfiedDependencyException异常
- constructor——试图在容器中查找与需要自动装配的Bean的构造函数参数一致的一个或多个Bean。如果存在不确定Bean或构造函数,容器会抛出异常org.springframework.beans.factory.UnsatisfiedDependencyException异常
- autodetect——首先尝试使用constructor来自动装配,然后使用byType方式。不确定性的处理与constructor方式和byType方式一样。
自动装配示例:
1.6.2 默认自动装配
默认情况下,Bean不会被自动装配,除非你设置了autowire属性。然而,通过在Spring配置文件的根元素<beans>中设置default autowire就可以将所有的Bean设置为自动装配,如:
1.7控制Bean创建
1.7.1 Bean范围
默认时,所有Spring Bean都是单一的,意思是在整个Spring应用中,Bean的实例只有一个。可以在<bean>中添加scope属性来修改这个默认值。scope属性可用的值如下表:
范围化规则列表
范围 完成任务
singleton |
定义Bean的范围为每个Spring容器一个实例(默认值) |
prototype |
允许Bean可以被多次实例化(使用一次就创建一个实例) |
request |
定义Bean的范围是HTTP请求。只有在使用有Web能力的Spring上下文时才有效 |
session |
定义Bean的范围是HTTP会话。只有在使用有Web能力的Spring上下文时才有效 |
global-session |
定义Bean的范围是全局HTTP会话。只有在portlet上下文中有效 |
1.7.2 利用工厂方法创建Bean
<bean>元素中有一个factory-method属性,通过该属性来设置工厂方法。示例:
1.7.3 初始化和销毁Bean
通过<bean>的init-method、destroy-method属性来声明初始化和销毁Bean的方法。比如,在演奏乐器之前需要调整乐器(tuneInstrument方法),演奏完成后清理乐器(cleanInstrument方法),示例如下:
默认的初始化和销毁方法
可以在<beans>元素中添加 default-init-method、default-destroy-method来声明默认的初始化、销毁方法。示例:
另外,还可以通过实现InitializingBean和DisposableBean来初始化和销毁Bean
2 高级Bean装配
2.1 Bean的继承
<bean>元素提供了两个特殊的属性:abstract和parent来配置继承关系。
- parent:指明Bean的id。它对于<bean>的作用就相当于关键字extends对于Java类的作用。
- abstract:如果设置为true,就表示<bean>声明是抽象的,不能被Spring实例化。
示例如下:
2.1.1 抽象共同属性
子Bean不必具有相同的父类型。两个class属性值完全不同的<bean>仍然可以从一个父bean继承一组相同的属性。示例:
2.2 方法注入
Spring支持两种形式的方法注入:
- 方法替换:可以在运行时用新的实现替换现有方法(抽象或具体的)。
- 获取器注入:可以在运行时用新的实现代替现有方法(抽象或具体的),从Spring上下文中返回特定的Bean。
2.2.1 基本方法替换
下面引用魔术师和他的魔法盒的例子来阐述这个功能。
当前状态下,这位魔法师永远只能从魔法盒中变出一位美丽的助手,而不能变出一头老虎。如何让魔法师能变出老虎呢?这就需要用到Spring的基本方法替换了。
这里TigerReplacer实现了Spring的MethodReplacer接口,该接口只需要实现reimplement方法。reimplement方法包含有三个参数:要替换方法的目标对象、要被替换的方法、传递给方法的参数。下面就是通过xml配置来实现基本方法替换:
现在美丽的助手变成了凶猛的老虎了。这里的getContent()是个具体的方法,但它也可以是个抽象的方法。
2.2.2 获取器注入(getter 注入)
获取器注入实际就是setter注入的反面。现在有一个新的Instrumentalist类,它抽象了getter方法。
那么,现在就可以使用<bean>下的<lookup-method>子元素来声明抽象的getter方法返回的是哪个bean,如下
2.3 向非Spring Bean注入
当有些对象不是通过Spring实例化(像自定义的JSP标记、ORM对象等)的时候,而我们又需要向这些对象注入一些值的时候,就需要用到这方面的功能了。下面是钢琴手的配置内容:
另外,还得给Instrumentalist类级别上添加Configurable注解:
注解@Configurable的作用有两个:
- 第一,它表示Instrumentalist实例即使是在Spring之外创建的,仍然可以由Spring进行配置
- 第二,它把Instrumentalist类与id为pianist的Bean关联起来。当Spring企图配置Instumentalist实例时,会以pianist Bean作为模板。
最后,在Spring配置中添加如下内容,告诉Spring有一些Bean需要配置:
2.4 注册自定义属性编辑器
这个功能实际上是利用简单的String值设置复杂的属性。比如常见的通过一个String值来配置一个java.net.URL对象:
实际上,这个功能并不是由Spring提供的,而是来自原始JavaBeans API的一个鲜为人知的特性。Java.beans.PropertyEditor接口提供了一种手段,让我们能够自定义String如何映射到非String值。java.beans.PropertyEditorSupport是这个接口的一种简便实现方式,它有两个方法比较吸引人:
- getAsText()——返回属性值的String表达形式。
- setAsText(String value)——把传递来的String值设置给Bean的属性
Spring具有多个基于PropertyEditorSupport的自定义编辑器,比如org.springframework.beans.propertyeditors.URLEditor,它实现了Strings与java.net.URL对象之间的转化。下表列出来Spring的自定义编辑器集合:
Spring自定义编辑器,它们能自动把注入的String值转化为更复杂的类型
属性编辑器 功能
ClassEditor |
从一个String值设置java.lang.Class属性,前者包含一个完整描述的类名 |
CustomDateEditor |
从一个String值设置java.util.Date属性,前者使用自定义的java.text.DateFormat对象 |
FileEditor |
从一个String值设置java.io.File属性,前者包含文件的路径 |
LocalEditor |
从一个String值设置java.util.Locale属性,前者包含地域的文本表示(比如en_US) |
StringArrayPropertyEditor |
把逗号分隔的String转化为一个String数组属性 |
StringTrimmerEditor |
对String属性进行自动裁剪;设置一个选项后可以把空的String值转化为null |
URLEditor |
从一个String值设置java.net.URL属性,前者包含一个URL |
除了Spring提供的自定义编辑器外,我们还可以扩展PropertyEditorSupport类来编写自己的自定义编辑器。比如说要实现把一个String值转换成一个自定义的bean——PhoneNumber(电话号码):
自定义PhoneEditor:
最后,我们得让Spring在装配Bean属性时能够知道我们的自定义编辑器,为此,需要使用Spring的CustomEditorConfigurer,它是一个BeanFactoryPostProcessor,通过调用registerCustomEditor()方法把自定义编辑器加载到BeanFactory。(或者,在得到了Bean工厂的实例之后,我们可以自己在代码里调用registerCustomEditor()方法。)下面是XML的配置:
现在,我们就可以用简单的String值来配置Contact对象的phoneNumber属性。
2.5 使用Spring的特殊Bean
通过实现特定的接口,我们可以让Spring以特殊方式对待Bean——把它当作Spring框架本身的一部分。利用这些特殊的Bean,可以得到下面的好处:
- 通过对Bean配置的后处理来介入Bean的创建和Bean工厂的生命周期
- 从外部属性文件加载配置信息
- 从属性文件加载文本消息,包括国际化的消息
- 监听和响应由其他Bean和Spring容器本身发布的程序事件
- 知道它们在Spring容器里的身份
2.5.1 后处理Bean(BeanPostProcessor)
BeanPostProcessor接口为我们提供了两个机会,可以在Bean创建和装配之后来修改Bean:
下面是对BeanPostProcessor应用的一个例子:
最后,还得注册Bean后处理器。
如果程序运行在BeanFactory内,那么通过BeanFactory的addBeanPostProcessor()方法来注册每个BeanPostProcessor:
如果程序运行在ApplicationContext,那么只需要通过XML配置就可以自动注册BeanPostProcessor:
Spring自己的后处理器
Spring框架在私下使用了BeanPostProcessor的多个实现。比如ApplicationContextAwareProcessor就是个BeanPostProcessor,它把ApplicationContext设置到实现了ApplicationContextAware接口的Bean。我们不需要注册ApplicationContextAwareProcessor,程序容器本身对它进行了预注册。
2.5.2 Bean工厂的后处理(BeanFactoryPostProcessor)
BeanFactoryPostProcessor只能应用于ApplicationContext,不能用于BeanFactory。BeanFactoryPostProcessor接口定义如下:
在全部Bean定义被加载之后,但在任何一个Bean被实例化之前(包括BeanPostProcessor Bean),Spring容器会调用postProcessBeanFactory()方法。
注册BeanFactoryPostProcessor与声明一个普通的Bean一样简单:
2.5.3 配置属性的外在化——PropertyPlaceholderConfigurer
如果使用ApplicationContext作为Spring容器,属性外在化是很容易的。Spring使用PropertyPlaceholderConfigurer从外部属性文件加载特定的配置。为了启用这个特性,需要在xml配置文件上添加如下Bean:
jdbc.properties的配置信息如下:
如果需要把配置分散到多个属性文件里,应该使用PropertyPlaceholderConfigurer的locations属性来设置属性文件的List:
现在我们就可以用占位变量替代硬编码了:
2.5.4 国际化
Spring的ApplicationContext可以通过MessageSource接口把国际化消息提供给容器。Spring提供了MessageSource的一个实现——ResourceBundleMessageSource就是使用java自己的java.util.ResourceBundle来提取消息。配置如下:
Notic:这个Bean的名称必须是messageSource,因为ApplicationContext在建立其内部消息源时,会查找这个名称的Bean。我们可以通过ApplicationContext.getMessage()方法来访问消息。
在Spring的web模块中,可以使用Spring的JSP标记<spring:message>获取消息。
2.5.5 程序事件的解耦
在Spring里,容器中的任何Bean都可以作为事件监听器、事件发布者或两者都是。
2.5.5.1 发布事件
首先,定义一个自定义事件上,比如下面这个CourseFullEvent:
接着通过ApplicationContext接口的publishEvent()方法可以发布ApplicationEvents。在程序上下文里注册的任何一个ApplicationLIstener都会由它的onApplicationEvent()方法接收并处理事件:
为了发布事件,Bean需要访问ApplicationContext,这意味着Bean必须了解其运行所在的容器。
2.5.5.2 监听事件
Spring容器本身在程序运行期间也会发布一些事件,它们都是抽象类org.springframework.context.APplicationEvent的子类。下面是这种程序事件的三个范例:
- ContextCloseEvent:程序上下文被关闭时发布。
- ContextRefreshedEvent:程序上下文被初始化或刷新时发布。
- RequestHandleEvent:当一个请求被处理时,在Web程序上下文里发布。
如果想让Bean响应程序事件,无论它是由另一个Bean还是容器发布的,需要做的事情就是实现org.springframework.context.ApplicationListener接口中的onApplicationEvent()方法来响应事件。
要注册监听器,只需要在xml中配置这个Bean:
这时,当有事件发布时便会调用onApplicationEvent()方法
2.6 让Bean知道更多
2.6.1 让Bean知道自己的名称
Spring容器通过BeanNameAware接口告诉Bean它自己的名称。这个接口只有一个setBeanName()方法。它接受在Bean装配文件里id或name的属性的String类型参数。
2.6.2 让Bean知道自己所在的容器
Spring的ApplicationContextAware和BeanFactoryAware接口能够让Bean知道自己所在的容器。
2.7 脚本化的Bean
略