返回

Spring 框架的核心技术

发布时间:2023-11-16 23:52:37 203

参考文档的这一部分涵盖了以下所有技术: 绝对是 Spring 框架不可或缺的一部分。

其中最重要的是Spring Framework的控制反转(IoC)容器。 对 Spring 框架的 IoC 容器进行彻底处理之后是 全面覆盖 Spring 面向方面的编程 (AOP) 技术。 Spring 框架有自己的 AOP 框架,在概念上很容易 了解并成功满足 AOP 要求的 80% 最佳点 在 Java 企业编程中。

Spring与AspectJ整合的报道(目前最富有 - 就 功能 — 当然是 Java 企业领域最成熟的 AOP 实现) 也提供了。

Spring 框架的核心技术_xml

1. 国际奥委会容器

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

1.1. 春季 IoC 容器和豆类简介

本章介绍控制反转的 Spring 框架实现 (国际奥委会)原则。IoC 也称为依赖注入 (DI)。这是一个过程,其中 对象仅通过定义其依赖项(即它们使用的其他对象) 构造函数参数、工厂方法的参数或在 对象实例在构造或从工厂方法返回后。容器 然后在创建 Bean 时注入这些依赖项。这个过程从根本上说是 bean本身的逆(因此得名,控制反转) 使用直接控制其依赖项的实例化或位置 类或机制(如服务定位器模式)的构造。

和包是基础 对于 Spring Framework 的 IoC 容器。BeanFactory界面提供了一种高级配置机制,能够管理任何类型的 对象。ApplicationContext是的子接口。它补充说:​​org.springframework.beans​​​​org.springframework.context​​​​BeanFactory​

  • 更容易与 Spring 的 AOP 功能集成
  • 消息资源处理(用于国际化)
  • 事件发布
  • 特定于应用程序层的上下文,例如用于 Web 应用程序。WebApplicationContext

简而言之,提供了配置框架和基本功能, 并添加了更多特定于企业的功能。这是 theand 的完整超集,专门使用 在本章中对 Spring 的 IoC 容器的描述。有关使用 而不是参见介绍BeanFactoryAPI 的部分。​​BeanFactory​​​​ApplicationContext​​​​ApplicationContext​​​​BeanFactory​​​​BeanFactory​​​​ApplicationContext,​

在 Spring 中,构成应用程序主干并被管理的对象 由Spring IoC容器称为bean。豆子是一个对象 由Spring IoC容器实例化,组装和管理。否则,一个 Bean 只是应用程序中的众多对象之一。Bean 和依赖项 其中,反映在容器使用的配置元数据中。

1.2. 容器概述

该接口代表春季 IoC 容器,负责实例化、配置和组装 豆。容器获取有关要处理哪些对象的指令 通过读取配置元数据来实例化、配置和组装。这 配置元数据以 XML、Java 注释或 Java 代码表示。它让 您表示组成应用程序的对象和丰富的相互依赖关系 在这些对象之间。​​org.springframework.context.ApplicationContext​

提供了接口的几种实现 与春天。在独立应用程序中,通常创建一个 ClassPathXmlApplicationContext 或FileSystemXmlApplicationContext的实例。 虽然 XML 一直是定义配置元数据的传统格式,但您可以 指示容器使用 Java 注释或代码作为元数据格式 提供少量 XML 配置,以声明方式启用对这些配置的支持 其他元数据格式。​​ApplicationContext​

在大多数应用程序场景中,不需要显式用户代码来实例化一个或 Spring IoC 容器的更多实例。例如,在 Web 应用程序方案中,一个 文件中简单的八行(左右)样板 Web 描述符 XML 的应用程序通常就足够了(请参阅Web 应用程序的便捷应用程序上下文实例化)。如果您使用 Spring Tools for Eclipse(Eclipse 驱动的开发) 环境),您可以通过单击几下鼠标轻松创建此样板配置或 击 键。​​web.xml​

下图显示了 Spring 工作原理的高级视图。您的应用程序类 与配置元数据相结合,以便在 已创建并初始化,您具有完全配置和可执行的系统,或者 应用。​​ApplicationContext​

Spring 框架的核心技术_spring_02

图1.春季 IoC 容器

1.2.1. 配置元数据

如上图所示,Spring IoC 容器使用一种形式 配置元数据。此配置元数据表示您作为 应用程序开发人员,告诉 Spring 容器实例化、配置和组装 应用程序中的对象。

传统上,配置元数据以简单直观的 XML 格式提供, 这是本章大部分内容用来传达关键概念和功能的内容 春季 IoC 容器。

有关将其他形式的元数据与 Spring 容器一起使用的信息,请参阅:

  • 基于注释的配置:引入 Spring 2.5 支持基于注释的配置元数据。
  • 基于 Java 的配置:从 Spring 3.0 开始,许多功能 由Spring JavaConfig项目提供,成为核心Spring框架的一部分。 因此,您可以使用 Java 来定义应用程序类外部的 bean。 而不是 XML 文件。若要使用这些新功能,请参阅@Configuration,@Bean,@Import, 和@DependsOn注释。

弹簧配置由至少一个(通常多个)bean组成 容器必须管理的定义。基于 XML 的配置元数据配置这些 bean 作为顶级元素中的元素。爪哇岛 配置通常在类中使用带注释的方法。​​​​​​@Bean​​​​@Configuration​

这些 Bean 定义对应于构成应用程序的实际对象。 通常,您可以定义服务层对象、数据访问对象 (DAO)、表示 对象(如 Strutsinstances)、基础设施对象(如 Hibernate、JMS 等)。通常,不配置 容器中的细粒度域对象,因为它通常是责任 的 DAO 和业务逻辑来创建和加载域对象。但是,您可以使用 Spring 与 AspectJ 的集成,用于配置在外部创建的对象 IoC 容器的控制。请参阅使用AspectJ 使用 Spring 注入依赖关系的域对象。​​Action​​​​SessionFactories​​​​Queues​

以下示例显示了基于 XML 的配置元数据的基本结构:


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">











属性的值是指协作对象。的 XML 此示例中未显示引用协作对象。有关详细信息,请参阅依赖项。​​id​

1.2.2. 实例化容器

一个或多个位置路径 提供给构造函数的是资源字符串,这些资源字符串允许 来自各种外部资源的容器加载配置元数据,例如 作为本地文件系统、Java 等。​​ApplicationContext​​​​CLASSPATH​

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

以下示例显示了服务层对象配置文件:​​(services.xml)​


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">











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


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">

class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">









在前面的示例中,服务层由类组成 和两个类型的数据访问对象和(基于 在 JPA 对象关系映射标准上)。元素是指 JavaBean 属性的名称,并且该元素引用另一个 Bean 的名称 定义。元素之间的这种联系表达了元素之间的依赖关系 协作对象。有关配置对象依赖关系的详细信息,请参阅依赖关系。​​PetStoreServiceImpl​​​​JpaAccountDao​​​​JpaItemDao​​​​property name​​​​ref​​​​id​​​​ref​

编写基于 XML 的配置元数据

让 Bean 定义跨越多个 XML 文件可能很有用。通常,每个人 XML 配置文件表示体系结构中的逻辑层或模块。

您可以使用应用程序上下文构造函数从所有这些中加载 Bean 定义 XML 片段。此构造函数采用多个位置,如上一节所示。或者,使用一个或多个 出现元素以从另一个文件加载 Bean 定义或 文件。以下示例演示如何执行此操作:​​Resource​​​








在前面的示例中,外部 Bean 定义是从三个文件加载的:、 和。所有位置路径都是 相对于执行导入的定义文件,SO 必须在 与执行导入的文件相同的目录或类路径位置,而 AND必须位于 导入文件的位置。如您所见,前导斜杠将被忽略。然而,鉴于 这些路径是相对的,最好不要使用斜杠。这 要导入的文件的内容(包括顶级元素)必须 根据 Spring 模式,是有效的 XML Bean 定义。​​services.xml​​​​messageSource.xml​​​​themeSource.xml​​​​services.xml​​​​messageSource.xml​​​​themeSource.xml​​​​resources​​​

命名空间本身提供导入指令功能。进一步 选项中提供了除普通 Bean 定义之外的配置功能 Spring 提供的 XML 命名空间,例如 theandnamespaces。​​context​​​​util​

时髦的豆定义DSL

作为外部化配置元数据的进一步示例,Bean 定义还可以 在 Spring 的 Groovy Bean Definition DSL 中表达,从 Grails 框架中已知。 通常,此类配置位于“.groovy”文件中,其结构显示在 以下示例:

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

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

1.2.3. 使用容器

这是能够维护的先进工厂的接口 不同 bean 及其依赖项的注册表。通过使用该方法,您可以检索 Bean 的实例。​​ApplicationContext​​​​T getBean(String name, Class requiredType)​

允许您读取 Bean 定义并访问它们,如下所示 示例显示:​​ApplicationContext​

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

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

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

使用 Groovy 配置,引导看起来非常相似。它有不同的背景 实现类是Groovy感知的(但也理解XML Bean定义)。 以下示例显示了 Groovy 配置:

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

最灵活的变体是与阅读器结合使用 委托 — 例如,withfor XML 文件,如下所示 示例显示:​​GenericApplicationContext​​​​XmlBeanDefinitionReader​

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

您也可以使用 for Groovy 文件,如下所示 示例显示:​​GroovyBeanDefinitionReader​

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

您可以在同一上混合和匹配此类读者委托, 从不同的配置源读取 Bean 定义。​​ApplicationContext​

然后,您可以使用 来检索 Bean 的实例。Theinterface还有其他一些检索bean的方法,但是,理想情况下,您的应用程序 代码永远不应该使用它们。事实上,你的应用程序代码根本不应该调用该方法,因此根本不依赖于Spring API。例如 Spring与Web框架的集成为各种Web提供了依赖注入 框架组件,如控制器和 JSF 管理的 bean,允许您声明 通过元数据(例如自动布线注释)对特定 Bean 的依赖关系。​​getBean​​​​ApplicationContext​​​​getBean()​

1.3. Bean 概述

Spring IoC 容器管理一个或多个 bean。这些豆子是用 提供给容器的配置元数据(例如,以 XML 定义的形式)。​

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

  • 包限定类名:通常为 正在定义的豆子。
  • Bean 行为配置元素,说明 Bean 在 容器(范围、生命周期回调等)。
  • 对 Bean 完成其工作所需的其他 Bean 的引用。这些 引用也称为协作者或依赖项。
  • 要在新创建的对象中设置的其他配置设置,例如,大小 池的限制或要在管理 连接池。

此元数据转换为构成每个 Bean 定义的一组属性。 下表描述了这些属性:

Table 1. The bean definition

财产

解释在...

实例化 Bean

名字

命名豆类

范围

Bean Scopes

构造函数参数

依赖注入

性能

依赖注入

自动接线模式

自动布线协作者

延迟初始化模式

延迟初始化的 Bean

初始化方法

初始化回调

销毁方法

销毁回调

除了包含有关如何创建特定 Bean,实现还允许注册现有的 在容器外部创建的对象(由用户创建)。这是通过访问 ApplicationContext'sthrough themethod,它返回 实现支持。 此注册通过方法和方法。但是,典型的应用程序仅适用于通过常规定义的 bean Bean 定义元数据。​​ApplicationContext​​​​BeanFactory​​​​getBeanFactory()​​​​DefaultListableBeanFactory​​​​DefaultListableBeanFactory​​​​registerSingleton(..)​​​​registerBeanDefinition(..)​

