系统化学习 Spring,逐字逐句为你解读 Spring Framework Documentation,持续更新中

想要更好的理解 Spring 吗?

  • 推荐阅读 Spring 官方文档

为什么?

  • 因为整体看下来,真的狠系统,体系化。而且,有一点,狠重要,就是权威

那么阅读官方文档,有语言障碍?读不下去怎么办?

  • 那就手把手带你逐字解读
  • 关注自己感兴趣或需要了解的章节

内容翻译中,持续更新。


更新日志:
重要通知:因为文章单篇文章有字数限制,更新到1.9章节之后,更多的内容就显示不出来了,后面会考虑将整个文档分解为多篇文章进行更新,内容持续更新中。

更新时间说明
2021-08-10更新至1.4.1 章节依赖注入
2021-08-12更新至1.4.2 章节详解依赖项和配置
2021-08-15更新至1.5 章节 bean 的作用域
2021-08-20更新至1.8 章节容器的扩展点
2021-08-22更新至1.9 章节基于注解的容器配置
2021-08-25更新至1.12.2 章节基于Java的容器配置

希望你能对 Spring 有新的认识!😄

文章目录

Spring Framework Documentation

译自:https://docs.spring.io/spring-framework/docs/5.3.9/reference/html/core.html#spring-core

5.3.9 版本 Spring 框架核心文档

阅读建议:

  • 解读方式:一段原文,配一段翻译。
  • 提示:有些自己想表达,或者提示的内容时,会使用“译者注”这种字样标识

正文开始!

5.3.9

This part of the reference documentation covers all the 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 企业编程中80% 的AOP 需求。

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 实现)。

1. IoC 容器

  1. The IoC Container

This chapter covers Spring’s Inversion of Control (IoC) container.

本章介绍了 Spring 的控制反转 (IoC) 容器。

1.1. Spring IoC 容器和 bean 介绍

1.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) 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.

本章介绍了控制反转(IoC)原则的 Spring 框架实现。IoC 也被称为依赖注入。这是一个过程,对象仅通过构造函数参数、工厂方法的参数或者在对象实例被构造或者从工厂方法返回后在其上设置的属性来定义它们的依赖关系(换句话说,它们使用的其他对象)。然后容器在创建 bean 时注入这些依赖项。这个过程从根本上来说是 bean 通过使用类的直接构造或服务定位器模式之类的机制来控制其依赖项的实例化或位置的逆过程(因此被称为控制反转)。

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:

org.springframework.beansorg.springframework.context 包是 Spring Framework 的 IoC 容器的基础。BeanFactory接口提供了能够管理任何类型对象的高级配置机制。ApplicationContext是 BeanFactory的子接口。它额外提供:

  • Easier integration with Spring’s AOP features

    更容易与 Spring 的 AOP 特性集成

  • Message resource handling (for use in internationalization)

    消息资源处理(用于国际化)

  • Event publication

    事件发布

  • Application-layer specific contexts such as the WebApplicationContext for use in web applications.

    应用层特定的上下文,例如 web 应用程序中使用的 WebApplicationContext。

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, see The BeanFactory.

简而言之,BeanFactory 提供了配置框架和基本功能,ApplicationContext 添加了更多特定于企业的功能。ApplicationContext 是 BeanFactory 的一个完整超集,在本章对 Spring 的 IoC 容器的描述中专门使用。有关使用 BeanFactory 而不是 ApplicationContext 的详细信息,请参阅 thebeanfactory。

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 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 容器管理的对象称为 bean。Bean 是由 Spring IoC 容器实例化、组装和管理的对象。否则,bean 只是应用程序中的许多对象之一。Bean 以及它们之间的依赖关系反映在容器使用的配置元数据中。

1.2. Container Overview

1.2. 容器概览

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the 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 lets you express the objects that compose your application and the rich interdependencies between those objects.

org.springframework.context.ApplicationContext接口表示 Spring IoC 容器,负责实例化、配置和装配 bean。容器通过读取配置元数据获取关于实例化、配置和组装什么对象的指令。配置元数据用 XML、 Java 注释或 Java 代码表示。它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。

Several implementations of the ApplicationContext interface are supplied with Spring. In stand-alone 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 接口的几个实现。在独立应用程序中,通常创建 ClassPathXmlApplicationContextFileSystemXmlApplicationContext 的实例。虽然 XML 一直是定义配置元数据的传统格式,但是您可以通过提供少量 XML 配置来声明性地支持这些额外的元数据格式,从而指示容器使用 Java 注释或代码作为元数据格式。

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 typically suffices (see Convenient ApplicationContext Instantiation for Web Applications). If you use the Spring Tools for Eclipse (an Eclipse-powered development environment), you can easily create this boilerplate configuration with a few mouse clicks or keystrokes.

在大多数应用程序场景中,不需要显式用户代码来实例化 Spring IoC 容器的一个或多个实例。例如,在 Web 应用程序场景中,应用程序的 web.xml 文件中一个简单的八行(或者更多)样板 Web 描述符 XML 通常就足够了(请参阅Convenient ApplicationContext Instantiation for Web Applications)。如果使用 Spring Tools for Eclipse (一个基于 Eclipse 的开发环境) ,只需点击几下鼠标或敲击几下键盘,就可以轻松创建此样板配置。

The following diagram shows 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 之后,您就拥有了一个完全配置和可执行的系统或应用程序。

container-magic

Figure 1. The Spring IoC container

1.2.1. 准备配置元数据

1.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 格式提供的,本章的大部分内容都使用这种格式来传达 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 的元数据并不是唯一允许的配置元数据形式。Springioc 容器本身与实际编写此配置元数据的格式完全分离。现在,很多开发者选择基于 java 的配置用于 Spring 应用程序

For information about using other forms of metadata with the Spring container, see:

有关在 Spring 容器中使用其他形式的元数据的信息,请参见:

  • Annotation-based configuration: Spring 2.5 introduced support for annotation-based configuration metadata.

    基于注解的配置: Spring 2.5 引入了对基于注解的配置元数据的支持。

  • 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.

    基于 java 的配置: 从 Spring 3.0开始,Spring JavaConfig 项目提供的许多特性成为核心 Spring 框架的一部分。因此,可以使用 Java 而不是 XML 文件在应用程序类之外定义 bean。要使用这些新特性,请参见@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 configures these beans as <bean/> elements inside a top-level <beans/> element. Java configuration typically uses @Bean-annotated methods within a @Configuration class.

Spring 配置由容器必须管理的至少一个且通常不止一个 bean 定义组成。基于 xml 的配置元数据将这些 bean 配置为顶级 < beans/> 元素中的 < bean/> 元素。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 定义对应于组成应用程序的实际对象。通常,定义服务层对象、数据访问对象(dao)、表示对象(如 Struts Action 实例)、基础层对象(如 Hibernate SessionFactories、 JMS Queues)等等。通常,不在容器中配置细粒度的领域对象,因为通常是 dao 和业务逻辑负责创建和加载领域对象。但是,您可以使用 Spring 与 AspectJ 的集成来配置在 IoC 容器控件之外创建的对象。请参见使用 AspectJ 对 Spring 进行依赖注入领域对象。

The following example shows the basic structure of XML-based configuration metadata:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>
1The id attribute is a string that identifies the individual bean definition.
这个id属性是标识单个 bean 定义的字符串
2The class attribute defines the type of the bean and uses the fully qualified classname.
这个class属性定义 bean 的类型,并使用完全限定的类名

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 属性的值是指合作对象。此示例中没有显示用于引用合作对象的 XML。有关更多信息,请参见依赖关系。

1.2.2. 实例化一个容器

1.2.2. Instantiating a Container

The location path or paths supplied to an ApplicationContext constructor are resource strings that let the container load configuration metadata from a variety of external resources, such as the local file system, the Java CLASSPATH, and so on.

提供给 ApplicationContext 构造函数的位置路径是资源字符串,它允许容器从各种外部资源(如本地文件系统、 Java CLASSPATH 等)加载配置元数据。

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

译者注:这样就初始化了一个容器。

After you learn about Spring’s IoC container, you may want to know more about Spring’s Resource abstraction (as described in 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 Application Contexts and Resource Paths.

了解了 Spring 的 IoC 容器之后,您可能希望更多地了解 Spring 的 Resource 抽象(如 Resources中所述) ,它提供了一种方便的机制,可以从 URI 语法中定义的位置读取 InputStream。特别是,资源路径用于构造应用程序上下文,如 Application Contexts and Resource Paths所述。

The following example shows the service layer objects (services.xml) configuration file:

下面的示例显示服务层对象(services.xml)配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>

The following example shows the data access objects daos.xml file:

下面的示例显示了数据访问对象 daos.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

In the preceding example, the service layer consists of the PetStoreServiceImpl class and two data access objects of the types 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 类和两个 JpaAccountDaoJpaItemDao 类型的数据访问对象组成(基于 JPA 对象关系映射标准)。property name 元素引用 JavaBean 属性的名称,ref 元素引用另一个 bean 定义的名称。Idref 元素之间的这种链接表达了合作对象之间的依赖关系。有关配置对象依赖项的详细信息,请参阅依赖项。

构建基于 xml 的配置元数据

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. The following example shows how to do so:

你可以使用应用程序上下文构造函数从所有这些 XML 片段中加载 bean 定义。此构造函数接受多个Resource 路径,如上一节所示。或者,使用一个或多个 < import/> 元素从另一个或多个文件中加载 bean 定义。下面的例子说明如何这样做:

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

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. However, 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.xmlmessageSource.xmlthemeSource.xml。所有位置路径都相对于执行导入的定义文件,因此 services.xml 必须与执行导入的文件位于同一目录或类路径位置,而 messageSource.xmlthemeSource.xml 必须位于导入文件位置之下的资源位置。如您所见,前导斜杠被忽略。但是,考虑到这些路径是相对的,最好不要使用斜杠。根据 Spring Schema,导入的文件的内容(包括顶层 < beans/> 元素)必须是有效的 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.

使用相对引用父目录中的文件是可能的,但不建议这样做。"…/" 路径。这样做会在当前应用程序之外的文件上创建一个依赖项。特别是,不建议对classpath: URLs进行此引用(例如,classpath:../services.xml) ,其中运行时解析进程选择“最近的”类路径根,然后查看其父目录。类路径配置的更改可能导致选择不同的、不正确的目录。

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.

您总是可以使用完全限定的资源位置而不是相对路径: 例如,file:C:/config/services.xmlclasspath:/config/services.xml。但是,请注意,您正在将应用程序的配置耦合到特定的绝对位置。对于这种绝对位置,通常更可取的做法是保持间接性ーー例如,通过在运行时根据 JVM 系统属性解析的“ ${ … }”占位符。

The namespace itself provides the import directive feature. Further configuration features beyond plain bean definitions are available in a selection of XML namespaces provided by Spring — for example, the context and util namespaces.

名称空间本身提供了导入指令特性。在 Spring 提供的一系列 XML 名称空间中,提供了普通 bean 定义之外的更多配置特性ーー例如,contextutil 名称空间。

Groovy Bean 定义 DSL

The Groovy Bean Definition DSL

As a further example for externalized configuration metadata, bean definitions can also be expressed in Spring’s Groovy Bean Definition DSL, as known from the Grails framework. Typically, such configuration live in a “.groovy” file with the structure shown in the following example:

作为外部化配置元数据的进一步示例,Bean 定义也可以在 Spring 的 Groovy Bean 定义 DSL 中表示,就像是 Grails 框架中一样。通常,这样的配置存在于 “.groovy” 文件,其结构如下面的示例所示:

beans {
    dataSource(BasicDataSource) {
        driverClassName = "org.hsqldb.jdbcDriver"
        url = "jdbc:hsqldb:mem:grailsDB"
        username = "sa"
        password = ""
        settings = [mynew:"setting"]
    }
    sessionFactory(SessionFactory) {
        dataSource = dataSource
    }
    myService(MyService) {
        nestedBean = { AnotherBean bean ->
            dataSource = dataSource
        }
    }
}

This configuration style is largely equivalent to XML bean definitions and even supports Spring’s XML configuration namespaces. It also allows for importing XML bean definition files through an importBeans directive.

这种配置风格在很大程度上等同于 xml bean 定义,甚至支持 Spring 的 XML 配置名称空间。它还允许通过 importBeans 指令导入 XML bean 定义文件。

译者注:Groovy 没使用过

1.2.3. 使用容器

1.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. By using the method T getBean(String name, Class<T> requiredType), you can retrieve instances of your beans.

ApplicationContext 是一个高级的工厂接口,它能够维护不同 bean 及其依赖项的注册。通过使用方法 T getBean(String name, Class<T> requiredType) ,您可以检索 bean 的实例。

The ApplicationContext lets you read bean definitions and access them, as the following example shows:

ApplicationContext 允许你读取 bean 定义并访问它们,如下面的例子所示:

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

With Groovy configuration, bootstrapping looks very similar. It has a different context implementation class which is Groovy-aware (but also understands XML bean definitions). The following example shows Groovy configuration:

使用 Groovy 配置,引导看起来非常相似。它有一个不同的上下文实现类,它是 groovy 感知的(但也理解 XML bean 定义)。下面的例子展示了 Groovy 的配置:

ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");

The most flexible variant is GenericApplicationContext in combination with reader delegates — for example, with XmlBeanDefinitionReader for XML files, as the following example shows:

最灵活的变体是==GenericApplicationContext==与读取策略相结合ー例如,使用 XmlBeanDefinitionReader 来处理 XML 文件,如下面的示例所示:

GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();

You can also use the GroovyBeanDefinitionReader for Groovy files, as the following example shows:

您还可以使用 GroovyBeanDefinitionReader 读取Groovy 文件,如下面的示例所示:

GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();

You can mix and match such reader delegates on the same ApplicationContext, reading bean definitions from diverse configuration sources.

您可以在相同的 ApplicationContext上混合和匹配这些读取器委托,从不同的配置源中读取 bean 定义。

You can then 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 have no dependency on Spring APIs at all. For example, Spring’s integration with web frameworks provides dependency injection for various web framework components such as controllers and JSF-managed beans, letting you declare a dependency on a specific bean through metadata (such as an autowiring annotation).

你可以使用 getBean 检索 bean 的实例。ApplicationContext 接口还有其他一些检索 bean 的方法,但是理想情况下,您的应用程序代码不应该使用它们。实际上,您的应用程序代码根本不应该调用 getBean ()方法,从而根本不依赖于 Spring api。例如,Spring 与 web 框架的集成为各种 web 框架组件提供了依赖注入,比如控制器和 jsf 管理的 bean,让你通过元数据(比如自动注入注解)声明对特定 bean 的依赖。

1.3. Bean 概览

1.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 容器管理一个或多个 bean。这些 bean 是使用您提供给容器的配置元数据创建的(例如,以 XML < bean/> 定义的形式)。

Within the container itself, these bean definitions are represented as BeanDefinition objects, which contain (among other information) the following metadata:

在容器本身中,这些 bean 定义被表示为 BeanDefinition 对象,其中包含(除其他信息外)以下元数据:

  • A package-qualified class name: typically, the actual implementation class of the bean being defined.

    包限定类名: 通常是定义的 bean 的实际实现类。

  • Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).

    Bean 行为配置元素,它们陈述 Bean 在容器中的行为(范围、生命周期回调等)。

  • References to other beans that are needed for the bean to do its work. These references are also called collaborators or dependencies.

    对 bean 完成工作所需的其他 bean 的引用。这些引用也称为协作者或依赖关系。

  • Other configuration settings to set in the newly created object — for example, the size limit of the pool or the number of connections to use in a bean that manages a connection pool.

    在新创建的对象中设置的其他配置设置ーー例如,池的大小限制或管理连接池的 bean 中使用的连接数。

This metadata translates to a set of properties that make up each bean definition. The following table describes these properties:

这些元数据转换成一组属性,组成每个 bean 的定义,下表介绍了这些属性:

Property 属性Explained in… 解释于…
ClassInstantiating Beans 实例化 Bean
NameNaming Beans Bean 命名
ScopeBean Scopes Bean 的范围
Constructor argumentsDependency Injection 依赖注入
PropertiesDependency Injection 依赖注入
Autowiring modeAutowiring Collaborators 自动注入候选对象
Lazy initialization modeLazy-initialized Beans 懒加载 Bean
Initialization methodInitialization Callbacks 初始化回调
Destruction methodDestruction 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 through the getBeanFactory() method, which returns the BeanFactory DefaultListableBeanFactory implementation. DefaultListableBeanFactory supports this registration through the registerSingleton(..) and registerBeanDefinition(..) methods. However, typical applications work solely with beans defined through regular bean definition metadata.

除了包含有关如何创建特定 bean 的信息的 bean 定义之外,ApplicationContext 实现还允许注册在容器之外(由用户)创建的现有对象。这是通过 getBeanFactory ()方法访问 ApplicationContext 的 BeanFactory 来完成的,该方法返回 BeanFactory DefaultListableBeanFactory 实现。DefaultListableBeanFactory 通过 registerSingleton(..)registerBeanDefinition(..)方法完成 bean 的注册。但是,典型的应用程序只使用通过常规 bean 定义元数据定义的 bean(即通常由容器本身来完成)。

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 existing metadata and existing singleton instances is supported to some degree, the registration of new beans at runtime (concurrently with live access to the factory) is not officially supported and may lead to concurrent access exceptions, inconsistent state in the bean container, or both.

