版本 5.2.25.RELEASE
目录
该参考文档涵盖了Spring Framework所有核心技术:
- 最重要的是Spring框架的控制反转(IoC)容器。
- 其次是Spring的面向方面编程(AOP)技术。Spring与AspectJ集成是目前 在功能方面最丰富最成熟的AOP实现。
1.IoC容器
1.1.Spring框架的IoC容器和Beans简介
IoC也称为依赖注入(DI)。对象通过以下几种方式定义其依赖关系:
- 构造函数参数
- 工厂方法的参数
- 通过上述方式返回的对象实例上设置的属性
容器在创建bean时注入这些依赖项。这个过程从根本上说是bean本身的反向(因此得名,反向控制)
Spring Framework IoC容器的基础包含:
- org.springframework.bean包
- org.springfframework.conf包
BeanFactory接口提供了一种高级配置机制,提供了配置框架和基本功能,能够管理任何类型的对象。ApplicationContext是BeanFactory的一个子接口,添加了更多特定于企业的功能,ApplicationContext是BeanFactory的一个完整超集:
- 更容易与Spring的AOP功能集成
- 消息资源处理(用于国际化)
- 事件发布
- 应用层特定的上下文,例如用于web应用程序的WebApplicationContext。
bean的定义:在Spring中,构成应用程序主干并由Spring IoC容器管理的对象,它是由Spring IoC容器实例化、组装和管理的对象。Bean之间的依赖关系反映在容器使用的配置元数据中。
1.2容器概述
org.springframework.context.ApplicationContext接口:
- 表示Spring IoC容器。
- 负责实例化、配置和组装bean。
- 通过读取配置元数据(配置元数据用XML、Java注释或Java代码表示)来获取关于实例化、配置和组装哪些对象的指令。
Spring提供了ApplicationContext接口的几个实现,如在独立应用程序中包含:
- ClassPathXmlApplicationContext
- FileSystemXmlApplicationContext
下图显示了Spring工作的视图:
1.2.1.配置元数据
如上图所示,Spring IoC容器使用一种形式的配置元数据。配置元数据的方式包含:
- 传统上,配置元数据是以简单直观的XML格式提供的
- Spring 2.5引入了对基于注解配置元数据的支持
- 基于Java的配置:从Spring 3.0开始,Spring JavaConfig项目提供的许多功能成为核心Spring框架的一部分。
Spring配置由容器必须管理的至少一个(通常不止一个)bean定义组成。
- 基于XML的配置元数据的顶层为<beans/>元素中的<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- 这个bean的合作者和配置转到这里 -->
</bean>
<bean id="..." class="...">
<!-- 这个bean的合作者和配置转到这里 -->
</bean>
<!-- 这里有更多bean定义 -->
</beans>
配置中的Bean属性说明:
- id属性是一个字符串,用于标识单个bean定义
- class属性定义bean的类型,并使用完全限定的类名。
1.2.2.实例化容器
提供给ApplicationContext构造函数的一个或多个位置路径是允许容器从各种外部资源(如本地文件系统、Java CLASSPATH等)加载配置元数据的资源文件。
Java
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
Kotlin
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
以下示例显示了服务层对象(services.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 服务 -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- 此bean的其他合作者和配置请访问此处 -->
</bean>
<!-- 有关服务的更多bean定义,请访问此处 -->
</beans>
以下示例显示了数据访问对象daos.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- 此bean的其他合作者和配置请访问此处 -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- 此bean的其他合作者和配置请访问此处-->
</bean>
<!--数据访问对象的bean定义越来越多 -->
</beans>
说明:在前面的示例中,服务层由PetStoreServiceImpl类和两个类型为JpaAccountDao和JpaItemDao的数据访问对象组成(基于JPA对象关系映射标准)。property name元素是指JavaBean属性的名称,ref元素是指另一个bean定义的名称。id和ref元素之间的这种链接表达了协作对象之间的依赖关系。
编写基于XML的配置元数据
bean定义跨越多个XML文件说明:
- 可以使用应用程序上下文构造函数从所有这些XML片段加载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>
在前面的例子中,外部bean定义是从三个文件加载的:services.xml、messageSource.xml和themeSource.xml。因采用相对路径进行文件导入,它们的路径需满足以下条件:
- services.xml与进行导入的文件位于同一目录或类路径位置
- messageSource.xml和themeSource.xml须位于导入文件位置下方的资源位置(如您所见,前导斜杠被忽略,所以最好不要使用斜线)。
可以(但不建议)使用相对“../”路径引用父目录中的文件。推荐使用完全限定的资源位置:例如,file:C:/config/services.xml或classpath:/config/services..xml。对于这样的绝对位置,通常优选保持间接性 — 例如,通过“${…}“在运行时根据JVM系统属性解析的占位符。
GroovyBean定义DSL
bean定义也可以在Spring的GroovyBean Definition DSL中表达,这样的配置存在于“.groovy”文件中。
其结构如以下示例所示:
beans {
dataSource(BasicDataSource) {
driverClassName = "org.hsqldb.jdbcDriver"
url = "jdbc:hsqldb:mem:grailsDB"
username = "sa"
password = ""
settings = [mynew:"setting"]
}
sessionFactory(SessionFactory) {
dataSource = dataSource
}
myService(MyService) {
nestedBean = { AnotherBean bean ->
dataSource = dataSource
}
}
}
这种配置风格在很大程度上等同于XMLbean定义,甚至支持Spring的XML配置名称空间。它还允许通过importBeans指令导入XMLbean定义文件
1.2.3.使用容器
ApplicationContext是高级工厂的接口,能够维护不同bean及其依赖项的注册。通过使用方法T getBean(String name,Class<T>requiredType),可以检索bean的实例。
ApplicationContext允许您读取bean定义并访问它们,如下例所示:
Java
// 创建和配置bean
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// 检索已配置的实例
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// 使用配置的实例
List<String> userList = service.getUsernameList();
Kotlin
import org.springframework.beans.factory.getBean
//创建和配置bean
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
// 检索已配置的实例
val service = context.getBean<PetStoreService>("petStore")
// 使用配置的实例
var userList = service.getUsernameList()
以下示例显示Groovy配置:
Java
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
Kotlin
val context = GenericGroovyApplicationContext("services.groovy", "daos.groovy")
最灵活的变体是GenericApplicationContext与读者委托相结合 — 例如,使用XML文件的XmlBeanDefinitionReader,如下例所示:
Java
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
Kotlin
val context = GenericApplicationContext()
XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml")
context.refresh()
您还可以将GroovyBeanDefinitionReader用于Groovy文件,如下例所示:
Java
GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
Kotlin
val context = GenericApplicationContext()
GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy")
context.refresh()
您可以在同一ApplicationContext上混合和匹配这样的读取器委托,从不同的配置源读取bean定义。
然后可以使用getBean来检索bean的实例。
ApplicationContext接口还有一些其他方法用于检索bean,但理想情况下,应用程序代码不应该使用它们。实际上,您的应用程序代码根本不应该调用getBean()方法,因此根本不依赖于SpringAPI。例如,Spring与web框架的集成为各种web框架组件(如控制器和JSF管理的bean)提供了依赖注入,允许您通过元数据(如自动装配注解)声明对特定bean的依赖。
1.3.Bean概述
Spring IoC容器管理一个或多个bean。其中Bean包含(除其他信息外)以下元数据:
- 包限定的类名:通常是要定义的bean的实际实现类。
- Bean行为配置元素,说明Bean在容器中的行为(作用域、生命周期回调等)。
- 对bean执行其工作所需的其他bean的引用。这些引用也称为合作者或依赖关系。
- 要在新创建的对象中设置的其他配置设置 — 例如,池的大小限制或在管理连接池的bean中使用的连接数。
下表介绍了Bean属性:
属性 | 在以下章节解析 |
---|---|
Class | 实例化Bean |
Name | 命名Bean |
Scope | bean的作用域 |
Constructor arguments | 依赖项注入 |
Properties | 依赖项注入 |
Autowiring mode | 自动装配器 |
Lazy initialization mode | 懒初始化bean |
Initialization method | 初始化回调 |
Destruction method | 销毁回调 |
除配置元数据方法定义bean之外,ApplicationContext实现还允许由用户注册并在容器外部创建的现有对象。
- 通过getBeanFactory()方法访问ApplicationContext的BeanFactory返回BeanFactory DefaultListableBeanFactory
- DefaultListableBeanFactory通过registerSingleton(..)和registerBeanDefinition(..)方法支持此注册。
Bean元数据和手动提供的singleton实例需要尽早注册,以便容器在自动装配和其他内部步骤期间正确地配置它们。官方不支持在运行时注册新的bean(与对工厂的实时访问同时进行),这可能会导致并发访问异常、bean容器中的状态不一致,或两者兼而有之。
1.3.1.命名beans
bean标识符说明:
- 在基于XML的配置元数据中使用id属性、name属性或两者来指定bean标识符。id属性只允许指定一个id,可以通过设置name属性指定bean的多个别名(用,、;或空格分隔)
- 在Spring3.1之前的版本中,id属性被定义为xsd:id类型,它约束了可能的字符。
- 从3.1开始,它被定义为xsd:string类型。bean id的唯一性由容器强制执行
- 如果没有显式提供名称或id,容器将为该bean生成一个唯一的名称。
- 如果想通过使用ref元素或ServiceLocator样式的查找来按名称引用该bean,则必须提供一个名称。
- 不提供名称则使用内部bean和自动装配器完成依赖注入。
- bean名称以小写字母开头,并从此处开始使用驼色大小写。
通过类路径中的组件扫描,Spring为未命名的组件生成bean名称,遵循前面描述的规则:本质上,采用简单的类名并将其初始字符改为小写。然而,在(不寻常的)特殊情况下,当有多个字符,并且第一个和第二个字符都是大写时,原始大小写会被保留。这些规则与java.beans.Interspector.decapital(Spring在这里使用)定义的规则相同。
在Bean定义之外别名Bean
在基于XML的配置元数据中,可以使用<alias/>元素来实现Bean的别名。以下示例显示了如何执行此操作:
<alias name="fromName" alias="toName"/>
例如,
- 子系统A的配置元数据可以通过子系统A DataSource的名称引用DataSource。
- 子系统B的配置元数据可以通过子系统B DataSource的名称引用DataSource。
- 在编写同时使用这两个子系统的主应用程序时,主应用程序通过myApp DataSource的名称引用DataSource。
要使所有三个名称都引用同一对象,可以将以下别名定义添加到配置元数据中:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
现在,每个组件和主应用程序都可以通过一个唯一的名称引用dataSource,并保证不会与任何其他定义冲突(有效地创建了一个命名空间),但它们引用的是同一个bean。
Java配置如果您使用Javaconfiguration,@Bean注释可以用于提供别名。有关详细信息,请参阅使用@Bean注释。
1.3.2.实例化Beans
使用基于XML的配置元数据,则在<bean/>元素的class属性中指定要实例化的对象的类型(或类)。通过以下两种方式之一使用Class属性:
- 通常,在容器本身通过反射调用其构造函数直接创建Bean的情况下,指定要构造的Bean类。
- 指定包含静态工厂方法的实际类,该方法被调用来创建对象,在不太常见的情况下,容器调用类上的静态工厂方法来创建bean。调用静态工厂方法返回的对象类型可能是同一个类,也可能完全是另一个类。
嵌套类名
为嵌套类配置bean定义使用嵌套类的二进制名称或源名称。
如com.example包中有一个名为SomeThing的类,而这个SomeThig类有一个称为Otherhing的静态嵌套类,它们可以用 $ 或 . 分隔。class属性是com.example.SomeThing$Otherhing或com.example.SemeThing.Otherhing。
使用构造函数实例化
必要条件:类需要一个默认(空)构造函数。
基于XML配置,如下所示:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
有关在构造对象后向构造函数提供参数(如果需要)和设置对象实例属性的机制的详细信息,请参阅注入依赖项。
使用静态工厂方法进行实例化
必要条件:class属性指定包含静态工厂方法的类,并使用名为factory- method的属性指定工厂方法本身的名称。
基于XML配置,如下所示:
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
以下示例显示了一个可以与前面的bean定义一起使用的类:
Java
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
Kotlin
class ClientService private constructor() {
companion object {
private val clientService = ClientService()
fun createInstance() = clientService
}
}
有关向工厂方法提供(可选)参数以及在从工厂返回对象后设置对象实例属性的机制的详细信息,请参阅详细信息中的依赖项和配置。
使用实例工厂方法进行实例化
实例工厂方法:从容器中调用现有bean的非静态方法来创建新bean。
必要条件:
- class属性保留为空
- 在factory-bean属性中指定工厂名称。
- 使用factory-method属性设置工厂方法本身的名称
以下示例显示了如何配置这样一个bean:
<!-- 工厂bean,它包含一个名为createInstance()的方法 -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- 要通过工厂bean创建的bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
以下示例显示了相应的类:
Java
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
Kotlin
class DefaultServiceLocator {
companion object {
private val clientService = ClientServiceImpl()
}
fun createClientServiceInstance(): ClientService {
return clientService
}
}
一个工厂类也可以包含多个工厂方法,如下例所示:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- 注入该定位器bean所需的任何依赖项 -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
以下示例显示了相应的类:
Java
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
Kotlin
class DefaultServiceLocator {
companion object {
private val clientService = ClientServiceImpl()
private val accountService = AccountServiceImpl()
}
fun createClientServiceInstance(): ClientService {
return clientService
}
fun createAccountServiceInstance(): AccountService {
return accountService
}
}
这种方法表明,工厂bean本身可以通过依赖注入(DI)进行管理和配置。请参阅“依赖项和配置”的详细信息。
确定Bean的运行时类型
特定bean的运行时类型是很难确定的。了解特定bean的实际运行时类型的推荐方法是对指定bean名称进行BeanFactory.getType调用。这将考虑以上所有情况,并返回BeanFactory.getBean调用将为相同的bean名称返回的对象类型。
bean元数据定义中的指定类只是一个初始类引用,可能与声明的工厂方法组合,或者是FactoryBean类,这可能会导致bean的运行时类型不同,或者在实例级工厂方法的情况下根本没有设置(而是通过指定的工厂bean名称解析)。此外,AOP代理可以使用基于接口的代理来包装bean实例,该代理对目标bean的实际类型(仅其实现的接口)具有有限的公开。