1.3.1. 命名 Bean

每个 Bean 都有一个或多个标识符。这些标识符在 承载 Bean 的容器。一个 Bean 通常只有一个标识符。但是,如果它 需要多个,多余的可以被视为别名。

在基于 XML 的配置元数据中,您可以使用属性、属性或 两者都用于指定 Bean 标识符。属性允许您指定 正好一个ID。按照惯例,这些名称是字母数字(“myBean”, “someService”等),但它们也可以包含特殊字符。如果你愿意 为 Bean 引入其他别名,也可以在属性中指定它们,用逗号 ()、分号 () 或空格分隔。作为一个 历史注释,在Spring 3.1之前的版本中,属性是 定义为 antype,它约束了可能的字符。从 3.1 开始, 它被定义为 Antype。请注意,豆子唯一性仍然是 由容器强制执行,但不再由 XML 解析器强制执行。​​id​​​​name​​​​id​​​​name​​​​,​​​​;​​​​id​​​​xsd:ID​​​​xsd:string​​​​id​

您不需要为豆子提供或提供。如果不显式提供主动脉,容器将为该 Bean 生成唯一的名称。然而 如果你想用名字来引用那个豆子,通过使用元素或 服务定位器样式查找时,必须提供一个名称。 不提供名称的动机与使用内部名称有关 豆类和自动接线协作者。​​name​​​​id​​​​name​​​​id​​​​ref​

Bean 命名约定

约定是在以下情况下对实例字段名称使用标准 Java 约定 命名豆子。也就是说,Bean 名称以小写字母开头,并且是驼峰大小写的 从那里。此类名称的示例包括,,,,等等。​​accountManager​​​​accountService​​​​userDao​​​​loginController​

一致地命名 bean 使您的配置更易于阅读和理解。 此外,如果您使用Spring AOP,则在将建议应用于一组豆子时会有很大帮助。 按名称相关。

在 Bean 定义之外为 Bean 设置别名

在 Bean 定义本身中,您可以使用 最多一个由属性指定的名称和任意数量的其他名称的组合 属性中的名称。这些名称可以是同一 Bean 的等效别名 并且在某些情况下很有用,例如让应用程序中的每个组件 通过使用特定于该组件的 Bean 名称来引用公共依赖项 本身。​​id​​​​name​

指定实际定义 Bean 的所有别名并不总是足够的, 然而。有时需要为定义的 Bean 引入别名 别处。这在配置拆分的大型系统中很常见 在每个子系统中,每个子系统都有自己的一组对象定义。 在基于 XML 的配置元数据中,您可以使用元素来完成 这。以下示例演示如何执行此操作:​

在这种情况下,名为 bean(在同一容器中)也可以, 使用这个别名定义后,称为。​​fromName​​​​toName​

例如,子系统 A 的配置元数据可能通过 的名称。子系统 B 的配置元数据可以参考 a 数据源的名称。编写主应用程序时 使用这两个子系统,主应用程序引用数据源由 的名称。要让所有三个名称都引用同一个对象,您可以 将以下别名定义添加到配置元数据:​​subsystemA-dataSource​​​​subsystemB-dataSource​​​​myApp-dataSource​


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

Java-configuration

如果使用 Javaconfiguration,则可以使用注释来提供别名。 有关详细信息,请参阅使用@Bean注释。​​@Bean​

1.3.2. 实例化 Bean

Bean 定义本质上是创建一个或多个对象的配方。这 当询问时,容器会查看命名 Bean 的配方并使用配置 由该 Bean 定义封装的元数据,用于创建(或获取)实际对象。

如果使用基于 XML 的配置元数据,请指定对象的类型(或类) 即在元素的属性中实例化。此属性(在内部是实例上的属性)通常是强制性的。(有关异常,请参阅使用实例工厂方法实例化和Bean 定义继承。 可以通过以下两种方式之一使用该属性:​​class​​​​​​class​​​​Class​​​​BeanDefinition​​​​Class​

  • 通常,指定要在容器的情况下构造的 Bean 类 本身通过反射性地调用其构造函数来直接创建 Bean,在某种程度上 等效于带有运算符的 Java 代码。new
  • 指定包含工厂方法的实际类,即 调用以创建对象,在不太常见的情况下,容器在类上调用 afactory 方法来创建 Bean。返回的对象类型 从调用的工厂方法可以是同一个类或另一个 完全是类。staticstaticstatic

嵌套类名

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

例如,如果你有一个在包中调用的类,并且 这个类有嵌套的类调用,它们可以是 用美元符号 () 或点 () 分隔。所以属性的值在 豆子的定义将是。​​SomeThing​​​​com.example​​​​SomeThing​​​​static​​​​OtherThing​​​​$​​​​.​​​​class​​​​com.example.SomeThing$OtherThing​​​​com.example.SomeThing.OtherThing​

使用构造函数实例化

当您通过构造函数方法创建 Bean 时,所有普通类都可以由 和 与弹簧兼容。也就是说,正在开发的类不需要实现 任何特定接口或以特定方式编码。只需指定 bean 类应该就足够了。但是,根据您用于该特定 IoC 的类型 Bean,您可能需要一个默认(空)构造函数。

Spring IoC 容器几乎可以管理您希望它管理的任何类。是的 不仅限于管理真正的JavaBeans。大多数Spring用户更喜欢实际的JavaBeans 仅对默认(无参数)构造函数和适当的 setter 和 getter 进行建模 在容器中的属性之后。您还可以拥有更多异国情调的非豆式 容器中的类。例如,如果您需要使用旧版连接池 绝对不遵守JavaBean规范,Spring可以将其管理为: 井。

使用基于 XML 的配置元数据,您可以按如下方式指定 Bean 类:



有关向构造函数提供参数的机制的详细信息(如果需要) 并在构造对象后设置对象实例属性,请参阅注入依赖项。

使用静态工厂方法实例化

定义使用静态工厂方法创建的 Bean 时,请使用属性指定包含工厂方法和属性的类 named:指定工厂方法本身的名称。你应该是 能够调用此方法(使用可选参数,如下所述)并返回 live 对象,随后被视为通过构造函数创建的。 这种 Bean 定义的一个用途是在遗留代码中调用工厂。​​class​​​​static​​​​factory-method​​​​static​

以下 Bean 定义指定将通过调用 工厂方法。该定义未指定返回对象的类型(类), 而是包含工厂方法的类。在此示例中,方法必须是方法。下面的示例演示如何 指定工厂方法:​​createInstance()​​​​static​

    class="examples.ClientService"
factory-method="createInstance"/>

下面的示例演示了一个可以使用前面的 Bean 定义的类:

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

public static ClientService createInstance() {
return clientService;
}
}

有关向工厂方法提供(可选)参数的机制的详细信息 并在从工厂返回对象后设置对象实例属性, 请参阅依赖项和配置详细信息。

使用实例工厂方法实例化

类似于通过静态实例化工厂方法,使用实例实例化 工厂方法调用非静态 容器中现有 Bean 创建新 Bean 的方法。要使用此 机制,将属性留空,并在属性中, 指定当前(或父或祖先)容器中包含以下内容的 Bean 的名称 要调用以创建对象的实例方法。设置名称 工厂方法本身与属性。以下示例显示 如何配置这样的 bean:​​class​​​​factory-bean​​​​factory-method​







factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>

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

public class DefaultServiceLocator {

private static ClientService clientService = new ClientServiceImpl();

public ClientService createClientServiceInstance() {
return clientService;
}
}

一个工厂类还可以保存多个工厂方法,如以下示例所示:





factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>

factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>

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

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;
}
}

这种方法表明工厂 Bean 本身可以通过 依赖关系注入 (DI)。请参阅依赖项和 详细配置。

确定 Bean 的运行时类型

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

了解特定 Bean 的实际运行时类型的推荐方法是 指定 Bean 名称的 acall。这需要以上所有内容 考虑案例并返回 acall 是的对象类型 将返回相同的豆名。​​BeanFactory.getType​​​​BeanFactory.getBean​

1.4. 依赖关系

典型的企业应用程序不是由单个对象(或 春季用语)。即使是最简单的应用程序也有一些对象可以协同工作 呈现最终用户认为是连贯的应用程序。下一节将介绍如何 您从定义许多独立的 Bean 定义到完全实现 对象协作以实现目标的应用程序。

1.4.1. 依赖注入

