文章作者:Tyan
博客:noahsnail.com
Part I. Overview of Spring Framework
The Spring Framework is a lightweight solution and a potential one-stop-shop for building your enterprise-ready applications. However, Spring is modular, allowing you to use only those parts that you need, without having to bring in the rest. You can use the IoC container, with any web framework on top, but you can also use only theHibernate integration code or the JDBC abstraction layer. The Spring Framework supports declarative transaction management, remote access to your logic through RMI or web services, and various options for persisting your data. It offers a full-featured MVC framework, and enables you to integrate AOP transparently into your software.
Spring框架是一个轻量级的解决方案,对于构建一个企业级应用来说,Spring框架也是一种可能的一站式服务。Spring是模块化的,允许你仅使用你需要的那部分功能,而不必引入其它的部分。你可以在任何web框架上使用IoC容器,也可以只使用Hibernate集成代码或JDBC抽象层。Spring框架支持声明式的业务管理,支持通过RMI或web service来远程访问你的逻辑,并且支持多种持久化数据的方式。Spring提供了一个包含所有功能的MVC框架,使你能将AOP透明的集成到软件中。
Spring is designed to be non-intrusive, meaning that your domain logic code generally has no dependencies on the framework itself. In your integration layer (such as the data access layer), some dependencies on the data access technology and the Spring libraries will exist. However, it should be easy to isolate these dependencies from the rest of your code base.
Spring被设计为非侵入式的,这意味着你自己的逻辑代码通常情况下不依赖于框架本身。在你的集成层(例如数据访问层),将会存在一些数据访问技术的依赖和Spring的库。不管怎样,从你其余的代码中分离这些依赖应该是很容易的。
This document is a reference guide to Spring Framework features. If you have any requests, comments, or questions on this document, please post them on the user mailing list. Questions on the Framework itself should be asked on StackOverflow (see https://spring.io/questions).
这篇文档是Spring框架功能的参考手册。如果你有任何关于这篇文档的要求、评论或问题,请向用户邮寄列表中的人发邮件。关于框架本身的问题可以在StackOverflow上提问。
1.Getting Started with Spring
This reference guide provides detailed information about the Spring Framework. It provides comprehensive documentation for all features, as well as some background about the underlying concepts (such as “Dependency Injection”) that Spring has embraced.
这本参考手册提供了关于Spring框架的详细信息,它提供了关于所有功能的全面文档,也介绍了Spring中的基本概念(例如依赖注入)的一些背景。
If you are just getting started with Spring, you may want to begin using the Spring Framework by creating a Spring Boot based application. Spring Boot provides a quick (and opinionated) way to create a production-ready Spring based application. It is based on the Spring Framework, favors convention over configuration, and is designed to get you up and running as quickly as possible.
如果你刚开始学习Spring,你可能想创建一个基于Spring Boot的应用,Spring Boot提供了一个快速(和武断的)方式来创建一个用于生产环境的基于Spring的应用。它是基于Spring框架的,支持约定大于配置,被设计为可以快速启动并且尽可能快的运行起来。
You can use start.spring.io to generate a basic project or follow one of the “Getting Started” guides like the Getting Started Building a RESTful Web Service one. As well as being easier to digest, these guides are very task focused, and most of them are based on Spring Boot. They also cover other projects from the Spring portfolio that you might want to consider when solving a particular problem.
你可以用start.spring.io 来生产一个基本的工程或遵循『Getting Started』指南中的一个,例如『Started Building a RESTful Web Service』指南。除了容易理解吸收之外,这些指南主要是基于任务的,它们中的大多数是基于Spring Boot的。它们也包含了Spring的其它工程,当解决一个特定问题时你可能会考虑它们。
2.Introduction to the Spring Framework
The Spring Framework is a Java platform that provides comprehensive infrastructure support for developing Java applications. Spring handles the infrastructure so you can focus on your application.
Spring框架是一个为支持开发Java应用提供全面基础架构的Java平台。Spring处理基础架构,因此你可以集中精力在你有应用上。
Spring enables you to build applications from “plain old Java objects” (POJOs) and to apply enterprise services non-invasively to POJOs. This capability applies to the Java SE programming model and to full and partial Java EE.
Spring使你能创建普通Java对象(POJO)并能非侵入式的将企业服务应用到普通Java对象(POJO)上。
Examples of how you, as an application developer, can benefit from the Spring platform:
- Make a Java method execute in a database transaction without having to deal with transaction APIs.
- Make a local Java method a remote procedure without having to deal with remote APIs.
- Make a local Java method a management operation without having to deal with JMX APIs.
- Make a local Java method a message handler without having to deal with JMS APIs.
作为一个应用开发者,下面是一些你能从Spring平台受益的例子:
- 在一个数据库业务中执行一个Java方法而不必处理业务APIs
- 使一个本地的Java方法可以远程调用而不必处理远程APIs
- 使一个本地Java方法变为管理操作而不必处理JMX APIs
- 使一个本地Java方法变为消息处理器而不必处理JMS APIs
2.1 Dependency Injection and Inversion of Control
A Java application — a loose term that runs the gamut from constrained, embedded applications to n-tier, server-side enterprise applications — typically consists of objects that collaborate to form the application proper. Thus the objects in an application have dependencies on each other.
Java应用——一个不精确的术语,既可以表示受限制的嵌入式应用又可以表示N层服务端的企业级应用——通常由许多对象构成,这些对象协作形成完整的应用程序。因此一个应用程序中的对象是相互依赖的。
Although the Java platform provides a wealth of application development functionality, it lacks the means to organize the basic building blocks into a coherent whole, leaving that task to architects and developers. Although you can use design patterns such as Factory, Abstract Factory, Builder, Decorator, and Service Locator to compose the various classes and object instances that make up an application, these patterns are simply that: best practices given a name, with a description of what the pattern does, where to apply it, the problems it addresses, and so forth. Patterns are formalized best practices that you must implement yourself in your application.
尽管Java平台提供了大量的应用开发功能,但是它缺少把这些基本构建模块组织成一个连贯整体的方法,并把组织基本构建模块的任务留给了架构师和开发者。虽然你可以使用设计模式例如工厂模式、抽象工厂模式、生成器模式、装饰模式、服务定位模式来创建构成应用的各种类和对象实例,但这些设计模式很简单:命名的最佳方法、模式的作用描述、应用模式的位置、模式解决的问题等等。模式使最佳实践形式化了,这意味着你必须在你的应用中自己实现它。
The Spring Framework Inversion of Control (IoC) component addresses this concern by providing a formalized means of composing disparate components into a fully working application ready for use. The Spring Framework codifies formalized design patterns as first-class objects that you can integrate into your own application(s). Numerous organizations and institutions use the Spring Framework in this manner to engineer robust, maintainable applications.
Spring框架中的控制反转(IoC)组件通过提供一种形式化方法解决了这个问题,这个形式化方法将不同的组件创建到一个随时可用的完整的工作应用中。Spring框架将形式化的设计模式编码成了你可以集成到你自己的应用中的最好对象。许多组织和机构用这种方式应用Spring框架来构建鲁棒的、可维护的应用。
Background
“The question is, what aspect of control are [they] inverting?” Martin Fowler posed this question about Inversion of Control (IoC) on his site in 2004. Fowler suggested renaming the principle to make it more self-explanatory and came up with Dependency Injection.
背景
“问题是什么是控制反转?” 2004年Martin Fowler在他的网站上提出了这个关于控制反转(IoC)问题。Fowler建议重新命名这个原理使它更一目了然并且提出了依赖注入。
2.2 Modules
The Spring Framework consists of features organized into about 20 modules. These modules are grouped into Core Container, Data Access/Integration, Web, AOP (Aspect Oriented Programming), Instrumentation, Messaging, and Test, as shown in the following diagram.
Spring框架包含的功能大约由20个模块组成。这些模块按组可分为核心容器、数据访问/集成,Web,AOP(面向切面编程)、设备、消息和测试,如下图所示。
Figure 2.1. Overview of the Spring Framework
The following sections list the available modules for each feature along with their artifact names and the topics they cover. Artifact names correlate to artifact IDs used in Dependency Management tools.
接下来的章节列出了每个功能可用的模块、它们的工件名字以及它们包含的主题。工件名字与依赖管理工具中使用的artifact IDs有关。
2.2.1 Core Container
The Core Container consists of the spring-core
, spring-beans
, spring-context
, spring-context-support
, and spring-expression
(Spring Expression Language) modules.
核心容器功能包括spring-core
, spring-beans
, spring-context
, spring-context-support
, and spring-expression
(Spring表现语言)模块。
The spring-core
and spring-beans
modules provide the fundamental parts of the framework, including the IoC and Dependency Injection features. The BeanFactory
is a sophisticated implementation of the factory pattern. It removes the need for programmatic singletons and allows you to decouple the configuration and specification of dependencies from your actual program logic.
spring-core
和spring-beans
模块提供了框架的基础结构部分,包含控制反转(IoC)和依赖注入(DI)功能。BeanFactory
是工厂模式的高级实现。它去掉了程序单例模式的需求并且允许你从实际的程序逻辑中解耦配置和依赖关系。
The Context (spring-context
) module builds on the solid base provided by the Core and Beans modules: it is a means to access objects in a framework-style manner that is similar to a JNDI registry. The Context module inherits its features from the Beans module and adds support for internationalization (using, for example, resource bundles), event propagation, resource loading, and the transparent creation of contexts by, for example, a Servlet container. The Context module also supports Java EE features such as EJB, JMX, and basic remoting. The ApplicationContext
interface is the focal point of the Context module.spring-context-support
provides support for integrating common third-party libraries into a Spring application context, in particular for caching (EhCache, JCache) and scheduling (CommonJ, Quartz).
上下文(spring-context
)模块建立在由Core模块和Beans模块提供的坚实基础上:它是在类似于JNDI注册表式的框架风格模式中访问对象的一种方法。上下文模块继承了Beans模块的功能,并添加了对国际化(例如使用资源捆绑)、事件传播、资源加载和上下文透明创建(例如通过Servlet容器)的支持。上下文模块也支持Java EE功能例如EJB,JMX和基本的远程。ApplicationContext
接口是上下文模块的焦点。spring-context-support
支持将第三方库集成进Spring应用程序上下文中,特别是缓存(EhCache, JCache)和定时执行(CommonJ, Quartz)。
The spring-expression
module provides a powerful Expression Language for querying and manipulating an object graph at runtime. It is an extension of the unified expression language (unified EL) as specified in the JSP 2.1 specification. The language supports setting and getting property values, property assignment, method invocation, accessing the content of arrays, collections and indexers, logical and arithmetic operators, named variables, and retrieval of objects by name from Spring’s IoC container. It also supports list projection and selection as well as common list aggregations.
spring-expression
模块提供了强大的表达式语言用来在运行时查询和操作对象图。它是JSP 2.1规范中统一表达式语言(unified EL)的扩展。这个语言支持setting和getting属性值,属性分配,方法调用,访问数组、集合和索引器的内容,逻辑和算术操作,变量命名,从Spring Ioc容器中通过名字检索对象。它也支持它还支持列表投影、选择以及常见的列表聚合。
2.2.2 AOP and Instrumentation
The spring-aop
module provides an AOP Alliance-compliant aspect-oriented programming implementation allowing you to define, for example, method interceptors and pointcuts to cleanly decouple code that implements functionality that should be separated. Using source-level metadata functionality, you can also incorporate behavioral information into your code, in a manner similar to that of .NET attributes.
spring-aop
模块提供了AOP Alliance-compliant(AOP联盟)面向切面编程的实现,例如允许你自定义方法拦截器和切入点来清晰的解耦功能实现上应该分开的代码。使用源码级的元数据功能,你也可以将行为信息合并到你的代码中,在某种程度上这类似于.NET的属性值。
The separate spring-aspects
module provides integration with AspectJ.
独立的spring-aspects
模块提供了与AspectJ的集成。
The spring-instrument
module provides class instrumentation support and classloader implementations to be used in certain application servers. The spring-instrument-tomcat
module contains Spring’s instrumentation agent for Tomcat.
spring-instrument
模块提供了类设备支持和类加载器的实现,它们可以在某些应用服务器中使用。spring-instrument-tomcat
模块包含了Tomcat的Spring设备代理。
2.2.3 Messaging
Spring Framework 4 includes a spring-messaging
module with key abstractions from the Spring Integration project such as Message
, MessageChannel
, MessageHandler
, and others to serve as a foundation for messaging-based applications. The module also includes a set of annotations for mapping messages to methods, similar to the Spring MVC annotation based programming model.
Spring 4框架中包含了spring-messaging
模块,它对Spring集成项目例如Message
, MessageChannel
, MessageHandler
和其它作为消息应用服务基础的项目进行了重要的抽象。这个模块也包含了一系列将消息映射到方法上的注解,这个注解与基于编程模型Spring MVC注解类似。
2.2.4 Data Access/Integration
The Data Access/Integration layer consists of the JDBC, ORM, OXM, JMS, and Transaction modules.
数据访问/集成层包括JDBC,ORM,OXM,JMS和业务模块。
The spring-jdbc
module provides a JDBC-abstraction layer that removes the need to do tedious JDBC coding and parsing of database-vendor specific error codes.
spring-jdbc
模块提供了JDBC抽象层,不需要再编写单调的JDBC代码,解析数据库提供商指定的错误编码。
The spring-tx
module supports programmatic and declarative transaction management for classes that implement special interfaces and for all your POJOs (Plain Old Java Objects).
spring-tx
模块为实现指定接口和所有的普通Java对象(POJOs)的类提供编程式(programmatic)和声明式(declarative)的业务管理。
The spring-orm
module provides integration layers for popular object-relational mapping APIs, including JPA and Hibernate. Using the spring-orm
module you can use these O/R-mapping frameworks in combination with all of the other features Spring offers, such as the simple declarative transaction management feature mentioned previously.
spring-orm
模块提供流行的对象关系映射APIs的集成层,包括JPA和Hibernate。在使用spring-orm
模块时,你可以将Spring的其它功能与这些O/R-mapping框架结合起来使用,例如前面提到的简单声明式业务管理的功能。
The spring-oxm
module provides an abstraction layer that supports Object/XML mapping implementations such as JAXB, Castor, JiBX and XStream.
spring-oxm
模块提供对Object/XML映射实现例如JAXB,Castor,JiBx和XStream的抽象层。
The spring-jms
module (Java Messaging Service) contains features for producing and consuming messages. Since Spring Framework 4.1, it provides integration with the spring-messaging
module.
spring-jms
模块(Java消息服务)包含产生和处理消息的功能。从Spring 4.1框架开始它提供了与spring-messaging
的集成。
2.2.5 Web
The Web layer consists of the spring-web
, spring-webmvc
and spring-websocket
modules.
网络层包含spring-web
, spring-webmvc
和spring-websocket
模块。
The spring-web
module provides basic web-oriented integration features such as multipart file upload functionality and the initialization of the IoC container using Servlet listeners and a web-oriented application context. It also contains an HTTP client and the web-related parts of Spring’s remoting support.
spring-web
模块提供基本的面向网络集成功能,例如multipart文件上传功能,使用Servlet监听器来初始化Ioc容器和面向网络的应用程序上下文。它也包含了HTTP客户端和Spring远程支持中网络相关的部分。
The spring-webmvc
module (also known as the Web-Servlet module) contains Spring’s model-view-controller (MVC) and REST Web Services implementation for web applications. Spring’s MVC framework provides a clean separation between domain model code and web forms and integrates with all of the other features of the Spring Framework.
spring-webmvc
模块(也被称为Web-Servlet模块)包含了Spring的model-view-controller(MVC)和REST Web Services的网络应用实现。Spring的MVC框架提供了对领域模型代码,web表单,Spring框架其他功能的完全分离。
2.2.6 Test
The spring-test
module supports the unit testing and integration testing of Spring components with JUnit or TestNG. It provides consistent loading of Spring ApplicationContexts
and caching of those contexts. It also provides mock objects that you can use to test your code in isolation.
spring-test
模块支持单元测试,Spring组件和JUnit或TestNG的集成测试。它提供了Spring的ApplicationContexts
加载和这些上下文缓存的一致。它也提供了可以单独测试代码的模拟对象。
2.3 Usage scenarios
The building blocks described previously make Spring a logical choice in many scenarios, from embedded applications that run on resource-constrained devices to full-fledged enterprise applications that use Spring’s transaction management functionality and web framework integration.
前面描述的搭积木方式使Spring在许多场景中都有一个合理选择,从运行在资源受限的嵌入式应用到全面成熟的企业级应用都在使用Spring的业务管理功能和网络框架集成。
Figure 2.2. Typical full-fledged Spring web application
Spring’s declarative transaction management features make the web application fully transactional, just as it would be if you used EJB container-managed transactions. All your custom business logic can be implemented with simple POJOs and managed by Spring’s IoC container. Additional services include support for sending email and validation that is independent of the web layer, which lets you choose where to execute validation rules. Spring’s ORM support is integrated with JPA and Hibernate; for example, when using Hibernate, you can continue to use your existing mapping files and standard Hibernate SessionFactory
configuration. Form controllers seamlessly integrate the web-layer with the domain model, removing the need for ActionForms
or other classes that transform HTTP parameters to values for your domain model.
Spring的声明式业务管理功能使web应用全面的业务化,如果你用过EJB容器管理业务的话你会发现它们基本一样。你所有自定义的业务逻辑都可以用POJOs实现并通过Spring的IoC容器管理。附加业务包括支持邮件发送和验证,这个是独立于web层之外的,你可以自由选择验证规则执行的位置。Spring对ORM的支持与JPA和Hibernate进行了集成;例如,当你使用Hibernate时,你可以继续使用你现有的映射文件和标准的Hibernate SessionFactory
配置。表单控制器被无缝的将web层和领域模型进行了集成,对于你的领域模型来讲不再需要ActionForms
或其它的将HTTP参数转换成值的
Figure 2.3. Spring middle-tier using a third-party web framework
Sometimes circumstances do not allow you to completely switch to a different framework. The Spring Framework does not force you to use everything within it; it is not an all-or-nothing solution. Existing front-ends built with Struts, Tapestry, JSF or other UI frameworks can be integrated with a Spring-based middle-tier, which allows you to use Spring transaction features. You simply need to wire up your business logic using an ApplicationContext
and use a WebApplicationContext
to integrate your web layer.
有时候环境不允许你完全转成一个不同的框架。Spring框架不强迫你都采用它内部的东西;它不是一个要么全有要么全无的解决方案。现有的采用Struts,Tapestry,JSF或其它UI框架构建的前端可以与基于Spring的中间层进行集成,这可以让你使用Spring的业务功能。你只需要简单的用ApplicationContext
和WebApplicationContext
绑定你的业务逻辑然后集成到web层即可。
Figure 2.4. Remoting usage scenario
When you need to access existing code through web services, you can use Spring’s Hessian-
, Rmi-
or HttpInvokerProxyFactoryBean
classes. Enabling remote access to existing applications is not difficult.
当你需要通过web服务访问现有代码时,你可以使用Spring的Hessian-
, Rmi-
或 HttpInvokerProxyFactoryBean
类。这能让远程访问现有应用变得很容易。
Figure 2.5. EJBs - Wrapping existing POJOs
The Spring Framework also provides an access and abstraction layer for Enterprise JavaBeans, enabling you to reuse your existing POJOs and wrap them in stateless session beans for use in scalable, fail-safe web applications that might need declarative security.
Spring框架也为企业JavaBeans提供了访问和抽象层,使你能重用你现有的POJOs,为了可扩展使用可以将它们包装成无状态的session beans,自动防故障的web应用可能需要声明安全。
2.3.1 Dependency Management and Naming Conventions
Dependency management and dependency injection are different things. To get those nice features of Spring into your application (like dependency injection) you need to assemble all the libraries needed (jar files) and get them onto your classpath at runtime, and possibly at compile time. These dependencies are not virtual components that are injected, but physical resources in a file system (typically). The process of dependency management involves locating those resources, storing them and adding them to classpaths. Dependencies can be direct (e.g. my application depends on Spring at runtime), or indirect (e.g. my application depends on commons-dbcp
which depends on commons-pool
). The indirect dependencies are also known as “transitive” and it is those dependencies that are hardest to identify and manage.
依赖管理和依赖注入是完全不同的两件事。为了能你的应用中使用Spring的优秀特性(像依赖注入),你需要收集所有必要的库(jar文件)并在运行时将它们添加到classpath中,有可能在编译时就需要添加。这些依赖不是要被注入的虚拟组建,而是文件系统中的物理资源(通常情况下)。这些依赖管理的过程包括资源的定位、存储和添加到classpath中。依赖可以是直接的(例如:我的应用在运行时依赖Spring),或间接的(例如:我的应用依赖commons-dbcp
,而它依赖commons-pool
)。间接依赖也被称为”传递式”的,这些依赖也是最难识别和管理的。
If you are going to use Spring you need to get a copy of the jar libraries that comprise the pieces of Spring that you need. To make this easier Spring is packaged as a set of modules that separate the dependencies as much as possible, so for example if you don’t want to write a web application you don’t need the spring-web modules. To refer to Spring library modules in this guide we use a shorthand naming convention spring-*
or spring-*.jar
, where *
represents the short name for the module (e.g. spring-core
, spring-webmvc
, spring-jms
, etc.). The actual jar file name that you use is normally the module name concatenated with the version number (e.g. spring-core-5.0.0.BUILD-SNAPSHOT.jar
).
如果你想使用Spring,你需要有包含你需要的Spirng功能的jar库副本。为了使这个更容易,Spring被打包成了一系列尽可能将依赖分离开的模块,例如你不想写web应用那你就不需要spring-web模块。为了在本指南中谈及Spring的库模块,我们使用了一个简写命名约定spring-*
或spring-*.jar
,*
表示模块的简写名字(例如spring-core
, spring-webmvc
, spring-jms
等等)。实际中你使用的jar文件名字通常是模块名加上版本号(例如spring-core-5.0.0.BUILD-SNAPSHOT.jar
)。
Each release of the Spring Framework will publish artifacts to the following places:
- Maven Central, which is the default repository that Maven queries, and does not require any special configuration to use. Many of the common libraries that Spring depends on also are available from Maven Central and a large section of the Spring community uses Maven for dependency management, so this is convenient for them. The names of the jars here are in the form
spring-*-<version>.jar
and the Maven groupId isorg.springframework
. - In a public Maven repository hosted specifically for Spring. In addition to the final GA releases, this repository also hosts development snapshots and milestones. The jar file names are in the same form as Maven Central, so this is a useful place to get development versions of Spring to use with other libraries deployed in Maven Central. This repository also contains a bundle distribution zip file that contains all Spring jars bundled together for easy download.
So the first thing you need to decide is how to manage your dependencies: we generally recommend the use of an automated system like Maven, Gradle or Ivy, but you can also do it manually by downloading all the jars yourself.
Spring框架的每次发布都会下面的地方公布artifacts:
- Maven Central,Maven查询的默认仓库,使用时不需要任何特定的配置。Spring依赖的许多共通库也可以从Maven Central获得,Spring社区的很大一部分都在使用Maven进行依赖管理,因此这对他们来说是很方便的。jar包的命名形式是
spring-*-<version>.jar
,Maven GroupId是org.springframework
。 - 由Spring掌管的公开Maven库。除了最终的GA release(公开可获得的版本)之外,这个仓库也有开发版本的快照和milestone版本。jar包的命名形式和Maven Central一样,这是一个可以使用Spring开发版本有用地方,而其它的库部署在Maven Central。这个库也包含的捆绑分布的zip文件,这个zip文件中所有的Spring jar包被捆绑到一起很容易下载。
You will find bellow the list of Spring artifacts. For a more complete description of each modules, see Section 2.2, “Modules”.
你将在下面找到Spring artifacts列表。想要每个模块更全面的描述,请看2.2小节。
Table 2.1. Spring Framework Artifacts
Spring Dependencies and Depending on Spring
Although Spring provides integration and support for a huge range of enterprise and other external tools, it intentionally keeps its mandatory dependencies to an absolute minimum: you shouldn’t have to locate and download (even automatically) a large number of jar libraries in order to use Spring for simple use cases. For basic dependency injection there is only one mandatory external dependency, and that is for logging (see below for a more detailed description of logging options).
虽然Spring提供集成并支持大范围内的企业和其它外部工具,但它有意使它的强制性依赖到一个绝对最小化的程度:对于简单的用例你不应该为了使用Spring而定位和下载(即使是自动的)许多jar库。对于基本的依赖注入仅有一个强制性的外部依赖,那个依赖是关于日志的(在下面可以看到日志选择更详细的描述)。
Next we outline the basic steps needed to configure an application that depends on Spring, first with Maven and then with Gradle and finally using Ivy. In all cases, if anything is unclear, refer to the documentation of your dependency management system, or look at some sample code - Spring itself uses Gradle to manage dependencies when it is building, and our samples mostly use Gradle or Maven.
接下来我们概述配置一个依赖于Spring的应用需要的基本步骤,首先Maven的,其次是Gradle的,最后是Ivy的。在所有的案例中,如果有任何不清楚的地方,请参考你的依赖管理系统的文档,或者看一些示例代码——Spring本身构建时使用Gradle来管理依赖,我们例子中大多数是使用Gradle和Maven的。
Maven Dependency Management
If you are using Maven for dependency management you don’t even need to supply the logging dependency explicitly. For example, to create an application context and use dependency injection to configure an application, your Maven dependencies will look like this:
如果你正在使用Maven来进行依赖管理,那你不必显式的提供日志依赖。例如,为了创建一个应用上下文,使用依赖注入来配置一个应用,你的Maven依赖看上去是这样的:
1 | <dependencies> |
That’s it. Note the scope can be declared as runtime if you don’t need to compile against Spring APIs, which is typically the case for basic dependency injection use cases.
就是它。注意如果你不需要编译Spring APIs,scope可以被声明成rumtime,这是典型的基本依赖注入的情况。
The example above works with the Maven Central repository. To use the Spring Maven repository (e.g. for milestones or developer snapshots), you need to specify the repository location in your Maven configuration. For full releases:
上面的例子是采用Maven中心仓库的。为了使用Spring Maven仓库(例如:使用milestone版本或snapshot版本),你需要在Maven配置中指定仓库的位置,完整的版本:
1 | <repositories> |
For milestones:
对于milestone版本:
1 | <repositories> |
And for snapshots:
对于snapshot版本:
1 | <repositories> |
Maven “Bill Of Materials” Dependency
It is possible to accidentally mix different versions of Spring JARs when using Maven. For example, you may find that a third-party library, or another Spring project, pulls in a transitive dependency to an older release. If you forget to explicitly declare a direct dependency yourself, all sorts of unexpected issues can arise.
在使用Maven时,有可能会偶然的将不同版本的Spring JARs混合起来。例如,你可能找到一个第三方库,或另一个Spring项目,通过传递依赖进入了一个更旧的版本。如果你忘了自己显式的声明一个直接依赖,会产生各种意想不到的问题。
To overcome such problems Maven supports the concept of a “bill of materials” (BOM) dependency. You can import the spring-framework-bom
in your dependencyManagement
section to ensure that all spring dependencies (both direct and transitive) are at the same version.
为了解决这种问题,Maven支持”材料清单”(BOM)依赖的概念。你可以在你的dependencyManagement
部分导入spring-framework-bom
来确保所有的Spring依赖(直接和传递的)都是同一个版本。
1 | <dependencyManagement> |
An added benefit of using the BOM is that you no longer need to specify the <version>
attribute when depending on Spring Framework artifacts:
使用BOM的额外好处是当依赖Spring框架的artifacts时你不再需要指定<version>
属性:
1 | <dependencies> |
Gradle Dependency Management
To use the Spring repository with the Gradle build system, include the appropriate URL in the repositories
section:
为了在Gradle构建系统中使用Spring仓库,在repositories
部分需要包含合适的URL:
1 | repositories { |
You can change the repositories
URL from /release
to /milestone
or /snapshot
as appropriate. Once a repository has been configured, you can declare dependencies in the usual Gradle way:
当合适的时候你可以修改repositories
的URL从/release
到/milestone
或/snapshot
。一旦一个仓库被配置了,你可以用通常的Gradle方式声明依赖:
1 | dependencies { |
Ivy Dependency Management
If you prefer to use Ivy to manage dependencies then there are similar configuration options.
如果你更喜欢使用Ivy来管理依赖,这有类似的配置选择。
To configure Ivy to point to the Spring repository add the following resolver to your ivysettings.xml
:
为了配置Ivy指定Spring仓库,添加下面的解析器到你的ivysettings.xml
:
1 | <resolvers> |
You can change the root URL from /release/
to /milestone/
or /snapshot/
as appropriate.
当合适的时候你可以更改根URL从repositories
的URL从/release
到/milestone
或/snapshot
。
Once configured, you can add dependencies in the usual way. For example (in ivy.xml
):
一旦配置了,你可以通过一般的方式添加依赖。例如(在ivy.xml
):
1 | <dependency org="org.springframework" |
Distribution Zip Files
Although using a build system that supports dependency management is the recommended way to obtain the Spring Framework, it is still possible to download a distribution zip file.
尽管使用一个支持依赖管理的构建系统是获得Spring框架的推荐方式,但仍然可以下载发行版的Zip文件。
Distribution zips are published to the Spring Maven Repository (this is just for our convenience, you don’t need Maven or any other build system in order to download them).
发行版的zips是被发布到Spring Maven仓库(这只是为了我们的方便,为了下载它们你不需要Maven或任何其它的构建系统)。
To download a distribution zip open a web browser to http://repo.spring.io/release/org/springframework/spring and select the appropriate subfolder for the version that you want. Distribution files end -dist.zip
, for example spring-framework-{spring-version}-RELEASE-dist.zip. Distributions are also published for milestones and snapshots.
为了下载发行版zip,打开浏览器输入http://repo.spring.io/release/org/springframework/spring,然后选择你想要的版本的合适子文件夹。发行版文件以-dist.zip
结尾,例如spring-framework-{spring-version}-RELEASE-dist.zip。发行版也可以公布milestone版本或snapshots版本。
2.3.2 Logging
Logging is a very important dependency for Spring because a) it is the only mandatory external dependency, b) everyone likes to see some output from the tools they are using, and c) Spring integrates with lots of other tools all of which have also made a choice of logging dependency. One of the goals of an application developer is often to have unified logging configured in a central place for the whole application, including all external components. This is more difficult than it might have been since there are so many choices of logging framework.
日志对于Spring来说是一个非常重要的依赖,因为:a)它是唯一的强制性外部依赖,b)每个人都喜欢从他们使用的工具中看到一些输出,c)Spring集成了许多其它的工具,这些工具也选择了日志依赖。应用开发者的一个目标就是对于整个应用来讲,经常要有一个中心地方来进行日志的统一配置,包括所有的外部组件。比它更困难的可能是有太多的日志框架去选择。
The mandatory logging dependency in Spring is the Jakarta Commons Logging API (JCL). We compile against JCL and we also make JCL Log
objects visible for classes that extend the Spring Framework. It’s important to users that all versions of Spring use the same logging library: migration is easy because backwards compatibility is preserved even with applications that extend Spring. The way we do this is to make one of the modules in Spring depend explicitly on commons-logging
(the canonical implementation of JCL), and then make all the other modules depend on that at compile time. If you are using Maven for example, and wondering where you picked up the dependency on commons-logging
, then it is from Spring and specifically from the central module called spring-core
.
Spring中的强制日志依赖是Jakarta Commons Logging API (JCL)。我们编译JCL并使JCLlog
对象对类是可见的,这扩展了Spring框架。所有版本的Spring采用同一个日志库:移植是容易的,因为即使应用扩展了Spring但保留了向后兼容性,这一点对用户来说很重要。我们实现这个的方式是让Spring的模块之一显式的依赖commons-logging
(JCL的标准实现),然后使其它模块在编译时依赖这个模块。例如如果你在使用Maven,想找出依赖于commons-logging
的依赖在哪,它在Spring中,更确切的说它是在Spring的中心模块spring-core
中。
The nice thing about commons-logging
is that you don’t need anything else to make your application work. It has a runtime discovery algorithm that looks for other logging frameworks in well known places on the classpath and uses one that it thinks is appropriate (or you can tell it which one if you need to). If nothing else is available you get pretty nice looking logs just from the JDK (java.util.logging or JUL for short). You should find that your Spring application works and logs happily to the console out of the box in most situations, and that’s important.
关于commons-logging
的一件好事是要使你的应用工作你不需要任何其它的东西。它有一个运行时发现算法,这个算法能寻找其它的日志框架在知名的classpath中,并使用一个它认为是合适的(或者你告诉它你想用哪个如果你需要的话)。如果找不到任何别的你可以从JDK中找到一个非常美好漂亮的日志(java.util.logging或缩写为JUL)。在大多数环境中你可以发现你的Spring应用恰当地运行并输出日志到控制台输出框中,那是很重要的。
Not Using Commons Logging
Unfortunately, the runtime discovery algorithm in commons-logging
, while convenient for the end-user, is problematic. If we could turn back the clock and start Spring now as a new project it would use a different logging dependency. The first choice would probably be the Simple Logging Facade for Java ( SLF4J), which is also used by a lot of other tools that people use with Spring inside their applications.
不幸的是, 虽然commons-logging
的运行时发现算法对于终端用户是方便的,但它是有问题的。如果我们将时钟回拨,把Spring作为一个新项目重新开始,将会选择一个不同的日志依赖。第一个选择可能是Simple Logging Facade for Java(SLF4J),应用内部使用Spring的人使用的许多其它工具也用了SLF4J。
There are basically two ways to switch off commons-logging
:
- Exclude the dependency from the
spring-core
module (as it is the only module that explicitly depends oncommons-logging
) - Depend on a special commons-logging dependency that replaces the library with an empty jar (more details can be found in the SLF4J FAQ)
这儿有两种方式关掉commons-logging
:
- 从
spring-core
模块排除依赖(因为它是唯一的显式依赖)commons-logging
的模块 - 依赖于一个特定的
commons-logging
依赖,用一个空jar替换这个依赖(更多细节可以在SLF4J FAQ中找到)。
To exclude commons-logging
, add the following to your dependencyManagement
section:
为了排除commons-logging
,把下面的内容加入到你的dependencyManagement
部分:
1 | <dependencies> |
Now this application is probably broken because there is no implementation of the JCL API on the classpath, so to fix it a new one has to be provided. In the next section we show you how to provide an alternative implementation of JCL using SLF4J as an example.
现在这个应用可能是坏了的,因为在classpath中没有JCL API的实现,为了解决这个问题必须提供一个新的实现。在接下来的部分我们将向你展示怎样提供一个JCL替代实现,使用SLF4J就是一个例子。
Using SLF4J
SLF4J is a cleaner dependency and more efficient at runtime than commons-logging
because it uses compile-time bindings instead of runtime discovery of the other logging frameworks it integrates. This also means that you have to be more explicit about what you want to happen at runtime, and declare it or configure it accordingly. SLF4J provides bindings to many common logging frameworks, so you can usually choose one that you already use, and bind to that for configuration and management.
SLF4J是一个更纯净的依赖并且在运行时比commons-logging
更有效,因为它使用编译时绑定来代替运行时查找集成的其它日志框架。这也意味着你必须更清楚你想要运行时发生什么,然后相应的声明它或配置它。SLF4J提供跟许多常用日志框架的绑定,因此你通常可以选择一个你正在使用的日志框架,然后绑定到配置和管理上。
SLF4J provides bindings to many common logging frameworks, including JCL, and it also does the reverse: bridges between other logging frameworks and itself. So to use SLF4J with Spring you need to replace the commons-logging
dependency with the SLF4J-JCL bridge. Once you have done that then logging calls from within Spring will be translated into logging calls to the SLF4J API, so if other libraries in your application use that API, then you have a single place to configure and manage logging.
SLF4J提供跟许多常用日志框架的绑定,包括JCL,它做的恰恰相反,建立其它日志框架和它自己的纽带。因此为了在Spring中使用SLF4J,你需要用SLF4J-JCL连接器取替换commons-logging
依赖。一旦你在Spring内部使用了日志调用,Spring会将日志调用变为调用SLF4J API,如果你应用中其它的库调用了那个API,你将有一个单独的地方配置和管理日志。
A common choice might be to bridge Spring to SLF4J, and then provide explicit binding from SLF4J to Log4J. You need to supply 4 dependencies (and exclude the existing commons-logging
): the bridge, the SLF4J API, the binding to Log4J, and the Log4J implementation itself. In Maven you would do that like this
一个常用的选择连接Spring和SLF4J,然后提供SLF4J到Log4J的显式绑定。你需要提供四个依赖(排除现有的commons-logging
):连接、SLF4J API、到Log4J的绑定、Log4J本身的实现。在Maven中你可能这么做:
1 | <dependencies> |
That might seem like a lot of dependencies just to get some logging. Well it is, but it is optional, and it should behave better than the vanilla commons-logging
with respect to classloader issues, notably if you are in a strict container like an OSGi platform. Allegedly there is also a performance benefit because the bindings are at compile-time not runtime.
这可能看起来为了得到一些日志需要很多依赖。还好它是可选的,比起commons-logging
的关于类加载器的问题,尤其是你在一个像OSGi平台那样严格的容器中的时候,它应该更好操作。据说这儿也有一个性能提升,因为绑定是在编译时而不是在运行时。
A more common choice amongst SLF4J users, which uses fewer steps and generates fewer dependencies, is to bind directly to Logback. This removes the extra binding step because Logback implements SLF4J directly, so you only need to depend on two libraries not four ( jcl-over-slf4j
and logback
). If you do that you might also need to exclude the slf4j-api dependency from other external dependencies (not Spring), because you only want one version of that API on the classpath.
在SLF4J用户中,一个更通用的选择是直接绑定到Logback,这样使用步骤更少且依赖也更少。这去除了外部绑定步骤,因为Logback直接实现了SLF4J,因此你仅需要依赖两个库而不是四个(jcl-over-slf4j
和logback
)。如果你这样做的话你可能也需要从其它的外部应用中(不是从Spring)排除slf4j-api依赖,因为你在classpath中仅需要一个版本的API。
Using Log4J
Many people use Log4j as a logging framework for configuration and management purposes. It’s efficient and well-established, and in fact it’s what we use at runtime when we build and test Spring. Spring also provides some utilities for configuring and initializing Log4j, so it has an optional compile-time dependency on Log4j in some modules.
许多人使用Log4j作为配置和管理的日志框架。它有效且完善的,当我们构建和测试Spring时,实际上这就是在运行时我们使用的东西。Spring也提供一些配置和初始化Log4j的工具,因此在某些模块有可选的Log4j的编译时依赖。
To make Log4j work with the default JCL dependency (commons-logging
) all you need to do is put Log4j on the classpath, and provide it with a configuration file ( log4j.properties
or log4j.xml
in the root of the classpath). So for Maven users this is your dependency declaration:
为了使Log4j能与默认的JCL依赖(commons-logging
)一起工作,所有你需要做的是把Log4j放到classpath中,并提供一个配置文件(log4j.properties
或log4j.xml
在classpath的根目录)。对于Maven用户依赖声明如下:
1 | <dependencies> |
And here’s a sample log4j.properties for logging to the console:
下面是一个log4j.properties输出日志到控制台的样本:
Runtime Containers with Native JCL
Many people run their Spring applications in a container that itself provides an implementation of JCL. IBM Websphere Application Server (WAS) is the archetype. This often causes problems, and unfortunately there is no silver bullet solution; simply excluding commons-logging
from your application is not enough in most situations.
许多人在容器中运行他们的Spring应用,容器本身提供了一个JCL实现。IBM Websphere Application Server (WAS) 是原型。这经常会引起问题,不幸的是没有一劳永逸的解决方案;在大多数环境下简单的执行commons-logging
是不够的。
To be clear about this: the problems reported are usually not with JCL per se, or even with commons-logging
: rather they are to do with binding commons-logging
to another framework (often Log4J). This can fail because commons-logging
changed the way they do the runtime discovery in between the older versions (1.0) found in some containers and the modern versions that most people use now (1.1). Spring does not use any unusual parts of the JCL API, so nothing breaks there, but as soon as Spring or your application tries to do any logging you can find that the bindings to Log4J are not working.
为了使这个更清楚:报告的问题本质上一般不是关于JCL的,或关于commons-logging
的:而是他们去绑定commons-logging
到其它的框架上(通常是Log4j)。这可能会失败因为commons-logging
在一些容器的旧版本(1.0)和大多数人使用的现代版本(1.1)中改变了运行时发现方式。Spring不使用JCL API的和任何不常用的部分,因此不会有问题出现,但是一旦Spring或你的应用试图去输出日志,你可能发现到Log4j的绑定是不起作用的。
In such cases with WAS the easiest thing to do is to invert the class loader hierarchy (IBM calls it “parent last”) so that the application controls the JCL dependency, not the container. That option isn’t always open, but there are plenty of other suggestions in the public domain for alternative approaches, and your mileage may vary depending on the exact version and feature set of the container.
在这种情况下使用WAS最容易做的事是逆转类加载层(IBM称为”parent last”),为的是应用能控制依赖,而不是容器。虽然这种选择并非总是公开的,但在公共领域对于替代方法有许多其它的建议,你的解决这个问题花的时间可能是不同的,这取决于确定的版本和容器集合的特性。
Part II. Core Technologies
This part of the reference documentation covers all of those technologies that are absolutely integral to the Spring Framework.
这部分参考文档包含了所有完全集成到Spring框架中的那些技术。
Foremost amongst these is the Spring Framework’s Inversion of Control (IoC) container. A thorough treatment of the Spring Framework’s IoC container is closely followed by comprehensive coverage of Spring’s Aspect-Oriented Programming (AOP) technologies. The Spring Framework has its own AOP framework, which is conceptually easy to understand, and which successfully addresses the 80% sweet spot of AOP requirements in Java enterprise programming.
在这些中最重要的是Spring框架的控制反转(IoC)容器。对Spring框架IoC容器的彻底处理是紧随其后的Spring面向切面编程(AOP)技术的全面覆盖。Spring框架有它自己的AOP框架,这在概念上很容易理解,在Java企业级开发中成功了解决了AOP需求中80%的关键点。
Coverage of Spring’s integration with AspectJ (currently the richest - in terms of features - and certainly most mature AOP implementation in the Java enterprise space) is also provided.
Spring也提供了AspectJ的全面集成(目前是最丰富的-考虑到功能-并且确定在Java企业中是最成熟的AOP实现)。
- Chapter 3, The IoC container
- Chapter 4, Resources
- Chapter 5, Validation, Data Binding, and Type Conversion
- Chapter 6, Spring Expression Language (SpEL)
- Chapter 7, Aspect Oriented Programming with Spring
- Chapter 8, Spring AOP APIs
3. The IoC container
3.1 Introduction to the Spring IoC container and beans
This chapter covers the Spring Framework implementation of the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI). 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. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern.
这一章包含了Spring框架的控制反转(IoC)原理的实现。IoC也被称为依赖注入(DI)。它是一个处理过程,凭借对象之间依赖关系,也就是和它们一起工作的其它对象,只能通过构造函数参数,传递参数给工厂方法,在构造完成或工厂方法返回的对象实例之后再设置对象实例的属性。当创建bean时容器再将这些依赖对象注入进去。这个过程从根本上颠倒了bean本身通过直接构建类或通过一种机制例如服务定位模式来控制依赖对象的实例化或定位,因此命名为控制反转(IoC)。
The org.springframework.beans
and org.springframework.context
packages are the basis for Spring Framework’s IoC container. The BeanFactory
interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext
is a sub-interface of BeanFactory
. It adds easier integration with Spring’s AOP features; message resource handling (for use in internationalization), event publication; and application-layer specific contexts such as the WebApplicationContext
for use in web applications.
org.springframework.beans
和org.springframework.context
包是Spring框架控制反转容器的基础。BeanFactory
接口提供了一种能管理任何类型对象的高级配置机制。ApplicationContext
是BeanFactory
的一个子接口。ApplicationContext
增加了更容易集成Spring AOP功能;消息资源处理(用在国际化中),事件发布;应用层特定上下文例如WebApplicationContext
在web应用中的使用。
In short, the BeanFactory
provides the configuration framework and basic functionality, and the ApplicationContext
adds more enterprise-specific functionality. The ApplicationContext
is a complete superset of the BeanFactory
, and is used exclusively in this chapter in descriptions of Spring’s IoC container. For more information on using the BeanFactory
instead of the ApplicationContext
, refer to Section 3.16, “The BeanFactory”.
总之,BeanFactory
提供了配置框架和基本功能,ApplicationContext
增加了更多企业专用的功能。ApplicationContext
是BeanFactory
的一个全面超集,在这章仅仅是用来描述Spring的IoC容器。关于用BeanFactory
代替ApplicationContext
的更多信息请参考3.16小节”The BeanFactory”。
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.
在Spring中,被Spring IoC容器管理的那些形成你应用主干的对象被称为beans。bean是实例化、组装、以及其它的都被Spring IoC容器管理的对象。另外,bean仅仅是你应用中许多对象中的一个。Beans和它们之间的依赖关系,通过容器使用的配置元数据可以反映出来。
3.2 Container overview
The interface org.springframework.context.ApplicationContext
represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the aforementioned beans. The container gets its instructions on what objects to instantiate, configure, and assemble by reading configuration metadata. The configuration metadata is represented in XML, Java annotations, or Java code. It allows you to express the objects that compose your application and the rich interdependencies between such objects.
org.springframework.context.ApplicationContext
接口代表了Spring IoC容器并且负责实例化、配置和组装前面提到的beans。容器通过读取配置元数据得到说明什么对象要实例化、配置和组装。配置元数据可以用XML、Java注解或Java代码表示。它允许你表示构成应用的对象和对象间丰富的依赖关系。
Several implementations of the ApplicationContext
interface are supplied out-of-the-box with Spring. In standalone applications it is common to create an instance of ClassPathXmlApplicationContext
or FileSystemXmlApplicationContext
. While XML has been the traditional format for defining configuration metadata you can instruct the container to use Java annotations or code as the metadata format by providing a small amount of XML configuration to declaratively enable support for these additional metadata formats.
Spring提供了一些可以直接使用的ApplicationContext
接口实现。在单独的应用中通常是创建一个 ClassPathXmlApplicationContext
实例或FileSystemXmlApplicationContext
实例。虽然XML是定义配置元数据的传统格式,但你可以指示容器支持使用Java注解或代码作为元数据的格式并通过提供少量的XML配置声明使容器支持这些额外的元数据格式。
In most application scenarios, explicit user code is not required to instantiate one or more instances of a Spring IoC container. For example, in a web application scenario, a simple eight (or so) lines of boilerplate web descriptor XML in the web.xml
file of the application will typically suffice (see Section 3.15.4, “Convenient ApplicationContext instantiation for web applications”). If you are using the Spring Tool Suite Eclipse-powered development environment this boilerplate configuration can be easily created with few mouse clicks or keystrokes.
在大多数应用场景中,不会要求用户用显式的代码来实例化一个或多个Spring IoC容器的。例如,在web应用场景中,在应用的web.xml
文件中写一个简单的八行左右的样板web描述符XML就足够了(看3.13.4小节,『web应用中ApplicationContext的方便实例化』)。如果你正在使用Eclipse支持的Spring Tool Suite开发环境,可以很容易的通过点几下鼠标或键盘来创建样本配置。
The following diagram is a high-level view of how Spring works. Your application classes are combined with configuration metadata so that after the ApplicationContext
is created and initialized, you have a fully configured and executable system or application.
下面的图是从一个高层次的视野来看Spring是如何工作的。你的应用类与配置元数据结合起来为的是在ApplicationContext
创建和初始化之后,你有一个完整配置并可执行的系统或应用。
Figure 3.1. The Spring IoC container
3.2.1 Configuration metadata
As the preceding diagram shows, the Spring IoC container consumes a form of configuration metadata; this configuration metadata represents how you as an application developer tell the Spring container to instantiate, configure, and assemble the objects in your application.
如上图所示,Spring IoC容器使用了一种配置元数据的方式;配置元数据表示你作为一个应用开发者应该告诉Spring容器怎样去实例化、配置并组装应用中的对象。
Configuration metadata is traditionally supplied in a simple and intuitive XML format, which is what most of this chapter uses to convey key concepts and features of the Spring IoC container.
习惯上用简单直观下XML形式来提供配置元数据,这一章大部分使用XML文件来表达Spring IoC容器的核心概念及功能。
XML-based metadata is not the only allowed form of configuration metadata. The Spring IoC container itself is totally decoupled from the format in which this configuration metadata is actually written. These days many developers choose Java-based configuration for their Spring applications.
基于XML的元数据不是配置元数据的唯一许可形式。Spring IoC容器本身与配置元数据的实际书写形式是完全解构的。目前许多开发者在他们的Spring应用中选用基于Java配置的元数据形式。
For information about using other forms of metadata with the Spring container, see:
- Annotation-based configuration: Spring 2.5 introduced support for annotation-based configuration metadata.
- Java-based configuration: Starting with Spring 3.0, many features provided by the Spring JavaConfig project became part of the core Spring Framework. Thus you can define beans external to your application classes by using Java rather than XML files. To use these new features, see the
@Configuration
,@Bean
,@Import
and@DependsOn
annotations.
关于Spring容器中使用其它元数据形式的信息,请看:
- 基于注解的配置:Spring 2.5引入对了基于注解的配置元数据的支持。
- 基于Java的配置:从Spring 3.0开始,Spring JavaConfig工程提供的许多功能开始成为Spring框架核心中的一部分。因此你可以通过Java而不是XML文件来定义外部应用程序的beans。为了使用这些新功能,请看
@Configuration
,@Bean
,@Import
和@DependsOn
注解。
Spring configuration consists of at least one and typically more than one bean definition that the container must manage. XML-based configuration metadata shows these beans configured as <bean/>
elements inside a top-level <beans/>
element. Java configuration typically uses @Bean
annotated methods within a @Configuration
class.
Spring配置包括至少一个且通常不止一个容器必须管理的bean定义。基于XML的配置元数据中,这些beans作为<bean>
元素被配置在顶层<beans/>
元素中。Java配置通常在@Configuration
类中使用@Bean
注解的方法。
These bean definitions correspond to the actual objects that make up your application. Typically you define service layer objects, data access objects (DAOs), presentation objects such as Struts Action
instances, infrastructure objects such as Hibernate SessionFactories
, JMS Queues
, and so forth. Typically one does not configure fine-grained domain objects in the container, because it is usually the responsibility of DAOs and business logic to create and load domain objects. However, you can use Spring’s integration with AspectJ to configure objects that have been created outside the control of an IoC container. See Using AspectJ to dependency-inject domain objects with Spring.
这些bean定义与组成你应用的实际对象相对应。通常你会定义服务层对象,数据访问层对象(DAOs),描述对象例如Struts的Action
实例,底层对象例如Hibernate的SessionFactories
,JMS的Queues
等等。容器中细粒度的领域对象通常是不配置的,因为一般是由DAOs和业务逻辑负责创建和加载领域对象。然而你可以使用Spring集成的AspectJ去配置IoC容器控制之外创建的对象。请看”使用Spring的AspectJ来依赖注入领域对象”。
The following example shows the basic structure of XML-based configuration metadata:
下面的例子展示了基于XML配置元数据的基本结构:
1 | <?xml version="1.0" encoding="UTF-8"?> |
The id
attribute is a string that you use to identify the individual bean definition. The class
attribute defines the type of the bean and uses the fully qualified classname. The value of the id
attribute refers to collaborating objects. The XML for referring to collaborating objects is not shown in this example; see Dependencies for more information.
id
属性是一个你用来识别私有bean定义的字符串。class
属性定义了bean的类型并且使用了完全限定类型名称(全限定名称或完全限定名)。id
属性的值指的是协作对象。这个例子的中没有展示如何引用协作对象,更多信息请查看『依赖』。
3.2.2 Instantiating a container
Instantiating a Spring IoC container is straightforward. The location path or paths supplied to an ApplicationContext
constructor are actually resource strings that allow the container to load configuration metadata from a variety of external resources such as the local file system, from the Java CLASSPATH
, and so on.
实例化一个Spring IoC容器是简单的。一个或多个提供给ApplicationContext
构造函数的定位路径实际上是资源字符串,可以让容器从各种例如局部文件系统,Java的CLASSPATH
等外部资源中加载配置元数据。
1 | ApplicationContext context = |
After you learn about Spring’s IoC container, you may want to know more about Spring’s
Resource
abstraction, as described in Chapter 4, Resources, which provides a convenient mechanism for reading an InputStream from locations defined in a URI syntax. In particular,Resource
paths are used to construct applications contexts as described in Section 4.7, “Application contexts and Resource paths”.
在你学习Spring IoC容器之后,你可能想知道更多关于Spring的
Resource
抽象信息,介绍信息在『第四章 资源』中,Resource
抽象提供了一种方便的机制从URI语法定义的位置中读取输入流。Resource
路径通常被用来构建应用程序上下文,正如4.7 小节『应用上下文和资源路径』描述的那样。
The following example shows the service layer objects (services.xml
) configuration file:
下面的例子是服务层对象(services.xml
)的配置文件:
1 | <?xml version="1.0" encoding="UTF-8"?> |
The following example shows the data access objects daos.xml
file:
下面的例子是数据访问对象daos.xml
的配置文件:
1 | <?xml version="1.0" encoding="UTF-8"?> |
In the preceding example, the service layer consists of the class PetStoreServiceImpl
, and two data access objects of the type JpaAccountDao
and JpaItemDao
(based on the JPA Object/Relational mapping standard). The property name
element refers to the name of the JavaBean property, and the ref
element refers to the name of another bean definition. This linkage between id
and ref
elements expresses the dependency between collaborating objects. For details of configuring an object’s dependencies, see Dependencies.
在之前的例子中,服务层包括类PetStoreServiceImpl
和两个类型为JpaAccountDao
和JpaItemDao
数据访问对象(基于JPA对象/关系映射标准)。property name
元素指的是JavaBean属性的名称,ref
元素指的是另一个bean定义的名称。id
和ref
之间的连接表明了协作对象之间的关系。配置对象依赖的更详细信息请看『依赖』。
Composing XML-based configuration metadata
It can be useful to have bean definitions span multiple XML files. Often each individual XML configuration file represents a logical layer or module in your architecture.
bean定义跨越多个XML文件是非常有用的。通常每一个独立的XML配置文件表示你架构中的一个逻辑层或模块。
You can use the application context constructor to load bean definitions from all these XML fragments. This constructor takes multiple Resource
locations, as was shown in the previous section. Alternatively, use one or more occurrences of the <import/>
element to load bean definitions from another file or files. For example:
你可以使用应用上下文构造函数从所有XML片段中加载bean定义。如上小节所示,构造函数可以接收多个Resource
位置。也可以使用一个或同时使用多个<import/>
元素从另一个或另一些文件中加载bean定义。例如:
1 | <beans> |
In the preceding example, external bean definitions are loaded from three files: services.xml
, messageSource.xml
, and themeSource.xml
. All location paths are relative to the definition file doing the importing, so services.xml
must be in the same directory or classpath location as the file doing the importing, while messageSource.xml
and themeSource.xml
must be in a resources
location below the location of the importing file. As you can see, a leading slash is ignored, but given that these paths are relative, it is better form not to use the slash at all. The contents of the files being imported, including the top level <beans/>
element, must be valid XML bean definitions according to the Spring Schema.
在上面的例子中,外部bean定义从services.xml
、messageSource.xml
和themeSource.xml
三个文件中加载。所有位置路径都是相对于进行导入的定义文件的,因此services.xml
必须跟进行导入的文件在同一个目录下或同一个classpath位置下。如你所见,忽略了最前面的反斜杠,但给定的这些路径是相对的,最好是一点都不使用反斜杠。包括顶层的<beans/>
元素在内,被导入的文件内容必须是依据Spring Schema有效的XML bean定义。
It is possible, but not recommended, to reference files in parent directories using a relative “../“ path. Doing so creates a dependency on a file that is outside the current application. In particular, this reference is not recommended for “classpath:” URLs (for example, “classpath:../services.xml”), where the runtime resolution process chooses the “nearest” classpath root and then looks into its parent directory. Classpath configuration changes may lead to the choice of a different, incorrect directory.
You can always use fully qualified resource locations instead of relative paths: for example, “file:C:/config/services.xml” or “classpath:/config/services.xml”. However, be aware that you are coupling your application’s configuration to specific absolute locations. It is generally preferable to keep an indirection for such absolute locations, for example, through “${…}” placeholders that are resolved against JVM system properties at runtime.
在父目录的引用文件使用”../“相对路径是可以的,但不推荐这样做。这样做会产生一个当前应用之外文件依赖。引用文件特别不推荐在”classpath:” URLs中(例如”classpath:../services.xml”),运行时解析处理会选择”最近的”classpath根目录,然后去寻找它的父目录。Classpath配置的更改可能会导致进入一个不同且不正确的目录。
你可以总是使用完全限定资源位置代替相对路径:例如,”file:C:/config/services.xml”或”classpath:/config/services.xml”。但是要注意你正在将你的应用配置与特定的绝对路径耦合。通常更可取的方式是间接的访问绝对路径,例如,通过”${…}”占位符在运行时解析JVM系统属性。
3.2.3 Using the container
The ApplicationContext
is the interface for an advanced factory capable of maintaining a registry of different beans and their dependencies. Using the method T getBean(String name, Class<T> requiredType)
you can retrieve instances of your beans.
ApplicationContext
是一个更高级的工厂接口,它能维护不同beans及其依赖的注册表。使用方法T getBean(String name, Class<T> requiredType)
你可以取回你的beans实例。
The ApplicationContext
enables you to read bean definitions and access them as follows:
ApplicationContext
能让你用下面的方式读取bean定义及访问它们:
1 | // create and configure beans |
You use getBean()
to retrieve instances of your beans. The ApplicationContext
interface has a few other methods for retrieving beans, but ideally your application code should never use them. Indeed, your application code should have no calls to the getBean()
method at all, and thus no dependency on Spring APIs at all. For example, Spring’s integration with web frameworks provides for dependency injection for various web framework classes such as controllers and JSF-managed beans.
你可以用getBean()
取回你的beans实例。ApplicationContext
接口有一些其它的方法来取回beans,但理想的应用代码应该绝不使用它们。事实上,你的应用代码应该完全不调用getBean()
方法,因此完全不依赖Spring APIs。例如,Spring的集成web框架提供了各种web框架类的依赖注入,例如控制器和JSF管理的beans。
3.3 Bean overview
A Spring IoC container manages one or more beans. These beans are created with the configuration metadata that you supply to the container, for example, in the form of XML <bean/>
definitions.
Spring IoC容器管理一个或多个beans。这些beans由提供给容器的配置元数据生成,例如,XML形式的<bean/>
定义。
Within the container itself, these bean definitions are represented as BeanDefinition
objects, which contain (among other information) the following metadata:
- A package-qualified class name: typically the actual implementation class of the bean being defined.
- Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).
- References to other beans that are needed for the bean to do its work; these references are also called collaborators or dependencies.
- Other configuration settings to set in the newly created object, for example, the number of connections to use in a bean that manages a connection pool, or the size limit of the pool.
在容器本身内部,这些bean定义被表示成BeanDefinition
对象,含有以下元数据:
- 包限定的类名:通常是被定义的bean的实现类。
- bean行为配置元素,规定了bean在容器中的行为(作用范围、生命周期回调函数)等等。
- bean工作需要的引用的其它bean,这些引用也被称为协作者或依赖。
- 其它的配置在新创建的对象中设置,例如,bean中使用的连接数量控制着一个连接池,或连接池的大小限制。
This metadata translates to a set of properties that make up each bean definition.
元数据转化为一系列的属性,这些属性构成了每个bean的定义。
Table 3.1. The bean definition
Property | Explained in… |
---|---|
class | Section 3.3.2, “Instantiating beans” |
name | Section 3.3.1, “Naming beans” |
scope | Section 3.5, “Bean scopes” |
constructor arguments | Section 3.4.1, “Dependency Injection” |
properties | Section 3.4.1, “Dependency Injection” |
autowiring mode | Section 3.4.5, “Autowiring collaborators” |
lazy-initialization mode | Section 3.4.4, “Lazy-initialized beans” |
initialization method | the section called “Initialization callbacks” |
destruction method | the section called “Destruction callbacks” |
In addition to bean definitions that contain information on how to create a specific bean, the ApplicationContext
implementations also permit the registration of existing objects that are created outside the container, by users. This is done by accessing the ApplicationContext’s BeanFactory via the method getBeanFactory() which returns the BeanFactory implementation DefaultListableBeanFactory
. DefaultListableBeanFactory
supports this registration through the methods registerSingleton(..)
and registerBeanDefinition(..)
. However, typical applications work solely with beans defined through metadata bean definitions.
除了bean定义中包含怎么创建一个指定的bean的信息之外,ApplicationContext
实现也允许用户注册容器之外创建的现有对象。这是通过调用ApplicationContext’s BeanFactory的getBeanFactory()方法完成的,这个方法会返回BeanFactory的实现类DefaultListableBeanFactory
。DefaultListableBeanFactory
支持通过registerSingleton(..)
和registerBeanDefinition(..)
方法来注册。不管怎样,标准应用仅使用通过元数据bean定义定义的beans。
Bean metadata and manually supplied singleton instances need to be registered as early as possible, in order for the container to properly reason about them during autowiring and other introspection steps. While overriding of existing metadata and existing singleton instances is supported to some degree, the registration of new beans at runtime (concurrently with live access to factory) is not officially supported and may lead to concurrent access exceptions and/or inconsistent state in the bean container.
bean元数据和人工提供的单例需要尽可能早的进行注册,为了使容器在自动注入及其它的内省步骤时能恰当的推理它们。虽然在一定程度上是支持覆盖现有的元数据和单例的,但运行时新beans的注册(并发实时访问工厂)是不被正式支持的,可能会引起并发访问异常,在容器中的与/或状态不一致。
3.3.1 Naming beans
Every bean has one or more identifiers. These identifiers must be unique within the container that hosts the bean. A bean usually has only one identifier, but if it requires more than one, the extra ones can be considered aliases.
每个bean都有一个或多个标识符。这些托管bean的标识符在容器中必须是唯一的。一个bean通常只有一个标识符,但如果一个bean需要不止一个标识符,其它的标识符会被当成别名。
In XML-based configuration metadata, you use the id
and/or name
attributes to specify the bean identifier(s). The id
attribute allows you to specify exactly one id. Conventionally these names are alphanumeric (‘myBean’, ‘fooService’, etc.), but may contain special characters as well. If you want to introduce other aliases to the bean, you can also specify them in the name
attribute, separated by a comma (,
), semicolon (;
), or white space. As a historical note, in versions prior to Spring 3.1, the id
attribute was defined as an xsd:ID
type, which constrained possible characters. As of 3.1, it is defined as an xsd:string
type. Note that bean id
uniqueness is still enforced by the container, though no longer by XML parsers.
在基于XML的配置元数据中,你可以使用id
和/或name
属性指定bean标识符。id
属性允许你指定一个确定的id。按照惯例这些名字是字母数字的(‘myBean’, ‘fooService’等等),但也可能包含指定字符。如果你想引入bean其它的别名,你可以在name
属性中指定别名,用逗号 (,
),分号(;
),或空格分开。作为一个历史注解,在之前的Spring 3.1版本,id
属性被定义为一种xsd:ID
类型,可以通过合理字符来约束(XML控制id
唯一性)。从Spring 3.1开始,它被定义为xsd:string
类型。注意bean id
的唯一性仍然是容器强制的,虽然不再通过XML解析器来控制(容器控制id
唯一性)。
You are not required to supply a name or id for a bean. If no name or id is supplied explicitly, the container generates a unique name for that bean. However, if you want to refer to that bean by name, through the use of the ref
element or Service Locator style lookup, you must provide a name. Motivations for not supplying a name are related to using inner beans and autowiring collaborators.
bean的id和name不是必须提供的。如果没有明确指定name或id,容器会为bean产生一个唯一的名字。如果你想通过name引用bean,通过使用ref
元素或服务定位器模式查找,你必须提供一个名字。不提供name的动机是与内部bean的使用和协作bean的自动装配有关的。
Bean Naming Conventions
The convention is to use the standard Java convention for instance field names when naming beans. That is, bean names start with a lowercase letter, and are camel-cased from then on. Examples of such names would be (without quotes) ‘accountManager’, ‘accountService’, ‘userDao’, ‘loginController’, and so forth.
Naming beans consistently makes your configuration easier to read and understand, and if you are using Spring AOP it helps a lot when applying advice to a set of beans related by name.
Bean命名规范
当命名bean时,采用的规范是标准Java实例字段命名规范。bean名称以小写字母开头,采用驼峰式的命名规则。这种命名方式的例子(不带引号)有’accountManager’, ‘accountService’, ‘userDao’, ‘loginController’等等。
一致的命名beans可以使人更容易读懂和理解你的配置,如果你正在使用Spring AOP,使用一致性来命名一系列bean名称是非常有帮助的。
With component scanning in the classpath, Spring generates bean names for unnamed components, following the rules above: essentially, taking the simple class name and turning its initial character to lower-case. However, in the (unusual) special case when there is more than one character and both the first and second characters are upper case, the original casing gets preserved. These are the same rules as defined by
java.beans.Introspector.decapitalize
(which Spring is using here).
在classpath中进行组件扫描,Spring会根据上面的规则为未命名组件产生bean名称,本质上来说,是采用简单的类名并将其首字母改成小写。然而在特殊情况下(不平常的),当类名有不止一个字母且第一二个字母都是大写的情况下,会保留最初始的状态。与
java.beans.Introspector.decapitalize
定义中的规则是相同的(Spring也采用这个规则)。
Aliasing a bean outside the bean definition
In a bean definition itself, you can supply more than one name for the bean, by using a combination of up to one name specified by the id
attribute, and any number of other names in the name
attribute. These names can be equivalent aliases to the same bean, and are useful for some situations, such as allowing each component in an application to refer to a common dependency by using a bean name that is specific to that component itself.
在定义bean时,通过与id
属性指定的名称相结合,你可以为bean提供不止一个名字,在name
属性中定义任何数量的其它名字。这些名字是同一个bean的等价别名,在一些情况下是非常有用的,例如允许应用中的每个组件通过bean名称引用一个共通的依赖,这个依赖为每个组件本身指定了一个名称。
Specifying all aliases where the bean is actually defined is not always adequate, however. It is sometimes desirable to introduce an alias for a bean that is defined elsewhere. This is commonly the case in large systems where configuration is split amongst each subsystem, each subsystem having its own set of object definitions. In XML-based configuration metadata, you can use the <alias/>
element to accomplish this.
然而在bean实际定义的地方指定所有别名并不总是适当的。有时会要求引入一个在别的地方定义的bean的别名。这通常是在大的系统中而配置被分割在每个子系统中,每个子系统有它知道对象定义集合。在基于XML配置元数据中,你可以使用<alias/>
来完成别名的定义。
1 | <alias name="fromName" alias="toName"/> |
In this case, a bean in the same container which is named fromName
, may also, after the use of this alias definition, be referred to as toName
.
在这种情况下,在同一个容器中的bean被命名为fromName
,也可能是在别名定义使用之后,被作为toName
引用。
For example, the configuration metadata for subsystem A may refer to a DataSource via the name subsystemA-dataSource
. The configuration metadata for subsystem B may refer to a DataSource via the name subsystemB-dataSource
. When composing the main application that uses both these subsystems the main application refers to the DataSource via the name myApp-dataSource
. To have all three names refer to the same object you add to the MyApp configuration metadata the following aliases definitions:
例如,子系统A的配置元数据可能通过名称subsystemA-dataSource
引用数据源。子系统B的配置元数据可能通过名称subsystemB-dataSource
引用数据源。当构成主应用的时,主应用使用这些子系统并通过名称myApp-dataSource
引用数据源。为了使这三个名称引用同一个对象,你可以将如下的别名定义添加到MyApp配置元数据中:
1 | <alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/> |
Now each component and the main application can refer to the dataSource through a name that is unique and guaranteed not to clash with any other definition (effectively creating a namespace), yet they refer to the same bean.
现在主应用和每个组件都能通过名称引用数据源,这个名称是唯一的且能保证不与任何其它的定义相冲突(有效的创建了一个命名空间),但它们引用了同一个bean。
Java-configuration
If you are using Java-configuration, the
@Bean
annotation can be used to provide aliases see Section 3.12.3, “Using the @Bean annotation” for details.
Java配置
如果你正在使用Java配置,
@Bean
注解可以用来提供别名,更多细节请看3.12.3小节, “使用@Bean注解”。
3.3.2 Instantiating beans
A bean definition essentially is a recipe for creating one or more objects. The container looks at the recipe for a named bean when asked, and uses the configuration metadata encapsulated by that bean definition to create (or acquire) an actual object.
bean定义本质上来说是创建一个或多个对象的方法。当问及一个命名bean时,容器会查看这个方法并使用bean定义中封装的配置元数据创建(或取得)一个实际的对象。
If you use XML-based configuration metadata, you specify the type (or class) of object that is to be instantiated in the class
attribute of the <bean/>
element. This class
attribute, which internally is a Class
property on a BeanDefinition
instance, is usually mandatory. (For exceptions, see the section called “Instantiation using an instance factory method” and Section 3.7, “Bean definition inheritance”.) You use the Class
property in one of two ways:
Typically, to specify the bean class to be constructed in the case where the container itself directly creates the bean by calling its constructor reflectively, somewhat equivalent to Java code using the
new
operator.To specify the actual class containing the
static
factory method that will be invoked to create the object, in the less common case where the container invokes astatic
factory method on a class to create the bean. The object type returned from the invocation of thestatic
factory method may be the same class or another class entirely.
如果你使用基于XML的配置元数据,你可以指定对象的类型(或类),它将在<bean/>
元素中的class
属性中进行实例化。class
属性,在BeanDefinition
实例的内部是Class
性质,通常是必需的。(例外的情况,请看”使用实例工厂方法进行实例化”小节和3.7小节,”bean定义继承”)。你可以通过以下两种方式中的一种使用Class
属性:
通常情况下,指定要构造的bean类,容器本身通过反射调用bean的构造方法直接创建bean,这与Java代码中使用
new
操作符是等价的。在不常见的情况下,指定包含静态工厂方法的实际类,调用静态工厂方法创建对象,容器在类上调用静态工厂方法创建bean。静态工厂方法调用返回的对象类型可能是同一个类,也可能完全是另一个类。
Inner class names. If you want to configure a bean definition for a
static
nested class, you have to use the binary name of the nested class.For example, if you have a class called
Foo
in thecom.example
package, and thisFoo
class has astatic
nested class calledBar
, the value of the'class'
attribute on a bean definition would be…
com.example.Foo$Bar
Notice the use of the
$
character in the name to separate the nested class name from the outer class name.
内部类命名 如果你想为静态嵌套类配置bean定义,你必须使用嵌套类的二进制名字。
例如,如果你在
com.example
包中有个类叫Foo
,Foo
类中有一个静态嵌套类叫Bar
,'class'
属性在bean定义中的值为
com.example.Foo$Bar
注意名字中
$
符号的使用是为了将外部类名与嵌套类名分隔开。
Instantiation with a constructor
When you create a bean by the constructor approach, all normal classes are usable by and compatible with Spring. That is, the class being developed does not need to implement any specific interfaces or to be coded in a specific fashion. Simply specifying the bean class should suffice. However, depending on what type of IoC you use for that specific bean, you may need a default (empty) constructor.
当你使用构造方法创建bean时,所有的正常类都可以被Spring使用和兼容。也就是说,正在进行开发的类不需要实现任何特定的接口或以特定的方式进行编码。简单的指定bean类就足够了。然而,根据你为指定的bean所使用的IoC类型,你可能需要一个默认的(空的)构造函数。
The Spring IoC container can manage virtually any class you want it to manage; it is not limited to managing true JavaBeans. Most Spring users prefer actual JavaBeans with only a default (no-argument) constructor and appropriate setters and getters modeled after the properties in the container. You can also have more exotic non-bean-style classes in your container. If, for example, you need to use a legacy connection pool that absolutely does not adhere to the JavaBean specification, Spring can manage it as well.
事实上,Spring的IoC容器可以管理任何你想让它管理的类;它不受限于管理真实的JavaBeans。大多数Spring用户更喜欢实际的JavaBeans,在容器中它仅有一个默认(无参)的构造函数,并且属性之后有合适的setters,getters方法。在容器中你也可以有更多外来的非bean类型的类。例如,如果你需要使用遗留的连接池,这绝对不符合JavaBean规范,但Spring也可以管理它。
With XML-based configuration metadata you can specify your bean class as follows:
基于XML的配置元数据你可以用如下方式指定你的bean的类:
1 | <bean id="exampleBean" class="examples.ExampleBean"/> |
For details about the mechanism for supplying arguments to the constructor (if required) and setting object instance properties after the object is constructed, see Injecting Dependencies.
更多关于为构造函数提供参数(如果有必要的话)的机制和构造对象之后设置对象实例属性的细节,请看”依赖注入”。
Instantiation with a static factory method
When defining a bean that you create with a static factory method, you use the class
attribute to specify the class containing the static
factory method and an attribute named factory-method
to specify the name of the factory method itself. You should be able to call this method (with optional arguments as described later) and return a live object, which subsequently is treated as if it had been created through a constructor. One use for such a bean definition is to call static
factories in legacy code.
当定义的bean用静态工厂方法创建时,你可以使用class
属性指定包含静态工厂方法的类,用factory-method
属性指定工厂方法本身的名字。你应该能调用这个方法(用后面描述的可选参数)并且返回一个实时对象,随后对这个对象进行处理,就好像这个对象是通过构造函数创建的一样。这种bean定义的一个用法是在遗留代码(旧代码)中调用静态工厂方法。
The following bean definition specifies that the bean will be created by calling a factory-method. The definition does not specify the type (class) of the returned object, only the class containing the factory method. In this example, the createInstance()
method must be a static method.
下面的bean定义指定了一个通过调用工厂方法创建的bean。定义没有指定返回对象的类型只有包含工厂方法的类。在这个例子中,createInstance()
必须是一个静态方法。
1 | <bean id="clientService" |
1 | public class ClientService { |
For details about the mechanism for supplying (optional) arguments to the factory method and setting object instance properties after the object is returned from the factory, see Dependencies and configuration in detail.
更多关于为工厂方法提供(可选)参数的原理和从工厂方法返回对象后设置对象实例属性的信息,请看”依赖和详细配置”。
Instantiation using an instance factory method
Similar to instantiation through a static factory method, instantiation with an instance factory method invokes a non-static method of an existing bean from the container to create a new bean. To use this mechanism, leave the class
attribute empty, and in the factory-bean
attribute, specify the name of a bean in the current (or parent/ancestor) container that contains the instance method that is to be invoked to create the object. Set the name of the factory method itself with the factory-method
attribute.
与通过静态工厂方法进行实例化类似,通过实例化工厂方法进行实例化,要从容器中调用现有bean非静态方法创建一个新的bean。使用这种机制,要让class
属性为空,在factory-bean
属性中,在包含实例化方法的当前容器(或父/祖先)中指定bean的名字,通过调用实例化方法来创建对象。通过factory-method
属性设置工厂方法本身的名字。
1 | <!-- the factory bean, which contains a method called createInstance() --> |
1 | public class DefaultServiceLocator { |
One factory class can also hold more than one factory method as shown here:
一个工厂类可以拥有多个工厂方法,如下所示:
1 | <bean id="serviceLocator" class="examples.DefaultServiceLocator"> |
1 | public class DefaultServiceLocator { |
This approach shows that the factory bean itself can be managed and configured through dependency injection (DI). See Dependencies and configuration in detail.
这个方法展示了工厂bean本身可以通过依赖注入(DI)来管理和配置。更多细节请看”依赖和配置”。
In Spring documentation, factory bean refers to a bean that is configured in the Spring container that will create objects through an instance or static factory method. By contrast,
FactoryBean
(notice the capitalization) refers to a Spring-specificFactoryBean
.
在Spring文档中,工厂bean引用了配置在Spring容器中的bean,Spring容器将通过实例或静态工厂方法来创建对象。相比之下,
FactoryBean
(注意大写)引用了Spring特定的FactoryBean
。
3.4 Dependencies
A typical enterprise application does not consist of a single object (or bean in the Spring parlance). Even the simplest application has a few objects that work together to present what the end-user sees as a coherent application. This next section explains how you go from defining a number of bean definitions that stand alone to a fully realized application where objects collaborate to achieve a goal.
标准企业应用不会由一个对象(或Spring用语中的bean)组成。即使是最简单的应用也是由一些对象共同工作,呈现给终端用户用户看到的是一个连续的应用。接下来的一节阐述了如何从定义许多独立的bean定义到完全实现的应用,它是一个通过对象协作来实现目标的过程。
3.4.1 Dependency Injection
Dependency injection (DI) 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. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes, or the Service Locator pattern.
依赖注入(DI)是一个处理过程,凭借对象之间依赖关系,也就是和它们一起工作的其它对象,只能通过构造函数参数,传递参数给工厂方法,在构造完成或工厂方法返回对象实例之后再设置对象实例的属性。当创建bean时容器再将这些依赖对象注入进去。这个过程从根本上颠倒了bean本身通过直接构建类或通过一种机制例如服务定位模式来控制依赖对象的实例化或定位,因此命名为控制反转(IoC)。
Code is cleaner with the DI principle and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies, and does not know the location or class of the dependencies. As such, your classes become easier to test, in particular when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.
使用依赖注入原则会使代码更简洁,当对象由依赖关系提供时解耦更有效。对象不会查找它的依赖,不知道依赖的位置和依赖关系的类别。同样的,你的类也变的更容易测试,尤其是依赖关系在接口或抽象基类之间的时候,这种情况下单元测试中会要求存桩或模拟实现。(注:Stub和Mock都是软件测试中使用的东西,如有疑问请自行google或百度)。
DI exists in two major variants, Constructor-based dependency injection and Setter-based dependency injection.
依赖有两个主要变种,基于构造函数的依赖注入和基于Setter的依赖注入。
Constructor-based dependency injection
Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency. Calling a static
factory method with specific arguments to construct the bean is nearly equivalent, and this discussion treats arguments to a constructor and to a static
factory method similarly. The following example shows a class that can only be dependency-injected with constructor injection. Notice that there is nothing special about this class, it is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
基于构造函数的依赖注入通过容器调用有参数的构造函数来实现,每个参数表示一个依赖。调用指定参数的静态工厂方法来构造bean是近似等价的,这里的讨论将给构造函数和静态工厂方法传参看成是类似的。接下来的例子展示了一个类仅能通过构建函数注入进行依赖注入。注意这个类没什么特别的,它是一个POJO,不依赖于容器特定的接口,基类或注解。
1 | public class SimpleMovieLister { |
Constructor argument resolution
Constructor argument resolution matching occurs using the argument’s type. If no potential ambiguity exists in the constructor arguments of a bean definition, then the order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor when the bean is being instantiated. Consider the following class:
构造函数参数解析使用参数类型进行匹配。如果bean定义的构造函数参数中不存在潜在的歧义,bean定义中定义构造函数参数的顺序为bean实例化时提供给恰当构造函数的参数顺序。细想下面的类:
1 | package x.y; |
No potential ambiguity exists, assuming that Bar
and Baz
classes are not related by inheritance. Thus the following configuration works fine, and you do not need to specify the constructor argument indexes and/or types explicitly in the <constructor-arg/>
element.
不存在潜在的歧义,假设Bar
类和Baz
类之间不存在继承关系。因此下面的配置会工作良好,你不必在<constructor-arg/>
元素中显式的指定构造函数参数索引的与/或类型。
1 | <beans> |
When another bean is referenced, the type is known, and matching can occur (as was the case with the preceding example). When a simple type is used, such as <value>true</value>
, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class:
当引用另一个bean时,类型已知,匹配正确(像上面的例子一样)。当使用简单类型时,例如<value>true</value>
,Spring不能决定值的类型,因此没有帮助不能按类型匹配。考虑下面的例子:
1 | package examples; |
In the preceding scenario, the container can use type matching with simple types if you explicitly specify the type of the constructor argument using the type attribute. For example:
在上面的场景中,如果你用type
属性显式的指定了构造参数的类型,对于简单类型容器可以使用类型匹配。例如:
1 | <bean id="exampleBean" class="examples.ExampleBean"> |
Use the index
attribute to specify explicitly the index of constructor arguments. For example:
使用index
属性来显式的指定构造函数参数的索引,例如:
1 | <bean id="exampleBean" class="examples.ExampleBean"> |
In addition to resolving the ambiguity of multiple simple values, specifying an index resolves ambiguity where a constructor has two arguments of the same type. Note that the index is 0 based.
除了要解析多个简单值的歧义性之外,当构造函数有两个相同类型的的参数时,指定索引可以解决歧义问题。注意索引是从0开始的。
You can also use the constructor parameter name for value disambiguation:
你也可以使用构造函数参数名字解决值的歧义问题。
1 | <bean id="exampleBean" class="examples.ExampleBean"> |
Keep in mind that to make this work out of the box your code must be compiled with the debug flag enabled so that Spring can look up the parameter name from the constructor. If you can’t compile your code with debug flag (or don’t want to) you can use @ConstructorProperties
JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:
记住,要使这个起作用你的代码必须使用调试模式进行编译,这样Spring可以从构造函数中查找参数名称。如果你不能用调试模式进行编译(或不想),你可以使用JDK注解@ConstructorProperties
显式的命名你的构造函数参数。样板类如下所示:
1 | package examples; |
Setter-based dependency injection
Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean.
基于Setter的依赖注入在容器调用无参构造函数或无参静态工厂方法之后,通过调用bean的setter方法来实现依赖注入。
The following example shows a class that can only be dependency-injected using pure setter injection. This class is conventional Java. It is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
下面的例子显示了一个类只能通过纯粹的setter注入进行依赖注入。这个类是常见的Java类。它是一个不依赖于容器中特定接口、基类或注解的POJO。
1 | public class SimpleMovieLister { |
The ApplicationContext
supports constructor-based and setter-based DI for the beans it manages. It also supports setter-based DI after some dependencies have already been injected through the constructor approach. You configure the dependencies in the form of a BeanDefinition
, which you use in conjunction with PropertyEditor
instances to convert properties from one format to another. However, most Spring users do not work with these classes directly (i.e., programmatically) but rather with XML bean definitions, annotated components (i.e., classes annotated with @Component
, @Controller
, etc.), or @Bean
methods in Java-based @Configuration
classes. These sources are then converted internally into instances of BeanDefinition
and used to load an entire Spring IoC container instance.
ApplicationContext
支持基于构造函数和基于setter对它管理的bean进行依赖注入。它也支持一些依赖通过构造函数方法注入之后,使用基于setter的依赖注入。使用BeanDefinition
形式配置依赖项,结合PropertyEditor
实例可以将属性从一种形式转成另一种形式。然而大多数Spring用户直接使用这些类(例如以编程形式),而使用XML定义bean,注解组件(例如类中使用 @Component
,,@Controller
注解等等),或在基于Java的@Configuration
类使用@Bean
方法。
Constructor-based or setter-based DI?
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the
@Required
annotation on a setter method can be used to make the property a required dependency.The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not
null
. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through
JMX MBeans
is therefore a compelling use case for setter injection.Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.
使用基于构造函数的依赖注入还是基于setter的依赖注入?
你可以混合使用基于构造函数的依赖注入和基于setter的依赖注入,强制依赖使用构造函数注入,可选依赖使用setter方法或配置方法注入是一个很好的经验法则。注意在setter方法上使用
@Required
注解会检查依赖是否注入。当实现的应用组件是不可变对象时,Spring团队通常主张构造函数注入,这样可以确保所需的依赖非空。此外,基于构造函数注入的组件总是以完全初始化状态返回客户(调用)代码。作为附注,含有许多构造函数参数的代码给人的感觉很差,这意味着类可能有很多职责,应该进行重构以便更好的处理关注问题的分离。
setter注入应该主要用来可选依赖上,在类内可以给可选依赖指定合理的默认值。此外,在每处使用依赖的代码都要进行非空检查。setter注入的一个好处就是setter方法使类的对象在后面可以进行再配置或再注入。
JMX MBeans
的管理是setter注入一个非常好的案例。使用依赖注入的类型对于特定的类是最有意义的。有时候,当处理没有源码的第三方类时,使用哪种方式取决于你。例如,如果第三方库没有提供任何setter方法,构造函数注入可能是依赖注入唯一可行的方式。
Dependency resolution process
The container performs bean dependency resolution as follows:
The
ApplicationContext
is created and initialized with configuration metadata that describes all the beans. Configuration metadata can be specified via XML, Java code, or annotations.For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method if you are using that instead of a normal constructor. These dependencies are provided to the bean, when the bean is actually created.
Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container.
Each property or constructor argument which is a value is converted from its specified format to the actual type of that property or constructor argument. By default Spring can convert a value supplied in string format to all built-in types, such as int, long, String, boolean, etc.
容器按下面的过程处理bean依赖解析:
创建
ApplicationContext
并使用描述所有bean的配置元数据初始化ApplicationContext
,配置元数据可以通过XML,Java代码或注解指定。对于每一个bean,它的依赖通过属性、构造函数参数、或静态工厂方法参数的形式表示,静态工厂方法可以替代标准的构造函数。当bean在实际创建时,这些依赖会提供给bean。
每个属性或构造函数参数或者是根据实际定义设置的值,或者是容器中另一个bean的引用。
每个属性或构造函数参数是一个从指定形式转成实际类型的属性或构造函数参数的值。
The Spring container validates the configuration of each bean as the container is created. However, the bean properties themselves are not set until the bean is actually created. Beans that are singleton-scoped and set to be pre-instantiated (the default) are created when the container is created. Scopes are defined in Section 3.5, “Bean scopes”. Otherwise, the bean is created only when it is requested. Creation of a bean potentially causes a graph of beans to be created, as the bean’s dependencies and its dependencies’ dependencies (and so on) are created and assigned. Note that resolution mismatches among those dependencies may show up late, i.e. on first creation of the affected bean.
当容器创建后Spring容器会验证每个bean的配置。然而,bean属性本身只有bean创建时才会进行设置。bean是单例的并且当容器创建时会进行提前实例化(默认情况)。作用范围是在3.5 小节”Bean scopes”中定义的。此外,只有需要时候才会创建bean。bean的创建可能会引起beans图的创建,当bean的依赖和它的依赖的依赖(等等)创建和指定的时候。注意这些依赖中解析不匹配可能会在后面出现,例如,受影响的bean第一次创建时。
Circular dependencies
If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a
BeanCurrentlyInCreationException
.One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken/egg scenario).
循环依赖
如果你主要使用构造函数注入,有可能会出现一个不能解决的循环依赖状况。
例如,类A需要通过构造函数注入得到一个类B的实例,而类B需要通过构造函数获得一个类A的实例。如果你为类A和类B配置了互相注入的bean,Spring IoC容器在运行时检测到循环引用,会抛出
BeanCurrentlyInCreationException
。一个可能的解决方案是编译某个类的源代码使其通过setter注入而不是构造函数注入。或者,避免构造函数注入仅用setter注入。换句话说,尽管是不被推荐的,但你可以通过setter注入配置循环依赖。
不像通常的情况(没有循环依赖),bean A和bean B之间的循环依赖可以强制其中的一个bean优先注入另一个bean中,可以使其完全初始化(古老的鸡/蛋场景)。
You can generally trust Spring to do the right thing. It detects configuration problems, such as references to non-existent beans and circular dependencies, at container load-time. Spring sets properties and resolves dependencies as late as possible, when the bean is actually created. This means that a Spring container which has loaded correctly can later generate an exception when you request an object if there is a problem creating that object or one of its dependencies. For example, the bean throws an exception as a result of a missing or invalid property. This potentially delayed visibility of some configuration issues is why ApplicationContext implementations by default pre-instantiate singleton beans. At the cost of some upfront time and memory to create these beans before they are actually needed, you discover configuration issues when the ApplicationContext
is created, not later. You can still override this default behavior so that singleton beans will lazy-initialize, rather than be pre-instantiated.
通常情况下你可以信任Spring去做正确的事情。在容器加载时它检测配置问题,例如引用不存在的beans和循环依赖。当bean实际创建时,Spring设置属性和解析依赖尽可能的晚。这意味着Spring容器正确加载但后面可能会产生异常,当你请求一个对象时,创建对象或它的某个依赖时出现问题,这时容器就会抛出异常。例如,由于缺失或存在无效属性,bean会抛出异常。在真正需要这些beans之前创建它们,会花费一些前期时间和内存,但当ApplicationContext
创建时你会发现配置问题,而不是在创建之后。为了单例bean延迟初始化而不是预先实例化,你仍需要重写这个默认行为。
If no circular dependencies exist, when one or more collaborating beans are being injected into a dependent bean, each collaborating bean is totally configured prior to being injected into the dependent bean. This means that if bean A has a dependency on bean B, the Spring IoC container completely configures bean B prior to invoking the setter method on bean A. In other words, the bean is instantiated (if not a pre-instantiated singleton), its dependencies are set, and the relevant lifecycle methods (such as a configured init method or the InitializingBean callback method) are invoked.
如果没有循环依赖存在,当一个或更多协作beans注入到一个独立的bean中,在注入独立bean之前,每个协作bean都是完全配置的。这意味着如果bean A有个依赖为bean B,Spring IoC容器在调用bean A的setter方法之前会完整的配置bean B。换句话说,bean被实例化(不是预先实例化的单例),设置依赖和相关的生命周期方法(例如配置初始化方法或初始化bean回调方法)被调用。
Examples of dependency injection
The following example uses XML-based configuration metadata for setter-based DI. A small part of a Spring XML configuration file specifies some bean definitions:
下面的例子使用基于XML的配置元数据进行setter注入。Spring XML配置文件中的一小部分指定了一些bean的定义:
1 | <bean id="exampleBean" class="examples.ExampleBean"> |
1 | public class ExampleBean { |
In the preceding example, setters are declared to match against the properties specified in the XML file. The following example uses constructor-based DI:
在上面的例子中,setter声明匹配XML文件中指定的属性。下面的例子使用了基于构造函数的依赖注入:
1 | <bean id="exampleBean" class="examples.ExampleBean"> |
1 | public class ExampleBean { |
The constructor arguments specified in the bean definition will be used as arguments to the constructor of the ExampleBean
.
bean定义中指定的构造函数参数将作为ExampleBean
的构造函数参数使用。
Now consider a variant of this example, where instead of using a constructor, Spring is told to call a static factory method to return an instance of the object:
现在考虑这个例子的一个变种,不使用构造函数,而是Spring调用静态工厂方法返回对象的一个实例:
1 | <bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> |
1 | public class ExampleBean { |
Arguments to the static
factory method are supplied via <constructor-arg/>
elements, exactly the same as if a constructor had actually been used. The type of the class being returned by the factory method does not have to be of the same type as the class that contains the static
factory method, although in this example it is. An instance (non-static) factory method would be used in an essentially identical fashion (aside from the use of the factory-bean
attribute instead of the class
attribute), so details will not be discussed here.
静态工厂方法的参数通过<constructor-arg/>
元素提供,与构造函数使用的完全一样。虽然这个例子中工厂方法返回值的类型与包含静态工厂方法的类的类型一样,但它们可以不一样。工厂方法的实例(非静态)的使用本质上样式完全一样(除了使用factory-bean
属性代替class
属性之外),因此这儿不讨论这些细节。
3.4.2 Dependencies and configuration in detail
As mentioned in the previous section, you can define bean properties and constructor arguments as references to other managed beans (collaborators), or as values defined inline. Spring’s XML-based configuration metadata supports sub-element types within its <property/>
and <constructor-arg/>
elements for this purpose.
正如上一节提到的那样,你可以通过引用其它被管理bean(协作者)来定义bean的属性和构造函数参数,或者在行内定义值。为了实现这个功能,Spring的基于XML的配置元数据在它的<property/>
和<constructor-arg/>
中支持子元素类型。
Straight values (primitives, Strings, and so on)
The value
attribute of the <property/>
element specifies a property or constructor argument as a human-readable string representation. Spring’s conversion service is used to convert these values from a String
to the actual type of the property or argument.
<property/>
元素的value
属性指定了一个属性或构造函数参数作为可读的字符串表示。使用Spring的转换服务将这些值从String
转成属性或参数的真实类型。
1 | <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
The following example uses the p-namespace for even more succinct XML configuration.
下面的例子为了更简洁的XML配置使用了p命名空间.
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
The preceding XML is more succinct; however, typos are discovered at runtime rather than design time, unless you use an IDE such as IntelliJ IDEA
or the Spring Tool Suite (STS)
that support automatic property completion when you create bean definitions. Such IDE assistance is highly recommended.
上面的XML是更简洁的;然而,错别字是在运行时发现而不是在设计时,除非你使用IDE例如IntelliJ IDEA
或Spring Tool Suite (STS)
,当你创建bean定义时它们支持自动的属性补全。IDE辅助是强烈推荐的。
You can also configure a java.util.Properties
instance as:
你也可以配置java.util.Properties
实例:
1 | <bean id="mappings" |
The Spring container converts the text inside the <value/>
element into a java.util.Properties
instance by using the JavaBeans PropertyEditor
mechanism. This is a nice shortcut, and is one of a few places where the Spring team do favor the use of the nested <value/>
element over the value
attribute style.
Spring容器通过JavaBeans的PropertyEditor
机制将<value/>
元素内部的文本转成java.util.Properties
实例。这是一个很好的捷径,使用嵌入的<value/>
元素而不是使用value
属性的方式,是Spring团队支持的几个地方之一。
The idref element
The idref
element is simply an error-proof way to pass the id (string value - not a reference) of another bean in the container to a <constructor-arg/>
or <property/>
element.
在容器中传递另一个bean的id(字符串值,不是引用)到<constructor-arg/>
或<property/>
元素时,idref
元素是一种简单的的误差检验方式。
1 | <bean id="theTargetBean" class="..."/> |
The above bean definition snippet is exactly equivalent (at runtime) to the following snippet:
上面的bean定义片段与下面的代码片段是等价的(运行时):
1 | <bean id="theTargetBean" class="..." /> |
The first form is preferable to the second, because using the idref
tag allows the container to validate at deployment time that the referenced, named bean actually exists. In the second variation, no validation is performed on the value that is passed to the targetName
property of the client
bean. Typos are only discovered (with most likely fatal results) when the client
bean is actually instantiated. If the client
bean is a prototype bean, this typo and the resulting exception may only be discovered long after the container is deployed.
第一种形式优于第二种形式,因为idref
标签允许容器在部署时验证引用的bean,即命名的bean实际存在。在第二种形式中,当值传给client
的targetName
时没有进行验证。拼写错误只有在client
bean实际创建时才会发现(最可能有严重后果)。如果client
bean是原型bean,拼写错误和产生的异常可能只有在容器部署很长时间之后才会发现。
The
local
attribute on theidref
element is no longer supported in the 4.0 beans xsd since it does not provide value over a regular bean reference anymore. Simply change your existingidref local
references toidref bean
when upgrading to the 4.0 schema.
idref
元素的local
属性在4.0 beans xsd中不再支持,因为它不再为合格的bean引用提供值。简单将你现有的idref local
引用改成idref bean
当更新到4.0 schema时。
A common place (at least in versions earlier than Spring 2.0) where the <idref/>
element brings value is in the configuration of AOP interceptors in a ProxyFactoryBean
bean definition. Using <idref/>
elements when you specify the interceptor names prevents you from misspelling an interceptor id.
<idref/>
元素带来值的通常位置(至少在Spring 2.0之前)是在ProxyFactoryBean
bean定义中的AOP拦截器配置中。当你指定拦截器名字时使用<idref/>
元素来防止误拼拦截器id。
References to other beans (collaborators)
The ref
element is the final element inside a <constructor-arg/>
or <property/>
definition element. Here you set the value of the specified property of a bean to be a reference to another bean (a collaborator) managed by the container. The referenced bean is a dependency of the bean whose property will be set, and it is initialized on demand as needed before the property is set. (If the collaborator is a singleton bean, it may be initialized already by the container.) All references are ultimately a reference to another object. Scoping and validation depend on whether you specify the id/name of the other object through the bean
, local
, or parent
attributes.
ref
元素是<constructor-arg/>
或<property/>
定义元素的最终的元素。在这个元素中设置bean的指定属性的值,值为容器管理的另一个bean(协作bean)的引用。引用的bean是设置属性bean的依赖,在属性设置之前引用bean需要进行初始化。(如果协作bean是一个单例模式的bean,它可能已经被容器初始化了。)所有引用bean根本上都是另一个对象的引用。作用域和验证是根据你是否通过bean
,local
,或parent
属性指定了另一个对象的id/name来决定的。
Specifying the target bean through the bean
attribute of the <ref/>
tag is the most general form, and allows creation of a reference to any bean in the same container or parent container, regardless of whether it is in the same XML file. The value of the bean
attribute may be the same as the id
attribute of the target bean, or as one of the values in the name
attribute of the target bean.
通过<ref/>
标签的bean
属性指定目标bean是最常用的形式,允许创建同容器或父容器中任何bean的引用,不管它是否是在同一个XML文件中。bean
属性的值可能与目标bean的id
属性值相同,或与目标bean的name
属性值相同。
1 | <ref bean="someBean"/> |
Specifying the target bean through the parent
attribute creates a reference to a bean that is in a parent container of the current container. The value of the parent
attribute may be the same as either the id
attribute of the target bean, or one of the values in the name
attribute of the target bean, and the target bean must be in a parent container of the current one. You use this bean reference variant mainly when you have a hierarchy of containers and you want to wrap an existing bean in a parent container with a proxy that will have the same name as the parent bean.
通过parent
属性指定目标bean会引用当前容器的父容器中的bean。parent
属性的值可能与目标bean的id
值或name
值相同,目标bean必须在当前容器的父容器中。当你有一个容器分层的时候你可以使用parent
,你想将现有bean包裹在有代理的父容器中且现有bean与父容器中的bean同名,你可以使用parent
属性。
1 | <!-- in the parent context --> |
1 | <!-- in the child (descendant) context --> |
The
local
attribute on theref
element is no longer supported in the 4.0 beans xsd since it does not provide value over a regularbean
reference anymore. Simply change your existingref local
references toref bean
when upgrading to the 4.0 schema.
idref
元素的local
属性在4.0 beans xsd中不再支持,因为它不再为合格的bean引用提供值。简单将你现有的idref local
引用改成idref bean
当更新到4.0 schema时。
Inner beans
A <bean/>
element inside the <property/>
or <constructor-arg/>
elements defines a so-called inner bean.
<property/>
或<constructor-arg/>
元素内的<bean/>
元素中定义bean称为内部bean。
1 | <bean id="outer" class="..."> |
An inner bean definition does not require a defined id or name; if specified, the container does not use such a value as an identifier. The container also ignores the scope
flag on creation: Inner beans are always anonymous and they are always created with the outer bean. It is not possible to inject inner beans into collaborating beans other than into the enclosing bean or to access them independently.
内部bean定义不要求定义id或name;如果指定了,容器不用用这个值作为标识符。容器创建时也忽略scope
标记:内部bean总是匿名的且它们总是由外部bean创建。除了注入到封闭bean中或独立的访问它们,不可能将内部bean注入到协作bean中。
As a corner case, it is possible to receive destruction callbacks from a custom scope, e.g. for a request-scoped inner bean contained within a singleton bean: The creation of the inner bean instance will be tied to its containing bean, but destruction callbacks allow it to participate in the request scope’s lifecycle. This is not a common scenario; inner beans typically simply share their containing bean’s scope.
作为一种很少出现的情况,从特定的域中有可能会收到销毁回调函数,例如,对于请求域内的内部bean包含单例bean:内部bean实例的创建会绑定到它的包含bean,但销毁回调函数允许它进入到请求域的生命周期中。这不是一个常见的场景;内部bean通常简单的共享它们的包含bean的作用域。
Collections
In the <list/>
, <set/>
, <map/>
, and <props/>
elements, you set the properties and arguments of the Java Collection
types List
, Set
, Map
, and Properties
, respectively.
在<list/>
,<set/>
,<map/>
和<props/>
元素中,你要分别设置Java Collection
类型list
,set
,map
和Properties
的属性和参数。
1 | <bean id="moreComplexObject" class="example.ComplexObject"> |
The value of a map key or value, or a set value, can also again be any of the following elements:
map的key或value,或者是set value的值也可以是下面元素中的任何一个:
1 | bean | ref | idref | list | set | map | props | value | null |
Collection merging
The Spring container also supports the merging of collections. An application developer can define a parent-style <list/>
, <map/>
, <set/>
or <props/>
element, and have child-style <list/>
, <map/>
, <set/>
or <props/>
elements inherit and override values from the parent collection. That is, the child collection’s values are the result of merging the elements of the parent and child collections, with the child’s collection elements overriding values specified in the parent collection.
Spring也支持集合的合并。应用开发者可以定义父类型<list/>
,<map/>
,<set/>
或<props/>
元素,可以有继承和覆盖父集合的子类型元素<list/>
,<map/>
,<set/>
或<props/>
。也就是说,子集合的值是父集合和子集合中元素合并的结果,子集合元素覆盖了父集合元素的值。
This section on merging discusses the parent-child bean mechanism. Readers unfamiliar with parent and child bean definitions may wish to read the relevant section before continuing.
关于合并的这节讨论了父子bean机制。对父子bean定义不熟悉的读者可以去读相关的章节。
The following example demonstrates collection merging:
下面的例子示范了集合合并:
1 | <beans> |
Notice the use of the merge=true
attribute on the <props/>
element of the adminEmails
property of the child
bean definition. When the child
bean is resolved and instantiated by the container, the resulting instance has an adminEmails
Properties
collection that contains the result of the merging of the child’s adminEmails
collection with the parent’s adminEmails
collection.
注意child
bean定义中的adminEmails
属性下的<props/>
元素使用了merge=true
属性。当容器解析并实例化child
bean时,最终的实例含有adminEmails
Properties
集合,集合中的值是子adminEmails
集合和父adminEmails
集合合并的结果。
1 | administrator=administrator@example.com |
The child Properties
collection’s value set inherits all property elements from the parent <props/>
, and the child’s value for the support
value overrides the value in the parent collection.
子Properties
集合的值继承了父<props/>
中的所有属性元素,子集合中的support
值覆盖了父集合中的值。
This merging behavior applies similarly to the <list/>
, <map/>
, and <set/>
collection types. In the specific case of the <list/>
element, the semantics associated with the List
collection type, that is, the notion of an ordered
collection of values, is maintained; the parent’s values precede all of the child list’s values. In the case of the Map
, Set
, and Properties
collection types, no ordering exists. Hence no ordering semantics are in effect for the collection types that underlie the associated Map
, Set
, and Properties
implementation types that the container uses internally.
<list/>
,<map/>
和<set/>
集合类型中的合并与上面类似。在特定的<list/>
元素情况下,关于List
集合类型的语义,也就是说,有序集合值的概念仍然是保留的;父list中的值领先于所有子list中的值。在Map
,Set
和Properties
集合类型,不存在顺序。因此,无序语义在容器内部使用的集合类型Map
,Set
和Properties
的实现基础上是有效的。
Limitations of collection merging
You cannot merge different collection types (such as a Map
and a List
), and if you do attempt to do so an appropriate Exception
is thrown. The merge
attribute must be specified on the lower, inherited, child definition; specifying the merge
attribute on a parent collection definition is redundant and will not result in the desired merging.
你不能合并不同的集合类型(例如Map
和List
),如果你试图合并不同的集合类型会有适当的抛出Exception
。merge
属性必须在更低的、继承的子定义中;在父集合定义中指定merge
属性是多余的并且不会进行合并。
Strongly-typed collection
With the introduction of generic types in Java 5, you can use strongly typed collections. That is, it is possible to declare a Collection
type such that it can only contain String
elements (for example). If you are using Spring to dependency-inject a strongly-typed Collection
into a bean, you can take advantage of Spring’s type-conversion support such that the elements of your strongly-typed Collection
instances are converted to the appropriate type prior to being added to the Collection
.
随着Java 5中泛型的引入,你可以使用强类型集合。也就是说,你可以声明一个Collection
类型但它只能包含String
元素(例子)。如果你使用Spring将一个强类型的Collection
注入到bean中,你可以利用Spring的类型转换支持,例如在将元素添加到Collection
之前,将你的强类型Collection
实例中的元素转成恰当的类型。
1 | public class Foo { |
1 | <beans> |
When the accounts
property of the foo
bean is prepared for injection, the generics information about the element type of the strongly-typed Map<String, Float>
is available by reflection. Thus Spring’s type conversion infrastructure recognizes the various value elements as being of type Float
, and the string values 9.99, 2.75
, and 3.99
are converted into an actual Float
type.
当注入foo
bean的accounts
属性时,强类型Map<String, Float>
中元素类型的泛型信息可以通过反射得到。因此Spring的类型转换结构能识别各种值元素的类型为Float
,字符串9.99, 2.75
和3.99
会被转换成实际的Float
类型。
Null and empty string values
Spring treats empty arguments for properties and the like as empty Strings
. The following XML-based configuration metadata snippet sets the email property to the empty String
value (“”).
Spring把属性的空参数都处理为空Strings
。下面基于XML的配置元数据片段将email属性设为空String
值(“”)。
1 | <bean class="ExampleBean"> |
The preceding example is equivalent to the following Java code:
上面的例子与下面的Java代码是等价的:
1 | exampleBean.setEmail("") |
The <null/>
element handles null
values. For example:
<null/>
元素处理null
值。例如:
1 | <bean class="ExampleBean"> |
The above configuration is equivalent to the following Java code:
上面的配置与下面的Java代码等价。
1 | exampleBean.setEmail(null) |
XML shortcut with the p-namespace
The p-namespace enables you to use the bean
element’s attributes, instead of nested <property/>
elements, to describe your property values and/or collaborating beans.
p命名空间可以让你不需要嵌入<property/>
元素便能使用bean
元素的属性来描述你的属性值以及/或协作beans。
Spring supports extensible configuration formats with namespaces, which are based on an XML Schema definition. The beans configuration format discussed in this chapter is defined in an XML Schema document. However, the p-namespace is not defined in an XSD file and exists only in the core of Spring.
Spring支持含有命名空间的扩展配置形式,命名控件是基于XML Schema定义的。本章讨论的beans配置形式是在XML Schema文档中定义的。但是p命名空间不能在XSD文件中定义并且只在Spring core中存在。
The following example shows two XML snippets that resolve to the same result: The first uses standard XML format and the second uses the p-namespace.
下面的例子显示了两个XML片段,解析结果是相同的:第一个是标准的XML形式,第二个使用了p命名空间。
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
The example shows an attribute in the p-namespace called email in the bean definition. This tells Spring to include a property declaration. As previously mentioned, the p-namespace does not have a schema definition, so you can set the name of the attribute to the property name.
这个例子显示了bean定义中p命名空间中有个一个叫email的属性。这会通知Spring包含属性声明。如前面所述,p命名空间没有schema定义,因此你可以将特性值(attribute)设到属性值(property)上。
This next example includes two more bean definitions that both have a reference to another bean:
下面的例子包括两个bean定义,且它们都引用了另一个bean:
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
As you can see, this example includes not only a property value using the p-namespace, but also uses a special format to declare property references. Whereas the first bean definition uses <property name="spouse" ref="jane"/>
to create a reference from bean john
to bean jane
, the second bean definition uses p:spouse-ref="jane"
as an attribute to do the exact same thing. In this case spouse
is the property name, whereas the -ref
part indicates that this is not a straight value but rather a reference to another bean.
正如你所看到的,这个例子不仅包括使用了p命名空间的属性值,而且使用了一种特定的形式来声明属性引用。然而第一个bean定义使用<property name="spouse" ref="jane"/>
创建了一个从bean john
到bean jane
的引用,第二个bean定义使用p:spouse-ref="jane"
作为一个特性同样定义了从bean john
到bean jane
的引用。在spouse
是属性名的情况下,-ref
部分表示这不是一个直接的值而是另一个bean的引用。
The p-namespace is not as flexible as the standard XML format. For example, the format for declaring property references clashes with properties that end in
Ref
, whereas the standard XML format does not. We recommend that you choose your approach carefully and communicate this to your team members, to avoid producing XML documents that use all three approaches at the same time.
p命名空间不是标准的XML格式,例如,声明的属性引用会与以
Ref
结尾的属性相冲突,而标准XML格式则不会。我们建议你仔细的选择你的方法并与你的团队成员交流,避免生成的XML文档同时使用了三种方式。
XML shortcut with the c-namespace
Similar to the the section called “XML shortcut with the p-namespace”, the c-namespace, newly introduced in Spring 3.1, allows usage of inlined attributes for configuring the constructor arguments rather then nested constructor-arg
elements.
与“XML shortcut with the p-namespace”小节类似,在Spring 3.1新引入的c命名空间允许使用行内属性配置构造函数参数而不用嵌入constructor-arg
元素。
Let’s review the examples from the section called “Constructor-based dependency injection” with the c:
namespace:
让我们重新回顾一下“Constructor-based dependency injection”小节中的例子并使用c:
命名空间:
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
The c:
namespace uses the same conventions as the p:
one (trailing -ref
for bean references) for setting the constructor arguments by their names. And just as well, it needs to be declared even though it is not defined in an XSD schema (but it exists inside the Spring core).
c:
命名空间遵循与p:
命名空间相同的约定在通过名字设置构造函数参数时。同样的,它也需要进行声明,虽然它不能在XSD schema中使用(但在Spring core中存在)。
For the rare cases where the constructor argument names are not available (usually if the bytecode was compiled without debugging information), one can use fallback to the argument indexes:
对于很少出现的不能找到构造函数参数名字的情况(通常如果编译字节码且没有调试信息),可以使用参数索引:
1 | <!-- c-namespace index declaration --> |
Due to the XML grammar, the index notation requires the presence of the leading
_
as XML attribute names cannot start with a number (even though some IDE allow it).
由于XML语法,索引符号需要前面加上
_
,因为XML属性名字不能以数字开头(即使一些IDE允许)。
In practice, the constructor resolution mechanism is quite efficient in matching arguments so unless one really needs to, we recommend using the name notation through-out your configuration.
在实践中,构造函数解析机制能有效匹配参数,因此除非真的需要,否则我们推荐在配置中使用名字符号。
Compound property names
You can use compound or nested property names when you set bean properties, as long as all components of the path except the final property name are not null
. Consider the following bean definition.
当你设置bean属性时,你可以使用混合的或嵌入的属性名字,只要路径中除了最后的属性名之外所有组件都是非null
。考虑下面的bean定义。
1 | <bean id="foo" class="foo.Bar"> |
The foo
bean has a fred
property, which has a bob
property, which has a sammy
property, and that final sammy
property is being set to the value 123
. In order for this to work, the fred
property of foo
, and the bob
property of fred
must not be null
after the bean is constructed, or a NullPointerException
is thrown.
foo
bean有一个fred
属性,fred
有一个sammy
属性,bob
有一个sammy
属性,最后的sammy
属性设置值为123
。为了这样设置,foo
的fred
属性,fred
的bob
属性在bean创建后必须是非null
或抛出NullPointerException
。
3.4.3 Using depends-on
If a bean is a dependency of another that usually means that one bean is set as a property of another. Typically you accomplish this with the <ref/>
element in XML-based configuration metadata. However, sometimes dependencies between beans are less direct; for example, a static initializer in a class needs to be triggered, such as database driver registration. The depends-on
attribute can explicitly force one or more beans to be initialized before the bean using this element is initialized. The following example uses the depends-on
attribute to express a dependency on a single bean:
如果一个bean是另一个bean的一个依赖,这通常意味着一个bean作为另一个bean的一个属性去设置。在基于XML的配置元数据中通常使用<ref/>
元素实现。然而有时beans之间的依赖关系是间接的;例如,类中的静态初始化程序需要触发,例如数据驱动注册。depends-on
特性能显示的强制一个bean或多个beans在使用这个元素的bean初始化之前进行初始化。下面的例子使用depends-on
特性表示一个单一bean的一个依赖:
1 | <bean id="beanOne" class="ExampleBean" depends-on="manager"/> |
To express a dependency on multiple beans, supply a list of bean names as the value of the depends-on
attribute, with commas, whitespace and semicolons, used as valid delimiters:
为了表示多个bean上的依赖关系,提供一个bean名字列表作为depends-on
特性的值,用逗号,空格或分号作为有效分隔符:
1 | <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"> |
The
depends-on
attribute in the bean definition can specify both an initialization time dependency and, in the case of singleton beans only, a corresponding destroy time dependency. Dependent beans that define adepends-on
relationship with a given bean are destroyed first, prior to the given bean itself being destroyed. Thusdepends-on
can also control shutdown order.
depends-on
特性在bean定义中可以指定初始化时的依赖和对应的销毁时依赖(仅在单例情况下)。依赖beans与给定bean之间定义了一个depends-on
关系,依赖beans在给定bean本身被销毁之前首先被销毁。因此depends-on
也可以控制销毁顺序。
3.4.4 Lazy-initialized beans
By default, ApplicationContext
implementations eagerly create and configure all singleton beans as part of the initialization process. Generally, this pre-instantiation is desirable, because errors in the configuration or surrounding environment are discovered immediately, as opposed to hours or even days later. When this behavior is not desirable, you can prevent pre-instantiation of a singleton bean by marking the bean definition as lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.
默认情况下,作为初始化过程的一部分,ApplicationContext
实现时渴望创建并配置所有的单例beans。通常情况下,预实例化是必要的,因为配置中或周围环境中的错误可以立即发现,与几小时或几天后发现截然相反。当预实例化是不必要的时候,你可通过标记bean定义为延迟初始化来阻止单例bean的预实例化。延迟初始化的bean会通知IoC容器当第一次请求bean时创建一个bean实例,而不是在启动时创建。
In XML, this behavior is controlled by the lazy-init
attribute on the <bean/>
element; for example:
在XML中,延迟初始化通过<bean/>
元素中的lazy-init
特性来控制;例如:
1 | <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> |
When the preceding configuration is consumed by an ApplicationContext
, the bean named lazy
is not eagerly pre-instantiated when the ApplicationContext
is starting up, whereas the not.lazy
bean is eagerly pre-instantiated.
当ApplicationContext
读取到上面的配置,ApplicationContext
启动时名字为lazy
的bean不会进行预实例化,而名字为not.lazy
的bean会进行预实例化。
However, when a lazy-initialized bean is a dependency of a singleton bean that is not lazy-initialized, the ApplicationContext
creates the lazy-initialized bean at startup, because it must satisfy the singleton’s dependencies. The lazy-initialized bean is injected into a singleton bean elsewhere that is not lazy-initialized.
然而,当延迟初始化的bean是一个非延迟初始化的单例bean的依赖时,ApplicationContext
会在启动时创建延迟初始化的bean,因为它必须提供单例bean的依赖。延迟初始化的bean会注入到单例bean中,而在其它地方它是非延迟初始化的。
You can also control lazy-initialization at the container level by using the default-lazy-init
attribute on the <beans/>
element; for example:
你也可以在容器中通过<beans/>
中的default-lazy-init
特性控制延迟初始化;例如:
1 | <beans default-lazy-init="true"> |
3.4.5 Autowiring collaborators
The Spring container can autowire relationships between collaborating beans. You can allow Spring to resolve collaborators (other beans) automatically for your bean by inspecting the contents of the ApplicationContext
. Autowiring has the following advantages:
Autowiring can significantly reduce the need to specify properties or constructor arguments. (Other mechanisms such as a bean template discussed elsewhere in this chapter are also valuable in this regard.)
Autowiring can update a configuration as your objects evolve. For example, if you need to add a dependency to a class, that dependency can be satisfied automatically without you needing to modify the configuration. Thus autowiring can be especially useful during development, without negating the option of switching to explicit wiring when the code base becomes more stable.
Spring容器能自动装配协作beans之间的关联关系。你可以允许Spring通过检查ApplicationContext
中的内容自动的为你的bean解析协作者(其它bean)。自动装配有以下优势:
自动装配能明显减少指定属性或构造函数参数的需要。(其它的机制例如在本章其它地方讨论的bean模板在这一点上也是非常重要的。)
当对象变化时自动装配能更新配置。例如,如果你需要增加一个类的依赖项,依赖项可以是满足自动装配的而不需要你去修改配置。因此自动装配在开发时尤其有用,当代码基础变的更稳定时可以改为显式装配。
When using XML-based configuration metadata, you specify autowire mode for a bean definition with the autowire
attribute of the <bean/>
element. The autowiring functionality has four modes. You specify autowiring per bean and thus can choose which ones to autowire.
当使用基于XML的配置元数据时,通过使用<bean/>
元素的autowire
特性你可以指定一个bean定义的自动装配模式。自动注入功能有四种模式。你可以指定每个bean的自动装配模式,因此你可以选择使用哪一种模式。
Table 3.2. Autowiring modes
Mode | Explanation |
---|---|
no | (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. |
byName | Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master , and uses it to set the property. |
byType | Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set. |
constructor | Analogous to byType , but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised. |
表 3.2 自动装配模式
模式 | 解析 |
---|---|
no | (默认)无自动装配。引用bean必须通过ref 元素定义。对于更大的部署,不推荐更改默认设置,因为显式指定协作者更清晰并且更易控制。在某种程度上来说,它记录了系统的结构。 |
byName | 通过属性名称自动装配。Spring寻找与需要自动装配的属性同名的bean。例如,如果一个bean定义设置为通过名称自动装配,它有一个master 属性(也就是说,它有一个setMaster(..) 方法),Spring寻找名字为master 的bean定义,使用它设置属性值。 |
byType | 如果容器中含有属性类型已知的一个bean,那么可以允许按类型自动装配属性。如果此类型的bean不止一个,则会抛出致命的异常,这意味着你可能不能使用byType 来注入那个bean。如果没有匹配的bean,则什么也不做;属性没有被设置。 |
constructor | 与byType 类似,但是应用到构造函数参数上的。如果容器中没有一个构造函数参数bean的确定类型,将会抛出一个致命的异常。 |
With byType or constructor autowiring mode, you can wire arrays and typed-collections. In such cases all autowire candidates within the container that match the expected type are provided to satisfy the dependency. You can autowire strongly-typed Maps if the expected key type is String
. An autowired Maps values will consist of all bean instances that match the expected type, and the Maps keys will contain the corresponding bean names.
通过byType
或构造函数自动装配模式,你可以配置数组和集合类型。在这种情况下容器内所有能匹配期望类型的自动装配候选对象将被提供合适的依赖项。如果期望的key
类型是String
类型,你可以自动装配强类型的Maps
。自动装配的Maps
的值将有所有匹配期望类型的bean组成,Maps
的键将包含对应的bean名称。
You can combine autowire behavior with dependency checking, which is performed after autowiring completes.
你可以将依赖检查与自动装配相结合,它将在自动装配完成之后执行。
Limitations and disadvantages of autowiring
Autowiring works best when it is used consistently across a project. If autowiring is not used in general, it might be confusing to developers to use it to wire only one or two bean definitions.
当自动装配在整个工程中一致的使用时其效果最好。如果通常情况下不使用自动装配,仅在一两个bean定义中使用自动装配开发人员可能感到非常困惑。
Consider the limitations and disadvantages of autowiring:
Explicit dependencies in
property
andconstructor-arg
settings always override autowiring. You cannot autowire so-called simple properties such as primitives,Strings
, andClasses
(and arrays of such simple properties). This limitation is by-design.Autowiring is less exact than explicit wiring. Although, as noted in the above table, Spring is careful to avoid guessing in case of ambiguity that might have unexpected results, the relationships between your Spring-managed objects are no longer documented explicitly.
Wiring information may not be available to tools that may generate documentation from a Spring container.
Multiple bean definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or Maps, this is not necessarily a problem. However for dependencies that expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean definition is available, an exception is thrown.
考虑一下自动装配的限制与缺点:
property
和constructor-arg
中显式依赖的设置总是会覆盖自动装配。你不能自动装配所谓的简单属性例如基本类型,Strings
和Classes
(和简单类型的数组)。这是设计上的限制。与显式配置相比,自动装配是更不确定的。尽管Spring小心的避免猜测以防歧义性引起无法预料的后果,但Spring管理的对象之间的关系不再被显式的记录。
Spring容器中能产生文档的工具可能得不到配置信息。
setter方法或构造函数参数指定的类型进行自动装配时可能匹配到容器中多个bean的定义。对于数组,集合或
Maps
而言,这是一个不必要的问题。然而对于只期望一个值的依赖而言,这个歧义性不能任意解决。如果不能获得唯一的bean定义,会抛出异常。
In the latter scenario, you have several options:
Abandon autowiring in favor of explicit wiring.
Avoid autowiring for a bean definition by setting its
autowire-candidate
attributes tofalse
as described in the next section.Designate a single bean definition as the primary candidate by setting the
primary
attribute of its<bean/>
element totrue
.Implement the more fine-grained control available with annotation-based configuration, as described in Section 3.9, “Annotation-based container configuration”.
后面的方案中,你有一些选择:
放弃自动装配支持显式配置。
通过设置bean的
autowire-candidate
特性为false
来避免自动装配。通过设置
<bean/>
元素的primary
特性为true
来指定一个单例bean定义作为主要的候选bean。通过基于注解的配置实现更多细颗粒的控制,如3.9小节 “基于注解的容器配置”。
Excluding a bean from autowiring
On a per-bean basis, you can exclude a bean from autowiring. In Spring’s XML format, set the autowire-candidate
attribute of the <bean/>
element to false
; the container makes that specific bean definition unavailable to the autowiring infrastructure (including annotation style configurations such as @Autowired
).
在单个bean的基础上,你可以排除bean在自动装配之外。在Spring的XML形式中,设置<bean/>
元素的autowire-candidate
特性为false
;容器会使自动装配基础框架不能得到指定bean定义(包括注解类型的配置,例如@Autowired
)。
You can also limit autowire candidates based on pattern-matching against bean names. The top-level <beans/>
element accepts one or more patterns within its default-autowire-candidates
attribute. For example, to limit autowire candidate status to any bean whose name ends with Repository, provide a value of *Repository
. To provide multiple patterns, define them in a comma-separated list. An explicit value of true
or false
for a bean definitions autowire-candidate
attribute always takes precedence, and for such beans, the pattern matching rules do not apply.
你也可以根据bean名称的匹配模式限制自动装配的候选目标。顶层的<beans/>
元素可以接收default-autowire-candidates
特性中的一个或多个模式。例如,为了限制自动装配候选目标匹配任何名字以Repository
结尾的bean,可以提供一个*Repository
值。为了提供多种模式,可以定义一个以逗号为分隔符的列表。bean定义中autowire-candidate
特性显示的值true
或false
最是优先起作用的,对于这些bean而言,模式匹配规则不起作用。
These techniques are useful for beans that you never want to be injected into other beans by autowiring. It does not mean that an excluded bean cannot itself be configured using autowiring. Rather, the bean itself is not a candidate for autowiring other beans.
这些技术对于那些你从不想通过自动装配方式注入到其它bean中的beans而言是很有用的。这不意味着一个排除的bean它本身不能通过自动装配进行配置。更确切的说,bean本身不是一个进行其它bean进行自动装配的候选者。
3.4.6 Method injection
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean, or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container only creates the singleton bean A once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
在大多数应用场景中,容器中的大多数bean是单例的。当一个单例bean需要与另一个单例bean协作时,或一个非单例bean需要与另一个非单例bean协作时,你通常通过定义一个bean作为另一个bean的一个属性来处理这个依赖关系。当bean的生命周期不同时问题就出现了。假设一个单例bean A需要使用非单例(标准)bean B时,也许A中的每一个方法调用都要使用bean B。容器仅创建单例bean A一次,因此仅有一次设置属性的机会。容器不能在每次需要bean B时提供一个bean B的新的实例。
A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware
interface, and by making a getBean(“B”) call to the container ask for (a typically new) bean B instance every time bean A needs it. The following is an example of this approach:
一个解决方案是放弃一些控制反转。你可以使bean A通过实现ApplicationContextAware
接口感知到容器,每个bean A需要的时候就通过getBean("B")
调用向容器请求(通常是新的)一个bean B的实例。下面是这种方法的一个例子:
1 | // a class that uses a stateful Command-style class to perform some processing |
The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, allows this use case to be handled in a clean fashion.
前面所讲的不是让人满意的,因为业务代码能感知并耦合了Spring框架。方法注入,Spring IoC容器的一个有点高级的特性,允许使用一种干净的方式来处理这个案例。
You can read more about the motivation for Method Injection in this blog entry.
你可以在blog entry中了解更多关于方法注入的动机。
Lookup method injection
Lookup method injection is the ability of the container to override methods on container managed beans, to return the lookup result for another named bean in the container. The lookup typically involves a prototype bean as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to generate dynamically a subclass that overrides the method.
查找方法注入是容器的一种覆盖其管理的beans中的方法的能力,可以返回容器中另一个命名bean查找结果。查找通常会涉及到一个标准bean,如前一小节中讲的那样。Spring框架实现了查找方法注入,它是通过使用CGLIB库生成的字节码来动态的产生一个覆盖这个方法的子类。
For this dynamic subclassing to work, the class that the Spring bean container will subclass cannot be final, and the method to be overridden cannot be final either.
Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
Concrete methods are also necessary for component scanning which requires concrete classes to pick up.
A further key limitation is that lookup methods won’t work with factory methods and in particular not with
@Bean
methods in configuration classes, since the container is not in charge of creating the instance in that case and therefore cannot create a runtime-generated subclass on the fly.Finally, objects that have been the target of method injection cannot be serialized.
为了使动态子类化起作用,Spring bean容器要进行子类化的类不能是最终的类,要进行重写的方法也不是最终的方法。
单元测试一个含有抽象方法的类需要你自己对这个类进行子类化,并且提供这个抽象方法的
stub
实现。实体方法对于要求获得实体类的组件扫描也是必需的。
一个更关键的限制是查找方法不能与工厂方法一起工作,尤其是在配置类中不能与
@Bean
方法同时起作用,由于那种情况下容器不能控制实例的创建,因此不能在飞速写入中创建一个运行时产生的子类。最后,方法注入的目标对象不能被序列化。
Looking at the CommandManager
class in the previous code snippet, you see that the Spring container will dynamically override the implementation of the createCommand()
method. Your CommandManager
class will not have any Spring dependencies, as can be seen in the reworked example:
看一下前面代码片中的CommandManager
类,你可以看到Spring容器将会动态的覆盖createCommand()
方法的实现。CommandManager
类不会有任何Spring依赖,重写的例子如下:
1 | package fiona.apple; |
In the client class containing the method to be injected (the CommandManager
in this case), the method to be injected requires a signature of the following form:
客户类中包含要注入的方法(在这个例子中是CommandManager
),要注入的方法需要下面形式的一个签名:
1 | <public|protected> [abstract] <return-type> theMethodName(no-arguments); |
If the method is abstract, the dynamically-generated subclass implements the method. Otherwise, the dynamically-generated subclass overrides the concrete method defined in the original class. For example:
如果这个方法是抽象的,动态产生的子类会实现这个方法。另外,动态产生的子类会覆盖原来的类中定义的实体方法。例如:
1 | <!-- a stateful bean deployed as a prototype (non-singleton) --> |
The bean identified as commandManager calls its own method createCommand()
whenever it needs a new instance of the command
bean. You must be careful to deploy the command
bean as a prototype, if that is actually what is needed. If it is deployed as a singleton, the same instance of the command
bean is returned each time.
无论什么时候识别为commandManager
的bean需要一个command
bean的新实例,它都会调用它的createCommand()
方法。如果真的需要的话,你必须小心的部署command
bean为一个原型。如果它被部署为一个单例,每次都会返回同一个command
实例。
The interested reader may also find the
ServiceLocatorFactoryBean
(in theorg.springframework.beans.factory.config
package) to be of use. The approach used inServiceLocatorFactoryBean
is similar to that of another utility class,ObjectFactoryCreatingFactoryBean
, but it allows you to specify your own lookup interface as opposed to a Spring-specific lookup interface. Consult the javadocs of these classes for additional information.
感兴趣的读者可能也会发现
ServiceLocatorFactoryBean
(在org.springframework.beans.factory.config
包中)使用这种方法。ServiceLocatorFactoryBean
中使用的方法与另一个工具类ObjectFactoryCreatingFactoryBean
中的方法类似,但它允许你指定你自己的查找接口,与Spring特定的查找接口相反。这些类的额外信息请查询Java文档。
Arbitrary method replacement
A less useful form of method injection than lookup method injection is the ability to replace arbitrary methods in a managed bean with another method implementation. Users may safely skip the rest of this section until the functionality is actually needed.
一种比查找方法注入更少使用的形式是用另一种方法实现替换管理的bean中任意方法的能力。用户可以安全跳过本节剩下的部分,直到这个方法真正需要的时候再看。
With XML-based configuration metadata, you can use the replaced-method
element to replace an existing method implementation with another, for a deployed bean. Consider the following class, with a method computeValue
, which we want to override:
在基于XML的配置元数据中,对于一个部署的bean,你可以通过replaced-method
元素用另一个方法实现替换现有的方法实现。考虑下面的类,有一个我们想覆盖的computeValue
方法:
1 | public class MyValueCalculator { |
A class implementing the org.springframework.beans.factory.support.MethodReplacer
interface provides the new method definition.
实现了org.springframework.beans.factory.support.MethodReplacer
接口的类提供了一种新的方法定义。
1 | /** |
The bean definition to deploy the original class and specify the method override would look like this:
部署最初的类的bean定义和指定的重写方法如下:
1 | <bean id="myValueCalculator" class="x.y.z.MyValueCalculator"> |
You can use one or more contained <arg-type/>
elements within the <replaced-method/>
element to indicate the method signature of the method being overridden. The signature for the arguments is necessary only if the method is overloaded and multiple variants exist within the class. For convenience, the type string for an argument may be a substring of the fully qualified type name. For example, the following all match java.lang.String
:
你可以在<replaced-method/>
元素中使用一个或多个包含<arg-type/>
元素来指出要覆盖的方法的方法签名。只有类中进行了方法重载且有多个重载变种的时候,参数的签名才是必需的。为了简便,字符串类型的参数可能是全拼类型名称的一个子串。例如,下面的所有写法都能匹配java.lang.String
:
1 | java.lang.String |
Because the number of arguments is often enough to distinguish between each possible choice, this shortcut can save a lot of typing, by allowing you to type only the shortest string that will match an argument type.
因为参数数目经常是足够区分每个可能的选择的,通过允许定义匹配参数类型的最短字符串类型,这个缩写可以保存许多类型。
3.5 Bean scopes
When you create a bean definition, you create a recipe for creating actual instances of the class defined by that bean definition. The idea that a bean definition is a recipe is important, because it means that, as with a class, you can create many object instances from a single recipe.
当你创建bean定义时,你创建了一个配方用于创建bean定义中定义的类的实例。bean定义是配方的想法是很重要的,因为这意味着对于一个类,你可以根据一个配方创建许多对象实例。
You can control not only the various dependencies and configuration values that are to be plugged into an object that is created from a particular bean definition, but also the scope of the objects created from a particular bean definition. This approach is powerful and flexible in that you can choose the scope of the objects you create through configuration instead of having to bake in the scope of an object at the Java class level. Beans can be defined to be deployed in one of a number of scopes: out of the box, the Spring Framework supports six scopes, five of which are available only if you use a web-aware ApplicationContext
.
你不仅能管理要插入对象中的的各种依赖和配置值,而且能管理对象的作用域,对象是从特定的bean定义中创建的。这种方法是强大且灵活的,你可以通过配置文件选择你创建的对象的作用域,从而代替Java类级别对象的内置作用域。定义的beans将部署成多种作用域中的一种:开箱即用,Spring框架支持六种作用域,如果你使用感知web的ApplicationContext
,你只可以使用其中的五种作用域。
The following scopes are supported out of the box. You can also create a custom scope.
下面的作用域支持开箱即用。你也可以创建一个定制的作用域。
Table 3.3. Bean scopes
Scope | Description |
---|---|
singleton | (Default) Scopes a single bean definition to a single object instance per Spring IoC container. |
prototype | Scopes a single bean definition to any number of object instances. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request; that is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext. |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
表 3.3 bean作用域
作用域 | 描述 |
---|---|
singleton | (默认) 每个Spring IoC容器使单个bean定义只能创建一个对象实例。 |
prototype | 单个bean定义可以创建任何数量的对象实例。 |
request | 单个bean定义的创建实例的作用域为单个HTTP request的声明周期;也就是说,每个HTTP request有它自己的根据bean定义创建的实例。只在感知Spring ApplicationContext 的上下文中有效。 |
session | 单个bean定义的创建实例的作用域为HTTP Session 的生命周期. 只在感知Spring ApplicationContext 的上下文中有效。 |
application | 单个bean定义的创建实例的作用域为ServletContext 的生命周期。 只在感知Spring ApplicationContext 的上下文中有效。 |
websocket | 单个bean定义的创建实例的作用域为WebSocket 的生命周期。 只在感知Spring ApplicationContext 的上下文中有效。 |
As of Spring 3.0, a thread scope is available, but is not registered by default. For more information, see the documentation for
SimpleThreadScope
. For instructions on how to register this or any other custom scope, see the section called “Using a custom scope”.
从Spring 3.0,引入了
thread scope
作用域,但默认情况下是不注册的。更多的信息请看SimpleThreadScope
文档。关于怎么注册thread scope
作用域或任何其它的定制作用域的介绍,请看『Using a custom scope』小节。
3.5.1 The singleton scope
Only one shared instance of a singleton bean is managed, and all requests for beans with an id or ids matching that bean definition result in that one specific bean instance being returned by the Spring container.
单例bean只管理一个共享实例,id匹配bean定义的所有对beans的请求,Spring容器会返回一个特定的bean实例。
To put it another way, when you define a bean definition and it is scoped as a singleton, the Spring IoC container creates exactly one instance of the object defined by that bean definition. This single instance is stored in a cache of such singleton beans, and all subsequent requests and references for that named bean return the cached object.
换言之,当你定义一个bean定义时,它的作用域为单例,Spring IoC容器会根据bean定义创建一个确定的对象实例。这个单独的实例存储在单例beans的缓存中,接下来的对这个命名bean的所有请求和引用都会返回那个缓存的对象。
Spring’s concept of a singleton bean differs from the Singleton pattern as defined in the Gang of Four (GoF) patterns book. The GoF Singleton hard-codes the scope of an object such that one and only one instance of a particular class is created per ClassLoader. The scope of the Spring singleton is best described as per container and per bean. This means that if you define one bean for a particular class in a single Spring container, then the Spring container creates one and only one instance of the class defined by that bean definition. The singleton scope is the default scope in Spring. To define a bean as a singleton in XML, you would write, for example:
Spring中的单例bean概念不同于《设计模式》书中定义的单例模式。设计模式中的单例是对对象的作用域进行硬编码,为的是每个类加载器只能创建一个特定类的实例。Spring单例作用域最好的描述是每个容器每个类。这意味着如果你在单个的Spring容器中为一个特定的类定义了一个bean,Spring只会根据bean定义创建一个类的实例。在Spring中单例作用域是默认的作用域。为了在XML定义一个单例bean,你可以像下面一样写,例如:
1 | <bean id="accountService" class="com.foo.DefaultAccountService"/> |
3.5.2 The prototype scope
The non-singleton, prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made. That is, the bean is injected into another bean or you request it through a getBean()
method call on the container. As a rule, use the prototype scope for all stateful beans and the singleton scope for stateless beans.
非单例模式,bean部署采用原型作用域时,每次产生一个特定bean的请求时都会创建一个新的bean实例。也就是说,这个bean会注入到另一个bean中或你可以在容器中通过调用getBean()
方法来请求它。通常,对于所有有状态的beans使用原型作用域,对于无状态的beans使用单例作用域。
The following diagram illustrates the Spring prototype scope. A data access object (DAO) is not typically configured as a prototype, because a typical DAO does not hold any conversational state; it was just easier for this author to reuse the core of the singleton diagram.
下面的图阐述了Spring原型作用域。数据访问对象(DAO)通常是不会配置为原型的,因为一个典型的DAO不会有任何会话状态;对于作者来说很容易重用单例图的核心。
The following example defines a bean as a prototype in XML:
下面的例子在XML中定义一个原型bean:
1 | <bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/> |
In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean: the container instantiates, configures, and otherwise assembles a prototype object, and hands it to the client, with no further record of that prototype instance. Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype bean(s) are holding. To get the Spring container to release resources held by prototype-scoped beans, try using a custom bean post-processor, which holds a reference to beans that need to be cleaned up.
与其它作用域相比,Spring不管理原型bean的完整生命周期:容器初始化、配置,另外组装原型对象,并把它传递给客户端,之后不再记录原型实例。因此,虽然不管什么作用域初始化生命周期回调函数都会在所有对象上调用,但是在原型作用域的情况下,不会调用配置的销毁生命周期回调函数。客户端代码必须清理原型作用域的对象并释放原型bean拥有的昂贵资源。为了使Spring容器释放原型bean拥有的资源,尝试使用定制的bean后处理程序,它拥有需要清理的bean的引用。
In some respects, the Spring container’s role in regard to a prototype-scoped bean is a replacement for the Java new
operator. All lifecycle management past that point must be handled by the client. (For details on the lifecycle of a bean in the Spring container, see Section 3.6.1, “Lifecycle callbacks”.)
在有些方面,关于原型作用域,Spring容器的角色像是Java中new
操作符的替代品。所有生命周期的管理必须由客户端处理。(Spring容器中更多关于bean生命周期的细节,请看3.6.1小节,”生命周期回调”)。
3.5.3 Singleton beans with prototype-bean dependencies
When you use singleton-scoped beans with dependencies on prototype beans, be aware that dependencies are resolved at instantiation time. Thus if you dependency-inject a prototype-scoped bean into a singleton-scoped bean, a new prototype bean is instantiated and then dependency-injected into the singleton bean. The prototype instance is the sole instance that is ever supplied to the singleton-scoped bean.
当你使用含有原型bean依赖的单例作用域bean时,要意识到依赖解析是在实例化时。因此如果你使用依赖注入将原型作用域的bean注入到单例作用域的bean中时,将会实例化一个新的原型bean并依赖注入到单例bean中。原型bean实例曾经是唯一提供给单例作用域的bean的实例。
However, suppose you want the singleton-scoped bean to acquire a new instance of the prototype-scoped bean repeatedly at runtime. You cannot dependency-inject a prototype-scoped bean into your singleton bean, because that injection occurs only once, when the Spring container is instantiating the singleton bean and resolving and injecting its dependencies. If you need a new instance of a prototype bean at runtime more than once, see Section 3.4.6, “Method injection”.
假设你想在运行时让单例作用域的bean重复的获得原型作用域bean的新实例。你不能依赖注入原型作用域的bean到你的单例bean中,因为当Spring容器实例化单例bean,解析并注入它的依赖时,注入只发生一次。如果你在运行时不止一次需要原型bean的实例,请看3.4.6小节,”方法注入”。
3.5.4 Request, session, application, and WebSocket scopes
The request
, session
, application
, and websocket
scopes are only available if you use a web-aware Spring ApplicationContext
implementation (such as XmlWebApplicationContext
). If you use these scopes with regular Spring IoC containers such as the ClassPathXmlApplicationContext
, an IllegalStateException
will be thrown complaining about an unknown bean scope.
如果你使用感知web的Spring ApplicationContext
实现(例如XmlWebApplicationContext
),request
,session
,application
和websocket
作用域是唯一可用的作用域。如果你通过正规的Spring IoC容器例如ClassPathXmlApplicationContext
来使用这些作用域,会抛出IllegalStateException
异常,投诉使用了一个未知的bean作用域。
Initial web configuration
To support the scoping of beans at the request
, session
, application
, and websocket
levels (web-scoped beans), some minor initial configuration is required before you define your beans. (This initial setup is not required for the standard scopes, singleton
and prototype
.)
为了支持request
,session
,application
和websocket
标准的bean作用域,在你定义你的bean之前需要进行一些较小的初始化配置。(对于标准作用域singleton
和prototype
,初始化设置不需要的。)
How you accomplish this initial setup depends on your particular Servlet environment.
怎样完成这个初始化设置依赖于你特定的Servlet环境。
If you access scoped beans within Spring Web MVC, in effect, within a request that is processed by the Spring DispatcherServlet
, then no special setup is necessary: DispatcherServlet
already exposes all relevant state.
如果你在Spring Web MVC中访问具有作用域的beans,请求内部是通过Spring的DispatcherServlet
来处理的,不需要特定设置:DispatcherServlet
已经显示了所有相关的状态。
If you use a Servlet 2.5 web container, with requests processed outside of Spring’s DispatcherServlet
(for example, when using JSF or Struts), you need to register the org.springframework.web.context.request.RequestContextListener
ServletRequestListener
. For Servlet 3.0+, this can be done programmatically via the WebApplicationInitializer
interface. Alternatively, or for older containers, add the following declaration to your web application’s web.xml
file:
如果你使用Servlet 2.5的web容器,在Spring的DispatcherServlet
之外处理请求(例如使用JSF或Struts时),你需要注册org.springframework.web.context.request.RequestContextListener
ServletRequestListener
。对于Servlet 3.0+,能通过WebApplicationInitializer
接口以编程方式处理。对于更早的容器,可以在应用程序的web.xml
文件中添加下面的声明来代替:
1 | <web-app> |
Alternatively, if there are issues with your listener setup, consider using Spring’s RequestContextFilter
. The filter mapping depends on the surrounding web application configuration, so you have to change it as appropriate.
如果你的监听器设置有问题,作为一种选择,你可以考虑Spring的RequestContextFilter
。过滤器映射依赖于web应用程序的相关配置,因此你必须适当的更改它。
1 | <web-app> |
DispatcherServlet
, RequestContextListener
, and RequestContextFilter
all do exactly the same thing, namely bind the HTTP request object to the Thread
that is servicing that request. This makes beans that are request- and session-scoped available further down the call chain.
DispatcherServlet
,RequestContextListener
和RequestContextFilter
都是在做同样的事,也就是说将HTTP请求对象绑定到服务请求的Thread
上。这使得request作用域和session作用域的beans在更深一层的调用链中是可用的。
Request scope
Consider the following XML configuration for a bean definition:
考虑下面的bean定义的XML配置:
1 | <bean id="loginAction" class="com.foo.LoginAction" scope="request"/> |
The Spring container creates a new instance of the LoginAction
bean by using the loginAction
bean definition for each and every HTTP request. That is, the loginAction
bean is scoped at the HTTP request
level. You can change the internal state of the instance that is created as much as you want, because other instances created from the same loginAction
bean definition will not see these changes in state; they are particular to an individual request. When the request completes processing, the bean that is scoped to the request is discarded.
对于每一个HTTP请求,Spring容器通过使用loginAction
定义创建一个新的LoginAction
bean实例。也就是说,loginAction
bean的作用域是在HTTP request
级别的。你可以任意改变创建的实例的内部状态,因为其它的根据loginAction
bean定义创建的实例不会看到这些状态的改变;它们对于每个单独的请求都是独有的。当请求处理完成时,请求作用域的bean被销毁。
When using annotation-driven components or Java Config, the @RequestScope
annotation can be used to assign a component to the request scope.
当使用注解驱动的组件或Java配置时,@RequestScope
注解能用来指定一个组件的作用域为request。
1 |
|
Session scope
Consider the following XML configuration for a bean definition:
考虑下面的bean定义的XML配置:
1 | <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> |
The Spring container creates a new instance of the UserPreferences
bean by using the userPreferences
bean definition for the lifetime of a single HTTP Session. In other words, the userPreferences
bean is effectively scoped at the HTTP Session
level. As with request-scoped beans, you can change the internal state of the instance that is created as much as you want, knowing that other HTTP Session instances that are also using instances created from the same userPreferences
bean definition do not see these changes in state, because they are particular to an individual HTTP Session. When the HTTP Session is eventually discarded, the bean that is scoped to that particular HTTP Session is also discarded.
对于单个HTTP Session的生命周期,Spring容器通过userPreferences
bean定义创建一个UserPreferences
bean实例。换句话说,userPreferences
bean的有效作用域是HTTP Session
级别的。正如request作用域的beans一样,你可以任意改变你想改变的创建的bean实例的内部状态,知道其它的使用根据userPreferences
bean定义创建的HTTP Session实例也不会看到这些内部状态的改变,因为它们对于每个单独的HTTP Session都是独有的。当HTTP Session被最终销毁时,Session作用域的bean也被销毁。
When using annotation-driven components or Java Config, the @SessionScope
annotation can be used to assign a component to the session scope.
当使用注解驱动的组件或Java配置时,@SessionScope
注解能用来指定一个组件的作用域为session。
1 |
|
Application scope
Consider the following XML configuration for a bean definition:
考虑下面的bean定义的XML配置:
1 | <bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/> |
The Spring container creates a new instance of the AppPreferences
bean by using the appPreferences
bean definition once for the entire web application. That is, the appPreferences
bean is scoped at the ServletContext
level, stored as a regular ServletContext
attribute. This is somewhat similar to a Spring singleton bean but differs in two important ways: It is a singleton per ServletContext
, not per Spring ‘ApplicationContext’ (for which there may be several in any given web application), and it is actually exposed and therefore visible as a ServletContext
attribute.
对于整个web应用而言,Spring容器根据appPreferences
bean定义只创建一次AppPreferences
bean的新实例。也就是说,appPreferences
bean的作用域是ServletContext
级别的,作为一个正规的ServletContext
特性来存储。这有点类似于Spring的单例bean,但在两个方面是不同的:它对于每个ServletContext
是单例的,而不是每个Spring ApplicationContext
(在任何给定的web应用中可能有几个ApplicationContext
),它是真正显露的,因此作为一个ServletContext
特性是可见的。
When using annotation-driven components or Java Config, the @ApplicationScope
annotation can be used to assign a component to the application scope.
当使用注解驱动的组件或Java配置时,@ApplicationScope
注解能用来指定一个组件的作用域为Application。
1 |
|
Scoped beans as dependencies
The Spring IoC container manages not only the instantiation of your objects (beans), but also the wiring up of collaborators (or dependencies). If you want to inject (for example) an HTTP request scoped bean into another bean of a longer-lived scope, you may choose to inject an AOP proxy in place of the scoped bean. That is, you need to inject a proxy object that exposes the same public interface as the scoped object but that can also retrieve the real target object from the relevant scope (such as an HTTP request) and delegate method calls onto the real object.
Spring IoC容器不仅管理对象的实例化,而且管理协作者(或依赖)的绑定。例如,如果你想将一个具有HTTP request作用域的bean注入到另一个具有更长生命周期作用域的bean中,你可以选择注入一个AOP代理来代替具有作用域的bean。也就是说,你需要注入一个代理对象,这个对象能显露与具有作用域的对象相同的接口,但也能从相关的作用域中(例如HTTP request作用域)得到真正的目标对象,能通过委派方法调用到真正的对象。
You may also use
<aop:scoped-proxy/>
between beans that are scoped assingleton
, with the reference then going through an intermediate proxy that is serializable and therefore able to re-obtain the target singleton bean on deserialization.When declaring
<aop:scoped-proxy/>
against a bean of scopeprototype
, every method call on the shared proxy will lead to the creation of a new target instance which the call is then being forwarded to.Also, scoped proxies are not the only way to access beans from shorter scopes in a lifecycle-safe fashion. You may also simply declare your injection point (i.e. the constructor/setter argument or autowired field) as
ObjectFactory<MyTargetBean>
, allowing for agetObject()
call to retrieve the current instance on demand every time it is needed - without holding on to the instance or storing it separately.The JSR-330 variant of this is called
Provider
, used with aProvider<MyTargetBean>
declaration and a correspondingget()
call for every retrieval attempt. See here for more details on JSR-330 overall.
你也可以在作用域为
singleton
的beans之间使用<aop:scoped-proxy/>
,将通过中间代理的引用进行序列化,因此能通过反序列化重新获得目标的单例bean。当将作用域为
prototype
的bean声明为<aop:scoped-proxy/>
时,每个在共享代理上的方法调用会引起一个新目标实例(调用朝向的)的创建。通过生命周期安全的方式访问更短的作用域中beans,作用域代理也不是唯一的方式。你也可以简单的声明你的注入点(例如,构造函数/setter参数或自动装配领域)为
ObjectFactory<MyTargetBean>
,考虑到每次需要的时候通过getObject()
调用来取得索要的当前实例——没有分别控制实例或储存它。JSR-300变量被称作
Provider
,对于每一次取回尝试使用Provider<MyTargetBean>
声明和对应的get()
调用。关于JSR-330整体的更多细节请看这儿。
The configuration in the following example is only one line, but it is important to understand the “why” as well as the “how” behind it.
下面例子中的配置只有一行,但对于理解它背后的”why”和”how”是重要的。
1 | <?xml version="1.0" encoding="UTF-8"?> |
To create such a proxy, you insert a child <aop:scoped-proxy/>
element into a scoped bean definition (see the section called “Choosing the type of proxy to create” and Chapter 38, XML Schema-based configuration). Why do definitions of beans scoped at the request
, session
and custom-scope levels require the <aop:scoped-proxy/>
element? Let’s examine the following singleton bean definition and contrast it with what you need to define for the aforementioned scopes (note that the following userPreferences
bean definition as it stands is incomplete).
为了创建这样一个代理,你插入一个子元素<aop:scoped-proxy/>
到具有作用域的bean定义中(看”选择创建的代理类型”小节和38章,基于XML Schema的配置)。为什么bean定义的作用域为request
,session
和定制作用域级别需要<aop:scoped-proxy/>
元素?让我们检查下面的单例bean定义,并将它与你需要定义的前面提到的作用域进行比较(注意下面的userPreferences
bean定义按目前情况是不完全的)。
1 | <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> |
In the preceding example, the singleton bean userManager
is injected with a reference to the HTTP Session
-scoped bean userPreferences
. The salient point here is that the userManager
bean is a singleton: it will be instantiated exactly once per container, and its dependencies (in this case only one, the userPreferences
bean) are also injected only once. This means that the userManager
bean will only operate on the exact same userPreferences
object, that is, the one that it was originally injected with.
在上面的例子中,单例bean userManager
通过引用被注入到具有HTTP Session
作用域的bean userPreferences
中。这的突出点是userManager
bean是单例:每个容器它将确定的被实例化一次,它的依赖(在这个例子中只有一个,userPreferences
bean)也只注入一次。这意味着userManager
bean只能对确定的同一个userPreferences
对象进行操作,也就是最初注入的那个对象。
This is not the behavior you want when injecting a shorter-lived scoped bean into a longer-lived scoped bean, for example injecting an HTTP Session
-scoped collaborating bean as a dependency into singleton bean. Rather, you need a single userManager
object, and for the lifetime of an HTTP Session
, you need a userPreferences
object that is specific to said HTTP Session
. Thus the container creates an object that exposes the exact same public interface as the UserPreferences
class (ideally an object that is a UserPreferences
instance) which can fetch the real UserPreferences
object from the scoping mechanism (HTTP request, Session, etc.). The container injects this proxy object into the userManager
bean, which is unaware that this UserPreferences
reference is a proxy. In this example, when a UserManager
instance invokes a method on the dependency-injected UserPreferences
object, it actually is invoking a method on the proxy. The proxy then fetches the real UserPreferences
object from (in this case) the HTTP Session
, and delegates the method invocation onto the retrieved real UserPreferences
object.
当将一个短期作用域的bean注入到一个长期作用域的bean中时,这不是你想要的行为,例如将一个具有HTTP Session
作用域的协作bean作为一个依赖注入到一个单例bean中。当然,你需要一个单一的userManager
对象,对于HTTP Session
的生命周期,你需要一个特定的被称为HTTP Session
的userPreferences
对象。因此容器创建了一个与UserPreferences
类暴露相同的公共接口的对象(理想情况下是一个UserPreferences
实例),这个对象能从作用域机制中(HTTP request,Session等)取得真正的UserPreferences
对象。容器将这个代理对象注入到userManager
bean中,userManager
bean不会意识到UserPreferences
引用是一个代理。在这个例子中,当UserManager
实例调用依赖注入的UserPreferences
对象的方法时,它实际上调用的是代理中的一个方法。代理能从HTTP Session
中(在这个例子)取得真正的UserPreferences
对象,将方法调用委托到取得的真正的UserPreferences
对象上。
Thus you need the following, correct and complete, configuration when injecting request- and session-scoped beans into collaborating objects:
因此当注入具有request或session作用域的bean到协作对象中时,你需要下面的,正确的,完整的配置:
1 | <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> |
Choosing the type of proxy to create
By default, when the Spring container creates a proxy for a bean that is marked up with the <aop:scoped-proxy/>
element, a CGLIB-based class proxy is created.
当Spring容器为具有<aop:scoped-proxy/>
标记的bean创建代理时,默认情况下,创建一个基于CGLIB的类代理。
CGLIB proxies only intercept public method calls! Do not call non-public methods on such a proxy; they will not be delegated to the actual scoped target object.
CGLIB代理只拦截公有方法调用。在这个代理上不调用非公有方法;它们不能委托给实际作用域目标对象。
Alternatively, you can configure the Spring container to create standard JDK interface-based proxies for such scoped beans, by specifying false
for the value of the proxy-target-class
attribute of the <aop:scoped-proxy/>
element. Using JDK interface-based proxies means that you do not need additional libraries in your application classpath to effect such proxying. However, it also means that the class of the scoped bean must implement at least one interface, and that all collaborators into which the scoped bean is injected must reference the bean through one of its interfaces.
作为一种选择,对于这种具有作用域的bean你可以配置Spring容器创建标准JDK基于接口的代理,通过指定<aop:scoped-proxy/>
元素的proxy-target-class
特定的值为false
。使用JDK基于接口的代理意味着在你应用程序类路径中你不需要额外的库来支持这种代理的使用。然而,它也意味着具有作用域的bean的类必须实现至少一个接口,并且注入这个bean的所有协作者必须通过它接口中的一个来引用它。
1 | <!-- DefaultUserPreferences implements the UserPreferences interface --> |
For more detailed information about choosing class-based or interface-based proxying, see Section 7.6, “Proxying mechanisms”.
关于选择基于类或基于接口代理的更多细节信心,请看7.6小节,”代理机制”。
3.5.5 Custom scopes
The bean scoping mechanism is extensible; You can define your own scopes, or even redefine existing scopes, although the latter is considered bad practice and you cannot override the built-in singleton
and prototype
scopes.
bean作用域机制是可扩展的;你可以定义你自己的作用域,甚至重新定义现有的作用域,虽然后者被认为是一种不好的实践,你不能覆盖内置的singleton
作用域和prototype
作用域。
Creating a custom scope
To integrate your custom scope(s) into the Spring container, you need to implement the org.springframework.beans.factory.config.Scope
interface, which is described in this section. For an idea of how to implement your own scopes, see the Scope
implementations that are supplied with the Spring Framework itself and the Scope
javadocs, which explains the methods you need to implement in more detail.
为了将你的定制作用域集成到Spring容器中,你需要实现org.springframework.beans.factory.config.Scope
接口,这一节将描述这个接口。对于怎样实现你自己作用域的想法,请看Spring框架本身提供的Scope
实现和Scope
文档,它们解释了你需要实现的方法的更多细节。
The Scope
interface has four methods to get objects from the scope, remove them from the scope, and allow them to be destroyed.
Scope
接口有四个方法,从作用域中取得对象,从作用域中移除对象,并且允许它们被销毁。
The following method returns the object from the underlying scope. The session scope implementation, for example, returns the session-scoped bean (and if it does not exist, the method returns a new instance of the bean, after having bound it to the session for future reference).
下面的方法从潜在的作用域返回对象。以session作用域实现为例,返回具有session作用域的bean(如果它不存在,这个方法返回一个bean的新实例,然后绑定到session中准备将来引用)。
1 | Object get(String name, ObjectFactory objectFactory) |
The following method removes the object from the underlying scope. The session scope implementation for example, removes the session-scoped bean from the underlying session. The object should be returned, but you can return null if the object with the specified name is not found.
下面的方法从潜在作用域中移除对象。以session作用域实现为例,从潜在的session中移除session作用域的bean。对象应该被返回,但如果没有找到指定名字的对象会返回空。
1 | Object remove(String name) |
The following method registers the callbacks the scope should execute when it is destroyed or when the specified object in the scope is destroyed. Refer to the javadocs or a Spring scope implementation for more information on destruction callbacks.
下面的方法是注册当作用域销毁时或当作用域中的指定对象销毁时,作用域应该执行的回调函数。销毁回调函数的更多信息请看文档或Spring作用域实现。
1 | void registerDestructionCallback(String name, Runnable destructionCallback) |
The following method obtains the conversation identifier for the underlying scope. This identifier is different for each scope. For a session scoped implementation, this identifier can be the session identifier.
下面的方法是获得潜在作用域的会话标识符。每个作用域的标识符都是不同的。对于session作用域实现,标识符是session标识符。
1 | String getConversationId() |
Using a custom scope
After you write and test one or more custom Scope
implementations, you need to make the Spring container aware of your new scope(s). The following method is the central method to register a new Scope
with the Spring container:
在你编写和测试一个或多个定制Scope
实现之后,你需要让Spring容器感知到你的新作用域。下面是在Spring容器中注册一个新Scope
的主要方法:
1 | void registerScope(String scopeName, Scope scope); |
This method is declared on the ConfigurableBeanFactory
interface, which is available on most of the concrete ApplicationContext
implementations that ship with Spring via the BeanFactory
property.
这个方法是在ConfigurableBeanFactory
接口中声明的,在大多数具体的ApplicationContext
实现中都可获得,在Spring中通过BeanFactory
属性得到。
The first argument to the registerScope(..)
method is the unique name associated with a scope; examples of such names in the Spring container itself are singleton
and prototype
. The second argument to the registerScope(..)
method is an actual instance of the custom Scope
implementation that you wish to register and use.
registerScope(..)
方法中的第一个参数是关于作用域的唯一名字;Spring容器本身中的这种名字的例子是singleton
和prototype
。registerScope(..)
方法中的第二个参数是你想注册和使用的定制Scope
实现的真正实例。
Suppose that you write your custom Scope
implementation, and then register it as below.
假设你编写了你的定制Scope
实现并按如下注册。
The example below uses
SimpleThreadScope
which is included with Spring, but not registered by default. The instructions would be the same for your own customScope
implementations.
下面的例子使用Spring包含的
SimpleThreadScope
,但默认是不注册的。这个用法说明与你自己的定制Scope
是一样的。
1 | Scope threadScope = new SimpleThreadScope(); |
You then create bean definitions that adhere to the scoping rules of your custom Scope
:
然后创建具有你自己定制的Scope
规则的bean定义:
1 | <bean id="..." class="..." scope="thread"> |
With a custom Scope
implementation, you are not limited to programmatic registration of the scope. You can also do the Scope
registration declaratively, using the CustomScopeConfigurer
class:
在定制Scope
实现后,你不会受限于作用域的程序注册。你也可以声明式的进行Scope
注册,使用CustomScopeConfigurer
类:
1 | <?xml version="1.0" encoding="UTF-8"?> |
When you place
<aop:scoped-proxy/>
in aFactoryBean
implementation, it is the factory bean itself that is scoped, not the object returned fromgetObject()
.
当你在
FactoryBean
实现中放入<aop:scoped-proxy/>
时,它是工厂bean本身具有作用域,不是从getObject()
中返回的对象。
3.6 Customizing the nature of a bean
3.6.1 Lifecycle callbacks
To interact with the container’s management of the bean lifecycle, you can implement the Spring InitializingBean
and DisposableBean
interfaces. The container calls afterPropertiesSet()
for the former and destroy()
for the latter to allow the bean to perform certain actions upon initialization and destruction of your beans.
为了与容器中bean生命周期的管理进行交互,你可以实现Spring的InitializingBean
和DisposableBean
接口。当初始化beans时容器会调用InitializingBean
中的afterPropertiesSet()
方法,当销毁beans时容器会调用DisposableBean
中的destroy()
方法,在这两个方法中bean可以执行特定的行为。
The JSR-250
@PostConstruct
and@PreDestroy
annotations are generally considered best practice for receiving lifecycle callbacks in a modern Spring application. Using these annotations means that your beans are not coupled to Spring specific interfaces. For details see Section 3.9.8, “@PostConstruct and @PreDestroy”.If you don’t want to use the JSR-250 annotations but you are still looking to remove coupling consider the use of init-method and destroy-method object definition metadata.
在现代Spring应用中,通常认为JSR-250的
@PostConstruct
和@PreDestroy
注解是最佳实践接收生命周期回调函数的方法。使用这些注解意味着你的bean没有耦合Spring特定的接口。更多细节请看3.9.8小节,”@PostConstruct和@PreDestroy”。如果你不想使用JSR-250注解,但你仍要注意解耦,可以考虑使用对象定义元数据中的初始化方法和方法。
Internally, the Spring Framework uses BeanPostProcessor
implementations to process any callback interfaces it can find and call the appropriate methods. If you need custom features or other lifecycle behavior Spring does not offer out-of-the-box, you can implement a BeanPostProcessor
yourself. For more information, see Section 3.8, “Container Extension Points”.
在Spring内部,Spring框架使用BeanPostProcessor
实现来处理任何它能发现的回调接口并调用合适的方法。如果你需要定制Spring不能提供的开箱即用的功能或其它生命周期行为,你可以自己实现BeanPostProcessor
。更多信息请看3.8小节,”容器扩展点”。
In addition to the initialization and destruction callbacks, Spring-managed objects may also implement the Lifecycle
interface so that those objects can participate in the startup and shutdown process as driven by the container’s own lifecycle.
除了初始化回调函数和析构回调函数之外,Spring管理的对象也可以实现Lifecycle
接口,这些对象可以参与容器自身生命周期驱动的启动和关闭过程。
The lifecycle callback interfaces are described in this section.
本节描述了生命周期回调接口。
Initialization callbacks
The org.springframework.beans.factory.InitializingBean
interface allows a bean to perform initialization work after all necessary properties on the bean have been set by the container. The InitializingBean
interface specifies a single method:
org.springframework.beans.factory.InitializingBean
接口在容器设置了bean所有的必须属性之后,允许bean执行初始化工作。InitializingBean
接口指定了一个方法:
1 | void afterPropertiesSet() throws Exception; |
It is recommended that you do not use the InitializingBean
interface because it unnecessarily couples the code to Spring. Alternatively, use the @PostConstruct
annotation or specify a POJO initialization method. In the case of XML-based configuration metadata, you use the init-method
attribute to specify the name of the method that has a void no-argument signature. With Java config, you use the initMethod
attribute of @Bean
, see the section called “Receiving lifecycle callbacks”. For example, the following:
建议你不使用InitializingBean
接口,因为它对代码与Spring进行了不必要的耦合。作为一种替代方法,你可以使用@PostConstruct
注解或指定一个POPJO的初始化方法。在基于XML配置元数据的情况下,你可以使用init-method
特性来指定方法的名称,方法是没有返回值和参数的。如果使用Java配置,你可以使用@Bean
的initMethod
特性,请看”接收生命周期回调”小节。例如,下面的代码:
1 | <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> |
1 | public class ExampleBean { |
…is exactly the same as…
等价于:
1 | <bean id="exampleInitBean" class="examples.AnotherExampleBean"/> |
1 | public class AnotherExampleBean implements InitializingBean { |
but does not couple the code to Spring.
但没有与Spring代码耦合。
Destruction callbacks
Implementing the org.springframework.beans.factory.DisposableBean
interface allows a bean to get a callback when the container containing it is destroyed. The DisposableBean
interface specifies a single method:
实现org.springframework.beans.factory.DisposableBean
接口允许容器包含的bean销毁时调用回调函数。DisposableBean
接口指定了一个方法:
1 | void destroy() throws Exception; |
It is recommended that you do not use the DisposableBean
callback interface because it unnecessarily couples the code to Spring. Alternatively, use the @PreDestroy
annotation or specify a generic method that is supported by bean definitions. With XML-based configuration metadata, you use the destroy-method
attribute on the <bean/>
. With Java config, you use the destroyMethod
attribute of @Bean
, see the section called “Receiving lifecycle callbacks”. For example, the following definition:
建议你不使用DisposableBean
回调接口,因为它对代码与Spring进行了不必要的耦合。作为一种替代方法,你可以使用@PreDestroy
注解或指定一个bean定义支持的通用方法。在基于XML配置元数据的情况下,你可以使用<bean/>
的destroy-method
特性。如果使用Java配置,你可以使用@Bean
的destroyMethod
特性,请看”接收生命周期回调”小节。例如,下面的定义:
1 | <bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/> |
1 | public class ExampleBean { |
is exactly the same as:
等价于:
1 | <bean id="exampleInitBean" class="examples.AnotherExampleBean"/> |
1 | public class AnotherExampleBean implements DisposableBean { |
but does not couple the code to Spring.
但没有与Spring代码耦合。
The
destroy-method
attribute of a<bean>
element can be assigned a special(inferred)
value which instructs Spring to automatically detect a publicclose
orshutdown
method on the specific bean class (any class that implementsjava.lang.AutoCloseable
orjava.io.Closeable
would therefore match). This special(inferred)
value can also be set on thedefault-destroy-method
attribute of a<beans>
element to apply this behavior to an entire set of beans (see the section called “Default initialization and destroy methods”). Note that this is the default behavior with Java config.
<bean>
元素的destroy-method
特性可以分配一个特殊值(inferred)
,它会指导Spring自动检测指定的bean类的公有close
方法或shutdown
方法(可以匹配任何实现java.lang.AutoCloseable
或java.io.Closeable
的类)。为了将这种行为应用到beans的全部集合中,特殊值(inferred)
可以设置在<beans>
元素中的default-destroy-method
特性上(请看”默认初始化方法和销毁方法”小节)。注意Java配置的默认行为。
Default initialization and destroy methods
When you write initialization and destroy method callbacks that do not use the Spring-specific InitializingBean
and DisposableBean
callback interfaces, you typically write methods with names such as init()
, initialize()
, dispose()
, and so on. Ideally, the names of such lifecycle callback methods are standardized across a project so that all developers use the same method names and ensure consistency.
当你编写初始化回调函数和析构回调函数时,不要使用Spring特定的InitializingBean
和DisposableBean
回调接口,自己编写方法,方法名通常为init()
,initialize()
,dispose()
等等。理想情况下,这种生命周期回调方法的名称在整个工程中是标准化的,以便所有开发人员使用同样的方法名称,保证一致性。
You can configure the Spring container to look
for named initialization and destroy callback method names on every bean. This means that you, as an application developer, can write your application classes and use an initialization callback called init()
, without having to configure an init-method="init"
attribute with each bean definition. The Spring IoC container calls that method when the bean is created (and in accordance with the standard lifecycle callback contract described previously). This feature also enforces a consistent naming convention for initialization and destroy method callbacks.
你可以配置Spring容器查找每个bean的初始化方法和析构方法时的名字。这意味着,作为一个应用开发者,你可以编写应用程序类并使用名为init()
的初始化回调方法,而不必在每个bean定义中配置init-method="init"
特性。当bean创建时,Spring Ioc容器调用这个方法(按照前面描述的标准生命周期回调约定)。这个功能也强制了初始化方法和析构方法命名规范的一致性。
Suppose that your initialization callback methods are named init()
and destroy callback methods are named destroy()
. Your class will resemble the class in the following example.
假设你的初始化回调方法名为init()
,析构回调方法名为destroy()
。你的类应该与下面例子中的类类似。
1 | public class DefaultBlogService implements BlogService { |
1 | <beans default-init-method="init"> |
The presence of the default-init-method
attribute on the top-level <beans/>
element attribute causes the Spring IoC container to recognize a method called init
on beans as the initialization method callback. When a bean is created and assembled, if the bean class has such a method, it is invoked at the appropriate time.
位于顶层<beans/>
元素中的default-init-method
特性,会让Spring IoC容器将beans中的名为init
的方法识别为初始化回调方法。当一个bean创建和组装时,如果bean类有这样一个方法,它会在恰当的时间被调用。
You configure destroy method callbacks similarly (in XML, that is) by using the default-destroy-method
attribute on the top-level <beans/>
element.
类似的,你可以在顶层元素<beans/>
中设置default-destroy-method
特性来配置析构回调方法的名字。
Where existing bean classes already have callback methods that are named at variance with the convention, you can override the default by specifying (in XML, that is) the method name using the init-method
and destroy-method
attributes of the <bean/>
itself.
在现有的bean类已经有不符合命名规范的回调方法的情况下,你也可以通过在<bean/>
本身的init-method
和destroy-method
特性(在XML中)中指定方法名称来覆盖<beans/>
中的默认名称。
The Spring container guarantees that a configured initialization callback is called immediately after a bean is supplied with all dependencies. Thus the initialization callback is called on the raw bean reference, which means that AOP interceptors and so forth are not yet applied to the bean. A target bean is fully created first, then an AOP proxy (for example) with its interceptor chain is applied. If the target bean and the proxy are defined separately, your code can even interact with the raw target bean, bypassing the proxy. Hence, it would be inconsistent to apply the interceptors to the init method, because doing so would couple the lifecycle of the target bean with its proxy/interceptors and leave strange semantics when your code interacts directly to the raw target bean.
在bean被提供了所有依赖之后,Spring容器确保会立刻调用配置的初始化回调方法。因此初始化回调会在原生bean引用上调用,这意味着AOP拦截器等仍不能应用到bean中。首先要完整的创建目标bean,然后才会应用AOP代理(例如)等拦截器链。如果分别定义了目标bean和代理,你的代码甚至能绕过代理直接与原生的目标bean进行交互。将拦截器应用到初始化方法上可能会产生不一致性,因为这样做会使目标bean的生命周期与它的代理/拦截器相耦合,当你的代码与原生目标bean直接进行交互时,语义会变的很奇怪。
Combining lifecycle mechanisms
As of Spring 2.5, you have three options for controlling bean lifecycle behavior: the InitializingBean
and DisposableBean
callback interfaces; custom init()
and destroy()
methods; and the @PostConstruct
and @PreDestroy
annotations. You can combine these mechanisms to control a given bean.
从Spring 2.5开始,在控制bean的生命周期行为时,你有三中选择:InitializingBean和
DisposableBean回调接口;定制
init()和
destroy()方法;
@PostConstruct和
@PreDestroy`注解。在控制一个给定bean时你可以组合这些机制。
If multiple lifecycle mechanisms are configured for a bean, and each mechanism is configured with a different method name, then each configured method is executed in the order listed below. However, if the same method name is configured - for example,
init()
for an initialization method - for more than one of these lifecycle mechanisms, that method is executed once, as explained in the preceding section.
如果一个bean配置了多生命周期机制,每种机制配置了一个不同的方法名,那么每一个配置的方法会按照下面的顺序列表来执行。但是如果配置了相同的名字——例如,
init()
初始化方法——不止在一个生命周期机制中配置,那么这个方法只能执行一次,像之前所说的那样。
Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:
Methods annotated with
@PostConstruct
afterPropertiesSet()
as defined by theInitializingBean
callback interfaceA custom configured
init()
method
同一个bean配置了多生命周期机制,并有不同的初始化方法,那么调用顺序如下:
先调用有注解
@PostConstruct
的方法然后调用
InitializingBean
回调接口定义的afterPropertiesSet()
方法最好调用定制配置的
init()
方法
Destroy methods are called in the same order:
Methods annotated with
@PreDestroy
destroy()
as defined by theDisposableBean
callback interfaceA custom configured
destroy()
method
析构方法按同样的顺序调用:
先调用有
@PreDestroy
注解的方法再调用
DisposableBean
回调接口定义的destroy()
方法最好调用定制配置的
destroy()
方法
Startup and shutdown callbacks
The Lifecycle
interface defines the essential methods for any object that has its own lifecycle requirements (e.g. starts and stops some background process):
Lifecycle
接口定义了任何对象生命周期都需要的基本方法(例如启动和停止一些背景处理):
1 | public interface Lifecycle { |
Any Spring-managed object may implement that interface. Then, when the ApplicationContext
itself receives start and stop signals, e.g. for a stop/restart scenario at runtime, it will cascade those calls to all Lifecycle
implementations defined within that context. It does this by delegating to a LifecycleProcessor
:
任何Spring管理的对象都可以实现那个接口。当ApplicationContext
本身收到启动启动和关闭信号时,例如运行时关闭/再启动场景,它将级联调用所有的上下文定义的Lifecycle
实现。它通过委托LifecycleProcessor
来完成这个功能:
1 | public interface LifecycleProcessor extends Lifecycle { |
Notice that the LifecycleProcessor
is itself an extension of the Lifecycle
interface. It also adds two other methods for reacting to the context being refreshed and closed.
注意LifecycleProcessor
本身是Lifecycle
接口的一个扩展。它也添加了两个其它的方法来响应上下文的再刷新和关闭的。
Note that the regular
org.springframework.context.Lifecycle
interface is just a plain contract for explicit start/stop notifications and does NOT imply auto-startup at context refresh time. Consider implementingorg.springframework.context.SmartLifecycle
instead for fine-grained control over auto-startup of a specific bean (including startup phases). Also, please note that stop notifications are not guaranteed to come before destruction: On regular shutdown, allLifecycle
beans will first receive a stop notification before the general destruction callbacks are being propagated; however, on hot refresh during a context’s lifetime or on aborted refresh attempts, only destroy methods will be called.注意正规的
org.springframework.context.Lifecycle
接口只是一个显式启动/关闭通知的协议,并不意味着在上下文刷新时自动启动。考虑实现org.springframework.context.SmartLifecycle
接口来实现对指定bean自动启动的细粒度控制(包括启动时期)。请注意停止通知不能保证在销毁之前到来:在正式关闭时,所有的Lifecycle
beans在通常的析构回调传播之前首先会收到停止通知;但是,在上下文使用期间进行热刷新或尝试取消再刷新,只会调用析构方法。
The order of startup and shutdown invocations can be important. If a “depends-on” relationship exists between any two objects, the dependent side will start after its dependency, and it will stop before its dependency. However, at times the direct dependencies are unknown. You may only know that objects of a certain type should start prior to objects of another type. In those cases, the SmartLifecycle
interface defines another option, namely the getPhase()
method as defined on its super-interface, Phased
.
启动和关闭的调用顺序是很重要的。如果任何两个对象间存在一个”depends-on”关系,那么依赖关系将在它的依赖之后开始,在它的依赖之前停止。然而有时直接的依赖关系是未知的。你可能只知道某个类型的对象应该在另一个类型的对象之前启动。在那种情况下,SmartLifecycle
接口定义了另一种选择,也就是说getPhase()
定义在它的父接口Phased
中。
1 | public interface Phased { |
1 | public interface SmartLifecycle extends Lifecycle, Phased { |
When starting, the objects with the lowest phase start first, and when stopping, the reverse order is followed. Therefore, an object that implements SmartLifecycle
and whose getPhase()
method returns Integer.MIN_VALUE
would be among the first to start and the last to stop. At the other end of the spectrum, a phase value of Integer.MAX_VALUE
would indicate that the object should be started last and stopped first (likely because it depends on other processes to be running). When considering the phase value, it’s also important to know that the default phase for any “normal” Lifecycle
object that does not implement SmartLifecycle
would be 0. Therefore, any negative phase value would indicate that an object should start before those standard components (and stop after them), and vice versa for any positive phase value.
当开始时,最低相位的对象先启动,当停止时,最高相位的对象先停止。因此,实现了SmartLifecycle
接口,getPhase()
方法返回值为Integer.MIN_VALUE
的对象将最先启动并最后停止。另一方面,相位值Integer.MAX_VALUE
表明对象应该最后启动,最先停止(可能是因为它依赖其它运行的进程)。当考虑相位值时,知道任何没有实现SmartLifecycle
接口的Lifecycle
对象的默认值为0是很重要的。因此,任何负相位值表示对象应该在那么标准组件之前启动(在它们之后停止),反之为任何正相位值。
As you can see the stop method defined by SmartLifecycle
accepts a callback. Any implementation must invoke that callback’s run()
method after that implementation’s shutdown process is complete. That enables asynchronous shutdown where necessary since the default implementation of the LifecycleProcessor
interface, DefaultLifecycleProcessor
, will wait up to its timeout value for the group of objects within each phase to invoke that callback. The default per-phase timeout is 30 seconds. You can override the default lifecycle processor instance by defining a bean named “lifecycleProcessor” within the context. If you only want to modify the timeout, then defining the following would be sufficient:
正如你看到的,在SmartLifecycle
中定义的停止方法接收一个回调函数。任何实现在关闭进程完成之后都必须调用回调的run()
方法。当需要时这可以进行异步关闭,因为LifecycleProcessor
接口、DefaultLifecycleProcessor
接口的默认实现会等待每个阶段的对象组直到达到超时值,然后调用回调函数。默认每个时期的超时值为30秒。你可以在上下文中通过定义名为”lifecycleProcessor”的bean来覆盖默认的生命周期处理器实例。如果你只想修改超时值,如下定义是足够的:
1 | <bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor"> |
As mentioned, the LifecycleProcessor
interface defines callback methods for the refreshing and closing of the context as well. The latter will simply drive the shutdown process as if stop()
had been called explicitly, but it will happen when the context is closing. The refresh
callback on the other hand enables another feature of SmartLifecycle
beans. When the context is refreshed (after all objects have been instantiated and initialized), that callback will be invoked, and at that point the default lifecycle processor will check the boolean value returned by each SmartLifecycle
object’s isAutoStartup()
method. If “true”, then that object will be started at that point rather than waiting for an explicit invocation of the context’s or its own start()
method (unlike the context refresh, the context start does not happen automatically for a standard context implementation). The “phase” value as well as any “depends-on” relationships will determine the startup order in the same way as described above.
像上面提到的那样,LifecycleProcessor
接口为再刷新和上下文的关闭也定义了回调方法。后者会简单的驱动关闭进程就像显式的调用了stop()
方法一样,但当上下文关闭时它才会发生。另一方面refresh
回调能使SmartLifecycle
beans的另一个功能可用。当上下文再刷新时(所有对象已经实例化并初始化),回调函数将被调用,那时默认的生命周期处理器将会检查每个SmartLifecycle
对象的isAutoStartup()
方法返回的布尔值。如果为true
,对象将会在那时启动而不是等待上下文的显式调用或它自己的start()
方法(不像上下文再刷新,对于一个标准的上下文实现上下启动不会自动发生)。”phase”值以及”depends-on”关系将决定启动顺序,像上面描述的一样。
Shutting down the Spring IoC container gracefully in non-web applications
This section applies only to non-web applications. Spring’s web-based
ApplicationContext
implementations already have code in place to shut down the Spring IoC container gracefully when the relevant web application is shut down.
这一节只应用于非web应用。Spring的基于web的
ApplicationContext
实现已经有代码来处理当相关的web应用关闭时,妥善关闭Spring IoC容器的问题。
If you are using Spring’s IoC container in a non-web application environment; for example, in a rich client desktop environment; you register a shutdown hook with the JVM. Doing so ensures a graceful shutdown and calls the relevant destroy methods on your singleton beans so that all resources are released. Of course, you must still configure and implement these destroy callbacks correctly.
如果你在非web应用环境使用Spring的IoC容器;例如,在一个富桌面客户端环境中,你在JVM中注册一个关闭钩子。这样做确保了妥善的关闭,为了释放所有资源需要调用与单例beans相关的析构方法。当然,你仍然必须正确的配置和实现这些销毁回调函数。
To register a shutdown hook, you call the registerShutdownHook()
method that is declared on the ConfigurableApplicationContext
interface:
为了注册一个关闭钩子,你可以调用ConfigurableApplicationContext
接口中声明的registerShutdownHook()
方法:
1 | import org.springframework.context.ConfigurableApplicationContext; |
3.6.2 ApplicationContextAware and BeanNameAware
When an ApplicationContext
creates an object instance that implements the org.springframework.context.ApplicationContextAware
interface, the instance is provided with a reference to that ApplicationContext
.
当ApplicationContext
创建一个实现org.springframework.context.ApplicationContextAware
接口的对象实例时,这个实例会提供一个ApplicationContext
的引用。
1 | public interface ApplicationContextAware { |
Thus beans can manipulate programmatically the ApplicationContext
that created them, through the ApplicationContext
interface, or by casting the reference to a known subclass of this interface, such as ConfigurableApplicationContext
, which exposes additional functionality. One use would be the programmatic retrieval of other beans. Sometimes this capability is useful; however, in general you should avoid it, because it couples the code to Spring and does not follow the Inversion of Control style, where collaborators are provided to beans as properties. Other methods of the ApplicationContext
provide access to file resources, publishing application events, and accessing a MessageSource
. These additional features are described in Section 3.15, “Additional Capabilities of the ApplicationContext”.
因此beans可以以编程方式操纵创建它们的ApplicationContext
,通过ApplicationContext
接口,或通过将引用抛给这个接口的一个已知子类,例如ConfigurableApplicationContext
,它暴露了额外的功能。一个方法是编程式检索其他的bean。有时这个能力是很有用的,但是通常你应该避免使用它,因为它耦合了代码和Spring,不能遵循控制反转的风格,在控制反转中协作者是作为属性提供给beans的。ApplicationContext
的其它方法提供了对文件资源的访问,发布应用事件,访问MessageSource
的功能。这些额外的特性将在3.15小节『ApplicationContext”的额外能力』中描述。
As of Spring 2.5, autowiring is another alternative to obtain reference to the ApplicationContext
. The “traditional” constructor
and byType
autowiring modes (as described in Section 3.4.5, “Autowiring collaborators”) can provide a dependency of type ApplicationContext
for a constructor argument or setter method parameter, respectively. For more flexibility, including the ability to autowire fields and multiple parameter methods, use the new annotation-based autowiring features. If you do, the ApplicationContext
is autowired into a field, constructor argument, or method parameter that is expecting the ApplicationContext
type if the field, constructor, or method in question carries the @Autowired
annotation. For more information, see Section 3.9.2, “@Autowired”.
从Spring 2.5起,自动装配是另一种可替代的获得ApplicationContext
引用的方法。『传统的』constructor
和byType
自动装配模式(如3.4.5小节所述,『自动装配协作者』)可以分别为构造函数参数或setter方法参数提供ApplicationContext
类型的依赖。更多的灵活性包括自动装配变量的能力和多参数方法,使用新的基于注解的自动装配特性。如果你这一做的话,ApplicationContext
可以被自动装配到变量中,构造函数参数中或方法参数中,如果讨论的变量,构造函数或方法有@Autowired
注解,那么可以期望它是ApplicationContext
类型。更多信息请看3.9.2小节,@autowired
。
When an ApplicationContext
creates a class that implements the org.springframework.beans.factory.BeanNameAware
interface, the class is provided with a reference to the name defined in its associated object definition.
当ApplicationContext
创建一个实现了org.springframework.beans.factory.BeanNameAware
接口的类时,类中有相关的对象定义中定义的名称的引用。
1 | public interface BeanNameAware { |
The callback is invoked after population of normal bean properties but before an initialization callback such as InitializingBean
afterPropertiesSet or a custom init-method.
在正常的bean属性填入之后,回调方法调用,但在初始化回调方法之前,例如InitializingBean
的afterPropertiesSet或一个定制的初始化方法。
3.6.3 Other Aware interfaces
Besides ApplicationContextAware
and BeanNameAware
discussed above, Spring offers a range of Aware
interfaces that allow beans to indicate to the container that they require a certain infrastructure dependency. The most important Aware
interfaces are summarized below - as a general rule, the name is a good indication of the dependency type:
除了上面讨论的ApplicationContextAware
和BeanNameAware
之外,Spring给予了一系列Aware
接口来允许beans向容器表明它们需要一个确定的基础结构依赖。最重要的Aware
接口总结如下——作为一个通用规则,名字是依赖类型的一个很好暗示:
Table 3.4. Aware interfaces
Name | Injected Dependency | Explained in |
---|---|---|
ApplicationContextAware | Declaring ApplicationContext | Section 3.6.2, “ApplicationContextAware and BeanNameAware” |
ApplicationEventPublisherAware | Event publisher of the enclosing ApplicationContext | Section 3.15, “Additional Capabilities of the ApplicationContext” |
BeanClassLoaderAware | Class loader used to load the bean classes. | Section 3.3.2, “Instantiating beans” |
BeanFactoryAware | Declaring BeanFactory | Section 3.6.2, “ApplicationContextAware and BeanNameAware” |
BeanNameAware | Name of the declaring bean | Section 3.6.2, “ApplicationContextAware and BeanNameAware” |
BootstrapContextAware | Resource adapter BootstrapContext the container runs in. Typically available only in JCA aware ApplicationContexts | Chapter 28, JCA CCI |
LoadTimeWeaverAware | Defined weaver for processing class definition at load time | Section 7.8.4, “Load-time weaving with AspectJ in the Spring Framework” |
MessageSourceAware | Configured strategy for resolving messages (with support for parametrization and internationalization) | Section 3.15, “Additional Capabilities of the ApplicationContext” |
NotificationPublisherAware | Spring JMX notification publisher | Section 27.7, “Notifications” |
ResourceLoaderAware | Configured loader for low-level access to resources | Chapter 4, Resources |
ServletConfigAware | Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext | Chapter 18, Web MVC framework |
ServletContextAware | Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext | Chapter 18, Web MVC framework |
表3.4. Aware接口
Name | Injected Dependency | Explained in |
---|---|---|
ApplicationContextAware | 声明ApplicationContext |
Section 3.6.2, “ApplicationContextAware and BeanNameAware” |
ApplicationEventPublisherAware | 封装事件发布的ApplicationContext |
Section 3.15, “Additional Capabilities of the ApplicationContext” |
BeanClassLoaderAware | 用来加载bean的类加载器 | Section 3.3.2, “Instantiating beans” |
BeanFactoryAware | 声明BeanFactory |
Section 3.6.2, “ApplicationContextAware and BeanNameAware” |
BeanNameAware | 声明的bean的名字 | Section 3.6.2, “ApplicationContextAware and BeanNameAware” |
BootstrapContextAware | 容器运行的资源自适应BootstrapContext . 通常只在JCA aware ApplicationContexts 可获得 |
Chapter 28, JCA CCI |
LoadTimeWeaverAware | 加载时为处理类定义定义的weaver | Section 7.8.4, “Load-time weaving with AspectJ in the Spring Framework” |
MessageSourceAware | 解析消息配置策略 (支持参数化和国际化) | Section 3.15, “Additional Capabilities of the ApplicationContext” |
NotificationPublisherAware | Spring JMX通知发布器 | Section 27.7, “Notifications” |
ResourceLoaderAware | 为底层访问资源配置的加载器 | Chapter 4, Resources |
ServletConfigAware | 容器运行的当前ServletConfig 。 仅在web感知的Spring ApplicationContext 中有效 |
Chapter 18, Web MVC framework |
ServletContextAware | 容器运行的当前ServletContext 。 仅在web感知的Spring ApplicationContext 中有效 |
Chapter 18, Web MVC framework |
Note again that usage of these interfaces ties your code to the Spring API and does not follow the Inversion of Control style. As such, they are recommended for infrastructure beans that require programmatic access to the container.
注意这些接口的用法将你的代码与Spring进行了捆绑,不符合控制反转的风格。因此,它们是为那么需要以编程方式访问容器的基础结构beans推荐的。
3.7 Bean definition inheritance
A bean definition can contain a lot of configuration information, including constructor arguments, property values, and container-specific information such as initialization method, static factory method name, and so on. A child bean definition inherits configuration data from a parent definition. The child definition can override some values, or add others, as needed. Using parent and child bean definitions can save a lot of typing. Effectively, this is a form of templating.
Bean定义中可以包含许多配置信息,包括构造函数参数,属性值和容器的特定信息例如初始化方法,静态工厂方法名等等。子定义继承父定义的配置信息。子定义可以覆盖一些值,或按需要添加一些其它值。使用父子bean定义可以保存许多类型。实际上,这是一种模板形式。
If you work with an ApplicationContext
interface programmatically, child bean definitions are represented by the ChildBeanDefinition
class. Most users do not work with them on this level, instead configuring bean definitions declaratively in something like the ClassPathXmlApplicationContext
. When you use XML-based configuration metadata, you indicate a child bean definition by using the parent
attribute, specifying the parent bean as the value of this attribute.
如果你以编程方式使用ApplicationContext
接口,那么子bean定义是通过ChildBeanDefinition
定义来表示的。大多数用户不在这个层级上使用它们,而是在一些像ClassPathXmlApplicationContext
中声明式的配置bean定义。当你使用基于XML的配置元数据时,你可以使用parent
特性来指明一个子bean定义。在特性值中指定父bean。
1 | <bean id="inheritedTestBean" abstract="true" |
A child bean definition uses the bean class from the parent definition if none is specified, but can also override it. In the latter case, the child bean class must be compatible with the parent, that is, it must accept the parent’s property values.
如果子bean定义中没有指定要使用的bean类,则使用父定义中的bean类,但也可以覆盖它。在后一种情况下(没有指定要用的bean类),子bean定义必须与父bean协作,也就是说,它必须接收父定义的属性值。
A child bean definition inherits scope, constructor argument values, property values, and method overrides from the parent, with the option to add new values. Any scope, initialization method, destroy method, and/or static
factory method settings that you specify will override the corresponding parent settings.
子bean定义可以继承作用域,构造函数参数值,属性值,可以重写父方法,可以选择添加新值。你指定的任何作用域,初始化方法,析构方法,和/或静态工厂方法设置将会覆盖对应的父设置。
The remaining settings are always taken from the child definition: depends on, autowire mode, dependency check, singleton, lazy init.
其余的设置都是从子定义中获取:依赖关系,自动装配模式,依赖检查,单例,延迟初始化。
The preceding example explicitly marks the parent bean definition as abstract
by using the abstract attribute. If the parent definition does not specify a class, explicitly marking the parent bean definition as abstract
is required, as follows:
前面的例子使用抽象特性将父bean定义显式的标记为abstract
。如果父定义没有指定一个类,需要显式的将父bean定义为abstract
,形式如下:
1 | <bean id="inheritedTestBeanWithoutClass" abstract="true"> |
The parent bean cannot be instantiated on its own because it is incomplete, and it is also explicitly marked as abstract
. When a definition is abstract
like this, it is usable only as a pure template bean definition that serves as a parent definition for child definitions. Trying to use such an abstract
parent bean on its own, by referring to it as a ref property of another bean or doing an explicit getBean()
call with the parent bean id, returns an error. Similarly, the container’s internal preInstantiateSingletons()
method ignores bean definitions that are defined as abstract.
父bean不能实例化,因为它不完整,并且它被显式的标记为abstract
。当一个bean定义是abstract
时,它只能是一个纯粹的bean定义模板,作为一个为子定义服务的父定义。当试图使用一个abstract
父bean时,可以通过另一个bean的ref
属性来引用它或通过父bean的id为参数显式的调用getBean()
方法,会返回一个错误。类似的,容器内部的preInstantiateSingletons()
方法会忽略抽象bean定义。
ApplicationContext
pre-instantiates all singletons by default. Therefore, it is important (at least for singleton beans) that if you have a (parent) bean definition which you intend to use only as a template, and this definition specifies a class, you must make sure to set the abstract attribute to true, otherwise the application context will actually (attempt to) pre-instantiate theabstract
bean.
默认情况下
ApplicationContext
会预实例化所有的单例。因此,如果你想有一个(父)bean定义只作为模板来使用,这个定义中指定了一个类,那你必须确保设置abstract
特性为true
,否则应用上下文会(试图)预实例化这个abstract
bean。
3.8 Container Extension Points
Typically, an application developer does not need to subclass ApplicationContext
implementation classes. Instead, the Spring IoC container can be extended by plugging in implementations of special integration interfaces. The next few sections describe these integration interfaces.
通常情况下,应用开发者不需要继承ApplicationContext
的实现类。反而是Spring的IoC容器可以通过插入特定集成接口的实现来进行扩展。下面几节将描述这些集成接口。
3.8.1 Customizing beans using a BeanPostProcessor
The BeanPostProcessor
interface defines callback methods that you can implement to provide your own (or override the container’s default) instantiation logic, dependency-resolution logic, and so forth. If you want to implement some custom logic after the Spring container finishes instantiating, configuring, and initializing a bean, you can plug in one or more BeanPostProcessor
implementations.
BeanPostProcessor
接口定义了回调方法,你可以实现这个方法来提供你自己的(或覆盖容器默认的)实例化逻辑,依赖解析逻辑等等。如果你想在Spring容器完成实例化,配置和初始化bean之后实现一些定制的业务逻辑,你可以插入一个或多个BeanPostProcessor
实现。
You can configure multiple BeanPostProcessor
instances, and you can control the order in which these BeanPostProcessors
execute by setting the order
property. You can set this property only if the BeanPostProcessor
implements the Ordered
interface; if you write your own BeanPostProcessor
you should consider implementing the Ordered
interface too. For further details, consult the javadocs of the BeanPostProcessor
and Ordered
interfaces. See also the note below on programmatic registration of BeanPostProcessors
.
你可以配置多个BeanPostProcessor
实例,通过设置order
属性你可以控制BeanPostProcessors
的执行顺序。只有BeanPostProcessor
实现了Ordered
接口时你才可以设置这个属性;如果你编写了你自己的BeanPostProcessor
,你也应该考虑实现Ordered
接口。更多细节请参考BeanPostProcessor
接口和Ordered
接口的Java文档。也可以查看下面的BeanPostProcessors
编程注册的笔记。
BeanPostProcessors
operate on bean (or object) instances; that is to say, the Spring IoC container instantiates a bean instance and thenBeanPostProcessors
do their work.
BeanPostProcessors
are scoped per-container. This is only relevant if you are using container hierarchies. If you define aBeanPostProcessor
in one container, it will only post-process the beans in that container. In other words, beans that are defined in one container are not post-processed by aBeanPostProcessor
defined in another container, even if both containers are part of the same hierarchy.To change the actual bean definition (i.e., the blueprint that defines the bean), you instead need to use a
BeanFactoryPostProcessor
as described in Section 3.8.2, “Customizing configuration metadata with a BeanFactoryPostProcessor”.
BeanPostProcessors
操作一个bean(或对象)实例;也就是说,Spring Ioc容器实例化一个bean实例,然后BeanPostProcessors
完成它们的工作。
BeanPostProcessors
的作用域是每个容器。只有你在使用容器分层的情况下,这才是相关的。如果你在一个容器中定义了一个BeanPostProcessor
,它将只后处理容器中的beans。换句话说,某个容器中定义的beans不能被另一个容器中定义的BeanPostProcessor
进行后处理,即使这两个容器是同一层上的一部分。为了改变实际的bean定义(例如,定义bean的蓝图),你可以使用3.8.2小节中描述的
BeanFactoryPostProcessor
。
The org.springframework.beans.factory.config.BeanPostProcessor
interface consists of exactly two callback methods. When such a class is registered as a post-processor with the container, for each bean instance that is created by the container, the post-processor gets a callback from the container both before container initialization methods (such as InitializingBean’s afterPropertiesSet() and any declared init method) are called as well as after any bean initialization callbacks. The post-processor can take any action with the bean instance, including ignoring the callback completely. A bean post-processor typically checks for callback interfaces or may wrap a bean with a proxy. Some Spring AOP infrastructure classes are implemented as bean post-processors in order to provide proxy-wrapping logic.
org.springframework.beans.factory.config.BeanPostProcessor
接口包含恰好两个回调方法。当这样一个类在容器中注册为后处理器时,对于容器中创建的每一个bean实例,在容器初始化方法(例如InitializingBean
的afterPropertiesSet()
方法和任何已声明的初始化方法)被调用之前和任何bean初始化回调函数之后,后处理器会从容器中得到一个回调函数。后处理器可以对bean实例进行任何操作,包括完全忽略回调方法。bean后处理器通常检查回调接口或将bean包裹到代理中。为了提供代理包裹逻辑,一些Spring AOP基础结构类被实现为bean后处理器。
An ApplicationContext
automatically detects any beans that are defined in the configuration metadata which implement the BeanPostProcessor
interface. The ApplicationContext
registers these beans as post-processors so that they can be called later upon bean creation. Bean post-processors can be deployed in the container just like any other beans.
ApplicationContext
会自动检测任何配置元数据中定义的实现了BeanPostProcessor
接口的bean。为了能在后面bean创建时调用这些bean,ApplicationContext
会将这些bean注册为后处理器。bean后处理器可以像其它bean一样在容器进行部署。
Note that when declaring a BeanPostProcessor
using an @Bean
factory method on a configuration class, the return type of the factory method should be the implementation class itself or at least the org.springframework.beans.factory.config.BeanPostProcessor
interface, clearly indicating the post-processor nature of that bean. Otherwise, the ApplicationContext
won’t be able to autodetect it by type before fully creating it. Since a BeanPostProcessor
needs to be instantiated early in order to apply to the initialization of other beans in the context, this early type detection is critical.
注意当在一个配置类上使用@Bean
声明一个BeanPostProcessor
时,工厂方法的返回值应该是实现类本身或是org.springframework.beans.factory.config.BeanPostProcessor
接口,这能清晰的表明bean的后处理器特性。此外,在完整的创建它之前,ApplicationContext
不能通过类型自动检测它。由于BeanPostProcessor
需要早一点实例化,为了在上下文中初始化其它的beans,早期的类型检测是非常关键的。
While the recommended approach for
BeanPostProcessor
registration is throughApplicationContext
auto-detection (as described above), it is also possible to register them programmatically against aConfigurableBeanFactory
using theaddBeanPostProcessor
method. This can be useful when needing to evaluate conditional logic before registration, or even for copying bean post processors across contexts in a hierarchy. Note however thatBeanPostProcessors
added programmatically do not respect theOrdered
interface. Here it is the order of registration that dictates the order of execution. Note also thatBeanPostProcessors
registered programmatically are always processed before those registered through auto-detection, regardless of any explicit ordering.
虽然推荐的注册
BeanPostProcessor
的方法是通过ApplicationContext
自动检测(像前面描述的那样),但也可以通过以编程方法通过使用ConfigurableBeanFactory
的addBeanPostProcessor
方法来注册。在注册之前需要评估条件逻辑时,这是非常有用的,或者通过分层中的上下文来复制bean后处理器。注意以编程方式添加的BeanPostProcessors
不需要Ordered
接口。这种情况下注册顺序意味着执行顺序。注意以编程方式注册的BeanPostProcessors
中是在那些通过自动检测注册的BeanPostProcessors
之前进行处理,不管任何显式的顺序指定。
Classes that implement the
BeanPostProcessor
interface are special and are treated differently by the container. AllBeanPostProcessors
and beans that they reference directly are instantiated on startup, as part of the special startup phase of theApplicationContext
. Next, allBeanPostProcessors
are registered in a sorted fashion and applied to all further beans in the container. Because AOP auto-proxying is implemented as aBeanPostProcessor
itself, neitherBeanPostProcessors
nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.For any such bean, you should see an informational log message: “Bean foo is not eligible for getting processed by all
BeanPostProcessor
interfaces (for example: not eligible for auto-proxying)”.Note that if you have beans wired into your
BeanPostProcessor
using autowiring or@Resource
(which may fall back to autowiring), Spring might access unexpected beans when searching for type-matching dependency candidates, and therefore make them ineligible for auto-proxying or other kinds of bean post-processing. For example, if you have a dependency annotated with@Resource
where thefield/setter
name does not directly correspond to the declared name of a bean and no name attribute is used, then Spring will access other beans for matching them by type.
实现
BeanPostProcessor
接口的类是特别的并被容器不同对待。所有的BeanPostProcessors
和它们直接引用的beans在启动时进行实例化,它们是ApplicationContext
特定启动阶段的一部分。接下来,所有BeanPostProcessors
以有序形式进行注册,并适用于容器中所有更进一步的beans。由于AOP自动代理是作为BeanPostProcessor
本身实现的,既不是BeanPostProcessors
也不是它们直接引用的beans适合进行自动代理,因此没有融入它们的方面。对于这样的bean,你应该看到一个信息日志消息:”Bean foo没资格被所有的
BeanPostProcessor
接口进行处理(例如,不适合自动代理)。”。注意如果有beans使用自动装配或
@Resource
(可能回到自动装配)注入你的BeanPostProcessor
,当搜索类型匹配的依赖候选者时,Spring可能访问未预料到beans,因此使它们不适合自动代理或其他类型的进行后处理的bean。例如,如果你有一个带有@Resource
注解的依赖,field/setter
名称不能直接对应bean声明的名字,也没有使用name特性,Spring将通过类型匹配来访问其它的bean。
The following examples show how to write, register, and use BeanPostProcessors
in an ApplicationContext
.
下面的例子展示了在ApplicationContext
中如何编写,注册和使用BeanPostProcessors
。
Example: Hello World, BeanPostProcessor-style
This first example illustrates basic usage. The example shows a custom BeanPostProcessor
implementation that invokes the toString()
method of each bean as it is created by the container and prints the resulting string to the system console.
第一个例子阐述了基本用法。这个例子展示了一个定制BeanPostProcessor
实现,实现中调用了每一个bean的toString()
方法。当容器创建它时,会将结果字符串输出到系统控制台。
Find below the custom BeanPostProcessor
implementation class definition:
下面是定制BeanPostProcessor
实现的类定义:
1 | package scripting; |
1 | <?xml version="1.0" encoding="UTF-8"?> |
Notice how the InstantiationTracingBeanPostProcessor
is simply defined. It does not even have a name, and because it is a bean it can be dependency-injected just like any other bean. (The preceding configuration also defines a bean that is backed by a Groovy script. The Spring dynamic language support is detailed in the chapter entitled Chapter 31, Dynamic language support.)
注意InstantiationTracingBeanPostProcessor
是怎样简单定义的。它甚至没有一个名字,因为它是一个bean,它能像其它bean一样进行依赖注入。(前面的配置也定义了一个bean,它被Groovy脚本支持。Spring动态语言支持在31章『动态语言支持』中进行了详细描述。)
The following simple Java application executes the preceding code and configuration:
下面的简单Java应用执行了前面的代码和配置:
1 | import org.springframework.context.ApplicationContext; |
The output of the preceding application resembles the following:
前面的应用输出结果如下:
1 | Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961 |
Example: The RequiredAnnotationBeanPostProcessor
Using callback interfaces or annotations in conjunction with a custom BeanPostProcessor
implementation is a common means of extending the Spring IoC container. An example is Spring’s RequiredAnnotationBeanPostProcessor
- a BeanPostProcessor
implementation that ships with the Spring distribution which ensures that JavaBean
properties on beans that are marked with an (arbitrary) annotation are actually (configured to be) dependency-injected with a value.
使用回调函数接口或注解结合定制BeanPostProcessor
实现是扩展Spring IoC容器的常见方法。一个例子是Spring的RequiredAnnotationBeanPostProcessor
——一个BeanPostProcessor
实现附带在Spring发行中,它保证了标记有(任意)注解的beans上的JavaBean
属性能真正(配置成)通过值进行依赖注入。
3.8.2 Customizing configuration metadata with a BeanFactoryPostProcessor
The next extension point that we will look at is the org.springframework.beans.factory.config.BeanFactoryPostProcessor
. The semantics of this interface are similar to those of the BeanPostProcessor
, with one major difference: BeanFactoryPostProcessor
operates on the bean configuration metadata; that is, the Spring IoC container allows a BeanFactoryPostProcessor
to read the configuration metadata and potentially change it before the container instantiates any beans other than BeanFactoryPostProcessors
.
接下来我们要看到的扩展点是org.springframework.beans.factory.config.BeanFactoryPostProcessor
。这个接口的语义与那些BeanPostProcessor
类似,但有一个主要的不同:BeanFactoryPostProcessor
可以操作配置元数据;也就是说,Spring IoC容器允许在容器实例化除了BeanFactoryPostProcessor
之外的任何beans之前,BeanFactoryPostProcessor
读取配置元数据并可能修改它们。
You can configure multiple BeanFactoryPostProcessors
, and you can control the order in which these BeanFactoryPostProcessors
execute by setting the order
property. However, you can only set this property if the BeanFactoryPostProcessor
implements the Ordered
interface. If you write your own BeanFactoryPostProcessor
, you should consider implementing the Ordered
interface too. Consult the javadocs of the BeanFactoryPostProcessor
and Ordered
interfaces for more details.
你可以配置多个BeanFactoryPostProcessors
,你可以通过设置order
属性来控制这些BeanFactoryPostProcessors
的执行顺序。但是,只有BeanFactoryPostProcessor
实现了Ordered
接口时你才可以设置这个属性。如果你编写了你自己的BeanFactoryPostProcessor
,你也应该考虑实现Ordered
接口。关于BeanFactoryPostProcessor
和Ordered
的更多细节请看文档。
If you want to change the actual bean instances (i.e., the objects that are created from the configuration metadata), then you instead need to use a
BeanPostProcessor
(described above in Section 3.8.1, “Customizing beans using a BeanPostProcessor”). While it is technically possible to work with bean instances within aBeanFactoryPostProcessor
(e.g., usingBeanFactory.getBean()
), doing so causes premature bean instantiation, violating the standard container lifecycle. This may cause negative side effects such as bypassing bean post processing.Also,
BeanFactoryPostProcessors
are scoped per-container. This is only relevant if you are using container hierarchies. If you define aBeanFactoryPostProcessor
in one container, it will only be applied to the bean definitions in that container. Bean definitions in one container will not be post-processed byBeanFactoryPostProcessors
in another container, even if both containers are part of the same hierarchy.
如果你想改变真正的bean实例(例如,从配置元数据中创建的对象),你应该需要使用
BeanPostProcessor
(3.8.1小节中描述的)。尽管在BeanFactoryPostProcessor
中处理bean实例在技术上是可能的(例如使用BeanFactory.getBean()
),但这样做会引起过早的bean实例化,违背标准的容器生命周期。这可能会产生负面影响例如绕过bean后处理。
BeanFactoryPostProcessors
的作用域也是在每个容器中。这仅对于容器分层而言。如果在一个容器中你定义了一个BeanFactoryPostProcessor
,它将适用于那个容器中的bean定义。一个容器中的bean定义不能被另一个容器中的BeanFactoryPostProcessors
进行后处理,即使两个容器是在同一个分层中。
A bean factory post-processor is executed automatically when it is declared inside an ApplicationContext
, in order to apply changes to the configuration metadata that define the container. Spring includes a number of predefined bean factory post-processors, such as PropertyOverrideConfigurer
and PropertyPlaceholderConfigurer
. A custom BeanFactoryPostProcessor
can also be used, for example, to register custom property editors.
为了修改定义在容器中的配置元数据,当一个bean工厂后处理器在ApplicationContext
中声明时,它会自动执行。Spring包含许多预先定义的bean工厂后处理器,例如PropertyOverrideConfigurer
和PropertyPlaceholderConfigurer
。定制的BeanFactoryPostProcessor
也可以使用,例如,为了注册定制的属性编辑器。
An ApplicationContext
automatically detects any beans that are deployed into it that implement the BeanFactoryPostProcessor
interface. It uses these beans as bean factory post-processors, at the appropriate time. You can deploy these post-processor beans as you would any other bean.
ApplicationContext
会自动检测任何部署在它之内的实现了BeanFactoryPostProcessor
接口的bean。在合适的时间,它会使用这些beans作为bean工厂后处理器。你可以像任何你使用的bean那样部署这些后处理器beans。
As with
BeanPostProcessors
, you typically do not want to configureBeanFactoryPostProcessors
for lazy initialization. If no other bean references aBean(Factory)PostProcessor
, that post-processor will not get instantiated at all. Thus, marking it for lazy initialization will be ignored, and theBean(Factory)PostProcessor
will be instantiated eagerly even if you set the default-lazy-init attribute totrue
on the declaration of your<beans/>
element.
关于
BeanPostProcessors
, 通常情况下你不想配置BeanFactoryPostProcessors
为延迟初始化。 如果没有别的bean引用Bean(Factory)PostProcessor
,后处理器将不会实例化。因此,对它进行延迟初始化会被忽略,即使你将<beans/>
元素中的default-lazy-init
特性设置为true
,Bean(Factory)PostProcessor
也会急切的初始化。
Example: the Class name substitution PropertyPlaceholderConfigurer
You use the PropertyPlaceholderConfigurer
to externalize property values from a bean definition in a separate file using the standard Java Properties
format. Doing so enables the person deploying an application to customize environment-specific properties such as database URLs and passwords, without the complexity or risk of modifying the main XML definition file or files for the container.
你可以使用PropertyPlaceholderConfigurer
读取单独文件中的bean定义来使属性具体化,这个单独文件使用标准的Java Properties
格式。这样做可以在部署应用时定制特定环境属性例如数据库URLs和密码,没有复杂性或修改主XML定义文件及容器相关文件的风险。
Consider the following XML-based configuration metadata fragment, where a DataSource
with placeholder values is defined. The example shows properties configured from an external Properties
file. At runtime, a PropertyPlaceholderConfigurer
is applied to the metadata that will replace some properties of the DataSource
. The values to replace are specified as placeholders of the form ${property-name}
which follows the Ant/log4j/JSP EL style.
考虑一下下面的基于XML定义的配置元数据片段,其中定义了一个带有占位符的DataSource
。这个例子展示了从外部Properties
文件进行属性配置。在运行时,PropertyPlaceholderConfigurer
会应用到元数据中,将会替换DataSource
中的一些属性。通过${property-name}
形式的占位符指定要替换的值,这遵循了Ant/log4j/JSP EL风格。
1 | <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> |
The actual values come from another file in the standard Java Properties
format:
真正的属性值来自于另一个以标准Java Properties
形式编写的文件:
1 | jdbc.driverClassName=org.hsqldb.jdbcDriver |
Therefore, the string ${jdbc.username}
is replaced at runtime with the value ‘sa’, and the same applies for other placeholder values that match keys in the properties file. The PropertyPlaceholderConfigurer
checks for placeholders in most properties and attributes of a bean definition. Furthermore, the placeholder prefix and suffix can be customized.
因此,在运行是字符串${jdbc.username}
被替换为sa
,其它的匹配属性文件中的key的占位符的值以同样方式替换。PropertyPlaceholderConfigurer
会检查bean中大多数属性和特性的占位符。此外,占位符的前缀和后缀都可以定制。
With the context namespace introduced in Spring 2.5, it is possible to configure property placeholders with a dedicated configuration element. One or more locations can be provided as a comma-separated list in the location
attribute.
Spring 2.5中引入了上下文命名空间,可以通过专用配置元素配置属性占位符。在location
特性可以提供一个或多个位置,多个位置用逗号分开。
1 | <context:property-placeholder location="classpath:com/foo/jdbc.properties"/> |
The PropertyPlaceholderConfigurer
not only looks for properties in the Properties
file you specify. By default it also checks against the Java System
properties if it cannot find a property in the specified properties files. You can customize this behavior by setting the systemPropertiesMode
property of the configurer with one of the following three supported integer values:
never (0): Never check system properties
fallback (1): Check system properties if not resolvable in the specified properties files. This is the default.
override (2): Check system properties first, before trying the specified properties files. This allows system properties to override any other property source.
PropertyPlaceholderConfigurer
不仅仅查找指定Properties
文件中的属性。默认情况下,如果不能在指定属性文件中找到属性,它也检查Java System
属性。你可以通过下面三个支持的整数值中的一个设置配置器的systemPropertiesMode
属性,从而定制查找行为。
never (0): 从不检查
system
属性fallback (1): 如果不能在指定文件中解析属性,检查
system
属性,这是默认值。override (2): 在查找指定文件之前,首先检查
system
属性,这可以使系统属性覆盖任何其它属性源。
Consult the PropertyPlaceholderConfigurer
javadocs for more information.
更多信息请看PropertyPlaceholderConfigurer
文档。
You can use the
PropertyPlaceholderConfigurer
to substitute class names, which is sometimes useful when you have to pick a particular implementation class at runtime. For example:你可以
PropertyPlaceholderConfigurer
替换类名,有时候非常有用,特别是运行时你必须选择一个特别的实现类的情况下。例如:
1 | <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> |
If the class cannot be resolved at runtime to a valid class, resolution of the bean fails when it is about to be created, which is during the
preInstantiateSingletons()
phase of anApplicationContext
for a non-lazy-init bean.如果这个类不能在运行时解析成一个有效类,对于一个非懒惰初始化的bean,当它要创建时,在
ApplicationContext
的preInstantiateSingletons()
期间,bean会解析失败。
Example: the PropertyOverrideConfigurer
The PropertyOverrideConfigurer
, another bean factory post-processor, resembles the PropertyPlaceholderConfigurer
, but unlike the latter, the original definitions can have default values or no values at all for bean properties. If an overriding Properties
file does not have an entry for a certain bean property, the default context definition is used.
PropertyOverrideConfigurer
,另一个bean工厂后处理器,类似于PropertyPlaceholderConfigurer
,但不像后者,最初的定义可以有默认值或bean属性一点也没有值。如果一个覆写的Properties
文件对于某个bean属性没有任何输入,会使用默认的上下文定义。
Note that the bean definition is not aware of being overridden, so it is not immediately obvious from the XML definition file that the override configurer is being used. In case of multiple PropertyOverrideConfigurer
instances that define different values for the same bean property, the last one wins, due to the overriding mechanism.
注意bean定义没有意识到被覆写了,因此从XML定义文件中它不能立刻很明显的看出在使用覆写的配置器。为了防止多个PropertyOverrideConfigurer
实例对于同一个bean属性定义不同的值,根据覆写机制,使用最后一个定义的值。
Properties file configuration lines take this format:
属性文件配置形式如下:
1 | beanName.property=value |
For example:
例如:
1 | dataSource.driverClassName=com.mysql.jdbc.Driver |
This example file can be used with a container definition that contains a bean called dataSource
, which has driver
and url
properties.
例子文件可以被包含名为dataSource
bean的容器定义使用,它有一个driver
和url
属性。
Compound property names are also supported, as long as every component of the path except the final property being overridden is already non-null (presumably initialized by the constructors). In this example…
混合属性命名也支持,除了最后被覆写的属性,只要路径的每部分都已经是非空(假设构造函数进行初始化)。在这个例子中:
1 | foo.fred.bob.sammy=123 |
the sammy
property of the bob
property of the fred
property of the foo
bean is set to the scalar value 123
.
foo
bean中的fred
属性的bob
属性的sammy
属性设为标量值123
。
Specified override values are always literal values; they are not translated into bean references. This convention also applies when the original value in the XML bean definition specifies a bean reference.
指定的覆写值总是字面值;它们不能转成bean引用。当XML bean定义中的初始值指定了一个bean引用时,这个规范同样有效。
With the context namespace introduced in Spring 2.5, it is possible to configure property overriding with a dedicated configuration element:
Spring 2.5引入了上下文命名空间,可以用专用配置元素配置属性覆写:
1 | <context:property-override location="classpath:override.properties"/> |
3.8.3 Customizing instantiation logic with a FactoryBean
Implement the org.springframework.beans.factory.FactoryBean
interface for objects that are themselves factories.
为对象实现org.springframework.beans.factory.FactoryBean
接口的是工厂本身。
The FactoryBean
interface is a point of pluggability into the Spring IoC container’s instantiation logic. If you have complex initialization code that is better expressed in Java as opposed to a (potentially) verbose amount of XML, you can create your own FactoryBean
, write the complex initialization inside that class, and then plug your custom FactoryBean
into the container.
FactoryBean
接口是Spring IoC的实例化逻辑可插入性的一个点。如果你有复杂的初始化代码,相比于大量的冗余的XML代码用Java语言来表达会更好,那么你可以创建你自己的FactoryBean
,在类里面编写复杂的初始化逻辑,并将你定制的FactoryBean
插入到容器中。
The FactoryBean
interface provides three methods:
Object getObject()
: returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes.boolean isSingleton()
: returns true if this FactoryBean returns singletons, false otherwise.Class getObjectType()
: returns the object type returned by the getObject() method or null if the type is not known in advance.
FactoryBean
接口提供了三个方法:
Object getObject()
: 返回一个工厂创建的对象实例。这个实例可能被共享, 依赖于工厂是否返回一个单例或原型。boolean isSingleton()
: 如果FactoryBean
返回单例,返回true,否则返回false。Class getObjectType()
: 返回getObject()
方法返回的类型,如果类型不能提前知道则返回null。
The FactoryBean
concept and interface is used in a number of places within the Spring Framework; more than 50 implementations of the FactoryBean
interface ship with Spring itself.
FactoryBean
的概念和接口在Spring框架中的许多地方都使用了;Spring本身中有不止50个FactoryBean
接口的实现。
When you need to ask a container for an actual FactoryBean
instance itself instead of the bean it produces, preface the bean’s id with the ampersand symbol (&) when calling the getBean()
method of the ApplicationContext
. So for a given FactoryBean
with an id of myBean
, invoking getBean("myBean")
on the container returns the product of the FactoryBean
; whereas, invoking getBean("&myBean")
returns the FactoryBean
instance itself.
当你需要向容器请求一个真正的FactoryBean
实例本身来代替它产生的bean时,调用ApplicationContext
的getBean()
方法时,bean的id前面要加上一个$
符。因此给定一个id为myBean
的FactoryBean
,在容器中调用getBean("myBean")
,返回FactoryBean
的产品,但调用getBean("&myBean")
会返回FactoryBean
实例本身。
3.9 Annotation-based container configuration
Are annotations better than XML for configuring Spring?
The introduction of annotation-based configurations raised the question of whether this approach is ‘better’ than XML. The short answer is it depends. The long answer is that each approach has its pros and cons, and usually it is up to the developer to decide which strategy suits them better. Due to the way they are defined, annotations provide a lot of context in their declaration, leading to shorter and more concise configuration. However, XML excels at wiring up components without touching their source code or recompiling them. Some developers prefer having the wiring close to the source while others argue that annotated classes are no longer POJOs and, furthermore, that the configuration becomes decentralized and harder to control.
No matter the choice, Spring can accommodate both styles and even mix them together. It’s worth pointing out that through its JavaConfig option, Spring allows annotations to be used in a non-invasive way, without touching the target components source code and that in terms of tooling, all configuration styles are supported by the Spring Tool Suite.
在配置Spring时注解是否比XML更好?
基于注解配置的引入引出了一个问题——这种方式是否比基于XML的配置更好。简短的回答是视情况而定。长一点的回答是每种方法都有它的优点和缺点,通常是由开发者决定哪一种策略更适合他们。由于注解的定义方式,注解在它们的声明中提供了许多上下文,导致配置更简短更简洁。然而,XML擅长连接组件而不必接触源代码或重新编译它们。一些开发者更喜欢接近源代码,而另一些人则认为基于注解的类不再是POJOs,此外,配置变的去中心化,而且更难控制。
无论选择是什么,Spring都能容纳这两种风格,甚至可以将它们混合在一起。值得指出的是,通过它的Java配置选项,Spring允许注解以一种非入侵的方式使用,不触碰目标组件源码和那些工具,所有的配置风格由Spring工具套件支持。
An alternative to XML setups is provided by annotation-based configuration which rely on the bytecode metadata for wiring up components instead of angle-bracket declarations. Instead of using XML to describe a bean wiring, the developer moves the configuration into the component class itself by using annotations on the relevant class, method, or field declaration. As mentioned in the section called “Example: The RequiredAnnotationBeanPostProcessor”, using a BeanPostProcessor
in conjunction with annotations is a common means of extending the Spring IoC container. For example, Spring 2.0 introduced the possibility of enforcing required properties with the @Required
annotation. Spring 2.5 made it possible to follow that same general approach to drive Spring’s dependency injection. Essentially, the @Autowired
annotation provides the same capabilities as described in Section 3.4.5, “Autowiring collaborators” but with more fine-grained control and wider applicability. Spring 2.5 also added support for JSR-250 annotations such as @PostConstruct
, and @PreDestroy
. Spring 3.0 added support for JSR-330 (Dependency Injection for Java) annotations contained in the javax.inject
package such as @Inject
and @Named
. Details about those annotations can be found in the relevant section.
基于注解的配置提供了一种XML设置的可替代方式,它依赖于字节码元数据来组装组件,而不是用尖括号声明的方式。代替使用XML来描述bean组装,开发者通过将注解使用在相关的类,方法或字段声明中,将配置移动到了组件类本身的内部。正如在“Example: The RequiredAnnotationBeanPostProcessor”那节提到的那样,使用BeanPostProcessor
与注解结合是扩展Spring IoC容器的的常见方法。例如,Spring 2.0引入了@Required
注解来执行需要的属性的可能性。Spring 2.5使以同样地通用方法来驱动Spring的依赖注入变为可能。本质上来说,@Autowired
提供了如3.4.5小节描述的同样的能力。“Autowiring collaborators”但更细粒度的控制和更广的应用性。Spring 2.5也添加对JSR-250注解的支持,例如,@PostConstruct
和@PreDestroy
。Spring 3.0添加了对JSR-330,包含在javax.inject
包内的注解(Java的依赖注入)的支持,例如@Inject
和@Named
。关于这些注解的细节可以在相关的小节找到。
Annotation injection is performed before XML injection, thus the latter configuration will override the former for properties wired through both approaches.
注解注入在XML注入之前进行,因此对于通过两种方法进行组装的属性后者的配置会覆盖前者。
As always, you can register them as individual bean definitions, but they can also be implicitly registered by including the following tag in an XML-based Spring configuration (notice the inclusion of the context namespace):
跟以前一样,你可以作为单独的bean定义来注册它们,但也可以通过在一个基于XML的Spring配置(注入包含上下文命名空间)中包含下面的标签来隐式的注册它们:
1 | <?xml version="1.0" encoding="UTF-8"?> |
(The implicitly registered post-processors include AutowiredAnnotationBeanPostProcessor
, CommonAnnotationBeanPostProcessor
, PersistenceAnnotationBeanPostProcessor
, as well as the aforementioned RequiredAnnotationBeanPostProcessor
.)
(隐式注册的后处理器包括 AutowiredAnnotationBeanPostProcessor
,CommonAnnotationBeanPostProcessor
,PersistenceAnnotationBeanPostProcessor
和前面提到的RequiredAnnotationBeanPostProcessor
。)
<context:annotation-config/>
only looks for annotations on beans in the same application context in which it is defined. This means that, if you put<context:annotation-config/>
in aWebApplicationContext
for aDispatcherServlet
, it only checks for@Autowired
beans in your controllers, and not your services. See Section 18.2, “The DispatcherServlet” for more information.
<context:annotation-config/>
仅在定义它的同样的应用上下文中寻找注解的beans。这意味着,如果你在一个为DispatcherServlet
服务的WebApplicationContext
中放置了<context:annotation-config/>
,它只能在你的控制器中寻找@Autowired
注解的beans,而不是在你的服务层中。更多信息请看18.2小节,“The DispatcherServlet”。
3.9.1 @Required
The @Required
annotation applies to bean property setter methods, as in the following example:
@Required
注解应用到bean属性的setter方法上,例子如下:
1 | public class SimpleMovieLister { |
This annotation simply indicates that the affected bean property must be populated at configuration time, through an explicit property value in a bean definition or through autowiring. The container throws an exception if the affected bean property has not been populated; this allows for eager and explicit failure, avoiding NullPointerExceptions
or the like later on. It is still recommended that you put assertions into the bean class itself, for example, into an init method. Doing so enforces those required references and values even when you use the class outside of a container.
这个注解仅仅是表明受影响的bean属性必须在配置时通过显式的bean定义或自动组装填充。如果受影响的bean属性没有填充,容器会抛出一个异常,这允许及早明确的失败,避免NullPointerExceptions
或后面出现类似的情况。仍然建议你在bean类本身加入断言,例如,加入到初始化方法中。这样做可以强制这些需要的引用和值,甚至是你在容器外部使用这个类的时候。
3.9.2 @Autowired
JSR 330’s
@Inject
annotation can be used in place of Spring’s@Autowired
annotation in the examples below. See here for more details.
在下面的例子中JSR 330的
@Inject
注解可以用来代替Spring的@Autowired
注解。
You can apply the @Autowired
annotation to constructors:
你可以将@Autowired
注解应用到构造函数上。
1 | public class MovieRecommender { |
As of Spring Framework 4.3, the
@Autowired
constructor is no longer necessary if the target bean only defines one constructor. If several constructors are available, at least one must be annotated to teach the container which one it has to use.
从Spring框架4.3起,如果目标bena仅定义了一个构造函数,那么
@Autowired
注解的构造函数不再是必要的。如果一些构造函数是可获得的,至少有一个必须要加上注解,以便于告诉容器使用哪一个。
As expected, you can also apply the @Autowired
annotation to “traditional” setter methods:
正如预料的那样,你也可以将@Autowired
注解应用到“传统的”setter方法上:
1 | public class SimpleMovieLister { |
You can also apply the annotation to methods with arbitrary names and/or multiple arguments:
你也可以应用注解到具有任何名字和/或多个参数的方法上:
1 | public class MovieRecommender { |
You can apply @Autowired
to fields as well and even mix it with constructors:
你也可以应用@Autowired
到字段上,甚至可以与构造函数混合用:
1 | public class MovieRecommender { |
It is also possible to provide all beans of a particular type from the ApplicationContext
by adding the annotation to a field or method that expects an array of that type:
通过给带有数组的字段或方法添加@Autowired
注解,也可以从ApplicationContext
中提供一组特定类型的bean:
1 | public class MovieRecommender { |
The same applies for typed collections:
同样也可以应用到具有同一类型的集合上:
1 | public class MovieRecommender { |
Your beans can implement the
org.springframework.core.Ordered
interface or either use the@Order
or standard@Priority
annotation if you want items in the array or list to be sorted into a specific order.
如果你希望数组或列表中的项按指定顺序排序,你的bean可以实现
org.springframework.core.Ordered
接口,或使用@Order
或标准@Priority
注解。
Even typed Maps can be autowired as long as the expected key type is String
. The Map values will contain all beans of the expected type, and the keys will contain the corresponding bean names:
只要期望的key是String
,那么类型化的Maps就可以自动组装。Map的值将包含所有期望类型的beans,key将包含对应的bean名字:
1 | public class MovieRecommender { |
By default, the autowiring fails whenever zero candidate beans are available; the default behavior is to treat annotated methods, constructors, and fields as indicating required dependencies. This behavior can be changed as demonstrated below.
默认情况下,当没有候选beans可获得时,自动组装会失败;默认的行为是将注解的方法,构造函数和字段看作指明了需要的依赖。这个行为也可以通过下面的方式去改变。
1 | public class SimpleMovieLister { |
Only one annotated constructor per-class can be marked as required, but multiple non-required constructors can be annotated. In that case, each is considered among the candidates and Spring uses the greediest constructor whose dependencies can be satisfied, that is the constructor that has the largest number of arguments.
@Autowired
’srequired
attribute is recommended over the@Required
annotation. The required attribute indicates that the property is not required for autowiring purposes, the property is ignored if it cannot be autowired.@Required
, on the other hand, is stronger in that it enforces the property that was set by any means supported by the container. If no value is injected, a corresponding exception is raised.
每个类只有一个构造函数可以标记为必需的,但可以注解多个非必需的构造函数。在这种情况下,会考虑这些候选者中的每一个,Spring使用最贪婪的构造函数,即依赖最满足的构造函数,具有最大数目的参数。
建议在
@Required
注解之上使用@Autowired
的required
特性。required
特性表明这个属性自动装配是不需要的,如果这个属性不能被自动装配,它会被忽略。另一方面@Required
是更强大的,在它强制这个属性被任何容器支持的bean设置。如果没有值注入,会抛出对应的异常。
You can also use @Autowired
for interfaces that are well-known resolvable dependencies: BeanFactory
, ApplicationContext
, Environment
, ResourceLoader
, ApplicationEventPublisher
, and MessageSource
. These interfaces and their extended interfaces, such as ConfigurableApplicationContext
or ResourcePatternResolver
, are automatically resolved, with no special setup necessary.
你也可以对那些已知的具有可解析依赖的接口使用@Autowired
:BeanFactory
,ApplicationContext
,Environment
, ResourceLoader
,ApplicationEventPublisher
和MessageSource
。这些接口和它们的扩展接口,例如ConfigurableApplicationContext
或ResourcePatternResolver
,可以自动解析,不需要特别的设置。
1 | public class MovieRecommender { |
@Autowired
,@Inject
,@Resource
, and@Value
annotations are handled by SpringBeanPostProcessor
implementations which in turn means that you cannot apply these annotations within your ownBeanPostProcessor
orBeanFactoryPostProcessor
types (if any). These types must be ‘wired up’ explicitly via XML or using a Spring@Bean
method.
@Autowired
,@Inject
,@Resource
和@Value
注解是通过SpringBeanPostProcessor
实现处理,这反过来意味着你不能在你自己的BeanPostProcessor
或BeanFactoryPostProcessor
中应用这些注解(如果有的话)。这些类型必须显式的通过XML或使用Spring的@Bean
方法来’wired up’。
3.9.3 Fine-tuning annotation-based autowiring with @Primary
Because autowiring by type may lead to multiple candidates, it is often necessary to have more control over the selection process. One way to accomplish this is with Spring’s @Primary
annotation. @Primary
indicates that a particular bean should be given preference when multiple beans are candidates to be autowired to a single-valued dependency. If exactly one ‘primary’ bean exists among the candidates, it will be the autowired value.
因为根据类型的自动装配可能会导致多个候选目标,所以在选择过程中进行更多的控制经常是有必要的。一种方式通过Spring的@Primary
注解来完成。当有个多个候选bean要组装到一个单值的依赖时,@Primary
表明指定的bean应该具有更高的优先级。如果确定一个’primary’ bean位于候选目标中间,它将是那个自动装配的值。
Let’s assume we have the following configuration that defines firstMovieCatalog
as the primary MovieCatalog
.
假设我们具有如下配置,将firstMovieCatalog
定义为主要的MovieCatalog
。
1 |
|
With such configuration, the following MovieRecommender
will be autowired with the firstMovieCatalog
.
根据这样的配置,下面的MovieRecommender
将用firstMovieCatalog
进行自动装配。
1 | public class MovieRecommender { |
The corresponding bean definitions appear as follows.
对应的bean定义如下:
1 | <?xml version="1.0" encoding="UTF-8"?> |
3.9.4 Fine-tuning annotation-based autowiring with qualifiers
@Primary
is an effective way to use autowiring by type with several instances when one primary candidate can be determined. When more control over the selection process is required, Spring’s @Qualifier
annotation can be used. You can associate qualifier values with specific arguments, narrowing the set of type matches so that a specific bean is chosen for each argument. In the simplest case, this can be a plain descriptive value:
当有多个实例需要确定一个主要的候选对象时,@Primary
是一种按类型自动装配的有效方式。当需要在选择过程中进行更多的控制时,可以使用Spring的@Qualifier
注解。为了给每个选择一个特定的bean,你可以将限定符的值与特定的参数联系在一起,减少类型匹配集合。在最简单的情况下,这是一个纯描述性值:
1 | public class MovieRecommender { |
The @Qualifier
annotation can also be specified on individual constructor arguments or method parameters:
@Qualifier
注解也可以指定单个构造函数参数或方法参数:
1 | public class MovieRecommender { |
The corresponding bean definitions appear as follows. The bean with qualifier value “main” is wired with the constructor argument that is qualified with the same value.
对应的bean定义如下。限定符值为”main”的bean被组装到有相同值的构造函数参数中。
1 | <?xml version="1.0" encoding="UTF-8"?> |
For a fallback match, the bean name is considered a default qualifier value. Thus you can define the bean with an id “main” instead of the nested qualifier element, leading to the same matching result. However, although you can use this convention to refer to specific beans by name, @Autowired
is fundamentally about type-driven injection with optional semantic qualifiers. This means that qualifier values, even with the bean name fallback, always have narrowing semantics within the set of type matches; they do not semantically express a reference to a unique bean id. Good qualifier values are “main” or “EMEA” or “persistent”, expressing characteristics of a specific component that are independent from the bean id
, which may be auto-generated in case of an anonymous bean definition like the one in the preceding example.
对于回退匹配,bean名字被认为是默认的限定符值。因此你可以定义一个id为main
的bean来代替内嵌的限定符元素,会有同样的匹配结果。然而,尽管你可以使用这个约定根据名字引用特定的beans,但是@Autowired
从根本上来讲是使用可选的语义限定符来进行类型驱动注入的。这意味着限定符的值,即使回退到bean名称,总是缩小语义类型匹配的集合;它们没有从语义上将一个引用表达为一个唯一的bean id。好的限定符值是”main”或”EMEA”或”persistent”,表达一个特定组件的性质,这个组件是独立于bean id
的,即使前面例子中像这个bean一样的匿名bean会自动生成id。
Qualifiers also apply to typed collections, as discussed above, for example, to Set<MovieCatalog>
. In this case, all matching beans according to the declared qualifiers are injected as a collection. This implies that qualifiers do not have to be unique; they rather simply constitute filtering criteria. For example, you can define multiple MovieCatalog
beans with the same qualifier value “action”, all of which would be injected into a Set<MovieCatalog>
annotated with @Qualifier("action")
.
正如前面讨论的那样,限定符也可以应用到类型结合上,例如,Set<MovieCatalog>
。在这个例子中,根据声明的限定符匹配的所有beans作为一个集合进行注入。这意味着限定符不必是唯一的;它们只是构成过滤标准。例如,你可以定义多个具有同样限定符值”action”的MovieCatalog
,所有的这些都将注入到带有注解@Qualifier("action")
的Set<MovieCatalog>
中。
If you intend to express annotation-driven injection by name, do not primarily use
@Autowired
, even if is technically capable of referring to a bean name through@Qualifier
values. Instead, use the JSR-250@Resource
annotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process.@Autowired
has rather different semantics: After selecting candidate beans by type, the specifiedString
qualifier value will be considered within those type-selected candidates only, e.g. matching an “account” qualifier against beans marked with the same qualifier label.For beans that are themselves defined as a collection/map or array type,
@Resource
is a fine solution, referring to the specific collection or array bean by unique name. That said, as of 4.3, collection/map and array types can be matched through Spring’s@Autowired
type matching algorithm as well, as long as the element type information is preserved in@Bean
return type signatures or collection inheritance hierarchies. In this case, qualifier values can be used to select among same-typed collections, as outlined in the previous paragraph.As of 4.3,
@Autowired
also considers self references for injection, i.e. references back to the bean that is currently injected. Note that self injection is a fallback; regular dependencies on other components always have precedence. In that sense, self references do not participate in regular candidate selection and are therefore in particular never primary; on the contrary, they always end up as lowest precedence. In practice, use self references as a last resort only, e.g. for calling other methods on the same instance through the bean’s transactional proxy: Consider factoring out the affected methods to a separate delegate bean in such a scenario. Alternatively, use@Resource
which may obtain a proxy back to the current bean by its unique name.
@Autowired
applies to fields, constructors, and multi-argument methods, allowing for narrowing through qualifier annotations at the parameter level. By contrast,@Resource
is supported only for fields and bean property setter methods with a single argument. As a consequence, stick with qualifiers if your injection target is a constructor or a multi-argument method.
@Autowired
可以应用到字段,构造函数和多参数方法上,允许通过限定符注解在参数层面上缩减候选目标。相比之下,@Resource
仅支持字段和bean属性的带有单个参数的setter方法。因此,如果你的注入目标是一个构造函数或一个多参数的方法,坚持使用限定符。
如果你想通过名字表达注解驱动的注入,不要主要使用
@Autowired
,虽然在技术上能通过@Qualifier
值引用一个bean名字。作为可替代产品,可以使用JSR-250@Resource
注解,它在语义上被定义为通过组件唯一的名字来识别特定的目标组件,声明的类型与匹配过程无关。@Autowired
有不同的语义:通过类型选择候选beans,特定的String
限定符值被认为只在类型选择的候选目标中,例如,在那些标记为具有相同限定符标签的beans中匹配一个”account”限定符。对于那些本身定义在集合/映射或数组类型中的beans来说,
@Resource
是一个很好的解决方案,适用于特定的集合或通过唯一名字区分的数组bean。也就是说,自Spring 4.3起,集合/映射和数组类型中也可以通过Spring的@Autowired
类型匹配算法进行匹配,只要元素类型信息在@Bean
中保留,返回类型签名或集合继承体系。在这种情况下,限定符值可以用来在相同类型的集合中选择,正如在前一段中概括的那样。自Spring 4.3起,
@Autowired
也考虑自引用注入,例如,引用返回当前注入的bean。注意自注入是备用;普通对其它组件的依赖关系总是优先的。在这个意义上,自引用不参与普通的候选目标选择,因此尤其是从不是主要的;恰恰相反,它们最终总是最低的优先级。在实践中,自引用只是作为最后的手段,例如,通过bean的事务代理调用同一实例的其它方法:在考虑抽出受影响的方法来分隔代理bean的场景中。或者,使用@Resource
通过它的唯一名字可能得到一个返回当前bean的代理。
@Autowired
可以应用到字段,构造函数和多参数方法上,允许通过限定符注解在参数层面上缩减候选目标。相比之下,@Resource
仅支持字段和bean属性的带有单个参数的setter方法。因此,如果你的注入目标是一个构造函数或一个多参数的方法,坚持使用限定符。
You can create your own custom qualifier annotations. Simply define an annotation and provide the @Qualifier
annotation within your definition:
你可以创建自己的定制限定符注解。简单定义一个注解,在你自己的定义中提供@Qualifier
注解:
1 | ({ElementType.FIELD, ElementType.PARAMETER}) |
Then you can provide the custom qualifier on autowired fields and parameters:
然后你可以在自动装配的字段和参数上提供定制的限定符:
1 | public class MovieRecommender { |
Next, provide the information for the candidate bean definitions. You can add <qualifier/>
tags as sub-elements of the <bean/>
tag and then specify the type and value to match your custom qualifier annotations. The type is matched against the fully-qualified class name of the annotation. Or, as a convenience if no risk of conflicting names exists, you can use the short class name. Both approaches are demonstrated in the following example.
接下来,提供候选bean定义的信息。你可以添加<qualifier/>
标记作为<bean/>
标记的子元素,然后指定匹配你的定制限定符注解的类型和值。类型用来匹配注解的全限定类名称。或者,如果没有名称冲突的风险,为了方便,你可以使用简写的类名称。下面的例子证实了这些方法。
1 | <?xml version="1.0" encoding="UTF-8"?> |
In Section 3.10, “Classpath scanning and managed components”, you will see an annotation-based alternative to providing the qualifier metadata in XML. Specifically, see Section 3.10.8, “Providing qualifier metadata with annotations”.
在3.10小节,“类路径扫描和管理组件”中,你将看到一个基于注解的替代方法,在XML中提供限定符元数据。特别地,看3.10.8小节,“用注解提供限定符元数据”。
In some cases, it may be sufficient to use an annotation without a value. This may be useful when the annotation serves a more generic purpose and can be applied across several different types of dependencies. For example, you may provide an offline catalog that would be searched when no Internet connection is available. First define the simple annotation:
在某些情况下,使用没有值的注解就是足够的。当注解为了通用的目的时,这是非常有用的,可以应用到跨几个不同类型的依赖上。例如,当网络不可用时,你可以提供一个要搜索的离线目录。首先定义一个简单的注解:
1 | ({ElementType.FIELD, ElementType.PARAMETER}) |
Then add the annotation to the field or property to be autowired:
然后将注解添加到要自动装配的字段或属性上:
1 | public class MovieRecommender { |
Now the bean definition only needs a qualifier type:
现在bean定义只需要一个限定符类型:
1 | <bean class="example.SimpleMovieCatalog"> |
You can also define custom qualifier annotations that accept named attributes in addition to or instead of the simple value attribute. If multiple attribute values are then specified on a field or parameter to be autowired, a bean definition must match all such attribute values to be considered an autowire candidate. As an example, consider the following annotation definition:
你也可以定义接收命名属性之外的定制限定符注解或代替简单的值属性。如果要注入的字段或参数指定了多个属性值,bean定义必须匹配所有的属性值才会被认为是一个可自动装配的候选目标。作为一个例子,考虑下面的注解定义:
1 | @Target({ElementType.FIELD, ElementType.PARAMETER}) |
In this case Format
is an enum:
这种情况下Format
是枚举类型:
1 | public enum Format { |
The fields to be autowired are annotated with the custom qualifier and include values for both attributes: genre
and format
.
要自动装配的字段使用定制限定符进行注解,并且包含了两个属性值:genre
和format
。
1 | public class MovieRecommender { |
Finally, the bean definitions should contain matching qualifier values. This example also demonstrates that bean meta attributes may be used instead of the <qualifier/>
sub-elements. If available, the <qualifier/>
and its attributes take precedence, but the autowiring mechanism falls back on the values provided within the <meta/>
tags if no such qualifier is present, as in the last two bean definitions in the following example.
最后,bean定义应该包含匹配的限定符值。这个例子也证实了bean元属性可以用来代替<qualifier/>
子元素。如果可获得<qualifier/>
,它和它的属性优先级更高,如果当前没有限定符,自动装配机制会将<meta/>
内的值作为备用,正如下面的例子中的最后两个bean定义。
1 | <?xml version="1.0" encoding="UTF-8"?> |
3.9.5 Using generics as autowiring qualifiers
In addition to the @Qualifier
annotation, it is also possible to use Java generic types as an implicit form of qualification. For example, suppose you have the following configuration:
除了@Qualifier
注解外,也可以使用Java的泛型类型作为限定符的一种隐式方式。例如,假设你有如下配置:
1 | @Configuration |
Assuming that beans above implement a generic interface, i.e. Store<String>
and Store<Integer>
, you can @Autowire
the Store
interface and the generic will be used as a qualifier:
假设上面的beans实现了一个泛型接口,例如,Store<String>
和Store<Integer>
,你可以@Autowire
Store
接口,泛型将作为限定符使用:
1 |
|
Generic qualifiers also apply when autowiring Lists
, Maps
and Arrays
:
当自动装配Lists
,Maps
和Arrays
时,也会应用泛型限定符:
1 | // Inject all Store beans as long as they have an <Integer> generic |
3.9.6 CustomAutowireConfigurer
The CustomAutowireConfigurer
is a BeanFactoryPostProcessor
that enables you to register your own custom qualifier annotation types even if they are not annotated with Spring’s @Qualifier
annotation.
CustomAutowireConfigurer
是一个能使你注册自己的定制限定符注解类型的BeanFactoryPostProcessor
,即使它们不能使用Spring的@Qualifier
注解进行注解。
1 | <bean id="customAutowireConfigurer" |
The AutowireCandidateResolver
determines autowire candidates by:
the
autowire-candidate
value of each bean definitionany
default-autowire-candidates
pattern(s) available on the<beans/>
elementthe presence of
@Qualifier
annotations and any custom annotations registered with theCustomAutowireConfigurer
AutowireCandidateResolver
通过下面的方式决定自动装配的候选目标:
每个bean定义的
autowire-candidate
在
<beans/>
元素可获得的任何default-autowire-candidates
模式存在
@Qualifier
注解和任何在CustomAutowireConfigurer
中注册的定制注解
When multiple beans qualify as autowire candidates, the determination of a “primary” is the following: if exactly one bean definition among the candidates has a primary
attribute set to true
, it will be selected.
当多个beans符合条件成为自动装配的候选目标时,”primary” bean的决定如下:如果在候选目标中某个确定的bean中的primary
特性被设为true
,它将被选为目标bean。
3.9.7 @Resource
Spring also supports injection using the JSR-250 @Resource
annotation on fields or bean property setter methods. This is a common pattern in Java EE 5 and 6, for example in JSF 1.2 managed beans or JAX-WS 2.0 endpoints. Spring supports this pattern for Spring-managed objects as well.
Spring也支持使用JSR-250 @Resource
对字段或bean属性setter方法进行注入。这是在Java EE 5和6中的一种通用模式,例如在JSF 1.2管理的beans或JAX-WS 2.0的端点。Spring对它管理的对象也支持这种模式。
@Resource
takes a name attribute, and by default Spring interprets that value as the bean name to be injected. In other words, it follows by-name semantics, as demonstrated in this example:
@Resource
采用名字属性,默认情况下Spring将名字值作为要注入的bean的名字。换句话说,它遵循by-name语义,下面的例子证实了这一点:
1 | public class SimpleMovieLister { |
If no name is specified explicitly, the default name is derived from the field name or setter method. In case of a field, it takes the field name; in case of a setter method, it takes the bean property name. So the following example is going to have the bean with name “movieFinder” injected into its setter method:
如果没有显式的指定名字,默认名字从字段名或setter方法中取得。在字段情况下,它采用字段名称;在setter方法情况下,它采用bean的属性名。因此下面的例子将名字为movieFinder
的bean注入到它的setter方法中:
1 | public class SimpleMovieLister { |
The name provided with the annotation is resolved as a bean name by the
ApplicationContext
of which theCommonAnnotationBeanPostProcessor
is aware. The names can be resolved through JNDI if you configure Spring’sSimpleJndiBeanFactory
explicitly. However, it is recommended that you rely on the default behavior and simply use Spring’s JNDI lookup capabilities to preserve the level of indirection.
注解提供的名字被
CommonAnnotationBeanPostProcessor
感知的ApplicationContext
解析为bean名字。如果你显式地配置了Spring的SimpleJndiBeanFactory
,名字会通过JNDI解析。但是建议你依赖默认行为,简单使用Spring的JNDI查找功能保护间接查找级别。
In the exclusive case of @Resource
usage with no explicit name specified, and similar to @Autowired
, @Resource
finds a primary type match instead of a specific named bean and resolves well-known resolvable dependencies: the BeanFactory
, ApplicationContext
, ResourceLoader
, ApplicationEventPublisher
, and MessageSource
interfaces.
在@Resource
特有的没有显式名字指定的情况下,类似于@Autowired
,@Resource
会进行主要的匹配类型来代替指定名字的bean并解析已知的可解析依赖:BeanFactory
,ApplicationContext
,ResourceLoader
,ApplicationEventPublisher
和MessageSource
接口。
Thus in the following example, the customerPreferenceDao
field first looks for a bean named customerPreferenceDao
, then falls back to a primary type match for the type CustomerPreferenceDao
. The “context” field is injected based on the known resolvable dependency type ApplicationContext
.
因此在下面的例子中,customerPreferenceDao
字段首先查找名字为customerPreferenceDao
的bean,然后回退到主要的类型为CustomerPreferenceDao
的类型匹配。”context”字段会注入基于已知的可解析依赖类型ApplicationContext
。
1 | public class MovieRecommender { |
3.9.8 @PostConstruct and @PreDestroy
The CommonAnnotationBeanPostProcessor
not only recognizes the @Resource
annotation but also the JSR-250 lifecycle annotations. Introduced in Spring 2.5, the support for these annotations offers yet another alternative to those described in initialization callbacks and destruction callbacks. Provided that the CommonAnnotationBeanPostProcessor
is registered within the Spring ApplicationContext
, a method carrying one of these annotations is invoked at the same point in the lifecycle as the corresponding Spring lifecycle interface method or explicitly declared callback method. In the example below, the cache will be pre-populated upon initialization and cleared upon destruction.
CommonAnnotationBeanPostProcessor
不仅识别@Resource
注解,而且识别JSR-250生命周期注解。在Spring 2.5引入了对这些注解的支持,也提供了在初始化回调函数和销毁回调函数中描述的那些注解的一种可替代方式。假设CommonAnnotationBeanPostProcessor
在Spring的ApplicationContext
中注册,执行这些注解的方法在生命周期的同一点被调用,作为对应的Spring生命周期接口方法或显式声明的回调方法。在下面的例子中,缓存会预先放置接近初始化之前,并在销毁之前清除。
1 | public class CachingMovieLister { |
For details about the effects of combining various lifecycle mechanisms, see the section called “Combining lifecycle mechanisms”.
关于组合各种生命周期机制的影响的更多细节,请看“组合生命周期机制”小节。