Bean 元数据和手动提供的单例实例需要尽早注册,以便容器在自动装配和其他自省步骤期间对它们进行适当的推理。虽然在某种程度上支持覆盖现有的元数据和现有的单例实例,但在运行时注册新 bean (与对工厂的实时访问同时进行)不受官方支持,并可能导致并发访问异常、 bean 容器中的不一致状态,或者两者兼而有之。

1.3.1. Bean 的命名

1.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. However, if it requires more than one, the extra ones can be considered aliases.

每个 bean 都有一个或多个标识符。这些标识符在承载 bean 的容器中必须是唯一的。一个 bean 通常只有一个标识符。但是,如果它需要多于一个,那么额外的那些可以被认为是别名

In XML-based configuration metadata, you use the id attribute, the name attribute, or both to specify the bean identifiers. The id attribute lets you specify exactly one id. Conventionally, these names are alphanumeric (‘myBean’, ‘someService’, etc.), but they can contain special characters as well. If you want to introduce other aliases for 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”、“ someService”等) ,但它们也可以包含特殊字符。如果要为 bean 引入其他别名,还可以在 name 属性中指定它们,中间用逗号 (,), 、分号(;)或空白分隔。作为一个历史记录,在 Spring 3.1之前的版本中,ID 属性被定义为 xsd:ID类型,它约束了可能的字符。从3.1开始,它被定义为xsd:string类型。注意,虽然 XML 解析器不再强制 bean id 唯一性,但是容器仍然强制 bean id 唯一性。

You are not required to supply a name or an id for a bean. If you do not supply a name or id 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 a 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 提供nameid。如果没有显式地提供nameid,则容器将为该 bean 生成唯一的名称。但是,如果要通过名称引用该 bean,则必须通过使用 ref 元素或 Service Locator 的风格查找来查找名称。不提供名称可以正常使用的原理与 inner beansautowiring collaborators 有关。

Bean Naming Conventions

Bean 命名约定

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 there. Examples of such names include accountManager, accountService, userDao, loginController, and so forth.

在命名 bean 时对实例字段名使用标准的 Java 约定。也就是说,bean 名称以小写字母开头,并以驼峰格式显示。这类名称的例子包括 accountManageraccountServiceuserDaologinController 等等。

Naming beans consistently makes your configuration easier to read and understand. Also, if you use Spring AOP, it helps a lot when applying advice to a set of beans related by name.

一致的 bean 命名可以使您的配置更容易阅读和理解。另外,如果您使用 Spring AOP,那么在将 advice 应用于按名称相关的一组 bean 时,它会有很大帮助。

With component scanning in the classpath, Spring generates bean names for unnamed components, following the rules described earlier: 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 uses here).

通过在类路径中进行组件扫描,Spring 为未命名的组件生成 bean 名称,遵循前面描述的规则(译者注:留意下这个规则哈): 基本上,使用简单的类名并将其初始字符转换为小写。但是,在(不寻常的)特殊情况下,当有多个字符且第一个和第二个字符都是大写字母时,原始大小写将得到保留。这些规则与下面的定义相同java.beans.Introspector.decapitalize (which Spring uses here). (Spring 在这里使用)

在Bean定义之外给Bean添加别名

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 letting each component in an application refer to a common dependency by using a bean name that is specific to that component itself.

在 bean 定义本身中,您可以为 bean 提供多个名称,方法是使用由 id 属性指定的最多一个名称和 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, with each subsystem having its own set of object definitions. In XML-based configuration metadata, you can use the <alias/> element to accomplish this. The following example shows how to do so:

但是,指定实际定义 bean 的所有别名并不总是适当的。有时候需要为在别处定义的 bean 引入别名。在大型系统中,配置通常在每个子系统之间进行分割,每个子系统都有自己的一组对象定义。在基于 xml 的配置元数据中,可以使用 < alias/> 元素来实现这一点。下面的例子说明如何这样做:

<alias name="fromName" alias="toName"/>

In this case, a bean (in the same container) named fromName may also, after the use of this alias definition, be referred to as toName.

在这个例子中,在使用这个别名定义之后,名为 fromName 的 bean (在同一容器中)也可以被称为 toName

For example, the configuration metadata for subsystem A may refer to a DataSource by the name of subsystemA-dataSource. The configuration metadata for subsystem B may refer to a DataSource by the name of subsystemB-dataSource. When composing the main application that uses both these subsystems, the main application refers to the DataSource by the name of myApp-dataSource. To have all three names refer to the same object, you can add the following alias definitions to the configuration metadata:

例如,子系统 a 的配置元数据可以通过 subsystemA-DataSource 的名称引用 DataSource。子系统 b 的配置元数据可以通过 subsystemB-DataSource 的名称来引用 DataSource。当组合使用这两个子系统的主应用程序时,主应用程序通过 myapp-DataSource 的名称引用 DataSource。要让所有三个名称都指向同一个对象,可以向配置元数据添加以下别名定义:

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-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.

现在,每个组件和主应用程序都可以通过一个惟一的名称引用 dataSource,并保证不会与任何其他定义发生冲突(有效地创建名称空间) ,但它们引用的是同一个 bean。

Java-configuration

Java 配置

If you use Javaconfiguration, the @Bean annotation can be used to provide aliases. See Using the @Bean Annotation for details.

如果使用 Javaconfiguration,可以使用@bean 注释提供别名。详情查看 Using the @Bean Annotation

1.3.2. 实例化 Bean 的方式

1.3.2. Instantiating Beans

A bean definition is essentially 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 Instantiation by Using an Instance Factory Method and Bean Definition Inheritance.) You can use the Class property in one of two ways:

如果使用基于 xml 的配置元数据,则要在 < bean/> 元素的 class 属性中指定要实例化的对象的类型(或类)。这个类属性(在内部是 BeanDefinition 实例上的 Class 属性)通常是强制的。(对于例外情况,请参阅 Instantiation by Using an Instance Factory MethodBean Definition Inheritance。)

您可以通过以下两种方式之一使用 Class 属性:

  • 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 with the new operator.

    通常,在容器本身通过反射调用其构造函数直接创建 bean 的情况下,指定要构造的 bean 类,这在一定程度上等同于使用 new 操作符的 Java 代码。

  • To specify the actual class containing the static factory method that is invoked to create the object, in the less common case where the container invokes a static factory method on a class to create the bean. The object type returned from the invocation of the static factory method may be the same class or another class entirely.

    指定包含用来创建对象的静态工厂方法的实际类,在不太常见的情况下,容器调用类上的静态工厂方法来创建 bean。从静态工厂方法调用返回的对象类型可能是同一个类,也可能完全是另一个类。

Nested class names

嵌套的类名

If you want to configure a bean definition for a nested class, you may use either the binary name or the source name of the nested class.

如果要为嵌套类配置 bean 定义,可以使用嵌套类的二进制名称或源名称。

For example, if you have a class called SomeThing in the com.example package, and this SomeThing class has a static nested class called OtherThing, they can be separated by a dollar sign ($) or a dot (.). So the value of the class attribute in a bean definition would be com.example.SomeThing$OtherThing or com.example.SomeThing.OtherThing.

例如,如果在 com.example 包中有一个名为 SomeThing 的类,而这个 SomeThing 类有一个名为 OtherThing 的静态嵌套类,它们可以用美元符号($) 或点 (.)分隔。所以 bean 定义中 class 属性的值应该是 com.example.SomeThing$OtherThing 或者 com.example.SomeThing.OtherThing

使用构造函数实例化

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使用并与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 容器实际上可以管理您希望它管理的任何类。它不仅限于管理真正的 javabean。大多数 Spring 用户更喜欢只有默认(无参数)构造函数和根据容器中的属性建模的 setters 和 getter 的实际 javabean。您的容器中还可以有更多奇特的非 bean 风格类。例如,如果您需要使用一个完全不遵循 JavaBean 规范的遗留连接池,那么 Spring 也可以管理它。

With XML-based configuration metadata you can specify your bean class as follows:

使用基于 xml 的配置元数据,您可以按照以下方式指定 bean 类:

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

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, use the class attribute to specify the class that contains 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 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. The following example shows how to specify a factory method:

下面的 bean 定义指定通过调用工厂方法创建 bean。该定义没有指定返回对象的类型(类) ,只指定包含工厂方法的类。在本例中,createInstance ()方法必须是静态方法。下面的示例演示如何指定工厂方法:

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>

The following example shows a class that would work with the preceding bean definition:

下面的示例显示了一个与前面的 bean 定义一起工作的类:

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return 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 by 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 or 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. The following example shows how to configure such a bean:

与通过静态工厂方法进行的实例化类似,使用实例工厂方法进行的实例化从容器中调用现有 bean 的非静态方法来创建新 bean。要使用这种机制,保留 class 属性为空,并在 factory-bean 属性中,在当前(或父或祖先)容器中指定 bean 的名称,该 bean 包含要调用来创建对象的实例方法。使用 factory-method 属性设置 factory 方法本身的名称。

下面的例子展示了如何配置这样一个 bean:

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

The following example shows the corresponding class:

下面的示例显示了相应的类:

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

One factory class can also hold more than one factory method, as the following example shows:

一个工厂类也可以容纳多个工厂方法,如下面的示例所示:

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>

The following example shows the corresponding class:

下面的示例显示了相应的类:

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

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 and that creates objects through an instance or static factory method. By contrast, FactoryBean (notice the capitalization) refers to a Spring-specific FactoryBean implementation class.

在 Spring 文档中,“factory bean” 指的是在 Spring 容器中配置的 bean,它通过实例方法 or 或静态工厂方法 来创建对象。相比之下,FactoryBean (注意大小写)指的是一个特定于 spring 的FactoryBean实现类

译者注:静态工厂方法和实例工厂方法,本质是一种借鸡生蛋的行为。

确定 Bean 的运行时类型

Determining a Bean’s Runtime Type

The runtime type of a specific bean is non-trivial to determine. A specified class in the bean metadata definition is just an initial class reference, potentially combined with a declared factory method or being a FactoryBean class which may lead to a different runtime type of the bean, or not being set at all in case of an instance-level factory method (which is resolved via the specified factory-bean name instead). Additionally, AOP proxying may wrap a bean instance with an interface-based proxy with limited exposure of the target bean’s actual type (just its implemented interfaces).

确定特定 bean 的运行时类型并不简单。Bean 元数据定义中的指定类仅仅是一个初始类引用,它可能与已声明的工厂方法或 FactoryBean 类结合在一起,这可能导致 bean 的不同运行时类型,或者在实例级工厂方法(可以通过指定的factory-bean 名称解析)的情况下根本不设置类。此外,AOP 代理可以使用基于接口的代理来包装 bean 实例,对目标 bean 的实际类型(仅仅是它实现的接口)进行有限的公开。

The recommended way to find out about the actual runtime type of a particular bean is a BeanFactory.getType call for the specified bean name. This takes all of the above cases into account and returns the type of object that a BeanFactory.getBean call is going to return for the same bean name.

查找特定 bean 的实际运行时类型的推荐方法是 BeanFactory.getType 调用指定的 bean 名称。这将考虑上述所有情况,并返回和调用BeanFactory.getBean 返回的相同对象类型。

1.4. 依赖的概念

1.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 定义到完全实现的应用程序,在这些应用程序中,对象通过协作来实现目标。
译者注:依赖,其实就是协作的概念。

1.4.1. 依赖注入

1.4.1. Dependency Injection

Dependency injection (DI) is a process whereby objects define their dependencies (that is, the other objects with which they work) 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) 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 本身通过使用类的直接构造或 Service Locator 模式来控制依赖项的实例化或位置的逆过程(因此得名为控制反转) 。

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 a result, your classes become easier to test, particularly when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.

使用 DI 原则的代码更加整洁,并且当对象具有其依赖关系时,解耦更加有效。对象不查找其依赖项,也不知道依赖项的位置或类。因此,您的类变得更容易测试,特别是当依赖关系位于接口或抽象基类上时,这允许在单元测试中使用 stub 或 mock 实现。

DI exists in two major variants: Constructor-based dependency injection and Setter-based dependency injection.

DI 有两个主要的变体: 基于构造函数的依赖注入基于 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:

基于构造函数的 DI 是通过容器调用具有许多参数的构造函数来实现的,每个参数表示一个依赖项。调用具有特定参数的静态工厂方法来构造 bean 几乎是等效的,本文将类似地将参数处理为构造函数和静态工厂方法。下面的示例显示了一个只能通过构造函数依赖注入的类:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    // SimpleMovieLister 有一个 MovieFinder 类型的依赖
    private final MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    // 有这么个构造器,以便 Spring 容器注入 MovieFinder 对象。
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
    // 省略了实际使用依赖 MovieFinder 的业务逻辑...
}

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.

请注意,这个类没有什么特别之处。它是一个不依赖于特定于容器的接口、基类或注释的 POJO。

构造函数参数解析

Constructor Argument Resolution

Constructor argument resolution matching occurs by using the argument’s type. If no potential ambiguity exists in the constructor arguments of a bean definition, 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 被实例化时这些参数被提供给相应的构造函数的顺序。考虑下面的类:

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}

Assuming that the ThingTwo and ThingThree classes are not related by inheritance, no potential ambiguity exists. Thus, the following configuration works fine, and you do not need to specify the constructor argument indexes or types explicitly in the <constructor-arg/> element.

假设 ThingTwoThingThree 类不存在继承关联,则不存在潜在的歧义。因此,下面的配置可以很好地工作,您不需要在 < constructor-arg/> 元素中显式地指定构造函数参数索引或类型。

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>

    <bean id="beanThree" class="x.y.ThingThree"/>
</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 无法确定值的类型,因此在没有帮助的情况下无法按类型进行匹配。考虑下面的类:

package examples;

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private final int years;

    // The Answer to Life, the Universe, and Everything
    private final String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
构造函数参数类型匹配

Constructor argument type matching

In the preceding scenario, the container can use type matching with simple types if you explicitly specify the type of the constructor argument by using the type attribute, as the following example shows:

在前面的场景中,如果您使用 type 属性显式指定构造函数参数的类型,那么容器可以对简单类型使用类型匹配,如下面的示例所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>
构造函数参数索引

Constructor argument index

You can use the index attribute to specify explicitly the index of constructor arguments, as the following example shows:

可以使用 index 属性显式指定构造函数参数的索引,如下面的示例所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

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.

除了解决多个简单值的不确定性,指定索引还可以解决构造函数具有两个相同类型的参数时的不确定性。

The index is 0-based.

该指数以0为起点

构造函数参数名

Constructor argument name

You can also use the constructor parameter name for value disambiguation, as the following example shows:

也可以使用构造函数参数名进行值消歧,如下面的示例所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

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 cannot or do not want to compile your code with the debug flag, you can use the @ConstructorProperties JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:

请记住,为了实现这个功能,必须在编译代码时启用调试标志,以便 Spring 可以从构造函数中查找参数名。如果您不能或不想使用调试标志编译代码,可以使用@constructorproperties JDK 注释显式地命名构造函数参数。然后,样本类必须看起来如下:

译者注:不常用。

package examples;

public class ExampleBean {

    // Fields omitted

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
基于 setter 的依赖注入

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 a no-argument static factory method to instantiate your bean.

基于 Setter 方法的注入,在在调用无参数构造函数或无参数静态工厂方法实例化 bean 之后,容器调用 bean 的 setter 方法完成的。

The following example shows a class that can only be dependency-injected by 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。

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    // 拥有一个 setter 方法,以便 Spring 容器注入 MovieFinder 对象。
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

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 (that is, programmatically) but rather with XML bean definitions, annotated components (that is, classes annotated with @Component, @Controller, and so forth), 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 对它管理的 bean 支持基于构造函数和基于 setter 的 DI。在通过构造函数方法注入一些依赖项之后,它还支持基于 setter 的 DI(译者注:划重点)。您可以以 BeanDefinition 的形式配置依赖关系,将其与 PropertyEditor 实例结合使用,以将属性从一种格式转换为另一种格式。然而,大多数 Spring 用户并不直接使用这些类(也就是说,编程式的方式) ,而是使用 XML bean 定义、带注解的组件(也就是用@component@Controller 等注解的类)或基于 java 的@Configuration 类中的 @bean 方法。然后,这些源在内部转换为 BeanDefinition 实例,并用于加载整个 Spring IoC 容器实例。

Constructor-based or setter-based DI?

基于构造函数还是基于 setter 的 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 be a required dependency; however, constructor injection with programmatic validation of arguments is preferable.

由于可以混合使用基于构造函数和基于 setter 的 DI,因此对于强制依赖项使用构造函数和对于可选依赖项使用 setter 方法或配置方法(译者注:这里的配置方法,应该是类似 setter 的设置属性的方法)是一个很好的经验法则。注意,在 setter 方法上使用@required 注释可以使该属性成为必需的依赖项; 但是,带有参数编程验证的构造函数注入更可取。

The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the 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.

Spring 团队通常提倡构造函数注入,因为它允许您将应用程序组件实现为不可变对象,并确保所需的依赖项不为空(译者注:画重点)。此外,构造函数注入的组件总是以完全初始化的状态返回给客户机(调用)代码。作为一个旁注,大量的构造函数参数是一个糟糕的代码味道,这意味着类可能有太多的责任,应该重构以更好地解决适当的关注点分离/代码。

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.

Setter 注入应该主要用于可选的依赖项,这些依赖项可以在类中分配合理的默认值。否则,必须在代码使用依赖项的所有地方执行非空检查。Setter 注入的一个好处是 setter 方法使该类的对象容易在以后重新配置或重新注入。因此,通过 JMX mbean 进行管理是 setter 注入的一个引人注目的用例。

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.

使用对特定类最有意义的 DI 样式。有时,在处理您没有源的第三方类时,选择已为你做好。例如,如果第三方类没有公开任何 setter 方法,那么构造函数注入可能是 DI 唯一可用的形式。

依赖解析过程

Dependency Resolution Process

The container performs bean dependency resolution as follows:

容器执行 bean 依赖项解析如下:

  • The ApplicationContext is created and initialized with configuration metadata that describes all the beans. Configuration metadata can be specified by XML, Java code, or annotations.

    使用描述了所有 bean 的配置元数据创建并初始化 ApplicationContext。配置元数据可以通过 XML、 Java 代码或注解方式指定。

  • For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method (if you use that instead of a normal constructor). These dependencies are provided to the bean, when the bean is actually created.

    对于每个 bean,它的依赖关系以属性构造函数参数静态工厂方法(如果您使用静态工厂方法而不是普通的构造函数)的参数的形式表示。在实际创建 bean 时,这些依赖项被提供给 bean。

  • Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container.

    每个属性或构造函数参数都是要设置的实际定义的值,或对容器中另一个 bean 的引用

  • Each property or constructor argument that 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, and so forth.

    作为的每个属性或构造函数参数都将从其指定的格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring 可以将以字符串格式提供的值转换为所有内置类型,如 intlongStringboolean 等。

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 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 — that is, on first creation of the affected bean.

Spring 容器在创建容器时验证每个 bean 的配置。但是,在 bean 实际创建之前,不会设置 bean 属性本身。在创建容器时,将创建 singleton-scoped 并设置为预实例化(默认情况)的 bean。作用域在 Bean Bean Scopes 中定义。否则,只有在请求 bean 时才会创建它。创建 bean 时可能会导致创建 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.

例如: Class A 需要通过构造函数注入的 class B 实例,而 B 类需要通过构造函数注入的 A 类实例。如果将 bean 配置为像类 A 和类 B 相互注入,那么 Spring IoC 容器在运行时检测到这个循环引用,并抛出 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.

一个可能的解决方案是修改某些类的源代码为 setters 而不是构造函数配置的。或者,避免构造函数注入,只使用 setter 注入。换句话说,尽管不推荐使用 setter 注入,但是您可以使用 setter 注入配置循环依赖项。

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-and-egg scenario).

与典型情况(没有循环依赖)不同,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 that 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 initialize lazily, rather than being eagerly pre-instantiated.

通常可以相信 Spring 会做正确的事情。它在容器加载时检测配置问题,例如对不存在的 bean 和循环依赖项的引用。Spring 在实际创建 bean 时设置属性并尽可能晚地解析依赖项。这意味着,如果创建对象或其某个依赖项时出现问题,那么正确加载的 Spring 容器随后可以在请求对象时生成异常ーー例如,由于缺少或无效属性,bean 抛出异常。一些配置问题的可见性可能会延迟,这就是为什么默认情况下 ApplicationContext 实现会预先实例化单例 bean。在实际需要这些 bean 之前,需要花费一些前期时间和内存来创建它们,因此在创建 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 it is 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.

如果不存在循环依赖关系,当一个或多个合作 bean 被注入到依赖 bean 中时,每个合作 bean 在被注入到依赖 bean 之前都会被完全配置。这意味着,如果 bean A 对 bean B 有依赖关系,那么在调用 bean A 上的 setter 方法之前,Spring IoC 容器将完全配置 bean B。换句话说,bean 被实例化(如果它不是预实例化的单例) ,它的依赖关系被设置,相关的生命周期方法(如配置的 init 方法或 InitializingBean 回调方法)被调用。

了解这一段描述,对从源码的角度了解 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 as follows:

下面的示例为使用基于 xml 的配置元数据的基于 setter 的 DI 。Spring XML 配置文件的一小部分指定了一些 bean 定义如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <!-- 使用嵌套元素 ref 完成 setter 注入 -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <!-- 使用属性 ref 完成 setter 注入 -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

The following example shows the corresponding ExampleBean class:

下面的示例显示了相应的 ExampleBean 类:

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }
}

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 文件中指定的属性。下面的示例使用基于构造函数的 DI:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

The following example shows the corresponding ExampleBean class:

下面的示例显示了相应的 ExampleBean 类:

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}

The constructor arguments specified in the bean definition are 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 被告知调用一个静态工厂方法来返回一个对象的实例,而不是使用一个构造函数:

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

The following example shows the corresponding ExampleBean class:

下面的示例显示了相应的 ExampleBean 类:

public class ExampleBean {

    // a private constructor
    // 私有的构造器
    private ExampleBean(...) {
        ...
    }

    // a static factory method; the arguments to this method can be
    // considered the dependencies of the bean that is returned,
    // regardless of how those arguments are actually used.
    // 一个静态工厂方法:这个方法的参数可以被认为是要返回 bean 的依赖,而不用管这些参数如何被使用的
    public static ExampleBean createInstance (
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

        ExampleBean eb = new ExampleBean (...);
        // some other operations...
        return eb;
    }
}

Arguments to the static factory method are supplied by <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 can be used in an essentially identical fashion (aside from the use of the factory-bean attribute instead of the class attribute), so we do not discuss those details here.

静态工厂方法的参数由 < constructor-arg/> 元素提供,与实际使用的构造函数完全相同(译者注:这种结论挺奇妙的,学到了,可以记一下,认为静态工厂方法和构造函数在实例化 bean 时作用类似)。工厂方法返回的类的类型不必与包含静态工厂方法的类的类型相同(尽管在本例中它是)。实例(非静态)工厂方法可以以本质上相同的方式使用(除了使用 factory-bean 属性而不是 class 属性之外) ,因此我们在这里不讨论这些细节。

1.4.2. 详解依赖项和配置

1.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 属性和构造函数参数定义为对其他被 Spring 托管的 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. The following example shows various values being set:

<property/>value 属性将属性或构造函数参数指定为人类可读的字符串表示形式。Spring 的 conversion service 用于将这些值从 String 转换为属性或参数的实际类型。下面的示例显示了正在设置的各种值:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="misterkaoli"/>
</bean>

The following example uses the p-namespace for even more succinct XML configuration:

下面的示例使用 p 名称空间进行更简洁的 XML 配置:

笔者注:不太建议使用。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="misterkaoli"/>

</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 Tools for Eclipse) that supports automatic property completion when you create bean definitions. Such IDE assistance is highly recommended.

前面的 XML 更简洁。但是,错误是在运行时而不是设计时发现的,除非您使用 IDE (比如 IntelliJ IDEA 或 Spring Tools for Eclipse) ,在创建 bean 定义时支持自动完成属性。强烈推荐这种 IDE 协助。

You can also configure a java.util.Properties instance, as follows:

你还可以配置 java.util.Properties 实例,如下所示:

<bean id="mappings"
    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">

    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>

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 容器将 < value/> 元素内的文本转换为java.util.Properties,使用 JavaBeans PropertyEditor 机制。这是一个很好的快捷方式,也是 Spring 团队确实喜欢使用嵌套的 < value/> 元素而不是 value 属性样式的少数几个地方之一。

idref 元素

The idref element

The idref element is simply an error-proof way to pass the id (a string value - not a reference) of another bean in the container to a <constructor-arg/> or <property/> element. The following example shows how to use it:

idref 元素只是一种防错的方法,用于将容器中另一个 bean 的 id (字符串值——而不是引用)传递给一个 < constructor-arg/>< property/> 元素。下面的例子展示了如何使用它:

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>

The preceding bean definition snippet is exactly equivalent (at runtime) to the following snippet:

前面的 bean 定义片段与下面的片段完全等效(在运行时) :

<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>

The first form is preferable to the second, because using the idref tag lets the container 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 的 targetName 属性的值执行验证。只有在实际实例化客户端 bean 时才会发现输入错误(很可能会导致致命的结果)。如果客户端 bean 是一个 prototype bean,那么只有在部署容器之后很长时间才能发现这个排版错误和由此产生的异常。

The local attribute on the idref element is no longer supported in the 4.0 beans XSD, since it does not provide value over a regular bean reference any more. Change your existing idref local references to idref bean when upgrading to the 4.0 schema.
4.0 版 Bean XSD 不再支持idref元素上的local属性,因为它不再提供超过普通 Bean 引用的价值。在升级到 4.0 模式时,将你现有的idref local引用改为 idref bean

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拼错。

对其他 bean (协作者)的引用

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 is to be set, and it is initialized on demand as needed before the property is set. (If the collaborator is a singleton bean, it may already be initialized by the container.) All references are ultimately a reference to another object. Scoping and validation depend on whether you specify the ID or name of the other object through the bean or parent attribute.

ref元素是 < constructor-arg/>< property/> 元素中的最后一个元素。在这里,您将 bean 的指定属性的值设置为对容器管理的另一个 bean (协作者)的引用。引用的 bean 是要设置其属性的 bean 的依赖项,并且在设置属性之前根据需要初始化它。(如果合作者是单例 bean,那么它可能已经被容器初始化了)。所有的引用本质上是对另一个对象的引用。作用域和验证取决于是否通过 beanparent 属性指定其他对象的 ID 或名称。

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 be the same as one of the values in the name attribute of the target bean. The following example shows how to use a ref element:

通过 < ref/> 标记的 bean 属性指定目标 bean 是最常用的形式,它允许创建对同一容器或父容器中任何 bean 的引用,而不管它是否在同一 XML 文件中。Bean 属性的值可能与目标 bean 的 id 属性相同,或者与目标 bean 的 name 属性中的一个值相同(译者注:别名的概念)。下面的示例演示如何使用 ref 元素:

<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. The target bean must be in a parent container of the current one. You should 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 has the same name as the parent bean. The following pair of listings shows how to use the parent attribute:

通过parent 属性指定目标 bean 将创建对当前容器的父容器中的 bean 的引用(译者注:parent 属性的作用)。parent 属性的值可能与目标 bean 的 id 属性或目标 bean 的 name 属性中的一个值相同。目标 bean 必须位于当前 bean 的父容器中。当您有一个容器层次结构,并且您希望用与父 bean 同名的代理将现有 bean 包装在父容器中时,您应该主要使用这个 bean 引用变体。下面的两个列表显示了如何使用父属性:

<!-- in the parent context -->
<!-- 父容器中 -->
<bean id="accountService" class="com.something.SimpleAccountService">
    <!-- insert dependencies as required as here -->
</bean>

<!-- in the child (descendant) context -->
<!-- 子容器中 -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
    </property>
    <!-- insert other configuration and dependencies as required here -->
</bean>
The local attribute on the ref element is no longer supported in the 4.0 beans XSD, since it does not provide value over a regular bean reference any more. Change your existing ref local references to ref bean when upgrading to the 4.0 schema.
已翻译,同上。
内部 bean

I nner Beans

译者注:业务中没用过这种。

A <bean/> element inside the <property/> or <constructor-arg/> elements defines an inner bean, as the following example shows:

<property/><constructor-arg/>元素中的<bean/>元素定义了一个内部 bean,如下面的示例所示:

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <!-- 简单的定义一个内部 bean,而没有通过引用引用目标 bean -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean 这是一个内部 bean-->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

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, because inner beans are always anonymous and are always created with the outer bean. It is not possible to access inner beans independently or to inject them into collaborating beans other than into the enclosing bean.

内部 bean 定义不需要定义的 ID 或名称。如果指定,容器也不使用此值作为标识符(译者注:你说气人不?)。容器在创建时还忽略scope标志,因为内部 bean 总是匿名的,并且总是用外部 bean 创建的。不可能独立地访问内部 bean,也不可能将它们注入到合作 bean 中,而是注入到包围的 bean 中。译者注:可以关注下它的这些特性。

As a corner case, it is possible to receive destruction callbacks from a custom scope — for example, for a request-scoped inner bean contained within a singleton bean. The creation of the inner bean instance is tied to its containing bean, but destruction callbacks let it participate in the request scope’s lifecycle. This is not a common scenario. Inner beans typically simply share their containing bean’s scope.

作为一个极端情况,可以从custom scope接收销毁回调,例如,对于包含在单例 bean 中的请求范围(request-scoped)的内部 bean。内部 bean 实例的创建与其包含的 bean 绑定在一起,但是销毁回调允许它参与请求范围的生命周期。这种情况并不常见。内部 bean 通常只是简单地共享它们的外围 bean 的作用域。

集合

Collections

The <list/>, <set/>, <map/>, and <props/> elements set the properties and arguments of the Java Collection types List, Set, Map, and Properties, respectively. The following example shows how to use them:

<list/>, <set/>, <map/>, 和 <props/> 元素分别设置了 Java Collection 类型 ListSetMapProperties 的属性和参数。下面的例子展示了如何使用它们:

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <!-- 作用于 setAdminEmails(java.util.Properties) 方法调用 -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <!-- 作用于 setSomeList(java.util.List) 方法调用 -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <!-- 作用于 setSomeMap(java.util.Map) 方法调用 -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <!-- 作用于 setSomeSet(java.util.Set) 方法调用 -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

译者注:其实感觉这个例子不太生动。

The value of a map key or value, or a set value, can also be any of the following elements:

Map 键或值或 set 值的值也可以是以下任何一个元素:

bean | ref | idref | list | set | map | props | value | null
集合合并

Collection Merging

The Spring container also supports merging collections. An application developer can define a parent , , or element and have child , , or 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 和子 bean 定义的读者可能希望在继续阅读之前阅读 相关章节

The following example demonstrates collection merging:

下面的示例演示集合合并:

<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">administrator@example.com</prop>
                <prop key="support">support@example.com</prop>
            </props>
        </property>
    </bean>
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <!-- the merge is specified on the child collection definition -->
            <!-- 在子容器定义中指定了 merge 属性 -->
            <props merge="true">
                <prop key="sales">sales@example.com</prop>
                <prop key="support">support@example.co.uk</prop>
            </props>
        </property>
    </bean>
<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 merging the child’s adminEmails collection with the parent’s adminEmails collection. The following listing shows the result:

注意在子 bean 定义的 adminEmails 属性的 < props/> 元素上使用 merge = true 属性。当子 bean 被容器解析并实例化时,产生的实例具有 adminEmails Properties 集合,该集合包含将子的 adminEmails 集合与父的 adminEmails 集合合并的结果。下面的清单显示了结果:

administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk

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集合类型(即值的有序集合的概念)相关联的语义。父列表的值位于所有子列表的值之前。对于 MapSetProperties 集合类型,不存在排序。因此,作为容器内部使用的以MapSetProperties为基础的集合实现类型没有有效的排序语义。

汇集合并的限制

Limitations of Collection Merging

You cannot merge different collection types (such as a Map and a List). 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 does not result in the desired merging.

不能合并不同的集合类型(如 MapList)。如果您尝试这样做,则会引发相应的 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 (for example) String elements. If you use 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. The following Java class and bean definition show how to do so:

通过在 java 5 中引入泛型类型,您可以使用强类型集合。也就是说,可以声明一个只能包含(例如) String 元素 的Collection 类型。如果使用 Spring 将一个强类型集合注入到 bean 中,您可以利用 Spring 的类型转换支持,这样您的强类型集合实例的元素在被添加到集合之前就被转换为适当的类型。下面的 Java 类和 bean 定义展示了如何做到这一点:

public class SomeClass {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<beans>
    <bean id="something" class="x.y.SomeClass">
        <property name="accounts">
            <map>
                <entry key="one" value="9.99"/>
                <entry key="two" value="2.75"/>
                <entry key="six" value="3.99"/>
            </map>
        </property>
    </bean>
</beans>

When the accounts property of the something 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.

something bean 的 accounts 属性准备注入时,通过反射可以获得关于强类型 Map < String,Float > 的元素类型的泛型信息。因此,Spring 的类型转换基础组件将各种值元素识别为 Float 类型,并将字符串值(9.99、2.75和3.99)转换为实际的 Float 类型。

Null 和空字符串值

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 将属性和类似属性的空参数视为空字符串。下面的基于 xml 的配置元数据片段将 email 属性设置为空 String 值(“”)。

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

The preceding example is equivalent to the following Java code:

上面的例子等价于下面的 Java 代码:

exampleBean.setEmail("");

The <null/> element handles null values. The following listing shows an example:

<null/>元素处理null 值。下面的清单显示了一个例子:

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

The preceding configuration is equivalent to the following Java code:

前面的配置等价于下面的 Java 代码:

exampleBean.setEmail(null);
带有 p 名称空间的 XML 快捷方式

XML Shortcut with the p-namespace

The p-namespace lets you use the bean element’s attributes (instead of nested <property/> elements) to describe your property values collaborating beans, or both.

p-namespace 允许您使用 bean 元素的属性(而不是嵌套的 < property/> 元素)来描述合作 bean 的属性值,或者两者都使用。

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 定义的名称空间的可扩展配置格式。本章讨论的 bean 配置格式在 XML Schema 文档中定义。但是,在 XSD 文件中没有定义 p 名称空间,它只存在于 Spring 的核心中。

The following example shows two XML snippets (the first uses standard XML format and the second uses the p-namespace) that resolve to the same result:

下面的示例显示了解析为相同结果的两个 XML 代码段(第一个使用标准 XML 格式,第二个使用 p-namespace) :

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="someone@somewhere.com"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="someone@somewhere.com"/>
</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 定义中显示了名为 email 的 p 名称空间中的一个属性。这告诉 Spring 包含一个属性声明。正如前面提到的,p 命名空间没有模式(schema )定义,因此可以将属性名称设置为属性名称(译者注:你在说绕口令嘛?)。

This next example includes two more bean definitions that both have a reference to another bean:

下一个例子包括两个 bean 定义,它们都有对另一个 bean 的引用:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <!-- 对比看看 p 名称空间和普通的区别 -->
    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>

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 命名空间的属性值,而且还使用特殊格式(p:spouse-ref)声明属性引用。然而第一个 bean 定义使用 <property name="spouse" ref="jane"/>创建一个从 bean john 到 bean jane 的引用,第二个 bean 定义使用 p:spouse-ref="jane"作为一个属性来做同样的事情。在这种情况下,spouse 是属性名,而-ref 部分表示这不是一个直值(straight value ),而是对另一个 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文档。
C 命名空间的 XML 快捷方式

XML Shortcut with the c-namespace

Similar to the XML Shortcut with the p-namespace, the c-namespace, introduced in Spring 3.1, allows inlined attributes for configuring the constructor arguments rather then nested constructor-arg elements.

与 p 名称空间的 XML 快捷方式类似,Spring 3.1中引入的 c 名称空间允许内联属性配置构造函数参数,而不是嵌套的 constructor-arg 元素。

The following example uses the c: namespace to do the same thing as the from Constructor-based Dependency Injection:

下面的示例使用 c: 名称空间做与基于构造函数的依赖注入相同的事情(VS):

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    <!-- traditional declaration with optional argument names -->
    <!-- 传统的使用可选参数名的声明方式 -->
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="something@somewhere.com"/>
    </bean>