依赖关系注入 (DI) 是对象定义其依赖关系的过程 (即它们使用的其他对象)仅通过构造函数参数, 工厂方法的参数,或在对象实例之后设置的属性 它是从工厂方法构造或返回的。然后容器注入这些 创建 Bean 时的依赖关系。这个过程基本上是相反的(因此 控制实例化的 Bean 本身的名称 Inversion of Control(控制反转) 或通过使用类的直接构造来定位其依赖项本身的位置,或者 服务定位器模式。

使用 DI 原则的代码更简洁,当对象 提供了它们的依赖项。该对象不会查找其依赖项,而是 不知道依赖项的位置或类。因此,您的课程变得更加轻松 测试,特别是当依赖项位于接口或抽象基类上时, 允许在单元测试中使用存根或模拟实现。

DI 存在于两个主要变体中:基于构造函数 依赖注入和基于 Setter 的依赖注入。

基于构造函数的依赖注入

基于构造函数的 DI 是通过容器调用具有 参数数,每个参数表示一个依赖项。调用工厂方法 用具体的参数来构造 Bean 几乎是等价的,这个讨论 以类似方式处理构造函数和 afactory 方法的参数。这 下面的示例演示了一个只能使用构造函数注入依赖项的类 注射:​​static​​​​static​

public class SimpleMovieLister {

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

// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

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

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

构造函数参数解析

构造函数参数解析匹配通过使用参数的类型进行。如果没有 Bean 定义的构造函数参数中存在潜在的歧义,即 在 Bean 定义中定义构造函数参数的顺序是 当 Bean 是 正在实例化。请考虑以下类:

package x.y;

public class ThingOne {

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

假设 theandclasses 不通过继承相关,则 存在潜在的歧义。因此,以下配置工作正常,而您不 需要在元素中显式指定构造函数参数索引或类型。​​ThingTwo​​​​ThingThree​​​










当引用另一个 Bean 时,类型是已知的,并且可以进行匹配(就像 与前面的示例不同)。当使用简单类型时,例如,Spring 无法确定值的类型,因此无法匹配 按类型无需帮助。请考虑以下类:​true

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;
}
}

构造函数参数类型匹配

在前面的方案中,容器可以将类型匹配与简单类型一起使用,如果 通过使用属性显式指定构造函数参数的类型, 如以下示例所示:​​type​




构造函数参数索引

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




除了解决多个简单值的歧义外,指定索引 解决构造函数具有两个相同类型的参数的歧义问题。

构造函数参数名称

还可以使用构造函数参数名称来消除值歧义,如下所示 示例显示:




请记住,要使这项工作开箱即用,您的代码必须使用 启用了调试标志,以便 Spring 可以从构造函数中查找参数名称。 如果不能或不想使用 debug 标志编译代码,则可以使用 @ConstructorPropertiesJDK 注释显式命名构造函数参数。示例类将 然后必须如下所示:

package examples;

public class ExampleBean {

// Fields omitted

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

基于 setter 的 DI 是通过容器在 调用无参数构造函数或无参数工厂方法后的 bean 实例化你的 bean。​​static​

下面的示例演示一个只能通过使用纯 二传手注射。这个类是传统的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
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

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

支持基于构造函数和基于二传手的 DI 对于它 管理。它还支持基于资源库的 DI,因为某些依赖项已经存在 通过构造函数方法注入。您可以以以下形式配置依赖项: a,您将其与实例结合使用 将属性从一种格式转换为另一种格式。但是,大多数Spring用户不工作 直接使用这些类(即以编程方式),而不是使用 XML定义、带注释的组件(即,用 、 等注释的类)或基于 Java 的类中的方法。 然后将这些源在内部转换为实例并用于 加载整个 Spring IoC 容器实例。​​ApplicationContext​​​​BeanDefinition​​​​PropertyEditor​​​​bean​​​​@Component​​​​@Controller​​​​@Bean​​​​@Configuration​​​​BeanDefinition​

基于构造函数还是基于资源库的 DI?

由于您可以混合使用基于构造函数和基于资源库的 DI,因此 对强制依赖项和 setter 方法或配置方法使用构造函数 对于可选依赖项。请注意,在 setter 方法上使用 @Required注释可用于使属性成为必需的依赖项; 但是,最好使用以编程方式验证参数的构造函数注入。

Spring 团队通常提倡构造函数注入,因为它可以让您实现 应用程序组件作为不可变对象,并确保所需的依赖项 不是。此外,构造函数注入的组件始终返回给客户端 处于完全初始化状态的(调用)代码。作为旁注,大量的构造函数 参数是一种糟糕的代码气味,暗示该类可能有太多 责任,应重构以更好地解决关注点的适当分离问题。​​null​

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

使用对特定类最有意义的 DI 样式。有时,在交易时 对于您没有源代码的第三方类,将为您做出选择。 例如,如果第三方类不公开任何 setter 方法,则构造函数 注射可能是 DI 的唯一可用形式。

依赖关系解析过程

容器执行 Bean 依赖关系解析,如下所示:

  • 使用配置元数据创建和初始化 描述了所有的豆子。配置元数据可以通过 XML、Java 代码或 附注。ApplicationContext
  • 对于每个 bean,其依赖关系以属性、构造函数的形式表示 参数或静态工厂方法的参数(如果您使用它而不是 普通构造函数)。当 Bean 是 实际创建。
  • 每个属性或构造函数参数都是要设置的值的实际定义,或者 对容器中另一个 Bean 的引用。
  • 作为值的每个属性或构造函数参数都从其指定的值转换 格式为该属性或构造函数参数的实际类型。默认情况下,Spring 可以将以字符串格式提供的值转换为所有内置类型,例如,,,,等。intlongStringboolean

Spring 容器在创建容器时验证每个 Bean 的配置。 但是,在实际创建 Bean 之前,不会设置 Bean 属性本身。 创建单例作用域并设置为预实例化(默认值)的 Bean 创建容器时。作用域在Bean 作用域中定义。否则 仅当请求时才会创建 Bean。创建 Bean 可能会导致 要创建的 bean 图,作为 bean 的依赖项及其依赖项 创建并分配依赖项(等)。请注意,分辨率不匹配 这些依赖项可能会延迟显示 — 即在首次创建受影响的 Bean 时。

循环依赖关系

如果主要使用构造函数注入,则可以创建不可解析的 循环依赖方案。

例如:类 A 需要通过构造函数注入的类 B 实例,并且 类 B 需要通过构造函数注入的类 A 实例。如果配置 用于 A 类和 B 类的 bean 相互注入,Spring IoC 容器 在运行时检测到此循环引用,并抛出 a。​​BeanCurrentlyInCreationException​

一种可能的解决方案是编辑某些要配置的类的源代码 二传手而不是构造函数。或者,避免构造函数注入和使用 仅二传手注射。换句话说,虽然不建议这样做,但您可以配置 具有二传手注入的循环依赖关系。

与典型情况(没有循环依赖关系)不同,循环依赖关系 在豆 A 和豆 B 之间迫使其中一个豆子在 自己完全初始化(经典的先有鸡还是先有蛋的场景)。

您通常可以信任Spring会做正确的事情。它检测配置问题, 例如对不存在的 bean 和循环依赖项的引用,在容器中 加载时间。Spring 设置属性并尽可能晚地解析依赖关系,当 Bean 实际上是创建的。这意味着已加载的弹簧容器 如果存在 创建该对象或其依赖项之一时出现问题 — 例如,Bean 抛出 由于属性缺失或无效而导致的异常。这可能会延迟 某些配置问题的可见性是实现的原因 默认预实例化单例 Bean。以一些前期时间和内存为代价 在实际需要之前创建这些 bean,您会发现配置问题 创建时,而不是以后。您仍然可以覆盖此默认值 行为,以便单例 Bean 延迟初始化,而不是急切地初始化 预实例化。​​ApplicationContext​​​​ApplicationContext​

如果不存在循环依赖关系,则当一个或多个协作 Bean 正在 注入到依赖 Bean 中,每个协作 Bean 都完全配置在 被注入依赖的豆中。这意味着,如果 bean A 依赖于 Bean B,Spring IoC 容器在调用 Bean B 之前完全配置 豆A上的二传手方法。换句话说,bean 被实例化(如果它不是 预实例化的单例),设置其依赖项以及相关的生命周期 方法(例如配置的 init方法或初始化Bean 回调方法) 被调用。

依赖注入示例

以下示例将基于 XML 的配置元数据用于基于资源库的 DI。一个小 Spring XML 配置文件的一部分指定了一些 Bean 定义,如下所示:













以下示例显示了相应的类:​​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;
}
}

在前面的示例中,将资源库声明为与指定的属性匹配 在 XML 文件中。下面的示例使用基于构造函数的 DI:














以下示例显示了相应的类:​​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;
}
}

Bean 定义中指定的构造函数参数用作 的构造函数。​​ExampleBean​

现在考虑这个例子的一个变体,其中,Spring 不是使用构造函数,而是 告诉调用 afactory 方法以返回对象的实例:​​static​








以下示例显示了相应的类:​​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.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

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

工厂方法的参数由元素提供, 与实际使用的构造函数完全相同。所要的类的类型 工厂方法返回的不必与 包含工厂方法(尽管在此示例中,它是)。一个实例 (非静态)工厂方法可以以基本相同的方式使用(除了 从使用属性而不是属性),所以我们 不要在这里讨论这些细节。​​static​​​​​​static​​​​factory-bean​​​​class​

1.4.2. 依赖关系和配置详解

如上一节所述,您可以定义 bean 属性和构造函数参数作为对其他托管 Bean(协作者)的引用 或作为内联定义的值。Spring 基于 XML 的配置元数据支持 itsand元素中的子元素类型 为此 目的。​​​

直线值(基元、字符串等)

元素的属性指定属性或构造函数 参数作为人类可读的字符串表示形式。Spring 的转换服务用于转换这些 从 ato 到属性或参数的实际类型的值。 以下示例显示了正在设置的各种值:​​value​​​​​​String​







以下示例使用p 命名空间以更加简洁 XML 配置:

    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">

destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="misterkaoli"/>

前面的 XML 更简洁。但是,拼写错误是在运行时发现的,而不是 设计时,除非您使用 IDE(如IntelliJ IDEA或 Spring Toolsfor Eclipse) 支持在创建 Bean 定义时自动完成属性。这样的集成开发地址 强烈建议您提供帮助。

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

    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">




jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb


Spring 容器使用 JavaBeans 机制将元素中的文本转换为实例。这 是一个很好的捷径,也是Spring团队喜欢使用的少数几个地方之一 属性样式上的嵌套元素。​​​​java.util.Properties​​​​PropertyEditor​​​​​​value​

元素​​idref​

元素只是一种防错的方式来传递(字符串值 - 不是 一个引用)容器中另一个豆子到主动脉元素。以下示例演示如何使用它:​​idref​​​​id​​​​​







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





第一种形式比第二种形式更可取,因为使用 thetag 可以让 容器在部署时验证引用的名为 Bean 的实际 存在。在第二个变体中,不对传递的值执行验证 到底豆的财产。拼写错误只被发现(大多数 可能是致命的结果),当 thebean 实际实例化时。如果 thebean 是原型bean,则此拼写错误和由此产生的异常 可能只有在部署容器后很长时间才能发现。​​idref​​​​targetName​​​​client​​​​client​​​​client​

一个常见的地方(至少在 Spring 2.0 之前的版本中),其中元素 带来价值在于 abean 定义中​​AOP 拦截器的​​​配置。指定时使用元素 侦听器名称可防止您拼错侦听器 ID。​​​​ProxyFactoryBean​​​

对其他豆类的引用(合作者)

