目录
核心技术
1. IoC容器
1.1. Spring IoC容器和Bean简介
1.2. 容器概述
1.2.1. 配置元数据
1.2.3. 使用容器
1.3. Bean概览
1.3.1. Bean命名
在Bean定义之外别名Bean
1.3.2. Bean的实例
Version 5.2.3.RELEASE
参考文档的这一部分涵盖了Spring框架绝对必要的所有技术。
其中最重要的是Spring框架的控制反转(IoC)容器。 对Spring框架的IoC容器进行彻底的处理之后,将全面介绍Spring的面向方面的编程(AOP)技术。 Spring框架拥有自己的AOP框架,该框架在概念上易于理解,并且成功解决了Java企业编程中AOP要求的80%的难题。
还提供了Spring与AspectJ的集成(目前,在功能上最丰富)以及Java企业领域中最成熟的AOP实现)。
本章介绍了Spring容器的控制反转(IoC)。
本章介绍了控制反转(IoC)原理的Spring框架实现。 IoC也称为依赖注入(DI)。 在此过程中,对象仅通过构造函数参数,工厂方法的参数或在构造或从工厂方法返回后在对象实例上设置的属性来定义其依赖项(即,与它们一起使用的其他对象) 。 然后,容器在创建bean时注入那些依赖项。 此过程从根本上讲是通过使用类的直接构造或诸如服务定位器模式之类的机制来控制其依赖项的实例化或位置的bean本身的逆向过程(因此称为Control Inversion)。
org.springframework.beans和org.springframework.context包是Spring Framework的IoC容器的基础。 BeanFactory接口提供了一种高级配置机制,能够管理任何类型的对象。 ApplicationContext是BeanFactory的子接口。 它增加了:
简而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext添加了更多企业特定的功能。 ApplicationContext是BeanFactory的完整超集,在本章中仅在Spring的IoC容器描述中使用。 有关使用BeanFactory而不是ApplicationContext的更多信息,请参见 BeanFactory
。
在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。 Bean是由Spring IoC容器实例化,组装和以其他方式管理的对象。 否则,bean仅仅是应用程序中许多对象之一。 Bean及其之间的依赖关系反映在容器使用的配置元数据中。
org.springframework.context.ApplicationContext接口表示Spring IoC容器,并负责实例化,配置和组装Bean。 容器通过读取配置元数据来获取有关要实例化,配置和组装哪些对象的指令。 配置元数据以XML,Java批注或Java代码表示。 它使您能够表达组成应用程序的对象以及这些对象之间的丰富相互依赖关系。
Spring提供了ApplicationContext接口的几种实现。 在独立应用程序中,通常创建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的实例。 尽管XML是定义配置元数据的传统格式,但是您可以通过提供少量XML配置来声明性地启用对这些其他元数据格式的支持,从而指示容器将Java注解或代码用作元数据格式。
在大多数应用场景中,不需要显式的用户代码来实例化一个Spring IoC容器的一个或多个实例。 例如,在Web应用程序场景中,应用程序的web.xml文件中简单的编写大约八行Web描述符XML通常就足够了(请参阅Convenient ApplicationContext Instantiation for Web Applications)。 如果使用Spring Tool Suite(基于Eclipse的开发环境),则只需单击几次鼠标或击键即可轻松创建此样板配置。
下图显示了Spring的工作原理的高级视图。 您的应用程序类与配置元数据结合在一起,以便在创建和初始化ApplicationContext之后,您将拥有一个完全配置且可执行的系统或应用程序。
图1. Spring IoC容器
如上图所示,Spring IoC容器使用一种形式的配置元数据。 此配置元数据表示您作为应用程序开发人员如何告诉Spring容器实例化,配置和组装应用程序中的对象。
传统上,配置元数据以简单直观的XML格式提供,这是本章大部分内容用来传达Spring IoC容器的关键概念和功能的内容。
基于XML的元数据不是配置元数据的唯一允许方式。 Spring IoC容器本身与实际写入此配置元数据的格式完全脱钩。 如今,许多开发人员为他们的Spring应用程序选择基于Java的配置。 |
有关在Spring容器中使用其他形式的元数据的信息,请参见:
Annotation-based configuration: Spring 2.5引入了对基于注释的配置元数据的支持。
Java-based configuration: 从Spring 3.0开始,Spring JavaConfig项目提供的许多功能已成为核心Spring Framework的一部分。 因此,您可以使用Java而不是XML文件来定义应用程序类外部的bean。 要使用这些新功能,请参见@Configuration
, @Bean
, @Import
, and @DependsOn
注解。
Spring配置由容器管理必须至少一个(通常是一个以上)bean定义组成。 基于XML的配置元数据将这些bean配置为顶级
这些bean定义对应于组成应用程序的实际对象。 通常,您定义服务层对象,数据访问对象(DAO),表示对象(例如Struts Action实例),基础结构对象(例如Hibernate SessionFactories,JMS队列)等等。 通常,不会在容器中配置细粒度的域对象,因为创建和加载域对象通常是DAO和业务逻辑的职责。 但是,您可以使用Spring与AspectJ的集成来配置在IoC容器控制之外创建的对象。See Using AspectJ to dependency-inject domain objects with Spring。
以下示例显示了基于XML的配置元数据的基本结构:
id属性是一个标识单个bean定义的字符串。
class属性定义bean的类型并使用完全限定的类名。
id属性的值是指协作对象。 在此示例中未显示用于引用协作对象的XML。 有关更多信息,请参见Dependencies 。
1.2.2. 实例化容器
提供给ApplicationContext构造函数的一个或多个位置路径是资源字符串,允许容器从各种外部资源(如本地文件系统、Java类路径等)加载配置元数据。
JAVA code
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
kotlin code
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
了解了Spring的IoC容器之后,您可能想了解更多有关Spring的Resource抽象(如参考资料所述),它提供了一种方便的机制,可以从URI语法中定义的位置读取InputStream。 尤其是,如应用程序上下文和资源路径中所述,资源路径用于构造应用程序上下文。
以下示例显示了服务层对象(services.xml)配置文件:
以下示例显示了数据访问对象daos.xml文件:
在前面的示例中,服务层由 PetStoreServiceImpl
类和两个JpaAccountDao和JpaItemDao
类型的数据访问对象组成(基于JPA对象关系映射标准)。 属性名称元素引用JavaBean属性的名称,而ref元素引用另一个bean定义的名称。 id和ref元素之间的这种联系表达了协作对象之间的依赖性。 有关配置对象的依存关系的详细信息,请参阅 Dependencies。
1.组成基于XML的配置元数据
使bean定义跨越多个XML文件可能很有用。 通常,每个单独的XML配置文件都代表体系结构中的逻辑层或模块。
您可以使用应用程序上下文构造函数从所有这些XML片段中加载bean定义。 如上一节中所示,此构造函数具有多个Resource位置。 或者,使用一个或多个
在前面的示例中,外部bean定义是从三个文件加载的:services.xml,messageSource.xml和themeSource.xml。 所有位置路径都相对于进行导入的定义文件,因此services.xml必须与进行导入的文件位于同一目录或类路径位置,而messageSource.xml和themeSource.xml必须位于该位置下方的资源位置 导入文件。 如您所见,斜杠被忽略。 但是,鉴于这些路径是相对的,最好不要使用任何斜线。 根据Spring Schema,导入的文件的内容(包括顶级
注意事项:
命名空间本身提供了导入指令功能。 Spring所提供的一系列XML名称空间(例如,上下文和util名称空间)中提供了超出普通bean定义的其他配置功能。
2.Groovy Bean定义DSL
作为外部化配置元数据的另一个示例,Bean定义也可以在Spring的Groovy Bean定义DSL中表达,这是从Grails框架中得知的。 通常,这种配置位于“ .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
}
}
}
这种配置样式在很大程度上等同于XML bean定义,甚至支持Spring的XML配置名称空间。 它还允许通过importBeans指令导入XML bean定义文件。
ApplicationContext是高级工厂的接口,该工厂类能够维护不同bean及其依赖关系的注册表。 通过使用方法T getBean(String name,Class
通过ApplicationContext,您可以读取和定义Bean定义,如以下示例所示:
java 示例代码
// 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 userList = service.getUsernameList();
kotlin示例代码
import org.springframework.beans.factory.getBean
// create and configure beans
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
// retrieve configured instance
val service = context.getBean("petStore")
// use configured instance
var userList = service.getUsernameList()
使用Groovy配置,引导看起来非常相似。 它有一个不同的上下文实现类,该类可识别Groovy(但也了解XML Bean定义)。 以下示例显示了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()方法,因此完全不依赖于Spring API。 例如,Spring与Web框架的集成为各种Web框架组件(例如控制器和JSF托管的bean)提供了依赖注入,从而使您可以通过元数据(例如自动装配注解)声明对特定bean的依赖。
Spring IoC容器管理一个或多个bean。 这些bean是使用您提供给容器的配置元数据创建的(例如,以XML
在容器本身内,这些bean定义表示为BeanDefinition对象,其中包含(除其他信息外)以下元数据:
包限定的类名:通常,定义了Bean的实际实现类。
Bean行为配置元素,用于声明Bean在容器中的行为(作用域,生命周期回调等)。
引用其他bean完成其工作所需的bean。 这些引用也称为协作者或依赖项。
要在新创建的对象中设置的其他配置设置,例如,池的大小限制或在管理连接池的bean中使用的连接数。
此元数据转换为构成每个bean定义的一组属性。 下表描述了这些属性:
属性 | 说明 |
Class | Instantiating Beans |
Name | Naming Beans |
Scope | Bean Scopes |
Constructor arguments | Dependency Injection |
Properties | Dependency Injection |
Autowiring mode | Autowiring Collaborators |
Lazy initialization mode | Lazy-initialized Beans |
Initialization method | Initialization Callbacks |
Destruction method | Destruction Callbacks |
除了包含有关如何创建特定bean的信息的bean定义之外,ApplicationContext 实现还允许注册在容器外部(由用户)创建的现有对象。 这是通过通过 getBeanFactory() 法访问ApplicationContext的BeanFactory来完成的,该方法返回BeanFactory DefaultListableBeanFactory实现。 DefaultListableBeanFactory通过 registerSingleton(..) 和 registerBeanDefinition(..) 方法支持此注册。 但是,典型的应用程序只能与通过常规bean定义元数据定义的bean一起使用。
注意 | Bean元数据和手动提供的singleton实例需要尽早注册,以便容器在自动连接和其他内省步骤中正确地解释它们。虽然在某种程度上支持现有元数据和现有的单实例,但新的bean在运行时的注册(同时对工厂的现场访问)不被正式支持,并且可能导致并发访问异常、bean容器中的不一致状态,或者两者兼而有之。 |
每个 bean 都有一个或多个标识符。这些 id 在承载 bean 的容器中必须是唯一的。一个 bean 通常只有一个 id 。但是,如果需要不止一个,则可以将额外的别名视为别名。
在基于XML的配置元数据中,可以使用 id 属性和/或 name 属性来指定bean标识符。id属性允许您只指定一个id。通常,这些名称是字母数字('myBean','someService',等等),但它们也可以包含特殊字符。如果要为bean引入其他别名,还可以在name属性中指定它们,并用逗号(,)、分号(;)或空格分隔。作为历史记录,在Spring3.1之前的版本中,id属性被定义为xsd:id类型,它约束了可能的字符。从3.1开始,它被定义为xsd:string类型。注意,bean id的唯一性仍然由容器强制,尽管不再由XML解析器强制。
不需要为bean提供name或id。如果不显式地提供name或id,容器将为该bean生成唯一的名称。但是,如果要按名称引用该bean,则必须通过使用ref元素或服务定位器样式的查找提供名称。不提供名称的动机与使用内部bean和自动连接协作器有关。
Bean Naming Conventions |
该约定是在命名bean时使用标准Java约定作为实例字段名。也就是说,bean名称以小写字母开头,并从那里开始用驼色大写。此类名称的示例包括accountManager、accountService、userDao、loginController等。 |
一致地命名bean使您的配置更易于阅读和理解。另外,如果您使用Spring AOP,那么在将建议应用于一组按名称相关的bean时,它会有很大帮助。 |
注意事项 通过类路径中的组件扫描,Spring按照前面描述的规则为未命名的组件生成bean名称:本质上,采用简单的类名并将其初始字符转换为小写。然而,在(不寻常的)特殊情况下,当有多个字符并且第一个和第二个字符都是大写时,原始大小写将被保留。这些规则与java.beans.Introspector.decapitalize(Spring在这里使用)定义的规则相同。 |
在bean定义本身中,您可以为bean提供多个名称,方法是使用id属性指定的最多一个名称和name属性中任何数量的其他名称的组合。这些名称可以等效于同一个be an的别名,在某些情况下非常有用,例如通过使用特定于组件本身的bean名称,让应用程序中的每个组件引用公共依赖项。
然而,在实际定义bean的地方指定所有别名并不总是足够的。有时需要为在别处定义的bean引入别名。在大型系统中,通常是这样,配置在每个子系统之间进行拆分,每个子系统都有自己的一组对象定义。在基于XML的配置元数据中,您可以使用
在这种情况下,在使用这个别名定义之后,名为fromName的bean(在同一个容器中)也可以被称为toName。
例如,子系统A的配置元数据可以通过子系统A数据源的名称引用数据源。子系统B的配置元数据可以通过子系统B数据源的名称引用数据源。在组合使用这两个子系统的主应用程序时,主应用程序使用myApp DataSource的名称引用数据源。要使三个名称都引用同一对象,可以将以下别名定义添加到配置元数据中:
现在,每个组件和主应用程序都可以通过一个唯一的名称引用数据源,该名称保证不会与任何其他定义冲突(有效地创建一个名称空间),但它们引用的是同一个bean。
如果使用Javaconfiguration,@Bean注释可以用来提供别名。有关详细信息,请参见使用 @Bean 注解。 |
bean定义本质上是创建一个或多个对象的配方。容器在被询问时会查看命名bean的配方,并使用该bean定义封装的配置元数据来创建(或获取)实际对象。
如果使用基于XML的配置元数据,可以指定要在
通常,指定在容器本身通过反射调用其构造函数直接创建bean的情况下要构造的bean类,这在某种程度上相当于使用new运算符的Java代码。
要指定包含用于创建对象的static
工厂方法的实际类,在不太常见的情况下,容器在类上调用static
工厂方法来创建bean。 从static
工厂方法的调用返回的对象类型可以是同一类或完全是另一类。
内部类名 如果要为静态嵌套类配置bean定义,则必须使用嵌套类的二进制名称。 例如,如果在com.example包中有一个名为SomeThing的类,而这个SomeThing类有一个名为OtherThing的静态嵌套类,那么bean定义的class属性的值将是com.example.SomeThing$OtherThing。 注意名称中使用$字符将嵌套类名与外部类名分开。 |
用构造函数实例化
当您使用构造函数方法创建bean时,所有普通类都可以由Spring使用并与之兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。简单地指定bean类就足够了。但是,根据您对特定bean使用的IoC类型,您可能需要一个默认(empty)构造函数。
Spring IoC容器几乎可以管理您要管理的任何类。 它不仅限于管理真正的JavaBean。 大多数Spring用户更喜欢实际的JavaBean,它们仅具有默认(无参数)构造函数,并具有根据容器中的属性建模的适当的setter和getter。 您还可以在容器中具有更多奇特的非bean样式类。 例如,如果您需要使用绝对不符合JavaBean规范的旧式连接池,则Spring也可以对其进行管理。
使用基于XML的配置元数据,您可以如下指定bean类:
有关向构造函数提供参数(如果需要)和在构造对象后设置对象实例属性的机制的详细信息, see Injecting Dependencies.
用静态工厂方法实例化
定义使用静态工厂方法创建的bean时,请使用class属性指定包含静态工厂方法的类,并使用名为factory-method的属性指定工厂方法本身的名称。 您应该能够调用此方法(使用可选参数,如稍后所述)并返回一个活动对象,该对象随后将被视为已通过构造函数创建。 这种bean定义的一种用法是在旧版代码中调用静态工厂。
以下bean定义指定通过调用工厂方法创建bean。定义没有指定返回对象的类型(class),只指定包含工厂方法的类。在本例中,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
}
}
有关为工厂方法提供(可选)参数以及在对象从工厂返回后设置对象实例属性的机制的详细信息,请参阅 Dependencies and Configuration in Detail。
使用实例工厂方法实例化
类似于通过静态工厂方法进行实例化,使用实例工厂方法进行实例化会从容器中调用现有bean的非静态方法来创建新bean。 要使用此机制,请将class属性保留为空,并在factory-bean属性中,在当前(或父或祖先)容器中指定包含要创建该对象的实例方法的bean的名称。 使用factory-method属性设置工厂方法本身的名称。 以下示例显示了如何配置此类Bean:
以下示例显示了相应的类:
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
}
}
一个工厂类也可以包含一个以上的工厂方法,如以下示例所示:
以下示例显示了相应的类:
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)进行管理和配置。See Dependencies and Configuration in Detail.
在Spring文档中,“ factory bean”是指在Spring容器中配置并通过实例或静态工厂方法创建对象的bean。 相比之下,FactoryBean(注意大小写)是指特定于Spring的FactoryBean。