    <!-- c-namespace declaration with argument names -->
    <!-- c-namespace 使用参数名的生命方式 -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>

The c: namespace uses the same conventions as the p: one (a trailing -ref for bean references) for setting the constructor arguments by their names. Similarly, it needs to be declared in the XML file even though it is not defined in an XSD schema (it exists inside the Spring core).

C: 名称空间使用与 p: 一样(以-ref结尾代表 bean 的引用)相同的约定,根据它们的名称设置构造函数参数。类似地,它需要在 XML 文件中声明,即使它不是在 XSD 模式中定义的(它存在于 Spring 核心中)。

For the rare cases where the constructor argument names are not available (usually if the bytecode was compiled without debugging information), you can use fallback to the argument indexes, as follows:

对于构造函数参数名称不可用的罕见情况(通常如果字节码编译时没有调试信息) ,您可以退一步使用参数索引(译者注:您还真是锲而不舍啊),如下所示:

<!-- c-namespace index declaration -->
<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
    c:_2="something@somewhere.com"/>
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 IDEs allow it). A corresponding index notation is also available for <constructor-arg> elements but not commonly used since the plain order of declaration is usually sufficient there.
由于XML语法的原因,索引符号需要有前面的_,因为XML属性名不能以数字开头(尽管有些IDE允许这样做)。相应的索引符号也可用于元素,但并不常用,因为通常情况下,普通的声明顺序已经足够了。

In practice, the constructor resolution mechanism is quite efficient in matching arguments, so unless you really need 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 定义:

<bean id="something" class="things.ThingOne">
    <property name="fred.bob.sammy" value="123" />
</bean>

The something bean has a fred property, which has a bob property, which has a sammy property, and that final sammy property is being set to a value of 123. In order for this to work, the fred property of something and the bob property of fred must not be null after the bean is constructed. Otherwise, a NullPointerException is thrown.

Something bean 有一个 fred 属性,fred 有一个 bob 属性,bob 有一个 sammy 属性,最后一个 sammy 属性被设置为123。为了实现这一点,在构造 bean 之后,某个元素的 fred 属性和 fredbob 属性不能为 null。否则,将引发 NullPointerException

1.4.3. 使用depends-on

1.4.3. Using depends-on

If a bean is a dependency of another bean, that usually means that one bean is set as a property of another. Typically you accomplish this with the ref elementin XML-based configuration metadata. However, sometimes dependencies between beans are less direct. An example is when a static initializer in a class needs to be triggered, such as for 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/> 元素来实现这一点。然而,有时 bean 之间的依赖关系并不那么直接。例如,当需要触发类中的静态初始化时,例如用于数据库驱动程序注册。depends-on属性可以在使用该元素初始化 bean 之前显式强制初始化一个或多个 bean。下面的示例使用 depends-on 属性表示对单个 bean 的依赖:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

To express a dependency on multiple beans, supply a list of bean names as the value of the depends-on attribute (commas, whitespace, and semicolons are valid delimiters):

为了表达对多个 bean 的依赖关系,提供一个 bean 名称列表作为依赖属性的值(逗号、空格和分号是有效的分隔符) :

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
The depends-on attribute can specify both an initialization-time dependency and, in the case of singleton beans only, a corresponding destruction-time dependency. Dependent beans that define a depends-on relationship with a given bean are destroyed first, prior to the given bean itself being destroyed. Thus, depends-on can also control shutdown order.
depends-on属性既可以指定初始化时依赖关系,也可以指定对应的销毁时依赖关系(仅在单例bean中)。在销毁给定bean本身之前,首先销毁与它定义depends-on依赖关系的依赖bean。因此,依赖还可以控制销毁顺序。
1.4.4. 懒加载 bean

1.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 being 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 实现急切地创建和配置所有单例 bean,作为初始化过程的一部分。通常,这种预实例化是可取的,因为配置或周围环境中的错误会立即被发现,而不是在几个小时甚至几天之后。当这种行为不可取时,可以通过将 bean 定义标记为延迟初始化来防止单例 bean 的预实例化。一个延迟初始化的 bean 告诉 IoC 容器在第一次请求时创建一个 bean 实例,而不是在启动时。

In XML, this behavior is controlled by the lazy-init attribute on the <bean/> element, as the following example shows:

在 XML 中,这种行为由 < bean/> 元素上的 lazy-init 属性控制,如下面的示例所示:

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>

When the preceding configuration is consumed by an ApplicationContext, the lazy bean is not eagerly pre-instantiated when the ApplicationContext starts, whereas the not.lazy bean is eagerly pre-instantiated.

当前面的配置被 ApplicationContext 使用时,当 ApplicationContext 启动时,lazy bean 不会急切地预先实例化,而 not.lazybean 则急切地预先实例化。

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 的依赖项,而这个单例 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, as the following example shows:

您还可以在容器级别通过使用 < beans/> 元素的 default-lazy-init 属性来控制延迟初始化,如下面的示例所示:

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>
1.4.5. 自动装配依赖项

1.4.5. Autowiring Collaborators

译者注:Autowiring 理解为自动注入自动装配均可。

The Spring container can autowire relationships between collaborating beans. You can let Spring resolve collaborators (other beans) automatically for your bean by inspecting the contents of the ApplicationContext. Autowiring has the following advantages:

Spring 容器可以自动装配协作 bean 之间的关系。您可以通过检查 ApplicationContext 的内容,让 Spring 自动为您的 bean 解析协作者(其他 bean)。自动装配有以下优点:

  • 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.)

    自动装配可以显著减少指定属性或构造函数参数的需要。(其他章节部分讨论的 bean 模板等其他机制在这方面也很有价值。)

  • 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.

    自动装配可以根据对象的发展更新配置。例如,如果需要向类添加依赖项,则可以自动满足该依赖项,而无需修改配置。因此,自动连接在开发过程中特别有用,当代码库变得更加稳定时,无需放弃切换到显式配置的方式(译者注:即,还可以考虑显示配置的方式)。

When using XML-based configuration metadata (see Dependency Injection), you can specify the 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 can thus choose which ones to autowire. The following table describes the four autowiring modes:

当使用基于 xml 的配置元数据时(参见Dependency Injection) ,可以使用 < bean/> 元素的 autowire 属性为 bean 定义指定 自动装配模式。自动装配功能有四种模式。您可以为每个 bean 指定 autowiring,从而可以选择要自动连接哪些 bean。

下表描述了四种自动装配模式:

Mode
模式
Explanation
描述
no(Default) No autowiring. Bean references must be defined by ref elements. 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.
(默认值)没有自动装配。Bean 引用必须由 ref 元素定义。对于较大的部署,不建议更改默认设置,因为显式指定协作者可以提供更大的控制和清晰度。在某种程度上,它记录了一个系统的结构。
byNameAutowiring 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.
按属性名称自动装配。Spring 查找与需要自动装配的属性名称相同的 bean。例如,如果一个 bean 定义被设置为按名称自动装配,并且它包含一个master属性(即它有一个 setMaster(..)方法),Spring 查找名为 master 的 bean 定义并使用它来设置属性。
byTypeLets a property 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).
如果容器中恰好存在一个属性类型的 bean,则允许自动装配属性。如果存在多个,将引发致命异常,这表明您不能为该 bean 使用 byType 自动装配。如果没有匹配的 bean,则什么也不会发生(未设置属性)。
constructorAnalogous 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.
类似于 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 Map instances if the expected key type is String. An autowired Map instance’s values consist of all bean instances that match the expected type, and the Map instance’s keys contain the corresponding bean names.

使用 byType构造函数的自动装配模式,可以装配数组和类型化集合。在这种情况下,容器中与预期类型匹配的所有自动装配候选项都会被提供,以满足依赖性。如果预期的键类型是 String,则可以自动装配强类型Map实例。自动装配的 Map实例的值由所有匹配预期类型的 bean 实例组成,Map 实例的键包含相应的 bean 名称。

自动装配的局限性和缺点

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.

当在整个项目中一致地使用自动装配时,它的工作效果最好。如果通常不使用 autowiring,那么开发人员使用它只装配一个或两个 bean 定义可能会感到困惑。

Consider the limitations and disadvantages of autowiring:

考虑一下自动装配的局限性和缺点:

  • Explicit dependencies in property and constructor-arg settings always override autowiring. You cannot autowire simple properties such as primitives, Strings, and Classes (and arrays of such simple properties). This limitation is by-design.

    属性和构造函数参数设置中的显式依赖项总是覆盖自动装配。不能自动装配简单属性,如基本类型、字符串和类(以及这种简单属性的数组)。这种限制设计如此的。

  • Autowiring is less exact than explicit wiring. Although, as noted in the earlier 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.

    自动注入不如显式装配严格。尽管如前面的表所示,Spring 在歧义可能产生意外结果的时会小心避免猜测。Spring 管理的对象之间的关系不再被显式记录

  • Wiring information may not be available to tools that may generate documentation from a Spring container.

    对于可能从 Spring 容器生成文档的工具,装配信息可能不可用。

  • 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 Map instances, 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.

    容器中的多个 bean 定义可以匹配 setter 方法或构造函数参数指定的类型,以便自动装配。对于数组、集合或 Map 实例,这不一定是问题。但是,对于期望单个值的依赖项,不能随意解决这种不确定性。如果没有唯一的 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 to false, as described in the next section.

    通过将其 autowire-candidate 属性设置为 false 来避免为 bean 定义进行自动配线,如下一节所述。

  • Designate a single bean definition as the primary candidate by setting the primary attribute of its <bean/> element to true.

    通过将其 < bean/> 元素的primary属性设置为 true,将单个 bean 定义指定为主候选项。

  • Implement the more fine-grained control available with annotation-based configuration, as described in Annotation-based Container Configuration.

    使用基于注解的配置实现更细粒度的控制,如 基于注解的容器配置中所述。

排除 bean 的自动装配

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)中不能使用。

The autowire-candidate attribute is designed to only affect type-based autowiring. It does not affect explicit references by name, which get resolved even if the specified bean is not marked as an autowire candidate. As a consequence, autowiring by name nevertheless injects a bean if the name matches.
autowire-candidate属性被设计为只影响基于类型的自动连接,它不影响按名称显式引用,即使指定的 bean 没有标记为自动装配候选项,也会解析这些引用。因此,如果按照名称匹配,自动装配仍然会生效

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 definition’s autowire-candidate attribute always takes precedence. For such beans, the pattern matching rules do not apply.

您还可以根据对 bean 名称的模式匹配来限制自动连接候选对象。顶层 < beans/> 元素在其 default-autowire-candidates 属性中接受一个或多个模式。例如,若要将 autowire 候选状态限制为名称以 Repository 结尾的任何 bean,请提供一个值 * Repository。若要提供多个模式,请在以逗号分隔的列表中定义它们。Bean 定义的 autowire-candidate 属性的显式值 truefalse 始终优先。对于这样的 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 by using autowiring. Rather, the bean itself is not a candidate for autowiring other beans.

这些技术对于不希望通过自动装配注入到其他 bean 中的 bean 非常有用。这并不意味着不能通过使用自动配线来配置被排除的 bean。只是说,被排除的 bean 本身并不适合自动装配到其他 bean。

1.4.6. 方法注入

1.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 creates the singleton bean A only 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 A 一次,因此只有一次机会设置属性。每次需要 bean B 的新实例时,容器不能为 bean A 提供 bean A 的新实例。

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 example shows this approach:

一个解决方案是放弃一些控制反转。通过实现 ApplicationContextAware 接口,可以让 bean A 感知到(aware)容器,并且在每次 bean A 需要时对容器进行 getBean (“ b”)调用请求(通常是新的) bean b 实例。下面的例子说明了这种方法:

// a class that uses a stateful Command-style class to perform some processing
// 使用有状态命令样式类执行某些处理的类
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        // 获取适当命令的新实例
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        // 在(希望是全新的)命令实例上设置状态
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        // 注意 Spring API 依赖!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

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, lets you handle this use case cleanly.

前面的内容是不可取的,因为业务代码知道并耦合到 Spring 框架。方法注入(Method Injection)是 Spring IoC 容器的一个稍微高级的特性,它可以让您干净利落地处理这个用例。

You can read more about the motivation for Method Injection in this blog entry.

你可以在这篇博客中了解更多关于方法注入的动机。

查找方法注入

Lookup Method Injection

Lookup method injection is the ability of the container to override methods on container-managed beans and 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 dynamically generate a subclass that overrides the method.

查找方法注入是容器重写容器中 bean 上的方法并返回容器中另一个命名 bean 的查找结果的能力。查找通常涉及一个原型 bean,如前面部分所描述的场景。Spring 框架通过使用从 CGLIB 库生成字节码来动态生成覆盖该方法的子类来实现此方法注入。

  • For this dynamic subclassing to work, the class that the Spring bean container subclasses cannot be final, and the method to be overridden cannot be final, either.

    为了使这个动态子类化生效,Spring bean 容器子类的类不能是最终类,而要重写的方法也不能是最终类。

  • 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 do not work with factory methods and in particular not with @Bean methods in configuration classes, since, in that case, the container is not in charge of creating the instance and therefore cannot create a runtime-generated subclass on the fly.

    另一个关键限制是查找方法不能与工厂方法一起工作,特别是不能与配置类中的@bean 方法一起工作,因为在这种情况下,容器不负责创建实例,因此不能动态创建运行时生成的子类。

In the case of the CommandManager class in the previous code snippet, the Spring container dynamically overrides the implementation of the createCommand() method. The CommandManager class does not have any Spring dependencies, as the reworked example shows:

对于前面代码片段中的 CommandManager 类,Spring 容器会动态地覆盖 createCommand ()方法的实现。CommandManager 类没有任何 Spring 依赖项,正如修改后的示例所示:

译者注:

package fiona.apple;

// no more Spring imports! 没有 spring 相关导入

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    // 那么,这个方法的实现在哪呢?
    protected abstract Command createCommand();
}

In the client class that contains the method to be injected (the CommandManager in this case), the method to be injected requires a signature of the following form:

在包含要注入的方法的客户端类中(本例中为 CommandManager) ,要注入的方法需要以下形式的签名:

<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. Consider the following example:

如果该方法是抽象的,则动态生成的子类实现该方法。否则,动态生成的子类将覆盖原始类中定义的具体方法。考虑下面的例子:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<!-- 部署为原型(非单例)的一个有状态的 bean -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required 根据需要在此注入依赖-->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<!-- commandProcessor 使用状态化的辅助类 -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

The bean identified as commandManager calls its own createCommand() method whenever it needs a new instance of the myCommand bean. You must be careful to deploy the myCommand bean as a prototype if that is actually what is needed. If it is a singleton, the same instance of the myCommand bean is returned each time.

标识为 commandManager 的 bean 在需要 myCommand bean 的新实例时调用自己的 createCommand ()方法。如果实际上需要 myCommand bean,那么您必须谨慎地将其部署为原型。如果是单例模式,则每次返回 myCommand bean 的相同实例。

Alternatively, within the annotation-based component model, you can declare a lookup method through the @Lookup annotation, as the following example shows:

或者,在基于注解的组件模型中,可以通过@lookup 注释声明一个查找方法,如下面的示例所示:

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

Or, more idiomatically, you can rely on the target bean getting resolved against the declared return type of the lookup method:

或者,更进一步了解,你可以依靠目标 bean 根据声明的返回类型来解析查找方法:

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup// 译者注:这里没有指定 具体 bean
    protected abstract MyCommand createCommand();
}

Note that you should typically declare such annotated lookup methods with a concrete stub implementation, in order for them to be compatible with Spring’s component scanning rules where abstract classes get ignored by default. This limitation does not apply to explicitly registered or explicitly imported bean classes.