元素是主动脉定义元素内的最后一个元素。在这里,您将 Bean 的指定属性的值设置为 引用由容器管理的另一个 Bean(协作者)。引用的 bean 是要设置其属性的 Bean 的依赖项,它是按需初始化的 在设置属性之前根据需要。(如果协作者是单例 bean,则可能会 已由容器初始化。所有引用最终都是对 另一个对象。范围和验证取决于是否指定 ID 或名称 通过理论属性的其他对象。​​ref​​​​​​​​bean​​​​parent​

通过标签的属性指定目标 bean 是最 一般形式,并允许创建对同一容器中任何 Bean 的引用,或 父容器,无论它是否位于同一 XML 文件中。属性的值可以与目标 bean 的属性相同,也可以相同 作为目标 Bean 的属性中的值之一。以下示例 演示如何使用 aelement:​​bean​​​​​​bean​​​​id​​​​name​​​​ref​

通过属性指定目标 Bean 会创建对 Bean 的引用 即在当前容器的父容器中。属性的值可以与目标 Bean 的属性或 目标 Bean 的属性中的值。目标 Bean 必须位于 当前容器的父容器。您应该主要使用此 Bean 引用变体 当您具有容器层次结构并且想要将现有 Bean 包装在父容器中时 具有与父 Bean 同名的代理的容器。以下一对 列表显示如何使用属性:​​parent​​​​parent​​​​id​​​​name​​​​parent​






class="org.springframework.aop.framework.ProxyFactoryBean">




内豆

理论元素中的元素定义了一个 内 Bean,如以下示例所示:​​​​​









内部 Bean 定义不需要定义的 ID 或名称。如果指定,则容器 不使用此类值作为标识符。容器也忽略了标志 创建,因为内 Bean 始终是匿名的,并且总是与外部 豆。无法独立访问内豆或将其注入 协作豆,而不是进入封闭的豆子。​​scope​

作为极端情况,可以从自定义作用域接收销毁回调,例如,对于包含在单例 Bean 中的请求作用域内 Bean。创作 的内部 Bean 实例与其包含的 Bean 相关联,但销毁回调允许它 参与请求范围的生命周期。这不是常见的情况。内豆 通常只是简单地共享其包含 Bean 的范围。

收集

,,,和元素设置属性 和Javatypes的参数,,,以及, 分别。以下示例演示如何使用它们:​​​​​​​​​​Collection​​​​List​​​​Set​​​​Map​​​​Properties​





administrator@example.org
support@example.org
development@example.org





a list element followed by a reference













just some string



映射键或值的值,或设置值,也可以是任何 以下元素:

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

Spring 容器还支持合并集合。一个应用程序 开发人员可以定义父元素,或元素 并有子元素,或元素继承和 重写父集合中的值。也就是说,子集合的值为 将父集合和子集合的元素与子集合的 集合元素覆盖父集合中指定的值。​​​​​​​​​​​​​​​

关于合并的这一部分讨论了父子 Bean 机制。读者不熟悉 对于父 Bean 和子 Bean 定义,不妨先阅读相关章节,然后再继续。

以下示例演示集合合并:





administrator@example.com
support@example.com







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



请注意属性在元素上的使用 thebean 定义的属性。当枸豆被解析时 并由容器实例化,生成的实例具有一个集合,其中包含将子集合与父集合合并的结果。以下列表 显示结果:​​merge=true​​​​​​adminEmails​​​​child​​​​child​​​​adminEmails​​​​Properties​​​​adminEmails​​​​adminEmails​

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

子集合的值集从 父级,子项的值将覆盖 中的值 父集合。​​Properties​​​​​​support​

此合并行为同样适用于、和集合类型。在元素的特定情况下,语义 与集合类型(即值集合的概念)相关联。父项的值位于所有子列表的值之前 值。对于 、 和集合类型,无排序 存在。因此,对于作为基础的集合类型,没有排序语义有效 容器的关联、和实现类型 内部使用。​​​​​​​​​​List​​​​ordered​​​​Map​​​​Set​​​​Properties​​​​Map​​​​Set​​​​Properties​

集合合并的限制

不能合并不同的集合类型(如 aand a)。如果你 尝试这样做,抛出适当的。属性必须是 在较低的继承子定义上指定。指定属性 父集合定义是冗余的,不会导致所需的合并。​​Map​​​​List​​​​Exception​​​​merge​​​​merge​

强类型集合

由于 Java 对泛型类型的支持,您可以使用强类型集合。 也就是说,可以声明 atype,使其只能包含 (例如)元素。如果你使用 Spring 来依赖注入一个 强型成豆子,可以利用春天的 类型转换支持,以便强类型实例的元素在添加到 之前转换为适当的类型。 以下 Java 类和 Bean 定义演示了如何执行此操作:​​Collection​​​​String​​​​Collection​​​​Collection​​​​Collection​

public class SomeClass {

private Map accounts;

public void setAccounts(Map accounts) {
this.accounts = accounts;
}
}










当制备注射剂的性质时,仿制药 有关强类型DIS的元素类型的信息 可通过反射获得。因此,Spring 的类型转换基础结构可识别 各种值元素的类型,字符串值(,和)被转换为实际类型。​​accounts​​​​something​​​​Map​​​Float​​​​9.99​​​​2.75​​​​3.99​​​​Float​

空和空字符串值

Spring 将属性等的空参数视为空参数。这 以下基于 XML 的配置元数据代码段将属性设置为空值 (“”)。​​Strings​​​​email​​​​String​



前面的示例等效于以下 Java 代码:

exampleBean.setEmail("");

元素处理值。下面的清单显示了一个示例:​​​​null​





上述配置等效于以下 Java 代码:

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

p 命名空间允许您使用元素的属性(而不是嵌套元素)来描述您的属性值协作 bean,或两者兼而有之。​​bean​​​

Spring 支持带有命名空间的可扩展配置格式, 基于 XML 架构定义。中讨论的配置格式 本章在 XML 架构文档中定义。但是,未定义 p 命名空间 在XSD文件中,并且仅存在于Spring的核心中。​​beans​

下面的示例演示两个 XML 代码段(第一个使用 标准 XML 格式和第二个使用 p 命名空间),解析为相同的结果:

    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">





p:email="someone@somewhere.com"/>

该示例显示了 Bean 定义中调用的 p 命名空间中的一个属性。 这告诉 Spring 包含一个属性声明。如前所述, p 命名空间没有架构定义,因此可以设置属性的名称 到属性名称。​​email​

下一个示例包括另外两个 Bean 定义,它们都具有对 另一个豆子:

    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">






class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>




此示例不仅包括使用 p 命名空间的属性值 但也使用特殊格式来声明属性引用。而第一个豆子 定义用于从 Beanto Bean 创建引用,第二个 Bean 定义用作 属性来执行完全相同的操作。在这种情况下,是属性名称, 而 thepart 表示这不是一个直接的值,而是一个 引用另一个 bean。​​​​john​​​​jane​​​​p:spouse-ref="jane"​​​​spouse​​​​-ref​

带有 c 命名空间的 XML 快捷方式

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

下面的示例使用命名空间来执行与来自基于构造函数的依赖项注入相同的操作:​​c:​

    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">












c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

命名空间使用与 theone 相同的约定(尾随 Bean 引用),用于按构造函数参数的名称设置构造函数参数。同样地 它需要在 XML 文件中声明,即使它未在 XSD 架构中定义 (它存在于弹簧核心内)。​​c:​​​​p:​​​​-ref​

对于构造函数参数名称不可用的极少数情况(通常如果 字节码是在没有调试信息的情况下编译的),您可以使用回退到 参数索引,如下所示:


c:_2="something@somewhere.com"/>

在实践中,构造函数解析机制在匹配方面非常有效 参数,因此,除非您确实需要,否则我们建议您使用名称表示法 在整个配置中。

复合属性名称

设置 Bean 属性时,可以使用复合属性名称或嵌套属性名称,只要 路径的所有组件(最终属性名称除外)都不是。考虑 以下 Bean 定义:​​null​



Thebean 有 aproperty,它有 aproperty,它有 aproperty,并且 finalproperty 被设置为值 of。为了 这要工作,财产和财产不得 在豆子被构造出来之后。否则,ais 被抛出。​​something​​​​fred​​​​bob​​​​sammy​​​​sammy​​​​123​​​​fred​​​​something​​​​bob​​​​fred​​​​null​​​​NullPointerException​

1.4.3. 使用​​depends-on​

如果一个 Bean 是另一个 Bean 的依赖项,这通常意味着一个 Bean 被设置为 他人的财产。通常,您可以使用基于 XML 的配置元数据中的 element来完成此操作。但是,有时 豆子不那么直接。例如,当类中的静态初始值设定项需要 触发,例如用于数据库驱动程序注册。该属性可以 使用此元素显式强制在 Bean 之前初始化一个或多个 bean 已初始化。下面的示例使用属性来表示 对单个 Bean 的依赖:​​depends-on​​​​depends-on​


要表达对多个 Bean 的依赖关系,请提供 Bean 名称列表作为值 属性(逗号、空格和分号有效 分隔符):​​depends-on​






1.4.4. 延迟初始化的 Bean

默认情况下,实现急切地创建和配置所有单例bean 作为初始化的一部分 过程。通常,这种预实例化是可取的,因为错误在 立即发现配置或周围环境,而不是数小时 甚至几天后。当不希望此行为时,可以防止 通过将 Bean 定义标记为 延迟初始化。延迟初始化的 Bean 告诉 IoC 容器创建 Bean 实例在首次请求时,而不是在启动时。​​ApplicationContext​

在 XML 中,此行为由元素上的属性控制,如以下示例所示:​​lazy-init​​​


当上述配置被 bean 使用时,thebean 在开始时没有急切地预先实例化, 而底豆则急切地预先实例化。​​ApplicationContext​​​​lazy​​​​ApplicationContext​​​​not.lazy​

但是,当延迟初始化的 Bean 是单例 Bean 的依赖项时,它是 不是延迟初始化的,创建延迟初始化的 Bean 在 启动,因为它必须满足单一实例的依赖项。延迟初始化的 Bean 注入到其他地方未延迟初始化的单例 Bean 中。​​ApplicationContext​

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



1.4.5. 自动布线协作者

Spring 容器可以自动连接协作 Bean 之间的关系。您可以 让 Spring 自动为您的 Bean 解析协作者(其他 bean) 检查内容。自动布线具有以下功能 优势:​​ApplicationContext​

  • 自动布线可以显著减少指定属性或构造函数的需要 参数。(其他机制,如本章其他地方讨论的 Bean 模板也很有价值 在这方面。
  • 自动连线可以随着对象的发展而更新配置。例如,如果您需要 向类添加依赖项,可以自动满足该依赖项,而无需 您需要修改配置。因此自动布线可能特别有用 在开发过程中,不会否定在以下情况下切换到显式接线的选项 代码库变得更加稳定。

使用基于 XML 的配置元数据(请参阅依赖关系注入)时,您 可以为具有元素属性的 Bean 定义指定自动连线模式。自动接线功能有四种模式。指定自动接线 每个bean,因此可以选择自动连线的。下表描述了 四种自动接线模式:​​autowire​​​

Table 2. Autowiring modes

模式

解释

​no​

(默认)无自动接线。Bean 引用必须由元素定义。改变 对于较大的部署,不建议使用默认设置,因为指定 协作者明确提供了更大的控制和清晰度。在某种程度上,它 记录系统的结构。​​ref​

​byName​

按属性名称自动布线。春天寻找与 需要自动连线的属性。例如,如果 Bean 定义设置为 按名称自动连线,它包含属性(即,它有amethod),Spring 查找名为 bean 的定义并使用 它来设置属性。​​master​​​​setMaster(..)​​​​master​

​byType​

如果属性类型恰好存在一个 Bean,则允许自动连线属性 容器。如果存在多个,则会引发致命异常,指示 您不得对该 Bean 使用自动布线。如果没有匹配项 豆子,什么也没发生(属性未设置)。​​byType​

​constructor​

类似于 tobut,适用于构造函数参数。如果没有确切的 容器中构造函数参数类型的一个 Bean,将引发致命错误。​​byType​

使用自动布线模式,您可以连接阵列和 类型化集合。在这种情况下,容器内的所有自动连线候选项都 提供匹配的预期类型以满足依赖关系。您可以自动连线 强类型实例(如果预期的密钥类型为)。自动连线实例的值由与预期类型匹配的所有 Bean 实例组成,实例的键包含相应的 Bean 名称。​​byType​​​​constructor​​​​Map​​​​String​​​​Map​​​​Map​

自动布线的局限性和缺点

自动布线在整个项目中一致使用时效果最佳。如果自动接线 通常不使用,仅使用它来连接一个或 两个豆子定义。

考虑自动接线的局限性和缺点:

  • 显式依赖项 inandsettings 始终覆盖 自动接线。不能自动连接简单属性,例如基元、和(以及此类简单属性的数组)。此限制是 通过设计。propertyconstructor-argStringsClasses
  • 自动布线不如显式布线精确。虽然,如上表所述, 春天小心翼翼地避免猜测,以防出现可能意想不到的歧义 结果。Spring 管理的对象之间的关系不再 明确记录。
  • 接线信息可能不适用于可能从中生成文档的工具 弹簧容器。
  • 容器中的多个 Bean 定义可能与 要自动连线的 setter 方法或构造函数参数。对于数组、集合或实例,这不一定是问题。但是,对于依赖项 期望一个单一的值,这种歧义不是任意解决的。如果没有唯一的豆 定义可用,引发异常。Map

在后一种情况下,您有以下几种选择:

  • 放弃自动布线,转而使用显式布线。
  • 通过设置 Bean 定义的属性来避免自动连线 ,如下一节所述。autowire-candidatefalse
  • 通过将 bean 定义的属性设置为 ,将单个 Bean 定义指定为主要候选者。primarytrue
  • 实现基于注释的配置提供的更精细的控制, 如基于注释的容器配置中所述。
从自动配线中排除 Bean

在每 Bean 的基础上,您可以从自动连线中排除 Bean。在 Spring 的 XML 格式中,设置 元素的属性。容器 使该特定 Bean 定义对自动布线基础结构不可用 (包括注释样式配置,例如@Autowired)。​​autowire-candidate​​​​​​false​

您还可以根据与 Bean 名称的模式匹配来限制自动连线候选项。这 顶级元素接受其属性中的一个或多个模式。例如,限制自动连线候选项状态 对于名称以 Bean 结尾的任何 Bean,请提供值 of。自 提供多种模式,在逗号分隔的列表中定义它们。一个显式值 oforfor 一个 bean 定义的属性总是需要 优先。对于此类 bean,模式匹配规则不适用。​​​​default-autowire-candidates​​​​Repository​​​​*Repository​​​​true​​​​false​​​​autowire-candidate​

这些技术对于您永远不想注入其他豆子的豆子很有用 豆子通过自动接线。这并不意味着排除的 Bean 本身不能由 使用自动接线。相反,bean 本身不是自动连接其他 bean 的候选者。

1.4.6. 方法注入

在大多数应用场景中,容器中的大多数 bean 都是单例。当单例豆需要 与另一个单例 Bean 协作或非单例 Bean 需要协作 对于另一个非单例 Bean,您通常通过定义一个 豆子作为另一个属性。当 Bean 生命周期 不同。假设单例 bean A 需要使用非单例(原型)bean B, 也许在 A 上的每个方法调用上。容器仅创建单例 Bean A 一次,因此只有一次机会设置属性。容器不能 每次需要时,都为 Bean A 提供一个新的 Bean B 实例。

一个解决方案是放弃一些控制倒置。你可以制作 bean A 通过实现接口来感知容器, 并通过对容器进行getBean(“B”)调用请求 (a 通常是新的)Bean B实例,每次Bean A需要它时。以下示例 显示了这种方法:​​ApplicationContextAware​

// 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!
return this.applicationContext.getBean("command", Command.class);
}

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

上述情况是不可取的,因为业务代码知道并耦合到 弹簧框架。方法注入,Spring IoC的一个有点高级的功能 容器,让您干净地处理此用例。

您可以在这篇博客文章中阅读有关方法注入动机的更多信息。

查找方法注入

查找方法注入是容器重写方法的能力 容器管理的 Bean 并返回 容器。查找通常涉及原型 Bean,如所描述的场景所示 在上一节中。弹簧框架 通过使用 CGLIB 库中的字节码生成来实现此方法注入 动态生成重写该方法的子类。

在前面代码片段中的类的情况下, 弹簧容器动态覆盖方法的实现。该类没有任何 Spring 依赖项,因为 返工示例显示:​​CommandManager​​​​createCommand()​​​​CommandManager​

爪哇岛

科特林

package fiona.apple;

// no more Spring imports!

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();
}

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

 [abstract]  theMethodName(no-arguments);

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









识别的豆子调用自己的方法 每当它需要一个新的 thebean 实例时。您必须小心部署 Thebean作为一个原型,如果这实际上是需要的。如果是 单例,每次都返回相同的 Thebean 实例。​​commandManager​​​​createCommand()​​​​myCommand​​​​myCommand​​​​myCommand​

或者,在基于注释的组件模型中,可以声明查找 方法通过注释,如以下示例所示:​​@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();
}

或者,更惯用地说,您可以依靠目标 Bean 针对 查找方法的声明返回类型:

public abstract class CommandManager {

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

@Lookup
protected abstract Command createCommand();
}

请注意,通常应使用具体的 存根实现,以便它们与 Spring 的组件兼容 默认情况下忽略抽象类的扫描规则。此限制不会 适用于显式注册或显式导入的 Bean 类。

任意方法替换

与查找方法注入相比,方法注入的一种不太有用的形式是能够 将受管 Bean 中的任意方法替换为另一个方法实现。你 可以安全地跳过本节的其余部分,直到您实际需要此功能。

使用基于 XML 的配置元数据,您可以使用该元素来 对于已部署的 Bean,将现有方法实现替换为另一个方法实现。考虑 下面的类,它有一个叫做我们要重写的方法:​​replaced-method​​​​computeValue​

public class MyValueCalculator {

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

// some other methods...
}

实现接口的类提供新的方法定义,如以下示例所示:​​org.springframework.beans.factory.support.MethodReplacer​

/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
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 ...;
}
}

用于部署原始类并指定方法覆盖的 Bean 定义将 类似于以下示例:




String



可以在元素中使用一个或多个元素来指示被重写的方法的方法签名。签名 对于参数仅在方法重载且多个变体时才是必需的 存在于类中。为方便起见,参数的类型字符串可以是 完全限定类型名称的子字符串。例如,以下所有匹配项:​​​​​​java.lang.String​

java.lang.String
String
Str

因为参数的数量通常足以区分每个可能的 选择,此快捷方式可以节省大量键入,让您只键入 与参数类型匹配的最短字符串。

1.5. Bean 作用域

创建 Bean 定义时,将创建用于创建实际实例的配方 由该 Bean 定义定义的类。Bean 定义是一个想法 recipe 很重要,因为它意味着,就像一个类一样,你可以创建许多对象 单个配方中的实例。

您不仅可以控制各种依赖项和配置值,这些值是 插入到从特定 Bean 定义创建的对象中,但也控制 从特定 Bean 定义创建的对象的范围。这种方法是 强大而灵活,因为您可以选择所创建对象的范围 通过配置,而不必在 Java 的对象范围内烘焙 班级级别。可以将 Bean 定义为部署在多个作用域之一中。 Spring 框架支持六个作用域,其中四个仅在以下情况下可用 您使用网络感知。还可以创建自定义范围。​​ApplicationContext​

下表描述了支持的范围:

Table 3. Bean scopes

范围

描述

单身 人士

(默认)将单个 Bean 定义的范围限定为每个 Spring IoC 的单个对象实例 容器。

原型

将单个 Bean 定义的作用域限定为任意数量的对象实例。

请求

将单个 Bean 定义的范围限定为单个 HTTP 请求的生命周期。那是 每个 HTTP 请求都有自己的 Bean 实例,该实例是在单个 Bean 的背面创建的 Bean 定义。仅在 Web 感知的 Spring 上下文中有效。​​ApplicationContext​

会期

将单个 Bean 定义的范围限定为 HTTP 的生命周期。仅在以下情况下有效 网络感知的春季的上下文。​​Session​​​​ApplicationContext​

应用

将单个 Bean 定义的范围限定为 a 的生命周期。仅在以下情况下有效 网络感知的春季的上下文。​​ServletContext​​​​ApplicationContext​

网络套接字

将单个 Bean 定义的范围限定为 a 的生命周期。仅在以下情况下有效 网络感知的春季的上下文。​​WebSocket​​​​ApplicationContext​

1.5.1. 单例作用域

仅管理单例 Bean 的一个共享实例,以及所有 Bean 请求 与该 Bean 定义匹配的一个或多个 ID 会导致一个特定的 Bean 由 Spring 容器返回的实例。

换句话说,当您定义 Bean 定义并且它的作用域为 单例,Spring IoC 容器只创建对象的一个实例 由该 Bean 定义定义。这个单个实例存储在这样的缓存中 单例 Bean 以及该命名 Bean 的所有后续请求和引用 返回缓存的对象。下图显示了单一实例作用域的工作原理:

Spring 框架的核心技术_元数据_03

Spring 的单例 bean 概念与 中定义的单例模式不同 四人帮(GoF)模式书。GoF 单例对 对象,以便每个对象创建特定类的一个且仅一个实例 类加载器。Spring 单例的范围最好描述为每个容器 和每豆。这意味着,如果您为 单个 Spring 容器,Spring 容器创建一个且仅创建一个实例 由该 Bean 定义定义的类。单一实例作用域是默认作用域 在春天。要在 XML 中将 Bean 定义为单例,可以定义 Bean,如 以下示例:




1.5.2. 原型范围

Bean 部署的非单例原型范围导致创建新的 每次对该特定 Bean 发出请求时,Bean 实例。也就是豆子 被注入到另一个 Bean 中,或者您通过 容器。通常,您应该对所有有状态的 Bean 和 无状态 Bean 的单例范围。​​getBean()​

下图说明了 Spring 原型范围:

Spring 框架的核心技术_元数据_04