请注意,您通常应该使用具体的存根(stub ,译者注:存根这个词没怎么用过,看起来怪怪的,可以理解为兜底嘛?)实现来声明这种带注解的查找方法,以便它们与 Spring 的组件扫描规则兼容,在这些规则中,抽象类被默认忽略。此限制不适用于显式注册或显式导入的 bean 类。

Another way of accessing differently scoped target beans is an ObjectFactory/ Provider injection point. See Scoped Beans as Dependencies.You may also find the ServiceLocatorFactoryBean (in the org.springframework.beans.factory.config package) to be useful.
访问不同作用域的目标 bean 的另一种方法是 ObjectFactory/Provider 注入点。
您可能还会发现 ServiceLocatorFactoryBean (在 org.springframework.beans.factory.config 包中)非常有用。
任意方法替换

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. You can safely skip the rest of this section until you actually need this functionality.

查找方法注入相比,方法注入的一个不太有用的形式是用另一个方法实现替换托管 bean 中的任意方法的能力。您可以安全地跳过本节的其余部分,直到实际需要此功能为止(译者注:我确实不需要,包括 1.4.6 这个方法注入,工作中都没用到过。但是我得硬着头皮翻译。。。)。

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, which has a method called computeValue that we want to override:

使用基于 xml 的配置元数据,用于容器中的 bean 您可以使用replaced-method元素来替换现有的方法实现。考虑一下下面的类,它有一个叫做 computeValue 的方法,我们想要重写它:

public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }

    // some other methods...
}

A class that implements the org.springframework.beans.factory.support.MethodReplacer interface provides the new method definition, as the following example shows:

实现 org.springframework.beans.factory.support. MethodReplacer 接口的类提供了新的方法注入方式,如下面的示例所示:

/**
 * meant to be used to override the existing computeValue(String)
 * implementation in MyValueCalculator
 * 用于覆盖 MyValueCalculator 中现有的 computeValue(String)的实现
 */
public class ReplacementComputeValue implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        // 使用输入值,计算并返回结果
        String input = (String) args[0];
        ...
        return ...;
    }
}

The bean definition to deploy the original class and specify the method override would resemble the following example:

部署原始类并指定方法覆盖的 bean 定义类似于下面的示例:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- arbitrary method replacement -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

You can use one or more <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:

java.lang.String
String
Str

Because the number of arguments is often enough to distinguish between each possible choice, this shortcut can save a lot of typing, by letting you type only the shortest string that matches an argument type.

因为参数的数量通常足以区分每个可能的选择,所以这个快捷方式可以节省大量的输入,只允许您键入与参数类型匹配的最短字符串(译者注:净搞这些花里胡哨的)。

1.5. Bean 的作用域

1.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 control the scope of the objects created from a particular bean definition. This approach is powerful and flexible, because 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. The Spring Framework supports six scopes, four of which are available only if you use a web-aware ApplicationContext. You can also create a custom scope.

您不仅可以控制各种依赖关系和配置值,这些依赖关系和配置值将插入从特定 bean 定义创建的对象中,还可以控制从特定 bean 定义创建的对象的作用域。这种方法是强大而灵活的,因为您可以选择通过配置创建的对象的作用域,而不必在 Java 类级别的对象范围内进行操作。可以将 bean 定义为部署在许多作用域中的一个。Spring 框架支持六个作用域,其中四个作用域只有在使用感知 web (web 相关的)的 ApplicationContext 时才可用。您还可以创建自定义范围。

The following table describes the supported scopes:

下表描述了已支持的作用域:

Scope
作用域
Description
描述
singleton(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
(默认值)为每个 Spring IoC 容器将一个 bean 定义作用于一个对象实例。
prototypeScopes a single bean definition to any number of object instances.
将单个 bean 定义作用于任意数量的对象实例。
requestScopes 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.
将单个 bean 定义作用于单个 HTTP 请求的生命周期。也就是说,每个HTTP请求都有自己的Bean实例,这些实例是在单个Bean定义的基础上创建的。仅在感知 web 的 Spring ApplicationContext中有效。
sessionScopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
将单个 bean 定义作用于 HTTP Session的生命周期。仅在感知 web 的 Spring ApplicationContext中有效。
applicationScopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
将单个 bean 定义作用于 ServletContext 的生命周期。仅在感知 web 的 Spring ApplicationContext中有效。
websocketScopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
将一个 bean 定义作为 WebSocket 生命周期的范围。仅在感知 web 的 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 Using a Custom Scope.
在 Spring 3.0中,线程作用域是可用的,但默认情况下不注册。有关更多信息,请参见SimpleThreadScope。有关如何注册此或任何其他自定义范围的说明,请参阅使用自定义作用域.
1.5.1. 单例作用域

1.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 that match that bean definition result in that one specific bean instance being returned by the Spring container.

单例 bean 只有一个共享实例被管理,对所有使用 ID 或 IDs 请求bean时,匹配到的bean 定义,都将导致 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. The following image shows how the singleton scope works:
换句话说,当您定义一个 bean 定义并将其作用域定为单例时,Spring IoC 容器只创建由该 bean 定义定义的对象的一个实例(译者注:这才是人话嘛)。这个单一实例存储在这样的单例 bean 的缓存中,并且对该命名的所有后续的请求和引用都返回缓存的对象。下面的图片展示了单例模式的工作原理:

singleton

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 being per-container and per-bean. This means that, if you define one bean for a particular class in a single Spring container, 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 can define a bean as shown in the following example:
Spring 的单例 bean 概念不同于《四人帮》(GoF)模式书中定义的单例模式。GoF 单例硬编码一个对象的作用域,这样每个 ClassLoader 只创建特定类的一个实例。Spring 单例模式的作用域最好描述为每个容器和每个 bean。这意味着,如果在单个 Spring 容器中为特定类定义一个 bean,那么 Spring 容器将创建由该 bean 定义定义的类的一个且仅有一个实例。单例作用域是 Spring 中的默认作用域。要在 XML 中将 bean 定义为单例,可以定义如下示例所示的 bean:

<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
1.5.2. 原型作用域

1.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, you should use the prototype scope for all stateful beans and the singleton scope for stateless beans.

非单例的原型作用域将导致每次对特定 bean 发出请求时都会创建一个新的 bean 实例。也就是说,bean 被注入到另一个 bean 中,或者您通过容器上的 getBean ()方法调用请求它。通常,您应该为所有有状态 bean 使用原型范围,为无状态 bean 使用单例范围(译者注:不错的建议)。

The following diagram illustrates the Spring prototype scope:

下图说明了 Spring 的原型范围:

prototype

(A data access object (DAO) is not typically configured as a prototype, because a typical DAO does not hold any conversational state. It was easier for us to reuse the core of the singleton diagram.)
(数据访问对象(DAO)通常不配置为原型,因为典型的 DAO 不包含任何会话状态。对我们来说,重用单例图的核心更容易。)

The following example defines a bean as a prototype in XML:
下面的示例将 bean 定义为 XML 中的原型:

<bean id="accountService" class="com.something.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 beans hold. 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 Lifecycle Callbacks.)
在某些方面,Spring 容器在原型作用域的 bean 中的角色是 Java new 的替代品。超过该点的所有生命周期管理都必须由客户机处理。(有关 Spring 容器中 bean 的生命周期的详细信息,请参阅生命周期回调。)

1.5.3. 带有原型bean类型依赖的单例bean

1.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 的唯一实例(译者注:意思是这个原型的依赖就不会变了,下面一句有解释,这个其实在方法注入的章节中已经讨论过了)。

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 instantiates the singleton bean and resolves and injects its dependencies. If you need a new instance of a prototype bean at runtime more than once, see Method Injection

但是,假设您希望单例 bean 在运行时重复获取原型 bean 的新实例。不能将原型 bean 依赖注入到单例 bean 中,因为当 Spring 容器实例化单例 bean 并解析和注入它的依赖项时,注入只发生一次。如果在运行时多次需要原型 bean 的新实例,请参见方法注入

1.5.4. Request, Session, Application, 和 WebSocket 作用域

1.5.4. Request, Session, Application, and WebSocket Scopes

The request, session, application, and websocket scopes are available only 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 that complains about an unknown bean scope is thrown.

request, session, application, 和 websocket作用域只有在使用感知 web 的 Spring ApplicationContext 实现(比如 XmlWebApplicationContext)时才可用。如果将这些作用域与常规 Spring IoC 容器(如 ClassPathXmlApplicationContext)一起使用,则会抛出一个抱怨(译者注:外国人描述的真可爱呀)未知 bean 作用域的 IllegalStateException

初始化web配置

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级别(web 作用域的 bean)对 bean 进行作用域限定,在定义 bean 之前需要进行一些小的初始配置。(标准作用域singletonprototype不需要这种初始设置。)

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, no special setup is necessary. DispatcherServlet already exposes all relevant state.

如果您在 Spring Web MVC 中访问作用域 bean,实际上,一个请求是在 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 by using 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 之外处理请求(例如,在使用 JSFStruts 时) ,则需要注册 org.springframework.web.context.request.RequestContextListener ServletRequestListener。对于 Servlet 3.0 + ,这可以通过使用 WebApplicationInitializer 接口以编程方式完成。或者,对于较旧的容器,将以下声明添加到 web 应用程序的 web.xml 文件中:

<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</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. The following listing shows the filter part of a web application:

或者,如果侦听器设置有问题,可以考虑使用 Spring 的 RequestContextFilter。这个过滤器映射取决于周围的 web 应用程序配置,因此您必须根据需要更改它。下面的清单显示了 web 应用程序的过滤器部分:

<web-app>
    ...
    <filter>
        <filter-name>requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestContextFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    ...
</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.

DispatcherServletRequestContextListenerRequestContextFilter 都做完全相同的事情,即将 HTTP 请求对象绑定到服务该请求的 Thread。这使得请求范围会话范围的 bean 在调用链的下一级可用。

Request 作用域

Request scope

Consider the following XML configuration for a bean definition:

考虑以下 bean 定义的 XML 配置:

<bean id="loginAction" class="com.something.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 do 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.

Spring 容器通过为每个 HTTP 请求使用 loginActionbean 定义来创建 LoginAction bean 的新实例。也就是说,loginAction bean 的作用域在 HTTP 请求级别。您可以随心所欲地更改创建的实例的内部状态,因为从相同的 loginAction bean 定义创建的其他实例看不到这些状态更改。它们是针对个人要求而设计的。当请求完成处理时,request 作用域 bean 被丢弃。

When using annotation-driven components or Java configuration, the @RequestScope annotation can be used to assign a component to the request scope. The following example shows how to do so:

当使用注解驱动的组件或 Java 配置时,可以使用@Requestscope 注解将组件指定为request 范围。下面的例子说明了如何这样做:

@RequestScope
@Component
public class LoginAction {
    // ...
}
Session 作用域

Session Scope

Consider the following XML configuration for a bean definition:

考虑以下 bean 定义的 XML 配置:

<bean id="userPreferences" class="com.something.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.

Spring 容器通过在单个 HTTP Session 的生存期内使用 userPreferences bean 定义创建 UserPreferences bean 的新实例。换句话说,userPreferences bean 有效地限定在 HTTP Session 级别。与使用request 作用域的 bean 一样,您可以随心所欲地更改创建的实例的内部状态,因为您知道同样使用从相同 userPreferences bean 定义创建的实例的其他 HTTP Session 实例不会看到这些状态更改,因为它们是单个 HTTP Session 所特有的。当 HTTP 会话最终被丢弃时,特定 HTTP 会话作用域的 bean 也被丢弃。

When using annotation-driven components or Java configuration, you can use the @SessionScope annotation to assign a component to the session scope.

在使用注解驱动的组件或 Java 配置时,可以使用@SessionScope 注解将组件分配给会话范围。

@SessionScope
@Component
public class UserPreferences {
    // ...
}
Application 作用域

Application Scope

Consider the following XML configuration for a bean definition:

考虑以下 bean 定义的 XML 配置:

<bean id="appPreferences" class="com.something.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 and 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.

Spring 容器为整个 web 应用程序使用一次 appPreferences bean 定义,从而创建 AppPreferences bean 的一个新实例。也就是说,appPreferences bean 的作用域在 ServletContext 级别,并作为常规 ServletContext 属性存储。这有点类似于 Spring 单例 bean,但是在两个重要方面有所不同: 它是每个 ServletContext一个单例,而不是一个 Spring ApplicationContext(ApplicationContext在任何给定的 web 应用程序中都可能有多个) ,它实际上是公开的,因此可以作为一个 ServletContext 属性看到。

When using annotation-driven components or Java configuration, you can use the @ApplicationScope annotation to assign a component to the application scope. The following example shows how to do so:

在使用注解驱动的组件或 Java 配置时,可以使用@ApplicationScope 注解将组件分配给应用程序范围。下面的例子说明了如何这样做:

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}
作用域的 bean 作为依赖

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 容器不仅管理对象(bean)的实例化,还管理协作者(或依赖项)之间的注入。如果您想将一个 HTTP 请求范围的 bean 注入(例如)到另一个长寿命范围的 bean 中,您可以选择注入一个 AOP 代理来代替范围的 bean。也就是说,您需要注入一个代理对象,该代理对象公开与作用域对象相同的公共接口,但也可以从相关作用域中检索真正的目标对象(例如 HTTP 请求) ,并将委托方法调用引用到真正的对象上。

You may also use <aop:scoped-proxy/> between beans that are scoped as singleton, 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 scope prototype, every method call on the shared proxy leads to the creation of a new target instance to which the call is then being forwarded.Also, scoped proxies are not the only way to access beans from shorter scopes in a lifecycle-safe fashion. You may also declare your injection point (that is, the constructor or setter argument or autowired field) as ObjectFactory<MyTargetBean>, allowing for a getObject() call to retrieve the current instance on demand every time it is needed — without holding on to the instance or storing it separately.As an extended variant, you may declare ObjectProvider<MyTargetBean>, which delivers several additional access variants, including getIfAvailable and getIfUnique.The JSR-330 variant of this is called Provider and is used with a Provider<MyTargetBean> declaration and a corresponding get() call for every retrieval attempt. See here for more details on JSR-330 overall.
您还可以在作用域为单例的 bean 之间使用 < aop: scoped-proxy/> ,然后引用通过一个可序列化的中间代理,因此能够在反序列化时重新获得目标单例 bean。
当针对一个原型 bean 声明 < aop: scoped-proxy/> 时,调用共享代理上的每个方法都会创建一个新的目标实例,然后将调用转发到该实例。
而且,作用域代理并不是以生命周期安全的方式从较短的作用域访问 bean 的唯一方法。您还可以将注入点(即构造函数或 setter 参数或自动连接字段)声明为 ObjectFactory < mytargetbean > ,从而允许 getObject ()调用在每次需要时根据需要检索当前实例,而不必保留实例或分别存储它。
作为一个扩展的变体,您可以声明 ObjectProvider < mytargetbean > ,它提供了几个附加的访问变体,包括 getIfAvailablegetIfUnique
这种方法的 JSR-330变体被称为 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:

下面示例中的配置只有一行,但理解其背后的“为什么”和“如何”非常重要:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <!-- 一个 HTTP Session 作用域的 bean 暴露成一个代理 -->
    <bean id="userPreferences" class="com.something.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean -->
        <!-- 指示容器代理包围的bean -->
        <aop:scoped-proxy/> // (1).
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <!-- 一个单例 bean 注入上面的代理 -->
    <bean id="userService" class="com.something.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <!-- 一个代理 userPreferences 的 bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>

(1). The line that defines the proxy. 定义代理的行