(数据访问对象 (DAO) 通常不配置为原型,因为典型的 DAO 不成立 任何对话状态。我们更容易重用 单例图。

下面的示例将 Bean 定义为 XML 中的原型:

与其他作用域相比,Spring 不管理整个生命周期 原型豆。容器实例化、配置和以其他方式组装 原型对象并将其交给客户端,没有该原型的进一步记录 实例。因此,尽管在所有 对象不考虑范围,在原型的情况下,配置销毁 不调用生命周期回调。客户端代码必须清理原型范围 对象并释放原型 Bean 持有的昂贵资源。要得到 Spring 容器要释放原型范围 Bean 持有的资源,请尝试使用 自定义Bean 后处理器,其中包含对 需要清理的豆子。

在某些方面,Spring 容器在原型范围 Bean 中的作用是 替换 Javaoperator。超过该点的所有生命周期管理都必须 由客户端处理。(有关春季豆子生命周期的详细信息 容器,请参阅生命周期回调。​​new​

1.5.3. 具有原型 bean 依赖项的单例 bean

当您使用依赖于原型 Bean 的单例作用域 Bean 时,请注意 依赖项在实例化时解析。因此,如果您依赖注入 原型范围的 Bean 到单例范围的 Bean,一个新的原型 Bean 被实例化 然后将依赖注入到单例 Bean 中。原型实例是唯一的 曾经提供给单一实例范围的 Bean 的实例。

但是,假设您希望单例作用域的 Bean 获取 在运行时重复原型范围的 Bean。您不能依赖性注入 原型范围的 bean 到你的单例 bean 中,因为该注入只发生 一次,当 Spring 容器实例化单例 Bean 并解析 并注入其依赖项。如果您需要原型 Bean 的新实例,请在 多次运行时,请参阅方法注入。

1.5.4. 请求、会话、应用程序和 WebSocket 作用域

,,, 和示波器仅可用 如果您使用Web感知的Spring实现(例如)。如果将这些作用域与常规的 Spring IoC 容器一起使用, 比如,安那抱怨 抛出了一个未知的 Bean 范围。​​request​​​​session​​​​application​​​​websocket​​​​ApplicationContext​​​​XmlWebApplicationContext​​​​ClassPathXmlApplicationContext​​​​IllegalStateException​

初始 Web 配置

为了支持在,,,和级别(Web 范围的 bean)上限定 bean 的范围,一些次要的初始配置 在定义 Bean 之前是必需的。(不需要此初始设置 对于标准范围:和。​​request​​​​session​​​​application​​​​websocket​​​​singleton​​​​prototype​

如何完成此初始设置取决于您特定的 Servlet 环境。

如果你在Spring Web MVC中访问作用域的bean,实际上,在一个请求中 由 Spring 处理,无需特殊设置。已公开所有相关状态。​​DispatcherServlet​​​​DispatcherServlet​

如果您使用 Servlet 2.5 Web 容器,并且请求在 Spring 之外处理(例如,使用 JSF 或 Struts 时),则需要注册。 对于 Servlet 3.0+,这可以通过使用接口以编程方式完成。或者,或者对于较旧的容器,将以下声明添加到 Web 应用程序的文件:​​DispatcherServlet​​​​org.springframework.web.context.request.RequestContextListener​​​​ServletRequestListener​​​​WebApplicationInitializer​​​​web.xml​


...


org.springframework.web.context.request.RequestContextListener


...

或者,如果您的听众设置存在问题,请考虑使用 Spring 的。过滤器映射取决于周围的网络 应用程序配置,因此您必须根据需要对其进行更改。以下列表 显示 Web 应用程序的筛选器部分:​​RequestContextFilter​


...

requestContextFilter
org.springframework.web.filter.RequestContextFilter


requestContextFilter
/*

...

​DispatcherServlet​​,,并且都完全做到 同样的事情,即将HTTP请求对象绑定到正在服务 那个要求。这使得请求和会话范围的 bean 进一步可用 在呼叫链中向下。​​RequestContextListener​​​​RequestContextFilter​​​​Thread​

请求范围

请考虑 Bean 定义的以下 XML 配置:

Spring 容器通过使用每个 HTTP 请求的 bean 定义来创建 bean 的新实例。也就是说,bean 的作用域在 HTTP 请求级别。您可以更改内部 根据需要创建的实例的状态,因为其他实例 从 samebean 定义创建 看不到状态中的这些更改。 它们特定于单个请求。当请求完成处理时, 范围限定为请求的 Bean 将被丢弃。​​LoginAction​​​​loginAction​​​​loginAction​​​​loginAction​

当使用注解驱动的组件或 Java 配置时,注解 可用于将组件分配给示波器。以下示例演示如何 为此:​​@RequestScope​​​​request​

@RequestScope
@Component
public class LoginAction {
// ...
}
会话范围

请考虑 Bean 定义的以下 XML 配置:

Spring 容器通过在单个 HTTP 的生存期内使用 bean 定义来创建 bean 的新实例。在其他 words,thebean 在 HTTP 级别有效地限定了范围。如 使用请求范围的 Bean,您可以更改实例的内部状态 随心所欲地创建,知道其他 HTTP实例也是 使用从 samebean 定义创建的实例看不到这些 状态更改,因为它们特定于单个 HTTP。当 HTTP最终被丢弃,作用域为该特定HTTP的Bean也被丢弃。​​UserPreferences​​​​userPreferences​​​​Session​​​​userPreferences​​​​Session​​​​Session​​​​userPreferences​​​​Session​​​​Session​​​​Session​

使用注释驱动的组件或 Java 配置时,可以使用注释将组件分配给作用域。​​@SessionScope​​​​session​

@SessionScope
@Component
public class UserPreferences {
// ...
}
应用范围

请考虑 Bean 定义的以下 XML 配置:

Spring 容器通过对整个 Web 应用程序使用一次 bean 定义来创建 bean 的新实例。也就是说,bean 的作用域在级别并存储为常规属性。这有点类似于春季单例豆,但 在两个重要方面有所不同:它是每个单例,而不是每个 Spring(在任何给定的 Web 应用程序中可能有几个), 它实际上是暴露的,因此作为属性可见。​​AppPreferences​​​​appPreferences​​​​appPreferences​​​​ServletContext​​​​ServletContext​​​​ServletContext​​​​ApplicationContext​​​​ServletContext​

使用注释驱动的组件或 Java 配置时,可以使用注释将组件分配给作用域。这 以下示例演示如何执行此操作:​​@ApplicationScope​​​​application​

@ApplicationScope
@Component
public class AppPreferences {
// ...
}
网络套接字范围

WebSocket 作用域与 WebSocket 会话的生命周期相关联,适用于 通过 WebSocket 应用程序进行 Stomp 操作,有关详细信息,请参阅WebSocket 范围。

作用域内 Bean 作为依赖项

Spring IoC 容器不仅管理对象(bean)的实例化, 还有协作者(或依赖项)的连接。如果要注入(对于 例如)一个HTTP请求范围的Bean变成另一个生存期更长的Bean,你可以 选择注入 AOP 代理来代替作用域 Bean。也就是说,您需要注入 一个代理对象,它公开与作用域对象相同的公共接口,但可以 还从相关范围(例如 HTTP 请求)中检索实际目标对象 并将方法调用委托给实际对象。

以下示例中的配置只有一行,但重要的是 了解“为什么”以及背后的“如何”:


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 定义(请参阅​​选择要创建的代理类型和​​​​基于 XML 模式的配置​​)。 为什么 bean 的定义范围在 和 自定义范围 级别需要元素吗? 考虑以下单例 Bean 定义并将其与 您需要为上述范围定义什么(请注意,以下 Bean 定义不完整):​​​​request​​​​session​​​​​​userPreferences​





在前面的示例中,单例 bean () 注入了引用 到 HTTP 范围的 Bean ()。这里的要点是 thebean 是一个单例:它每 容器及其依赖项(在本例中只有一个,thebean)是 也只注射一次。这意味着 thebean 仅在 完全相同的对象(即最初注入的对象)。​​userManager​​​​Session​​​​userPreferences​​​​userManager​​​​userPreferences​​​​userManager​​​​userPreferences​

这不是将生存期较短的范围 Bean 注入 生存期更长的作用域 Bean(例如,注入 HTTP 作用域协作 Bean 作为单例 Bean 的依赖关系)。相反,你需要一个对象,并且在HTTP的生命周期内,你需要一个对象。 这是特定于HTTP的。因此,容器创建一个对象 公开与类完全相同的公共接口(理想情况下 对象是实例),它可以从范围机制(HTTP 请求等)获取真实对象 四)。容器将此代理对象注入到bean中,即 不知道此引用是代理。在此示例中,当 ainstance 调用依赖项注入对象上的方法时,它实际上是在代理上调用方法。然后,代理从(在本例中)HTTP 获取真实对象,并委托 对检索到的实对象进行方法调用。​​Session​​​​userManager​​​​Session​​​​userPreferences​​​​Session​​​​UserPreferences​​​​UserPreferences​​​​UserPreferences​​​​Session​​​​userManager​​​​UserPreferences​​​​UserManager​​​​UserPreferences​​​​UserPreferences​​​​Session​​​​UserPreferences​

因此,在将 andbeans 注入协作对象时,您需要以下(正确且完整)配置,如以下示例所示 显示:​​request-​​​​session-scoped​







选择要创建的代理类型

默认情况下,当 Spring 容器为标记为 元素,创建一个基于 CGLIB 的类代理。​

或者,您可以配置 Spring 容器以创建标准 JDK 此类作用域 Bean 的基于接口的代理,通过指定 元素的属性。使用 JDK 基于接口的代理意味着您不需要在 影响此类代理的应用程序类路径。但是,这也意味着 作用域 Bean 必须实现至少一个接口,并且所有协作者都必须实现 注入作用域 Bean 的 Bean 必须通过其之一引用 Bean 接口。以下示例显示了基于接口的代理:​​false​​​​proxy-target-class​​​








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

1.5.5. 自定义作用域

Bean 作用域机制是可扩展的。您可以定义自己的 范围甚至重新定义现有范围,尽管后者被认为是不好的做法 并且您无法覆盖内置和范围。​​singleton​​​​prototype​

创建自定义范围

要将自定义作用域集成到 Spring 容器中,您需要实现接口,如下所述 部分。有关如何实现自己的作用域的想法,请参阅Spring 框架本身和作用域javadoc 提供的实现, 这更详细地解释了您需要实现的方法。​​org.springframework.beans.factory.config.Scope​​​​Scope​

该接口有四种方法可以从范围中获取对象,将它们从 范围,并让他们被摧毁。​​Scope​

例如,会话作用域实现返回会话作用域的 Bean(如果 不存在,该方法在将 Bean 绑定到 供将来参考的会议)。以下方法从 基础范围:

Object get(String name, ObjectFactory objectFactory)

例如,会话作用域实现从 基础会话。应该返回该对象,但您可以返回如果 找不到具有指定名称的对象。以下方法从中删除对象 底层范围:​​null​

Object remove(String name)

以下方法注册一个回调,当范围 销毁或当作用域中的指定对象被销毁时:

void registerDestructionCallback(String name, Runnable destructionCallback)

请参阅javadoc或 Spring 范围实现,了解有关销毁回调的更多信息。

以下方法获取基础范围的会话标识符:

String getConversationId()

此标识符对于每个范围都是不同的。对于会话范围的实现,这 标识符可以是会话标识符。

使用自定义范围

编写并测试一个或多个自定义实现后,需要 Spring 容器知道您的新范围。以下方法是中心 使用 Spring 容器注册 new 的方法:​​Scope​​​​Scope​

void registerScope(String scopeName, Scope scope);

此方法在接口上声明,该接口可用 通过 属性 在 Spring 附带的大多数具体实现上。​​ConfigurableBeanFactory​​​​BeanFactory​​​​ApplicationContext​

方法的第一个参数是与 范围。Spring 容器本身中此类名称的示例是 and。方法的第二个参数是一个实际实例 您希望注册和使用的自定义实现。​​registerScope(..)​​​​singleton​​​​prototype​​​​registerScope(..)​​​​Scope​

假设您编写自定义实现,然后按如下所示注册它 在下一个示例中。​​Scope​

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

然后,您可以创建符合自定义范围规则的 Bean 定义,如下所示:​​Scope​

使用自定义实现,您不仅限于程序化注册 的范围。还可以使用 theclass 以声明方式进行注册,如以下示例所示:​​Scope​​​​Scope​​​​CustomScopeConfigurer​


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">




















1.6. 自定义 Bean 的性质

Spring 框架提供了许多接口,您可以使用它们来自定义性质 的豆子。本节按如下方式对它们进行分组:

  • 生命周期回调
  • ApplicationContextAware和BeanNameAware
  • 其他感知接口

1.6.1. 生命周期回调

要与容器对 Bean 生命周期的管理进行交互,您可以实现 斯普林安德。容器要求前者,后者让豆子 在初始化和销毁 Bean 时执行某些操作。​​InitializingBean​​​​DisposableBean​​​​afterPropertiesSet()​​​​destroy()​

在内部,Spring 框架使用实现来处理任何 它可以查找并调用相应方法的回调接口。如果您需要定制 功能或其他生命周期行为 Spring 默认不提供,您可以 实施自己。有关详细信息,请参阅容器扩展点​。​​BeanPostProcessor​​​​BeanPostProcessor​

除了初始化和销毁回调之外,Spring 管理的对象可以 还要实现接口,以便这些对象可以参与 启动和关闭过程,由容器自身的生命周期驱动。​​Lifecycle​

本节介绍了生命周期回调接口。

初始化回调

接口让豆子 在容器上设置了所有必要的属性后,执行初始化工作 豆。该接口指定单个方法:​​org.springframework.beans.factory.InitializingBean​​​​InitializingBean​

void afterPropertiesSet() throws Exception;

我们建议您不要使用该接口,因为它 不必要地将代码耦合到 Spring。或者,我们建议使用 @PostConstruct注释或 指定 POJO 初始化方法。对于基于 XML 的配置元数据, 可以使用属性指定具有 void 的方法的名称 无参数签名。使用Java配置,可以使用属性。请参阅接收生命周期回调。请考虑以下示例:​​InitializingBean​​​​init-method​​​​initMethod​​​​@Bean​

public class ExampleBean {

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

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

public class AnotherExampleBean implements InitializingBean {

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

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

销毁回调

实现接口可以让 Bean 在包含它的容器被销毁时收到回调。该接口指定单个方法:​​org.springframework.beans.factory.DisposableBean​​​​DisposableBean​

void destroy() throws Exception;

我们建议您不要使用回调接口,因为它 不必要地将代码耦合到 Spring。或者,我们建议使用 @PreDestroy注释或 指定 Bean 定义支持的泛型方法。使用基于 XML 的 配置元数据,可以在属性上使用。 使用Java配置,可以使用属性。请参阅接收生命周期回调。请考虑以下定义:​​DisposableBean​​​​destroy-method​​​​​​destroyMethod​​​​@Bean​

public class ExampleBean {

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

前面的定义与下面的定义几乎完全相同:

public class AnotherExampleBean implements DisposableBean {

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

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

默认初始化和销毁方法

编写不使用 特定于弹簧和回调接口,您 通常编写具有诸如 ,,, 等名称的方法 上。理想情况下,此类生命周期回调方法的名称在 项目,以便所有开发人员使用相同的方法名称并确保一致性。​​InitializingBean​​​​DisposableBean​​​​init()​​​​initialize()​​​​dispose()​

您可以将 Spring 容器配置为“查找”命名初始化和销毁 每个 Bean 上的回调方法名称。这意味着您作为应用程序 开发人员,可以编写应用程序类并使用调用的初始化回调,而无需为每个 Bean 配置 anattribute 定义。Spring IoC 容器在创建 bean 时调用该方法(并且在 根据前面描述的标准生命周期回调协定)。此功能还强制实施一致的命名约定 初始化和销毁方法回调。​​init()​​​​init-method="init"​

假设您的初始化回调方法被命名并且您的销毁 回调方法已命名。然后,您的类类似于 以下示例:​​init()​​​​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.");
}
}
}

然后,您可以在类似于以下内容的 Bean 中使用该类:







顶级元素上属性的存在 属性使 Spring IoC 容器识别在 Bean 上调用的方法 类作为初始化方法回调。当创建和组装 Bean 时,如果 Bean 类有这样的方法,它在适当的时候被调用。​​default-init-method​​​​​​init​

您可以通过在顶级元素上使用属性来类似地配置 destroy 方法回调(即在 XML 中)。​​default-destroy-method​​​

现有 Bean 类已具有以差异命名的回调方法 使用该约定,您可以通过指定(在 XML 中)来覆盖默认值 方法名称通过使用自身的属性。​​init-method​​​​destroy-method​​​

Spring 容器保证调用配置的初始化回调 在为 Bean 提供所有依赖项后立即。因此,初始化 回调是在原始 Bean 引用上调用的,这意味着 AOP 拦截器等 第四个尚未应用于豆子。首先完全创建目标 Bean,然后 然后应用 AOP 代理(例如)及其拦截器链。如果目标 Bean 和代理是分开定义的,您的代码甚至可以与 RAW 交互 目标 Bean,绕过代理。因此,适用 拦截器,因为这样做会耦合 将 Bean 定位到其代理或拦截器,并在代码时留下奇怪的语义 直接与原始目标 Bean 交互。​​init​

结合生命周期机制

从 Spring 2.5 开始,您有三个选项来控制 Bean 生命周期行为:

  • 初始化 Bean和DisposableBean回调接口
  • 自定义和方法init()destroy()
  • @PostConstruct和@PreDestroy注释。您可以组合这些机制来控制给定的 Bean。

为同一 Bean 配置了多个生命周期机制,具有不同的 初始化方法,调用如下:

  1. 用 注释的方法@PostConstruct
  2. ​afterPropertiesSet()​​由回调接口定义InitializingBean
  3. 自定义配置方法init()

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

  1. 用 注释的方法@PreDestroy
  2. ​destroy()​​由回调接口定义DisposableBean
  3. 自定义配置方法destroy()
启动和关闭回调

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

public interface Lifecycle {

void start();

void stop();

boolean isRunning();
}

任何 Spring 管理的对象都可以实现该接口。然后,当自身收到启动和停止信号时(例如,对于停止/重新启动 运行时的场景),它将这些调用级联到所有实现 在该上下文中定义。它通过委派给 a 来做到这一点,如下所示 在以下列表中:​​Lifecycle​​​​ApplicationContext​​​​Lifecycle​​​​LifecycleProcessor​

public interface LifecycleProcessor extends Lifecycle {

void onRefresh();

void onClose();
}

请注意,它本身就是接口的扩展。它还添加了另外两种方法,用于对正在刷新的上下文做出反应 并关闭。​​LifecycleProcessor​​​​Lifecycle​

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

public interface Phased {

int getPhase();
}

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

public interface SmartLifecycle extends Lifecycle, Phased {

boolean isAutoStartup();

void stop(Runnable callback);
}

启动时,相位最低的对象首先启动。停止时, 遵循相反的顺序。因此,一个实现和 谁的方法返回将是第一个开始的 最后一个停下来。在光谱的另一端,相位值 将表示对象应最后启动并停止 首先(可能是因为它依赖于要运行的其他进程)。在考虑 相位值,同样重要的是要知道任何未实现的“正常”对象的默认相位是。因此,任何 负相位值表示对象应在这些标准之前启动 组件(并在它们之后停止)。对于任何正相位值,情况正好相反。​​SmartLifecycle​​​​getPhase()​​​​Integer.MIN_VALUE​​​​Integer.MAX_VALUE​​​​Lifecycle​​​​SmartLifecycle​​​​0​

通过接受回调定义的停止方法。任何 实现必须在该实现之后调用该回调的方法 关机过程完成。这将在必要时启用异步关闭,因为 接口的默认实现,等待对象组的超时值 在每个阶段中调用该回调。默认的每阶段超时为 30 秒。 您可以通过在上下文中定义命名的 Bean 来覆盖缺省生命周期处理器实例。如果只想修改超时, 定义以下内容就足够了:​​SmartLifecycle​​​​run()​​​​LifecycleProcessor​​​​DefaultLifecycleProcessor​​​​lifecycleProcessor​




如前所述,该接口定义了 刷新和关闭上下文。后者驱动关机 进程,就像 好像被显式调用,但它发生在上下文 关闭。另一方面,“刷新”回调启用了 bean 的另一个功能。刷新上下文时(在所有对象都已刷新之后) 实例化和初始化),将调用该回调。在这一点上, 默认生命周期处理器检查每个对象的方法返回的布尔值。如果,该对象是 从该点开始,而不是等待上下文的显式调用 or 它自己的方法(与上下文刷新不同,上下文启动不会发生 自动用于标准上下文实现)。该值和任何 如前所述,“依赖”关系确定启动顺序。​​LifecycleProcessor​​​​stop()​​​​SmartLifecycle​​​​SmartLifecycle​​​​isAutoStartup()​​​​true​​​​start()​​​​phase​

在非 Web 应用程序中正常关闭 Spring IoC 容器

如果您在非Web应用程序环境中使用Spring的IoC容器(用于 例如,在富客户端桌面环境中),使用 JVM.这样做可确保正常关闭,并在您的 单例 bean,以便释放所有资源。您仍必须配置 并正确实现这些销毁回调。

若要注册关闭挂钩,请调用 在接口上声明,如以下示例所示:​​registerShutdownHook()​​​​ConfigurableApplicationContext​

爪哇岛

科特林

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.和​​ApplicationContextAware​​​​BeanNameAware​

创建实现接口的对象实例时,将提供该实例 并提及这一点。以下清单显示了定义 的界面:​​ApplicationContext​​​​org.springframework.context.ApplicationContextAware​​​​ApplicationContext​​​​ApplicationContextAware​

public interface ApplicationContextAware {

void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

因此,豆子可以通过编程方式操作创建它们的人, 通过接口或通过将引用强制转换为已知的 此接口的子类(例如,公开 附加功能)。一种用途是以编程方式检索其他 bean。 有时此功能很有用。但是,一般来说,您应该避免它,因为 它将代码耦合到 Spring 并且不遵循控制反转样式, 其中协作者作为属性提供给 bean。其他方法提供对文件资源的访问,发布应用程序事件, 并访问 a.应用程序上下文的其他功能中介绍了这些附加功能。​​ApplicationContext​​​​ApplicationContext​​​​ConfigurableApplicationContext​​​​ApplicationContext​​​​MessageSource​

自动布线是获取参考的另一种选择。传统和自动布线模式 (如自动连线协作者中所述)可以为构造函数参数或 setter 方法参数提供类型依赖, 分别。获得更大的灵活性,包括自动连线字段和 多种参数方法,使用基于注释的自动接线功能。如果你这样做, 自动连接到字段、构造函数参数或方法 参数,如果字段、构造函数或 所讨论的方法带有注释。有关详细信息,请参阅使用@Autowired。​​ApplicationContext​​​​constructor​​​​byType​​​​ApplicationContext​​​​ApplicationContext​​​​ApplicationContext​​​​@Autowired​

当 an 创建实现接口的类时,该类将提供 对其关联对象定义中定义的名称的引用。以下列表 显示了 BeanNameAware 接口的定义:​​ApplicationContext​​​​org.springframework.beans.factory.BeanNameAware​

public interface BeanNameAware {

void setBeanName(String name) throws BeansException;
}

回调是在填充正常 Bean 属性之后但在 初始化回调,例如自定义 初始化方法。​​InitializingBean.afterPropertiesSet()​

1.6.3. 其他接口​​Aware​

此外(前面讨论过), Spring 提供了广泛的回调接口,让 bean 向容器指示 它们需要一定的基础结构依赖性。作为一般规则,该名称表示 依赖项类型。下表总结了最重要的接口:​​ApplicationContextAware​​​​BeanNameAware​​​​Aware​​​​Aware​

Table 4. Aware interfaces

名字

注入依赖

解释在...

​ApplicationContextAware​

声明。​​ApplicationContext​

​​ApplicationContextAware和BeanNameAware​​

​ApplicationEventPublisherAware​

封闭的事件发布者。​​ApplicationContext​

应用程序上下文的其他功能

​BeanClassLoaderAware​

用于装入 Bean 类的类装入器。

实例化 Bean

​BeanFactoryAware​

声明。​​BeanFactory​

BeanFactoryAPI

​BeanNameAware​

声明 Bean 的名称。

ApplicationContextAware和BeanNameAware

​LoadTimeWeaverAware​

定义的编织器,用于在加载时处理类定义。

在 Spring 框架中使用 AspectJ 进行加载时编织

​MessageSourceAware​

配置的消息解析策略(支持参数化和 国际化)。

应用程序上下文的其他功能

​NotificationPublisherAware​

Spring JMX 通知发布者。

通知

​ResourceLoaderAware​

配置了用于对资源的低级别访问的加载程序。

资源

​ServletConfigAware​

当前容器运行。仅在具有网络感知的春季中有效。​​ServletConfig​​​​ApplicationContext​

春季MVC

​ServletContextAware​

当前容器运行。仅在具有网络感知的春季中有效。​​ServletContext​​​​ApplicationContext​

春季MVC

再次注意,使用这些接口将代码绑定到 Spring API,并且不会 遵循控制反转样式。因此,我们建议将它们用于基础设施 需要以编程方式访问容器的 Bean。

1.7. Bean 定义继承

Bean 定义可以包含大量配置信息,包括构造函数 参数、属性值和特定于容器的信息,例如初始化 方法、静态工厂方法名称等。继承子 Bean 定义 父定义的配置数据。子定义可以覆盖某些 值或根据需要添加其他值。使用父 Bean 和子 Bean 定义可以节省很多 的打字。实际上,这是一种模板形式。

如果以编程方式使用 aninterface,则子 bean 定义由类表示。大多数用户不工作 和他们在这个层面上。相反,它们在类中以声明方式配置 Bean 定义 比如。使用基于 XML 的配置时 元数据,您可以使用属性指示子 Bean 定义, 将父 Bean 指定为此属性的值。以下示例演示如何 为此:​​ApplicationContext​​​​ChildBeanDefinition​​​​ClassPathXmlApplicationContext​​​​parent​

        class="org.springframework.beans.TestBean">




class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">


子 Bean 定义使用父定义中的 Bean 类(如果没有 指定,但也可以覆盖它。在后一种情况下,子豆类必须是 与父级兼容(即,它必须接受父级的属性值)。

子 Bean 定义继承作用域、构造函数参数值、属性值和 方法从父级重写,并具有添加新值的选项。任何范围,初始化 方法、销毁方法、您指定的工厂方法设置 覆盖相应的父设置。​​static​

其余设置始终取自子定义:取决于, 自动连线模式、依赖关系检查、单例和延迟初始化。

前面的示例使用 属性。如果父定义未指定类,则显式 将父 Bean 定义标记为必需,如以下示例所示 显示:​​abstract​​​​abstract​






parent="inheritedTestBeanWithoutClass" init-method="initialize">


父 Bean 不能自行实例化,因为它不完整,并且它是 也明确标记为。当定义是时,它是 只能用作纯模板 Bean 定义,用作 子定义。尝试自己使用这样的母豆,通过参考 作为另一个 Bean 的 ref 属性或对 父 Bean ID 返回错误。同样,容器的内部方法忽略定义为 抽象。​​abstract​​​​abstract​​​​abstract​​​​getBean()​​​​preInstantiateSingletons()​

1.8. 容器扩展点

通常,应用程序开发人员不需要对实现类进行子类化。相反,Spring IoC容器可以通过插入来扩展。 特殊集成接口的实现。接下来的几节将介绍这些 集成接口。​​ApplicationContext​

1.8.1. 使用​​BeanPostProcessor​

该接口定义可以实现的回调方法 提供您自己的(或覆盖容器的默认)实例化逻辑、依赖项 解析逻辑,等等。如果要在 Spring 容器完成实例化、配置和初始化 bean,您可以 插入一个或多个自定义实现。​​BeanPostProcessor​​​​BeanPostProcessor​

您可以配置多个实例,并且可以控制顺序 其中这些实例通过设置属性运行。 仅当实现接口时,才能设置此属性。如果你自己编写,你应该考虑实现 界面也是。有关更多详细信息,请参阅BeanPostProcessor和Ordered接口的 javadoc。另请参阅注释 在程序化 注册 BeanPostProcessor实例。​​BeanPostProcessor​​​​BeanPostProcessor​​​​order​​​​BeanPostProcessor​​​​Ordered​​​​BeanPostProcessor​​​​Ordered​

该接口由 正好是两个回调方法。当此类注册为后处理器时 容器,对于容器创建的每个 Bean 实例, 后处理器在容器之前从容器获取回调 初始化方法(例如或任何 声明方法)被调用,并在任何 Bean 初始化回调之后调用。 后处理器可以对 Bean 实例执行任何操作,包括忽略 完全回调。Bean 后处理器通常检查回调接口, 或者它可以用代理包裹豆子。一些春季 AOP 基础设施类是 作为 Bean 后处理器实现,以提供代理包装逻辑。​​org.springframework.beans.factory.config.BeanPostProcessor​​​​InitializingBean.afterPropertiesSet()​​​​init​

自动检测 实现接口的配置元数据。将这些 bean 注册为后处理器,以便可以调用它们 后来,在豆子创造时。Bean 后处理器可以部署在容器中的 与任何其他豆子相同的方式。​​ApplicationContext​​​​BeanPostProcessor​​​​ApplicationContext​

请注意,当在 配置类,工厂方法的返回类型应该是实现 类本身或至少是接口,清楚地表明该 Bean 的后处理器性质。否则,在完全创建它之前无法按类型自动检测它。 由于需要尽早实例化才能应用于 初始化上下文中的其他 bean,这种早期类型检测至关重要。​​BeanPostProcessor​​​​@Bean​​​​org.springframework.beans.factory.config.BeanPostProcessor​​​​ApplicationContext​​​​BeanPostProcessor​

以下示例演示如何编写、注册和使用实例 在安。​​BeanPostProcessor​​​​ApplicationContext​

示例:你好世界,样式​​BeanPostProcessor​

第一个示例说明了基本用法。该示例显示了一个自定义实现,该实现调用每个 Bean 的方法为 它由容器创建,并将生成的字符串打印到系统控制台。​​BeanPostProcessor​​​​toString()​

下面的清单显示了定制实现类定义:​​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;
}
}

以下元素使用:​​beans​​​​InstantiationTracingBeanPostProcessor​


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">

script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">






请注意,这些仅仅是如何定义的。它没有 甚至有一个名字,而且,因为它是一个 bean,它可以像任何一样被依赖注入 其他豆子。(前面的配置还定义了一个由 Groovy 支持的 bean 脚本。Spring 动态语言支持在标题为动态语言支持的章节中有详细说明。​​InstantiationTracingBeanPostProcessor​

以下 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);
}

}

上述应用程序的输出类似于以下内容:

Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
示例:该​​AutowiredAnnotationBeanPostProcessor​

将回调接口或注释与自定义实现结合使用是扩展 Spring IoC 容器的常用方法。一个例子是 春天的——实施 附带弹簧分布和自动连线注释字段、设置器方法, 和任意配置方法。​​BeanPostProcessor​​​​AutowiredAnnotationBeanPostProcessor​​​​BeanPostProcessor​

1.8.2. 使用​​BeanFactoryPostProcessor​

我们要看的下一个扩展点是。的语义 这个界面类似于那些,有一个主要的 差异:对 Bean 配置元数据进行操作。 也就是说,Spring IoC 容器允许读取 配置元数据,并可能在容器实例化之前对其进行更改 除实例以外的任何豆类。​​org.springframework.beans.factory.config.BeanFactoryPostProcessor​​​​BeanPostProcessor​​​​BeanFactoryPostProcessor​​​​BeanFactoryPostProcessor​​​​BeanFactoryPostProcessor​

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

Bean 工厂后处理器在 an 内部声明时会自动运行,以便将更改应用于配置元数据 定义容器。Spring 包括许多预定义的 bean 工厂 后处理器,如桑德。您还可以使用自定义 — 例如,注册自定义属性编辑器。​​ApplicationContext​​​​PropertyOverrideConfigurer​​​​PropertySourcesPlaceholderConfigurer​​​​BeanFactoryPostProcessor​

自动检测部署到其中的任何 bean 实现接口。它使用这些豆子作为豆子工厂 后处理器,在适当的时间。您可以将这些后处理器 Bean 部署为 你会有任何其他豆子。​​ApplicationContext​​​​BeanFactoryPostProcessor​

示例:类名替换​​PropertySourcesPlaceholderConfigurer​

您可以使用 外部化属性值 通过使用标准 Javaformat 从单独文件中的 Bean 定义。 这样做使部署应用程序的人员能够自定义特定于环境的人员 属性,例如数据库 URL 和密码,没有 修改容器的主 XML 定义文件。​​PropertySourcesPlaceholderConfigurer​​​​Properties​

请考虑以下基于 XML 的配置元数据片段,其中定义了 awith 占位符值:​​DataSource​





class="org.apache.commons.dbcp.BasicDataSource">




该示例显示了从外部文件配置的属性。在运行时, 应用于替换某些元数据的 AIS 数据源的属性。要替换的值被指定为 形式,遵循 Ant 和 log4j 和 JSP EL 风格。​​Properties​​​​PropertySourcesPlaceholderConfigurer​​​​${property-name}​

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

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

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

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

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


您可以使用 theto 替换类名,这 当您必须在运行时选择特定的实现类时,有时很有用。 以下示例演示如何执行此操作:​​PropertySourcesPlaceholderConfigurer​






classpath:com/something/strategy.properties


custom.strategy.class=com.something.DefaultStrategy






如果无法在运行时将类解析为有效类,则解析 Bean 在即将创建时失败,这是在非惰性初始化 Bean 的阶段。​​preInstantiateSingletons()​​​​ApplicationContext​


示例:该​​PropertyOverrideConfigurer​

另一个豆厂后处理器,类似于,但与后者不同的是,原始定义 对于 Bean 属性,可以有默认值或根本没有值。如果覆盖文件没有某个 Bean 属性的条目,则缺省值 使用上下文定义。​​PropertyOverrideConfigurer​​​​PropertySourcesPlaceholderConfigurer​​​​Properties​

请注意,Bean 定义不知道被覆盖,因此它不是 从 XML 定义文件中可以立即明显看出覆盖配置程序正在 使用。在定义不同 同一 Bean 属性的值,由于覆盖机制,最后一个属性获胜。​​PropertyOverrideConfigurer​

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

beanName.property=value

以下清单显示了格式的示例:

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

此示例文件可以与包含名为 hasandproperties 的 bean 的容器定义一起使用。​​dataSource​​​​driver​​​​url​

还支持复合属性名称,只要路径的每个组件 除了被覆盖的最后一个属性已经是非 null(大概已初始化) 由构造函数)。在下面的示例中,属性的 属性 的 属性 设置为标量值:​​sammy​​​​bob​​​​fred​​​​tom​​​​123​

tom.fred.bob.sammy=123

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

1.8.3. 使用​​FactoryBean​

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

接口是可插拔到Spring IoC容器的一个点 实例化逻辑。如果您有复杂的初始化代码,最好用 与(可能)冗长的XML相比,Java可以创建自己的XML,在该类中编写复杂的初始化,然后插入您的 自定义到容器中。​​FactoryBean​​​​FactoryBean​​​​FactoryBean​

该接口提供三种方法:​​FactoryBean

  • ​T getObject()​​:返回此工厂创建的对象的实例。这 实例可以共享,具体取决于此工厂是否返回单例 或原型。
  • ​boolean isSingleton()​​:返回如果返回单例或其他。此方法的默认实现返回。trueFactoryBeanfalsetrue
  • ​Class getObjectType()​​:返回方法返回的对象类型 或者如果事先不知道类型。getObject()null

概念和界面在弹簧内的许多地方使用 框架。超过 50 种接口的实现随 Spring 一起提供 本身。​​FactoryBean​​​​FactoryBean​

当您需要向容器请求实际实例本身而不是 它生成的 bean,在 bean 前面加上 & 符号 () 时 调用的方法。因此,对于给定的 anof,调用容器返回 的乘积,而调用返回实例本身。​​FactoryBean​​​​id​​​​&​​​​getBean()​​​​ApplicationContext​​​​FactoryBean​​​​id​​​​myBean​​​​getBean("myBean")​​​​FactoryBean​​​​getBean("&myBean")​​​​FactoryBean​

版本 5.3.23

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线