To create such a proxy, you insert a child <aop:scoped-proxy/> element into a scoped bean definition (see Choosing the Type of Proxy to Create and XML Schema-based configuration). Why do definitions of beans scoped at the request, session and custom-scope levels require the <aop:scoped-proxy/> element? Consider 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 定义中(请参阅选择代理类型创建基于 XML schema 的配置)。为什么在request, session和自定义作用域级别定义的 bean 范围需要 < aop: scoped-proxy/> 元素?考虑下面的单例 bean 定义,并将其与您需要为前面提到的范围定义的内容进行对比(注意下面的 userPreferences bean 定义是不完整的) :

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

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 is 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 operates only on the exact same userPreferences object (that is, the one with which it was originally injected.

在前面的示例中,单例 bean (userManager)被注入一个 HTTP Session 作用域的的引用(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 the 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, and so forth). 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 is actually 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 时所希望的行为(例如,将一个 HTTPSession作用域的协作 bean 作为依赖注入到单例 bean 中)。相反,您需要一个 userManager 对象,并且在 HTTP Session 的生命周期中,您需要一个特定于 HTTP Session 的 userPreferences 对象。因此,容器创建了一个对象,该对象暴露了与 UserPreferences 类完全相同的公共接口(理想情况下是一个 UserPreferences 实例的对象) ,该对象可以从作用域机制(HTTP 请求、 Session 等)中获取真正的 UserPreferences 对象。容器将这个代理对象注入到 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, as the following example shows:

因此,在将请求会话范围内的 bean 注入到协作对象中时,您需要以下(正确和完整的)配置,如下面的示例所示:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
    <aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
选择要创建的代理类型

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 intercept only public method calls! Do not call non-public methods on such a proxy. They are not 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 affect 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. The following example shows a proxy based on an interface:

或者,您可以通过指定 < aop: scoped-proxy/> 元素的 proxy-target-class 属性值指定 false,配置 Spring 容器为这种作用域 bean 创建标准的基于 JDK 接口的代理。使用基于 JDK 接口的代理意味着在应用程序类路径中不需要额外的库来产生(affect )这种代理。然而,这也意味着作用域 bean 的类必须实现至少一个接口,并且注入作用域 bean 的所有协作者必须通过其中一个接口引用 bean。下面的例子显示了一个基于接口的代理:

<!-- DefaultUserPreferences implements the UserPreferences interface -->
<!-- DefaultUserPreferences 实现了 UserPreferences interface -->
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
    <aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.stuff.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

For more detailed information about choosing class-based or interface-based proxying, see Proxying Mechanisms.

有关选择基于类或基于接口的代理的详细信息,请参阅代理机制

1.5.5. 自定义作用域

1.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 作用域机制是可扩展的。您可以定义自己的作用域,甚至可以重新定义现有的作用域,尽管后者被认为是不好的实践,而且您不能覆盖内置的单例和原型作用域。

创建一个作用域

Creating a Custom Scope

To integrate your custom scopes 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 javadoc, which explains the methods you need to implement in more detail.

想要将你的自定义作用域集成到 Spring 容器中,您需要实现 org.springframework.beans.factory.config.Scope接口,在本节中对其进行了描述。有关如何实现自己的作用域的想法,请参阅 Spring Framework 本身和 Scope javadoc 提供的 Scope实现,后者更详细地解释了需要实现的方法。

The Scope interface has four methods to get objects from the scope, remove them from the scope, and let them be destroyed.

Scope接口有四个方法可以从作用域中获取对象,将它们从作用域中删除,并销毁它们。

The session scope implementation, for example, returns the session-scoped bean (if it does not exist, the method returns a new instance of the bean, after having bound it to the session for future reference). The following method returns the object from the underlying scope:

例如,会话范围实现返回会话范围的 bean (如果它不存在,方法在将 bean 绑定到会话以便将来引用之后返回一个新的 bean 实例)。下面的方法从基础作用域返回对象:

Object get(String name, ObjectFactory<?> objectFactory)

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. The following method removes the object from the underlying scope:

例如,会话范围实现将会话范围内的 bean 从基础会话中移除。应该返回该对象,但是如果找不到具有指定名称的对象,则可以返回 null。下面的方法将对象从基础作用域中移除:

Object remove(String name)

The following method registers a callback that the scope should invoke when it is destroyed or when the specified object in the scope is destroyed:

下面的方法注册了一个回调,当作用域被销毁或作用域中指定的对象被销毁时,作用域应该调用它:

void registerDestructionCallback(String name, Runnable destructionCallback)

See the javadoc or a Spring scope implementation for more information on destruction callbacks.

有关销毁回调的更多信息,请参见 javadoc或 Spring 作用域实现。

The following method obtains the conversation identifier for the underlying scope:

下面的方法获取基础作用域的会话标识符:

String getConversationId()

This identifier is different for each scope. For a session scoped implementation, this identifier can be the session identifier.

此标识符对于每个作用域是不同的。对于会话作用域的实现,此标识符可以是会话标识符。

使用自定义作用域

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 scopes. The following method is the central method to register a new Scope with the Spring container:

在编写和测试一个或多个自定义 Scope 实现之后,您需要让 Spring 容器知道您的新 Scope。下面的方法是用 Spring 容器注册一个新 Scope 的核心方法:

void registerScope(String scopeName, Scope scope);

This method is declared on the ConfigurableBeanFactory interface, which is available through the BeanFactory property on most of the concrete ApplicationContext implementations that ship with Spring.

此方法在 ConfigurableBeanFactory 接口上声明,该接口可通过 Spring 附带的大多数具体 ApplicationContext 实现上的 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 容器本身中这类名称的示例有singletonprototyperegisterScope(..)的第二个参数是您希望注册和使用的自定义 Scope 实现的实际实例。

Suppose that you write your custom Scope implementation, and then register it as shown in the next example.

假设您编写了自定义 Scope 实现,然后按下一个示例中所示的方式注册它。

The next example uses SimpleThreadScope, which is included with Spring but is not registered by default. The instructions would be the same for your own custom Scope implementations.
下一个示例使用SimpleThreadScope,它包含在Spring中,但默认情况下未注册。对于您自己的自定义范围实现,说明将是相同的。
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

You can then create bean definitions that adhere to the scoping rules of your custom Scope, as follows:

然后,您可以创建符合自定义 Scope 的作用域规则的 bean 定义,如下所示:

<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, by using the CustomScopeConfigurer class, as the following example shows:

使用自定义 Scope 实现,您不必局限于编程式注册Scope。您还可以使用 CustomScopeConfigurer 类以声明方式完成范围注册,如下面的示例所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="thread">
                    <bean class="org.springframework.context.support.SimpleThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="thing2" class="x.y.Thing2" scope="thread">
        <property name="name" value="Rick"/>
        <aop:scoped-proxy/>
    </bean>

    <bean id="thing1" class="x.y.Thing1">
        <property name="thing2" ref="thing2"/>
    </bean>

</beans>
When you place <aop:scoped-proxy/> within a <bean> declaration for a FactoryBean implementation, it is the factory bean itself that is scoped, not the object returned from getObject().
当您将<aop:scoped proxy/>放在<bean>声明中实现FactoryBean时,作用域是工厂bean本身,而不是从getObject()返回的对象。

1.6. 定制 Bean 的行为

1.6. Customizing the Nature of a Bean

The Spring Framework provides a number of interfaces you can use to customize the nature of a bean. This section groups them as follows:

Spring 框架提供了许多接口,您可以使用它们来自定义 bean 的性质。本节将它们归类如下:

1.6.1. 生命周期回调

1.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 let the bean perform certain actions upon initialization and destruction of your beans.

要与容器对bean生命周期的管理进行交互,您可以实现 Spring InitializingBeanDisposableBean 接口。容器为前者调用 afterPropertiesSet(),为后者调用 destroy() ,让 bean 在初始化和销毁 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 Using @PostConstruct and @PreDestroy.If you do not want to use the JSR-250 annotations but you still want to remove coupling, consider init-method and destroy-method bean definition metadata.
JSR-250@PostConstruct@PreDestroy 注解通常被认为是在现代 Spring 应用程序中接收生命周期回调的最佳实践。使用这些注释意味着您的 bean 没有耦合到特定于 spring 的接口。有关详细信息,请参阅使用@PostConstruct@PreDestroy
如果您不想使用 JSR-250注解,但是仍然希望删除耦合,那么可以考虑 init-methoddestroy-method bean 定义元数据。

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 by default offer, you can implement a BeanPostProcessor yourself. For more information, see Container Extension Points.

在内部,Spring 框架使用 BeanPostProcessor 实现来处理任何回调接口(它能够找到并调用适当方法)。如果您需要自定义特性或其他 Spring 默认不提供的生命周期行为,您可以自己实现 BeanPostProcessor。有关更多信息,请参见容器扩展点

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 lets a bean perform initialization work after the container has set all necessary properties on the bean. The InitializingBean interface specifies a single method:

org.springframework.beans.factory.InitializingBean 接口允许 bean 在容器已经在 bean 上设置了所有必要的属性之后执行初始化工作。初始化 bean 接口指定了一个方法:

void afterPropertiesSet() throws Exception;

We recommend that you do not use the InitializingBean interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PostConstruct annotation or specifying a POJO initialization method. In the case of XML-based configuration metadata, you can use the init-method attribute to specify the name of the method that has a void no-argument signature. With Java configuration, you can use the initMethod attribute of @Bean. See Receiving Lifecycle Callbacks. Consider the following example:

我们建议您不要使用 InitializingBean 接口,因为它不必要地将代码与 Spring 挂钩。或者,我们建议使用@PostConstruct 注解或指定一个 POJO 的初始化方法。对于基于 xml 的配置元数据,可以使用 init-method 属性指定具有void 无参数的方法的名称。使用 Java 配置,您可以使用@bean 的 initMethod 属性。请参见接收生命周期回调。考虑下面的例子:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {

    public void init() {
        // do some initialization work
    }
}

The preceding example has almost exactly the same effect as the following example (which consists of two listings):

前面的示例与下面的示例(由两个列表组成)具有几乎完全相同的效果:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // do some initialization work
    }
}

However, the first of the two preceding examples does not couple the code to Spring.

但是,前面两个示例中的第一个示例并没有将代码耦合到 Spring。

销毁回调

Destruction Callbacks

Implementing the org.springframework.beans.factory.DisposableBean interface lets a bean get a callback when the container that contains it is destroyed. The DisposableBean interface specifies a single method:

实现 org.springframework.beans.factory.DisposableBean接口允许 bean 在包含它的容器被销毁时获得一个回调。DisposableBean 接口指定了一个方法:

void destroy() throws Exception;

We recommend that you do not use the DisposableBean callback interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PreDestroy annotation or specifying a generic method that is supported by bean definitions. With XML-based configuration metadata, you can use the destroy-method attribute on the <bean/>. With Java configuration, you can use the destroyMethod attribute of @Bean. See Receiving Lifecycle Callbacks. Consider the following definition:

我们建议您不要使用 DisposableBean 回调接口,因为它不必要地将代码耦合到 Spring(译者注:真讨厌,又来!!!)。或者,我们建议使用@PreDestroy 注解或指定 bean 定义支持的通用方法。对于基于 xml 的配置元数据,可以在 < bean/> 上使用 destroy-method 属性。使用 Java 配置,您可以使用@BeandestroyMethod 属性。请参见接收生命周期回调。考虑下面的定义:

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

The preceding definition has almost exactly the same effect as the following definition:

前面的定义与下面的定义具有几乎完全相同的效果:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {

    @Override
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

However, the first of the two preceding definitions does not couple the code to Spring.

但是,前面两个定义中的第一个定义并没有将代码耦合到 Spring。

You can assign the destroy-method attribute of a <bean> element a special (inferred) value, which instructs Spring to automatically detect a public close or shutdown method on the specific bean class. (Any class that implements java.lang.AutoCloseable or java.io.Closeable would therefore match.) You can also set this special (inferred) value on the default-destroy-method attribute of a <beans> element to apply this behavior to an entire set of beans (see Default Initialization and Destroy Methods). Note that this is the default behavior with Java configuration.
您可以为<bean>元素的destroy-method属性指定一个特殊的(推断的)值,该值指示Spring自动检测特定bean类上的公共closeshutdown方法。(因此,任何实现java.lang.AutoCloseablejava.io.Closeable的类都可以匹配。)您还可以在<beans>元素的default-destroy-method属性上设置这个特殊的(推断的)值,以便将此行为应用到整个bean集(请参阅默认初始化和销毁方法)。注意,这是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 特定的 InitializingBeanDisposableBean 回调接口的初始化和销毁方法回调时,通常使用 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”属性。Spring IoC 容器在创建 bean 时(并根据前面描述的标准生命周期回调契约)调用该方法。此功能还强制执行初始化和销毁方法回调的一致命名约定。

Suppose that your initialization callback methods are named init() and your destroy callback methods are named destroy(). Your class then resembles the class in the following example:

假设将初始化回调方法命名为 init () ,并将 destroy 回调方法命名为 destroy ()。然后你的类类似于下面例子中的类:

public class DefaultBlogService implements BlogService {

    private BlogDao blogDao;

    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }

    // this is (unsurprisingly) the initialization callback method
    // 这就是(毫不奇怪的)初始化回调方法
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}

You could then use that class in a bean resembling the following:

然后,您可以在如下所示的Bean中使用该类:

<beans default-init-method="init">

    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>

</beans>

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 the bean class 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 容器将 bean 类上称为 init 的方法识别为初始化方法回调。在创建和组装 bean 时,如果 bean 类有这样的方法,则在适当的时候调用它。

You can 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 属性,以类似的方式配置 destroy 方法回调(即在 XML 中)。

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 by using the init-method and destroy-method attributes of the <bean/> itself.

如果现有的 bean 类已经有按照约定命名的回调方法,那么可以通过使用 < bean/> 本身的 init-methoddestroy-method 属性指定(在 XML 中)方法名称来覆盖缺省值。

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 and 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 to its proxy or interceptors and leave strange semantics when your code interacts directly with the raw target bean.

Spring 容器保证在提供所有依赖项的 bean 之后立即调用已配置的初始化回调。因此,在原始 bean 引用上调用初始化回调,这意味着还没有将 AOP 拦截器等应用到 bean 上。首先完全创建一个目标 bean,然后应用一个带有拦截器链的 AOP 代理(例如)。如果分别定义了目标 bean 和代理,您的代码甚至可以绕过代理与原始目标 bean 交互。因此,将拦截器应用于init方法是不一致的,因为这样做会将目标 bean 的生命周期与其代理拦截器耦合起来,并在代码直接与原始目标bean交互时留下奇怪的语义。

结合生命周期机制

Combining Lifecycle Mechanisms

As of Spring 2.5, you have three options for controlling bean lifecycle behavior:

在 Spring 2.5中,你有有三个方式控制 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 run in the order listed after this note. 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 run 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:

为同一个 bean 配置多个生命周期机制,使用不同的初始化方法,如下所示:

译者注:这个初始化和销毁的顺序,在源码中有体现,可以稍微记一下结论,面试中也可能会问到这种细节。

  1. Methods annotated with @PostConstruct

    使用@PostConstruct 注解的方法

  2. afterPropertiesSet() as defined by the InitializingBean callback interface

    InitializingBean 回调接口定义的 afterPropertiesSet ()

  3. A custom configured init() method

    自定义配置的 init ()方法

Destroy methods are called in the same order:

销毁方法的调用顺序相同:

  1. Methods annotated with @PreDestroy

    @PreDestroy 注释的方法

  2. destroy() as defined by the DisposableBean callback interface

    根据 DisposableBean 回调接口定义 destroy ()

  3. A custom configured destroy() method

    自定义配置的 destroy ()方法

启动和关闭回调

Startup and Shutdown Callbacks

The Lifecycle interface defines the essential methods for any object that has its own lifecycle requirements (such as starting and stopping some background process):

Lifecycle 接口为任何具有自己的生命周期需求的对象定义基本方法(例如启动和停止某个后台流程) :

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

Any Spring-managed object may implement the Lifecycle interface. Then, when the ApplicationContext itself receives start and stop signals (for example, for a stop/restart scenario at runtime), it cascades those calls to all Lifecycle implementations defined within that context. It does this by delegating to a LifecycleProcessor, shown in the following listing:

任何 Spring 管理的对象都可以实现 Lifecycle 接口。然后,当 ApplicationContext 本身接收到启动和停止信号(例如,在运行时的停止/重新启动场景)时,它将这些调用传递给在该上下文中定义的所有 Lifecycle 实现。它通过委托给一个 LifecycleProcessor 来实现这一点,如下面的清单所示:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

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 a plain contract for explicit start and stop notifications and does not imply auto-startup at context refresh time. For fine-grained control over auto-startup of a specific bean (including startup phases), consider implementing org.springframework.context.SmartLifecycle instead.Also, please note that stop notifications are not guaranteed to come before destruction. On regular shutdown, all Lifecycle beans first receive a stop notification before the general destruction callbacks are being propagated. However, on hot refresh during a context’s lifetime or on stopped refresh attempts, only destroy methods are called.
注意,常规的 org.springframework.context.Lifecycle接口是显式启动和停止通知的普通契约,并不意味着在上下文刷新时自动启动。对于细粒度控制特定的 bean(包括启动阶段)的自动启动,可以考虑实现 org.springframework.context.SmartLifecycle
另外,请注意,停止通知并不保证会在销毁之前到来。在通常的关闭时,所有生命周期bean在传播常规销毁回调之前首先收到停止通知。但是,在上下文的生存期内进行热刷新或停止刷新尝试时,只调用destroy方法。

The order of startup and shutdown invocations can be important. If a “depends-on” relationship exists between any two objects, the dependent side starts after its dependency, and it stops 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. The following listing shows the definition of the Phased interface:

启动和关闭调用的顺序可能很重要。如果任意两个对象之间存在“依赖”关系,依赖端在其依赖关系之后开始,在其依赖关系之前停止。但是,有时直接依赖关系是未知的。您可能只知道某种类型的对象应该先于另一种类型的对象开始。在这些情况下,SmartLifecycle 接口定义了另一个选项,即在其超级接口Phased上定义的 getPhase ()方法。以下清单显示了Phased接口的定义:

public interface Phased {

    int getPhase();
}

The following listing shows the definition of the SmartLifecycle interface:

下面的清单显示了 SmartLifecycle 接口的定义:

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}

When starting, the objects with the lowest phase start first. 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 is also important to know that the default phase for any “normal” Lifecycle object that does not implement SmartLifecycle is 0. Therefore, any negative phase value indicates that an object should start before those standard components (and stop after them). The reverse is true for any positive phase value.

开始时,最低阶段的对象首先开始。当停止时,按照相反的顺序。因此,实现 SmartLifecycle 并且其 getPhase ()方法返回 Integer.MIN_VALUE 的对象将首先启动,最后停止。另一方面,Integer.MAX_VALUE 的相位值表示对象应该最后启动并最先停止(可能是因为它依赖于正在运行的其他进程)。在考虑阶段值时,了解任何未实现SmartLifecycle的“正常”生命周期对象的默认阶段为0也很重要。因此,任何负相位值都表明对象应该在这些标准组件之前开始(并在它们之后停止)。对于任何正的相位值,反之亦然。

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, waits 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 want only to modify the timeout, defining the following would suffice:

SmartLifecycle定义的stop方法接受一个回调。任何实现都必须在该实现的关闭过程完成后调用该回调的run()方法。这允许在必要时异步关闭,因为LifecycleProcessor接口的默认实现DefaultLifecycleProcessor会等待每个阶段中的对象组的超时值来调用该回调。默认的每阶段超时时间是30秒。您可以通过在上下文中定义一个名为lifecycleProcessor的bean来覆盖默认的生命周期处理器实例。如果你只想修改超时,定义如下即可:

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- timeout value in milliseconds -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

As mentioned earlier, the LifecycleProcessor interface defines callback methods for the refreshing and closing of the context as well. The latter drives the shutdown process as if stop() had been called explicitly, but it happens 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 is invoked. At that point, the default lifecycle processor checks the boolean value returned by each SmartLifecycle object’s isAutoStartup() method. If true, that object is 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 and any “depends-on” relationships determine the startup order as described earlier.

如前所述,LifecycleProcessor 接口定义了用于刷新和关闭上下文的回调方法。后者驱动关闭进程,就好像已经显式调用了 stop ()一样,但是它发生在上下文关闭时。另一方面,“刷新”回调启用了 SmartLifecycle bean 的另一个特性。当上下文刷新(在所有对象实例化和初始化之后)时,将调用该回调。此时,默认的生命周期处理器检查每个 SmartLifecycle 对象的 isAutoStartup ()方法返回的布尔值。如果为真,那么该对象将在该点启动,而不是等待对上下文的显式调用或其自己的 start ()方法(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。阶段值和任何“依赖”关系决定前面描述的启动顺序。

在非 web 应用程序中优雅地关闭 Spring IoC 容器

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 gracefully shut down the Spring IoC container when the relevant web application is shut down.
本节仅适用于非 web 应用程序。Spring 的基于 web 的 ApplicationContext 实现已经准备好了代码,可以在相关 web 应用程序关闭时优雅地关闭 Spring IoC 容器。

If you use Spring’s IoC container in a non-web application environment (for example, in a rich client desktop environment), 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. You must still configure and implement these destroy callbacks correctly.

如果在非 web 应用程序环境中(例如,在富客户端桌面环境中)使用 Spring 的 IoC 容器,请向 JVM 注册一个 shutdown hook。这样做可以确保优雅地关闭并调用单例 bean 上的相关 destroy 方法,从而释放所有资源。您仍然必须正确地配置和实现这些销毁回调。

To register a shutdown hook, call the registerShutdownHook() method that is declared on the ConfigurableApplicationContext interface, as the following example shows:

要注册一个 shutdown hook,请调用在 ConfigurableApplicationContext 接口上声明的 registershutdown hook ()方法,如下面的示例所示:

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // add a shutdown hook for the above context...
        ctx.registerShutdownHook();

        // app runs here...

        // main method exits, hook is called prior to the app shutting down...
        // 主方法退出,在应用程序关闭之前调用钩子。。。
    }
}
1.6.2. ApplicationContextAwareBeanNameAware

1.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. The following listing shows the definition of the ApplicationContextAware interface:

ApplicationContext 创建一个实现 org.springframework.context.ApplicationContextAware 的对象实例时。在 ApplicationContextAware 接口中,实例提供了对该 ApplicationContext 的引用。下面的列表显示了 ApplicationContextAware 接口的定义:

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

Thus, beans can programmatically manipulate 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 Additional Capabilities of the ApplicationContext.

因此,bean 可以通过 ApplicationContext 接口,或者通过将引用强制转换为该接口的已知子类(例如 ConfigurableApplicationContext,它公开了额外的功能) ,以编程方式操作创建它们的 ApplicationContext。其中一个用途是以编程方式检索其他 bean。有时这种能力是有用的。但是,通常情况下,您应该避免使用它,因为它将代码与 Spring 绑定在一起,而不是遵循控制反转风格,即将协作者作为属性提供给 bean。ApplicationContext 的其他方法提供对文件资源的访问、发布应用程序事件和访问 MessageSource。这些在 ApplicationContext的附加能力中有所描述。

Autowiring is another alternative to obtain a reference to the ApplicationContext. The traditional constructor and byType autowiring modes (as described in Autowiring Collaborators) can provide a dependency of type ApplicationContext for a constructor argument or a setter method parameter, respectively. For more flexibility, including the ability to autowire fields and multiple parameter methods, use the annotation-based autowiring features. If you do, the ApplicationContext is autowired into a field, constructor argument, or method parameter that expects the ApplicationContext type if the field, constructor, or method in question carries the @Autowired annotation. For more information, see Using @Autowired.

自动装配是获取 ApplicationContext 引用的另一种选择。传统的构造函数byType 自动装配模式(如Autowiring Collaborators中所述)可以分别为构造函数参数或 setter 方法参数提供 ApplicationContext 类型的依赖项。为了获得更大的灵活性,包括自动装配字段和多参数方法的能力,请使用基于注解的自动装配特性。如果你这样做,ApplicationContext 就会自动装配到一个字段、构造函数参数或者方法参数中,如果这个字段、构造函数或者方法带有@autowired 注释,那么这个字段、构造函数或者方法就会自动装配到这个字段、构造函数参数或者方法参数中。有关更多信息,请参见 使用 @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. The following listing shows the definition of the BeanNameAware interface:

ApplicationContext 创建一个实现 org.springframework.beans.factory.BeanNameAware 接口的类,该类被提供了对其关联对象定义中定义的名称的引用。下面的清单显示了 BeanNameAware 接口的定义:

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}

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 属性填充之后,在诸如 InitializingBeanafterPropertiesSet 或自定义 init-method 等初始化回调之前调用该回调。(译者注:关于 bean 的生命周期,源码面试题喜欢问)

1.6.3. 其他Aware 接口

1.6.3. Other Aware Interfaces

Besides ApplicationContextAware and BeanNameAware (discussed earlier), Spring offers a wide range of Aware callback interfaces that let beans indicate to the container that they require a certain infrastructure dependency. As a general rule, the name indicates the dependency type. The following table summarizes the most important Aware interfaces:

除了 ApplicationContextAwareBeanNameAware (前面讨论过)之外,Spring 还提供了广泛的 Aware 回调接口,允许 bean 向容器指示它们需要某种基础结构依赖项。作为一般规则,名称表示依赖类型。下表总结了最重要的 Aware 接口:

Name
名称
Injected Dependency
注入的依赖
Explained in…
说明于
ApplicationContextAwareDeclaring ApplicationContext.
声明 ApplicationContext
ApplicationContextAware and BeanNameAware
ApplicationEventPublisherAwareEvent publisher of the enclosing ApplicationContext.
封装的ApplicationContext的事件发布者。
Additional Capabilities of the ApplicationContext
BeanClassLoaderAwareClass loader used to load the bean classes.
用于加载 bean 类的类装入器。
Instantiating Beans
BeanFactoryAwareDeclaring BeanFactory.
声明 BeanFactory
ApplicationContextAware and BeanNameAware
BeanNameAwareName of the declaring bean.
声明 bean 的名称。
ApplicationContextAware and BeanNameAware
LoadTimeWeaverAwareDefined weaver for processing class definition at load time.
定义了编织器,用于在加载时处理类定义。
Load-time Weaving with AspectJ in the Spring Framework
MessageSourceAwareConfigured strategy for resolving messages (with support for parametrization and internationalization).
配置消息解析策略(支持参数化和国际化)。
Additional Capabilities of the ApplicationContext
NotificationPublisherAwareSpring JMX notification publisher.
Spring JMX 通知发布者。
Notifications
ResourceLoaderAwareConfigured loader for low-level access to resources.
配置用于低级别资源访问的加载器。
Resources
ServletConfigAwareCurrent ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext.
容器运行所在的 ServletConfig ,只在感知 web 的 Spring ApplicationContext中有效。
Spring MVC
ServletContextAwareCurrent ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext.
容器运行所在的 ServletContext。仅在感知 web 的 Spring ApplicationContext中有效。
Spring MVC

Note again that using these interfaces ties your code to the Spring API and does not follow the Inversion of Control style. As a result, we recommend them for infrastructure beans that require programmatic access to the container.

请再次注意,使用这些接口将代码与 Spring API 绑定在一起,而不是遵循控制反转的应用程序风格。因此,对于需要以编程方式访问容器的基础结构 bean 时,我们建议使用它们。

1.7. Bean 定义继承

1.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 the initialization method, a 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 定义从父定义继承配置数据(译者注:属性继承这个其实开始已经有提过)。子定义可以重写一些值或根据需要添加其他值。使用父 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, they configure bean definitions declaratively in a class such as the ClassPathXmlApplicationContext. When you use XML-based configuration metadata, you can indicate a child bean definition by using the parent attribute, specifying the parent bean as the value of this attribute. The following example shows how to do so:

如果您以编程方式使用 ApplicationContext 接口,则使用 ChildBeanDefinition 类来表示子 bean 定义。大多数用户不在这个级别上与他们一起工作。相反,它们在类(如 ClassPathXmlApplicationContext)中以声明式的方式配置 bean 定义。当使用基于 xml 的配置元数据时,可以通过使用parent 属性,指定父 bean 为该属性的值。下面的例子说明了如何这样做:

<bean id="inheritedTestBean" abstract="true"
        class="org.springframework.beans.TestBean">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithDifferentClass"
        class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBean" init-method="initialize">  
    <property name="name" value="override"/>
    <!-- the age property value of 1 will be inherited from parent -->
    <!-- age 属性值为1,会从它的 parent 继承过来 -->
</bean>

Note the parent attribute.

注意 parent 属性。

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 类必须与父类兼容(即它必须接受父类的属性值)。

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, or static factory method settings that you specify override the corresponding parent settings.

子 bean 定义从父类继承作用域、构造函数参数值、属性值和方法重写,并带有添加新值的选项。您指定的任何作用域、初始化方法、销毁方法或静态工厂方法设置都将重写相应的父设置。

The remaining settings are always taken from the child definition: depends on, autowire mode, dependency check, singleton, and lazy init.

其余的设置总是从子定义中获取: 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 the following example shows:

前面的示例使用 abstract 属性将父 bean 定义显式标记为 abstract。如果父定义没有指定类属性,则需要显式地将父 bean 定义标记为 abstract,如下面的示例所示:

<bean id="inheritedTestBeanWithoutClass" abstract="true">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBeanWithoutClass" init-method="initialize">
    <property name="name" value="override"/>
    <!-- age will inherit the value of 1 from the parent bean definition-->
</bean>

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, 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 不能自己实例化,因为它是不完整的,并且显式地标记为抽象的。当定义是抽象的时候,它只能作为纯模板 bean 定义使用,纯模板 bean 定义作为子定义的父定义。尝试单独使用这样的抽象父 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 the abstract bean.
默认情况下,ApplicationContext预实例化所有单例。因此,重要的是(至少对于单例bean而言),如果您有一个(父)bean定义,并且该定义仅用作模板,并且该定义指定了一个类,那么必须确保将abstract属性设置为true,否则应用程序上下文将实际(尝试)预实例化abstractbean。

1.8. 容器扩展点

1.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 容器。接下来的几个部分将描述这些集成接口

1.8.1. 使用 BeanPostProcessor定制 bean

1.8.1. Customizing Beans by 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 custom BeanPostProcessor implementations.

BeanPostProcessor 接口定义了回调方法,您可以实现这些方法来提供您自己的(或者覆盖容器的默认)实例化逻辑、依赖解析逻辑等等。如果希望在 Spring 容器完成实例化、配置和初始化 bean 之后实现一些自定义逻辑,可以插入一个或多个自定义 BeanPostProcessor 实现(译者注:这里其实大致透露了 BeanPostProcessor 的执行时机)。

You can configure multiple BeanPostProcessor instances, and you can control the order in which these BeanPostProcessor instances run 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, see the javadoc of the BeanPostProcessor and Ordered interfaces. See also the note on programmatic registration of BeanPostProcessor instances.

您可以配置多个 BeanPostProcessor 实例,并且可以通过设置 order 属性来控制这些 BeanPostProcessor 实例的运行顺序。只有当 BeanPostProcessor 实现 Ordered 接口时,才能设置此属性。如果您编写自己的 BeanPostProcessor,那么也应该考虑实现 Ordered 接口。有关详细信息,请参阅 BeanPostProcessorOrdered接口的 javadoc。还请参阅关于 编程式注册 BeanPostProcessor 实例的说明。

BeanPostProcessor instances operate on bean (or object) instances. That is, the Spring IoC container instantiates a bean instance and then BeanPostProcessor instances do their work.BeanPostProcessor instances are scoped per-container. This is relevant only if you use container hierarchies. If you define a BeanPostProcessor in one container, it post-processes only the beans in that container. In other words, beans that are defined in one container are not post-processed by a BeanPostProcessor defined in another container, even if both containers are part of the same hierarchy.To change the actual bean definition (that is, the blueprint that defines the bean), you instead need to use a BeanFactoryPostProcessor, as described in Customizing Configuration Metadata with a BeanFactoryPostProcessor.
BeanPostProcessor 实例对 bean (或对象)实例进行操作。也就是说,Spring IoC 容器实例化一个 bean 实例,然后 BeanPostProcessor 实例执行它们的工作。
BeanPostProcessor 实例的作用域在每个容器范围内。只有在使用容器层次结构时,这才是相关的。如果您在一个容器中定义了一个 BeanPostProcessor,那么它只后处理该容器中的 bean。换句话说,在一个容器中定义的 bean 不会由在另一个容器中定义的 BeanPostProcessor 进行后处理,即使两个容器都属于相同的层次结构
要更改实际的 bean 定义(即定义 bean 的蓝图) ,您需要使用 BeanFactoryPostProcessor,如使用 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.afterPropertiesSet() or any declared init method) are called, and 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 it 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 ()或任何声明的 init 方法)之前和任何 bean 初始化回调之后都会从容器获得一个回调。后处理器可以对 bean 实例采取任何操作,包括完全忽略回调。Bean 后处理器通常检查回调接口,或者用代理包装 bean。为了提供代理包装逻辑,一些 Spring AOP 基础结构类被实现为 bean 后处理器。

An ApplicationContext automatically detects any beans that are defined in the configuration metadata that implements 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 in the same fashion as any other beans.

ApplicationContext 自动检测在配置元数据中定义的任何实现了 BeanPostProcessor 接口的 bean。ApplicationContext 将这些 bean 注册为后处理器,以便稍后在创建 bean 时调用它们。Bean 后处理器可以像其他 Bean 一样部署在容器中。

Note that, when declaring a BeanPostProcessor by 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 cannot 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 factory 方法==声明 BeanPostProcessor 时,factory 方法的返回类型应该是实现类本身,或者至少是 org.springframework.beans.factory.config.BeanPostProcessor 接口,清楚地表明了 bean 的后处理特性。否则,ApplicationContext 无法在完全创建它之前通过类型自动检测它。由于 BeanPostProcessor 需要在早期实例化,以便应用于上下文中其他 bean 的初始化,因此早期类型检测非常关键。

Programmatically registering BeanPostProcessor instancesWhile the recommended approach for BeanPostProcessor registration is through ApplicationContext auto-detection (as described earlier), you can register them programmatically against a ConfigurableBeanFactory by using the addBeanPostProcessor method. This can be useful when you need to evaluate conditional logic before registration or even for copying bean post processors across contexts in a hierarchy. Note, however, that BeanPostProcessor instances added programmatically do not respect the Ordered interface. Here, it is the order of registration that dictates the order of execution. Note also that BeanPostProcessor instances registered programmatically are always processed before those registered through auto-detection, regardless of any explicit ordering.
虽然BeanPostProcessor的推荐注册方式是通过ApplicationContext自动检测(如前所述),但您可以使用addBeanPostProcessor方法以编程方式对ConfigurableBeanFactory注册它们。当您需要在注册前计算条件逻辑时,或者在层次结构中跨上下文复制bean post处理器时,这是非常有用的。但是请注意,以编程方式添加的BeanPostProcessor实例不遵循Ordered接口。在这里,注册的顺序决定了执行的顺序。还请注意,通过编程注册的BeanPostProcessor实例总是在通过自动检测注册的实例之前被处理,无论任何显式的顺序。
BeanPostProcessor instances and AOP auto-proxyingClasses that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessor instances and beans that they directly reference are instantiated on startup, as part of the special startup phase of the ApplicationContext. Next, all BeanPostProcessor instances are registered in a sorted fashion and applied to all further beans in the container. Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessor instances nor the beans they directly reference 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 someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying).If you have beans wired into your BeanPostProcessor by 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 the field or setter name does not directly correspond to the declared name of a bean and no name attribute is used, Spring accesses other beans for matching them by type.
实现BeanPostProcessor接口的BeanPostProcessor实例和AOP自动代理类是特殊的,容器对它们的处理是不同的。所有BeanPostProcessor实例和它们直接引用的bean都是在启动时实例化的,作为ApplicationContext特殊启动阶段的一部分。接下来,将以排序方式注册所有BeanPostProcessor实例,并将其应用到容器中的所有其他bean。因为AOP的自动代理是作为BeanPostProcessor本身实现的,所以无论是BeanPostProcessor实例还是它们直接引用的bean都不符合自动代理的条件,因此没有将方面编织到它们之中。对于任何这样的bean,您都应该看到一条信息日志消息:Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)。如果通过使用自动装配@Resource(可能会退回到自动装配)将bean连接到BeanPostProcessor中,那么Spring在搜索类型匹配依赖项候选项时可能会访问意外的bean,从而使它们不适合自动代理或其他类型的bean后处理。例如,如果您有一个用@Resource注解的依赖项,其中字段或setter名称与声明的bean名称不直接对应,并且没有使用name属性,那么Spring将访问其他bean以按类型匹配它们。

The following examples show how to write, register, and use BeanPostProcessor instances in an ApplicationContext.

下面的示例演示如何在 ApplicationContext中编写、注册和使用 BeanPostProcessor 实例。

Example: Hello World, BeanPostProcessor-style

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 时调用每个 bean 的 toString ()方法,并将生成的字符串打印到系统控制台。

The following listing shows the custom BeanPostProcessor implementation class definition:

下面的清单显示了定制的 BeanPostProcessor 实现类定义:

package scripting;

import org.springframework.beans.factory.config.BeanPostProcessor;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

    // simply return the instantiated bean as-is
    // 简单的返回实例本身
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean; // we could potentially return any object reference here...
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }
}

The following beans element uses the InstantiationTracingBeanPostProcessor:

下面的 beans 元素使用 InstantiationTracingBeanPostProcessor:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/lang
        https://www.springframework.org/schema/lang/spring-lang.xsd">

    <lang:groovy id="messenger"
            script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
        <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
    </lang:groovy>

    <!--
    when the above bean (messenger) is instantiated, this custom
    BeanPostProcessor implementation will output the fact to the system console
	当上面的 bean (message) 被实例化了,自定义的 beanPostProcessor 会输出内容到系统控制台
    -->
    <bean class="scripting.InstantiationTracingBeanPostProcessor"/>

</beans>

Notice how the InstantiationTracingBeanPostProcessor is merely defined. It does not even have a name, and, because it is a bean, it can be dependency-injected as you would 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 Dynamic Language Support.)

注意InstantiationTracingBeanPostProcessor 是如何定义的。它甚至没有名称,因为它是一个 bean,所以它可以像其他 bean 一样被依赖注入。(前面的配置还定义了一个由 Groovy 脚本支持的 bean。Spring 为“动态语言支持”在动态语言支持的章节中有详细介绍。)

The following Java application runs the preceding code and configuration:

下面的 Java 应用程序运行前面的代码和配置:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
        Messenger messenger = ctx.getBean("messenger", Messenger.class);
        System.out.println(messenger);
    }

}

The output of the preceding application resembles the following:

上面的应用输出内容类似如下:

Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
Example: AutowiredAnnotationBeanPostProcessor

Example: The AutowiredAnnotationBeanPostProcessor

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 AutowiredAnnotationBeanPostProcessor — a BeanPostProcessor implementation that ships with the Spring distribution and autowires annotated fields, setter methods, and arbitrary config methods.

结合自定义 BeanPostProcessor 实现使用回调接口或注解是扩展 Spring IoC 容器的常用方法。一个例子是 Spring 的 AutowiredAnnotationBeanPostProcessor ー一个 BeanPostProcessor 实现,它附带在 Spring 发行版并且负责装配注解的字段、 setter 方法和任意配置方法

1.8.2. 使用 BeanFactoryPostProcessor定制配置元数据

1.8.2. Customizing Configuration Metadata with a BeanFactoryPostProcessor

The next extension point that we 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 lets a BeanFactoryPostProcessor read the configuration metadata and potentially change it before the container instantiates any beans other than BeanFactoryPostProcessor instances.

我们看到的下一个扩展点是 org.springframework.beans.factory.config.BeanFactoryPostProcessor。此接口的语义类似于 BeanPostProcessor,但有一个主要区别: BeanFactoryPostProcessor对 bean 配置元数据进行操作==(译者注:面试中被问到过,当时没注意这个点)。也就是说,Spring IoC 容器允许BeanFactoryPostProcessor在容器实例化除BeanFactoryPostProcessor` 实例以外的任何 bean 之前读取配置元数据并可能更改它==。

You can configure multiple BeanFactoryPostProcessor instances, and you can control the order in which these BeanFactoryPostProcessor instances run 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. See the javadoc of the BeanFactoryPostProcessor and Ordered interfaces for more details.

您可以配置多个 BeanFactoryPostProcessor 实例,并且可以通过设置 order 属性来控制这些 BeanFactoryPostProcessor 实例的运行顺序。但是,只有在 BeanFactoryPostProcessor 实现 Ordered 接口时,才能设置此属性。如果您编写自己的 BeanFactoryPostProcessor,那么也应该考虑实现 Ordered 接口。有关详细信息,请参阅 BeanFactoryPostProcessorOrdered 接口的 javadoc。

If you want to change the actual bean instances (that is, the objects that are created from the configuration metadata), then you instead need to use a BeanPostProcessor (described earlier in Customizing Beans by Using a BeanPostProcessor). While it is technically possible to work with bean instances within a BeanFactoryPostProcessor (for example, by using BeanFactory.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, BeanFactoryPostProcessor instances are scoped per-container. This is only relevant if you use container hierarchies. If you define a BeanFactoryPostProcessor in one container, it is applied only to the bean definitions in that container. Bean definitions in one container are not post-processed by BeanFactoryPostProcessor instances in another container, even if both containers are part of the same hierarchy.
如果您想要更改实际的 bean 实例(即从配置元数据创建的对象) ,那么您需要使用 BeanPostProcessor (前面在使用 BeanPostProcessor 自定义 Beans 中描述过)。虽然在技术上可以在 BeanFactoryPostProcessor 中使用 bean 实例(例如,使用 BeanFactory.getBean ()) ,但这样做会导致 bean 提前实例化,从而违反标准容器生命周期。这可能会导致负面的副作用,例如绕过 bean 的后期处理。
此外,BeanFactoryPostProcessor 实例的作用域为每个容器。只有在使用容器层次结构时,这才是相关的。如果在一个容器中定义 BeanFactoryPostProcessor,则它只应用于该容器中的 bean 定义。一个容器中的 Bean 定义不会由另一个容器中的 BeanFactoryPostProcessor 实例进行后处理,即使两个容器都属于同一层次结构。

A bean factory post-processor is automatically run 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 PropertySourcesPlaceholderConfigurer. You can also use a custom BeanFactoryPostProcessor — for example, to register custom property editors.

Bean 工厂后处理器在 ApplicationContext 中声明时自动运行,以便对定义容器的配置元数据应用更改。Spring 包括许多预定义的 bean 工厂后处理器,例如 PropertyOverrideConfigurerpropertysourceplaceholderconfigureer。您还可以使用自定义 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。它在适当的时候使用这些 bean 作为 bean 工厂的后处理器。您可以像部署其他 bean 一样部署这些后处理 bean。

As with BeanPostProcessors , you typically do not want to configure BeanFactoryPostProcessors for lazy initialization. If no other bean references a Bean(Factory)PostProcessor, that post-processor will not get instantiated at all. Thus, marking it for lazy initialization will be ignored, and the Bean(Factory)PostProcessor will be instantiated eagerly even if you set the default-lazy-init attribute to true on the declaration of your <beans /> element.
BeanPostProcessors一样,您通常不希望为BeanFactoryPostProcessors配置成延迟初始化。如果没有其他bean引用Bean(Factory)PostProcessor,那么该后处理器将根本不会被实例化。因此,将其标记为延迟初始化将被忽略,并且Bean(Factory)PostProcessor将被急切地实例化,即使在元素的声明中将默认的lazy init属性设置为true。
Example: 类名替换PropertySourcesPlaceholderConfigurer

Example: The Class Name Substitution PropertySourcesPlaceholderConfigurer

You can use the PropertySourcesPlaceholderConfigurer to externalize property values from a bean definition in a separate file by 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.

可以使用标准的 Java Properties 格式,使用 PropertySourcesPlaceholderConfigurer 通过一个单独的文件中的 bean 定义实现配置外部化。这样做可以使部署应用程序的人员自定义特定于环境的属性,如数据库 url 和密码,而无需复杂性或风险性的修改容器的主 XML 定义文件或文件。

Consider the following XML-based configuration metadata fragment, where a DataSource with placeholder values is defined:

考虑以下基于 xml 的配置元数据片段,其中定义了带有占位符值的 DataSource:

<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

The example shows properties configured from an external Properties file. At runtime, a PropertySourcesPlaceholderConfigurer is applied to the metadata that replaces some properties of the DataSource. The values to replace are specified as placeholders of the form ${property-name}, which follows the Ant and log4j and JSP EL style.

该示例显示从外部 Properties 文件配置的属性。在运行时,PropertySourcesPlaceholderConfigurer被应用于替换 DataSource 的某些属性的元数据。要替换的值被指定为形式为 ${property-name}的占位符,它遵循 Ant 和 log4j 以及 JSP EL 样式。

The actual values come from another file in the standard Java Properties format:

实际值来自另一个标准 Java Properties 格式的文件:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

Therefore, the ${jdbc.username} string is replaced at runtime with the value, ‘sa’, and the same applies for other placeholder values that match keys in the properties file. The PropertySourcesPlaceholderConfigurer checks for placeholders in most properties and attributes of a bean definition. Furthermore, you can customize the placeholder prefix and suffix.

因此,在运行时将 ${jdbc.username}字符串替换为值 ‘sa’,这同样适用于与属性文件中的键匹配的其他占位符值。PropertySourcesPlaceholderConfigurer 检查 bean 定义的大多数属性的占位符。此外,您还可以自定义占位符前缀和后缀。

With the context namespace introduced in Spring 2.5, you can configure property placeholders with a dedicated configuration element. You can provide one or more locations as a comma-separated list in the location attribute, as the following example shows:

使用 Spring 2.5中引入的==context 命名空间==,您可以使用专用的配置元素配置属性占位符。可以在 location 属性中以逗号分隔的列表形式提供一个或多个位置,如下面的示例所示:

<context:property-placeholder location="classpath:com/something/jdbc.properties"/>

The PropertySourcesPlaceholderConfigurer not only looks for properties in the Properties file you specify. By default, if it cannot find a property in the specified properties files, it checks against Spring Environment properties and regular Java System properties.

PropertySourcesPlaceholderConfigurer 不仅在您指定的 Properties 文件中查找属性。默认情况下,如果它无法在指定的属性文件中找到属性,它将检查 Spring Environment 属性和常规的 Java System属性。

You can use the PropertySourcesPlaceholderConfigurer to substitute class names, which is sometimes useful when you have to pick a particular implementation class at runtime. The following example shows how to do so:

您可以使用 PropertySourcesPlaceholderConfigurer 来替换类名,当您必须在运行时选择特定的实现类时(译者注:骚还是你骚呀!!!),这有时很有用。下面的例子说明了如何这样做:

<bean class="org.springframework.beans.factory.config.PropertySourcesPlaceholderConfigurer">
    <property name="locations">
        <value>classpath:com/something/strategy.properties</value>
    </property>
    <property name="properties">
        <value>custom.strategy.class=com.something.DefaultStrategy</value>
    </property>
</bean>

<bean id="serviceStrategy" class="${custom.strategy.class}"/>

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 an ApplicationContext for a non-lazy-init bean.

如果类不能在运行时解析为有效的类,那么在创建 bean 时,bean 的解析将失败,这是在为 ApplicationContext 非懒加载初始化 bean 的 preInstantiateSingletons()阶段(译者注:可以根据这个提示,关注下源码哦)。

Example: PropertyOverrideConfigurer

Example: The PropertyOverrideConfigurer

The PropertyOverrideConfigurer, another bean factory post-processor, resembles the PropertySourcesPlaceholderConfigurer, 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.

另一个 bean 工厂后处理器 PropertyOverrideConfigurer 类似于 PropertySourcesPlaceholderConfigurer,但与后者不同,原始定义可以有 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 the following format:

属性文件配置行采用以下格式:

beanName.property=value

The following listing shows an example of the format:

下面的清单显示了这种格式的一个例子:

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb

This example file can be used with a container definition that contains a bean called dataSource that has driver and url properties.

此示例文件可与容器定义一起使用,该容器定义包含一个名为 dataSource 的 bean,该 bean 具有driverurl 属性。

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 the following example, the sammy property of the bob property of the fred property of the tom bean is set to the scalar value 123:

也支持复合属性名,只要路径中除最后被重写的属性以外的每个组件都已经非空(可能由构造函数初始化)。在下面的示例中,tom bean 的 fred 属性的 bob 属性的 sammy 属性被设置为标量值123:

tom.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, as the following example shows:

用 Spring 2.5中引入的 context 命名空间,可以使用专用的配置元素配置重写属性,如下面的示例所示:

<context:property-override location="classpath:override.properties"/>
1.8.3. 使用 FactoryBean定制实例化逻辑

1.8.3. Customizing Instantiation Logic with a FactoryBean

译者注:FactoryBean 的概念和用途挺重要的哈。

You can implement the org.springframework.beans.factory.FactoryBean interface for objects that are themselves factories.

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.

您可以为本身就是工厂的对象实现 org.springframework.beans.factory.FactoryBean接口。

FactoryBean 接口是 Spring IoC 容器的实例化逻辑的可插入点。如果您有复杂的初始化代码,这些代码更好地用 Java 表示,而不是(可能)冗长的 XML,那么您可以创建自己的 FactoryBean,在该类中编写复杂的初始化,然后将自定义的 FactoryBean 插入容器中。

The FactoryBean<T> interface provides three methods:

FactoryBean 接口提供三种方法:

  • T 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.

    T getObject() : 返回此工厂创建的对象的实例。实例可以共享,这取决于该工厂返回的是单件还是原型。

  • boolean isSingleton(): Returns true if this FactoryBean returns singletons or false otherwise. The default implementation of this method returns true.

    boolean isSingleton(): 如果这个 FactoryBean 返回单例,则返回 true,否则返回 false。

  • Class<?> getObjectType(): Returns the object type returned by the getObject() method or null if the type is not known in advance.

    Class<?> getObjectType(): 返回 getObject() 方法返回的对象类型,如果事先不知道该类型,则返回 null

The FactoryBean concept and interface are 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, prefix 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 时,在调用 ApplicationContextgetBean ()方法时,用 & 符号作为 beanid 的前缀。因此,对于 idmyBean 的给定 FactoryBean,调用容器上的 getBean (“ myBean”)返回 FactoryBean 产生的对象,而调用 getBean("&myBean")返回 FactoryBean 实例本身。(译者注:记一下 FactoryBean 的这个特点哈,可以简单理解 FactoryBean 就是为了生蛋的鸡)

1.9. 基于注解的容器配置

1.9. Annotation-based Container Configuration

Are annotations better than XML for configuring Spring?

配置 Spring 方面,使用注解优于 XML 吗?

The introduction of annotation-based configuration 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.

基于注解的配置的引入产生了这种方式是否比 XML“更好”的问题。简而言之,答案是“视情况而定”。长的答案是,每种方法都有其优点和缺点,通常由开发人员决定哪种策略更适合他们。由于它们的定义方式,注解在其声明中提供了大量上下文,从而使配置更简洁。然而,XML 擅长将组件连接起来而不触及它们的源代码或重新编译它们。一些开发人员倾向于让连接接近源代码,而其他人则认为带注解的类不再是 pojo,而且配置变得分散且难以控制。

No matter the choice, Spring can accommodate both styles and even mix them together. It is worth pointing out that through its JavaConfig option, Spring lets annotations 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 Tools for Eclipse.

无论选择什么,Spring 都可以接纳这两种风格,甚至可以将它们混合在一起。值得指出的是,通过其 JavaConfig 选项,Spring 允许以非侵入性的方式使用注解,而不涉及目标组件的源代码,而且,在工具方面,Spring Tools for Eclipse 支持所有配置样式。

An alternative to XML setup is provided by annotation-based configuration, which relies 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 Example: The AutowiredAnnotationBeanPostProcessor, 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 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 AutowiredAnnotationBeanPostProcessor提到的,结合使用 BeanPostProcessor 和注解是扩展 Spring IoC 容器的常用方法。例如,Spring 2.0引入了使用@Required 注解强制所需属性的可能性。Spring 2.5 使得使用同样的方法来驱动 Spring 的依赖注入成为可能。从本质上讲,@Autowired注释提供了与 Autowiring Collaborators 相同的功能,但具有更细粒度的控制和更广泛的适用性(译者注:这是一个不错的结论,值得记忆一下,因为即使你钻到源码中,也很难得到这个结论)。Spring 2.5 还增加了对 JSR-250注解的支持,比如@PostConstruct@PreDestroy。Spring 3.0 增加了对 javax.inject 包中包含的 JSR-330(Java 依赖注入)注释的支持,比如@Inject@Named。关于这些注解的详细信息可以在 相关章节中找到。

Annotation injection is performed before XML injection. Thus, the XML configuration overrides the annotations for properties wired through both approaches.
注解注入在 XML 注入之前执行,因此,通过两种方式配置的画,XML 配置会覆通过注解配置的属性。

As always, you can register the post-processors 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 配置中包含以下标记来隐式注册它们(注意包含context 名称空间) :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

The <context:annotation-config/> element implicitly registers the following post-processors:

<context:annotation-config/>元素隐式地注册以下后处理器:

<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 a WebApplicationContext for a DispatcherServlet, it only checks for @Autowired beans in your controllers, and not your services. See The DispatcherServlet for more information.
<context:annotation-config/> 只在
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值