返回

Spring 框架的核心技术(三)

发布时间:2023-11-17 18:01:15 211

Spring 框架的核心技术(三)_sql

1.13. 环境抽象

环境界面 是集成在容器中的抽象,它对两个键进行建模 应用程序环境的各个方面:配置文件和属性。

概要文件是要注册到 Bean 定义的命名逻辑组 仅当给定配置文件处于活动状态时,容器。可以将 Bean 分配给配置文件 无论是在 XML 中定义还是使用批注定义。对象的作用与 与配置文件的关系在于确定哪些配置文件(如果有)当前处于活动状态, 以及默认情况下哪些配置文件(如果有)应处于活动状态。​​Environment​

属性在几乎所有应用中都起着重要作用,可能源于 各种来源:属性文件、JVM 系统属性、系统环境 变量、JNDI、servlet 上下文参数、临时对象、对象等 上。对象与属性相关的角色是提供 用户具有方便的服务界面,用于配置属性源和解析 他们的属性。​​Properties​​​​Map​​​​Environment​

1.13.1. Bean 定义配置文件

Bean 定义概要文件在核心容器中提供了一种机制,允许 在不同环境中注册不同的豆子。“环境”这个词, 对不同的用户可能意味着不同的事情,此功能可以帮助许多人 用例,包括:

  • 在开发中处理内存中的数据源与查找相同的数据源 在 QA 或生产环境中来自 JNDI 的数据源。
  • 仅在将应用程序部署到 性能环境。
  • 为客户 A 与客户注册 Bean 的自定义实现 B 部署。

考虑实际应用中需要 a 的第一个用例。在测试环境中,配置可能类似于以下内容:​​DataSource​

@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("my-schema.sql")
.addScript("my-test-data.sql")
.build();
}

现在考虑如何将此应用程序部署到 QA 或生产环境中 环境,假设应用程序的数据源已注册 使用生产应用程序服务器的 JNDI 目录。乌尔豆 现在看起来像下面的列表:​​dataSource​

@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}

问题是如何根据 当前环境。随着时间的推移,Spring用户已经设计了许多方法来 完成此操作,通常依赖于系统环境变量的组合 和包含解析令牌的 XML 语句 到正确的配置文件路径,具体取决于环境的值 变量。Bean 定义概要文件是一个核心容器功能,它提供 解决这个问题。​​​​${placeholder}​

如果我们概括前面特定于环境的 Bean 示例中所示的用例 定义,我们最终需要在 某些上下文,但在其他上下文中则不然。你可以说你想注册一个 情况 A 中 Bean 定义的某些配置文件和情况 A 中的不同配置文件 情况B.我们首先更新我们的配置以反映这种需求。

用​​@Profile​

@Profile注释允许您指示组件符合注册条件 当一个或多个指定的配置文件处于活动状态时。使用前面的示例,我们 可以按如下方式重写配置:​​dataSource​

@Configuration
@Profile("development")
public class StandaloneDataConfig {

@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("production")
public class JndiDataConfig {

@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}

配置文件字符串可能包含简单的配置文件名称(例如)或 配置文件表达式。配置文件表达式允许更复杂的配置文件逻辑 表示(例如,)。以下运算符在 配置文件表达式:​​production​​​​production & us-east​

  • ​!​​:配置文件的逻辑“非”
  • ​&​​:配置文件的逻辑“和”
  • ​|​​:配置文件的逻辑“或”

您可以用作目的的元注释​ 创建自定义组合注释。以下示例定义了一个自定义批注,您可以将其用作直接替换:​​@Profile​​​​@Production​​​​@Profile("production")​

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}

​@Profile​​也可以在方法级别声明为仅包含一个特定的 Bean 的配置类(例如,对于特定 Bean 的替代变体),作为 以下示例显示:

@Configuration
public class AppConfig {

@Bean("dataSource")
@Profile("development")
public DataSource standaloneDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}

@Bean("dataSource")
@Profile("production")
public DataSource jndiDataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
XML Bean 定义概要文件

XML 对应项是元素的属性。我们前面的示例 可以在两个 XML 文件中重写配置,如下所示:​​profile​​​

    xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="...">





    xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">


也可以避免在同一文件中拆分和嵌套元素, 如以下示例所示:​

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">













已约束为仅允许此类元素,例如 文件中的最后一个。这应该有助于提供灵活性而不会产生 XML 文件中杂乱无章。​​spring-bean.xsd​

激活配置文件

现在我们已经更新了我们的配置,我们仍然需要指示 Spring 哪个 配置文件处于活动状态。如果我们现在开始我们的示例应用程序,我们将看到 抛出,因为容器找不到 春豆命名。​​NoSuchBeanDefinitionException​​​​dataSource​

激活配置文件可以通过多种方式完成,但最直接的是 它以编程方式针对可通过 a 获得的 API。以下示例演示如何执行此操作:​​Environment​​​​ApplicationContext​

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();

此外,您还可以通过属性以声明方式激活配置文件,该属性可以通过系统环境指定 变量、JVM 系统属性、servlet 上下文参数,甚至作为 JNDI 中的条目(参见属性源抽象)。在集成测试中,活动 可以通过在模块中使用注释来声明配置文件(请参阅使用环境配置文件进行上下文配置)。​​spring.profiles.active​​​​web.xml​​​​@ActiveProfiles​​​​spring-test​

请注意,配置文件不是“非此即彼”的命题。您可以激活多个 一次配置文件。以编程方式,您可以向接受 varargs 的方法提供多个配置文件名称。以下示例 激活多个配置文件:​​setActiveProfiles()​​​​String…​

ctx.getEnvironment().setActiveProfiles("profile1", "profile2");

声明性地,可以接受以逗号分隔的配置文件名称列表, 如以下示例所示:​​spring.profiles.active​

-Dspring.profiles.active="profile1,profile2"
默认配置文件

默认配置文件表示默认启用的配置文件。考虑 以下示例:

@Configuration
@Profile("default")
public class DefaultDataConfig {

@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}

如果没有活动配置文件,则创建配置文件。你可以看到这个 作为为一个或多个 Bean 提供缺省定义的一种方法。如果有的话 配置文件已启用,则默认配置文件不适用。​​dataSource​

您可以使用 理论,声明性地,通过使用属性。​​setDefaultProfiles()​​​​Environment​​​​spring.profiles.default​

1.13.2.抽象​​PropertySource​

Spring 的抽象提供了对可配置的搜索操作 属性源的层次结构。请考虑以下列表:​​Environment​

ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);

在前面的片段中,我们看到了一种高级方法,询问 Spring 的属性是否为 为当前环境定义。为了回答这个问题,对象执行 搜索一组属性源对象。A是对任何键值对源的简单抽象,并且 Spring 的标准环境配置了两个 PropertySource 对象——一个代表一组 JVM 系统属性。 () 和一个表示系统环境变量集 ().​​my-property​​​​Environment​​​​PropertySource​​​​System.getProperties()​​​​System.getenv()​

具体来说,当你使用 时,如果系统属性或环境变量存在于 运行。​​StandardEnvironment​​​​env.containsProperty("my-property")​​​​my-property​​​​my-property​

最重要的是,整个机制是可配置的。也许您有一个自定义来源 要集成到此搜索中的属性。为此,请实施 并实例化您自己的并将其添加到集合中对于 当前。以下示例演示如何执行此操作:​​PropertySource​​​​PropertySources​​​​Environment​

爪哇岛

科特林

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());

在前面的代码中,已添加具有最高优先级的 搜索。如果它包含属性,则检测并返回该属性,以支持 任何其他财产。​​MutablePropertySources​​API 公开了许多方法,这些方法允许精确操作一组 属性源。​​MyPropertySource​​​​my-property​​​​my-property​​​​PropertySource​

1.13.3. 使用​​@PropertySource​

@PropertySource注释为添加 ato Spring 提供了一种方便的声明性机制。​​PropertySource​​​​Environment​

给定一个包含键值对的文件, 以下类以这样的方式使用 调用返回:​​app.properties​​​​testbean.name=myTestBean​​​​@Configuration​​​​@PropertySource​​​​testBean.getName()​​​​myTestBean​

@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {

@Autowired
Environment env;

@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}

资源位置中存在的任何占位符都是 针对已针对 环境,如以下示例所示:​​${…}​​​​@PropertySource​

@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
public class AppConfig {

@Autowired
Environment env;

@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}

假设它已经存在于其中一个属性源中 已注册(例如,系统属性或环境变量),占位符为 解析为相应的值。如果没有,则使用 作为默认值。如果未指定默认值且无法解析属性,则引发 anis 。​​my.placeholder​​​​default/path​​​​IllegalArgumentException​

1.13.4. 语句中的占位符解析

从历史上看,元素中占位符的值只能针对 JVM 系统属性或环境变量。情况已不再如此。因为 抽象集成在整个容器中,很容易 通过它路由占位符解析。这意味着您可以配置 以您喜欢的任何方式解决过程。您可以更改搜索的优先级 系统属性和环境变量或完全删除它们。您还可以添加您的 酌情拥有组合的属性来源。​​Environment​

具体来说,无论属性定义在哪里,以下语句都有效,只要它在:​​customer​​​​Environment​



1.14. 注册​​LoadTimeWeaver​

Theis 被 Spring 用来动态转换类,因为它们是 加载到 Java 虚拟机 (JVM) 中。​​LoadTimeWeaver​

要启用加载时编织,您可以将 thep 添加到您的一个类,如以下示例所示:​​@EnableLoadTimeWeaving​​​​@Configuration​

爪哇岛

科特林

@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}

或者,对于 XML 配置,您可以使用元素:​​context:load-time-weaver​



一旦配置了,其中的任何 bean 都可以实现,从而接收对加载时间的引用 织工实例。这与​​Spring 的 JPA 支持​​结合使用时特别有用,其中加载时间编织可能是 JPA 类转换所必需的。 有关更多详细信息,请参阅​​LocalContainerEntityManagerFactoryBean​​javadoc。有关 AspectJ 加载时编织的更多信息,请参阅​​Spring 框架中的 Load-time Weaving with AspectJ​​。​​ApplicationContext​​​​ApplicationContext​​​​LoadTimeWeaverAware​

1.15. 的附加功能​​ApplicationContext​

如​​本章引言​​中所讨论的,该包提供了管理和操作 bean 的基本功能,包括在 程序化方式。该包添加了​​ApplicationContext​​接口,该接口除了扩展其他接口外,还扩展了该接口 在更多应用程序中提供附加功能的接口 面向框架的风格。许多人使用他们的完全 声明式时尚,甚至不是以编程方式创建它,而是依赖于 支持类,例如自动实例化 Java EE Web 应用程序正常启动过程的一部分。​​org.springframework.beans.factory​​​​org.springframework.context​​​​BeanFactory​​​​ApplicationContext​​​​ContextLoader​​​​ApplicationContext​

为了以更面向框架的风格增强功能,上下文 包还提供以下功能:​​BeanFactory​

  • 通过界面访问 i18n 风格的消息。MessageSource
  • 通过界面访问资源,例如 URL 和文件。ResourceLoader
  • 事件发布,即实现接口的 bean, 通过使用接口。ApplicationListenerApplicationEventPublisher
  • 加载多个(分层)上下文,让每个上下文都集中在一个上下文上 特定层,例如应用程序的Web层,通过接口。HierarchicalBeanFactory

1.15.1. 国际化使用​​MessageSource​

该接口扩展了一个调用的接口, 因此,提供了国际化(“i18n”)功能。Spring 还提供了接口,可以分层解析消息。 这些接口共同为Spring 效果消息提供了基础。 分辨率。在这些接口上定义的方法包括:​​ApplicationContext​​​​MessageSource​​​​HierarchicalMessageSource​

  • ​String getMessage(String code, Object[] args, String default, Locale loc)​​:基本 用于从中检索消息的方法。未找到消息时 对于指定的区域设置,将使用默认消息。传入的任何参数都将成为 替换值,使用标准提供的功能 图书馆。MessageSourceMessageFormat
  • ​String getMessage(String code, Object[] args, Locale loc)​​:本质上与 以前的方法,但有一个区别:无法指定默认消息。如果 找不到消息,AI 已抛出。NoSuchMessageException
  • ​String getMessage(MessageSourceResolvable resolvable, Locale locale)​​:所有属性 上述方法中使用的也包装在名为的类中,可以与此方法一起使用。MessageSourceResolvable

加载 anis 时,它会自动搜索上下文中定义的 abean。豆子必须有名字。如果这样的豆子 找到,则对上述方法的所有调用都将委托给消息源。如果没有 找到消息源,尝试查找包含 同名豆。如果是这样,它将使用该豆子作为。如果找不到任何消息源,则实例化 emptyis 以便能够接受对 上面定义的方法。​​ApplicationContext​​​​MessageSource​​​​messageSource​​​​ApplicationContext​​​​MessageSource​​​​ApplicationContext​​​​DelegatingMessageSource​

Spring 提供了三种实现,和。所有这些都实现为了做嵌套 消息。Theis 很少使用,但提供了编程方式 将消息添加到源。以下示例显示:​​MessageSource​​​​ResourceBundleMessageSource​​​​ReloadableResourceBundleMessageSource​​​​StaticMessageSource​​​​HierarchicalMessageSource​​​​StaticMessageSource​​​​ResourceBundleMessageSource​


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


format
exceptions
windows



该示例假定您在类路径中调用和定义了三个资源包。任何解析消息的请求都是 以 JDK 标准方式处理,通过对象解析消息。对于 本示例的目的,假设上述两个资源包文件的内容 具体如下:​​format​​​​exceptions​​​​windows​​​​ResourceBundle​

# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.

下一个示例显示用于运行该功能的程序。 请记住,所有实现也是实现,因此可以强制转换为接口。​​MessageSource​​​​ApplicationContext​​​​MessageSource​​​​MessageSource​

public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
System.out.println(message);
}

上述程序的结果输出如下:

Alligators rock!

总而言之,theis 在一个调用的文件中定义,其中 存在于类路径的根目录中。底比亚的定义是指 通过其属性的资源包数。这三个文件 在列表中传递给属性作为文件存在于您的根目录 类路径和分别被调用、和。​​MessageSource​​​​beans.xml​​​​messageSource​​​​basenames​​​​basenames​​​​format.properties​​​​exceptions.properties​​​​windows.properties​

下一个示例显示传递给消息查找的参数。这些参数是 转换为对象并插入到查找消息的占位符中。​​String​













public class Example {

private MessageSource messages;

public void setMessages(MessageSource messages) {
this.messages = messages;
}

public void execute() {
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.ENGLISH);
System.out.println(message);
}
}

调用该方法的结果输出如下所示:​​execute()​

The userDao argument is required.

关于国际化(“i18n”),Spring 的各种实现遵循与标准 JDK 相同的语言环境解析和回退规则。简而言之,继续示例定义 以前,如果要解析针对英国 () 区域设置的邮件,则 将分别创建调用、和的文件。​​MessageSource​​​​ResourceBundle​​​​messageSource​​​​en-GB​​​​format_en_GB.properties​​​​exceptions_en_GB.properties​​​​windows_en_GB.properties​

通常,区域设置解析由 应用。在以下示例中,(英国)消息所针对的区域设置 已解决是手动指定的:

# in exceptions_en_GB.properties
argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
System.out.println(message);
}

运行上述程序的结果输出如下:

Ebagum lad, the 'userDao' argument is required, I say, required.

您还可以使用接口获取对已定义的任何内容的引用。在实现接口的 an 中定义的任何 Bean 都注入了 创建并配置 Bean 时的应用程序上下文。​​MessageSourceAware​​​​MessageSource​​​​ApplicationContext​​​​MessageSourceAware​​​​MessageSource​

1.15.2. 标准和自定义事件

事件处理是通过类和接口提供的。如果将实现接口的 Bean 部署到上下文中,则每次发布到 bean 时,都会通知该 Bean。 本质上,这是标准的观察者设计模式。​​ApplicationContext​​​​ApplicationEvent​​​​ApplicationListener​​​​ApplicationListener​​​​ApplicationEvent​​​​ApplicationContext​

下表描述了 Spring 提供的标准事件:

Table 7. Built-in Events

事件

解释

​ContextRefreshedEvent​

在初始化或刷新时发布(例如,由 在界面上使用方法)。 这里,“初始化”意味着加载了所有 bean,检测到后处理器 bean 并激活,单例被预先实例化,并且对象是 准备使用。只要上下文尚未关闭,就可以触发刷新 多次,前提是选择的人实际上支持这样的 “热”刷新。例如,支持热刷新,但不支持。​​ApplicationContext​​​​refresh()​​​​ConfigurableApplicationContext​​​​ApplicationContext​​​​ApplicationContext​​​​XmlWebApplicationContext​​​​GenericApplicationContext​

​ContextStartedEvent​

当使用接口上的方法启动时发布。在这里,“已启动”表示 allbean 接收到明确的启动信号。通常,此信号用于重新启动 bean 在显式停止后,但它也可用于启动尚未停止的组件 配置为自动启动(例如,尚未启动的组件 初始化)。​​ApplicationContext​​​​start()​​​​ConfigurableApplicationContext​​​​Lifecycle​

​ContextStoppedEvent​

当使用接口上的方法停止时发布。在这里,“停止”意味着所有豆子接收到明确的停止信号。停止的上下文可以通过调用重新启动。​​ApplicationContext​​​​stop()​​​​ConfigurableApplicationContext​​​​Lifecycle​​​​start()​

​ContextClosedEvent​

使用方法关闭时发布 在接口上或通过 JVM 关闭挂钩。这里 “关闭”意味着所有单例豆将被销毁。一旦上下文关闭, 它已达到其生命周期的终点,无法刷新或重新启动。​​ApplicationContext​​​​close()​​​​ConfigurableApplicationContext​

​RequestHandledEvent​

一个特定于 Web 的事件,告知所有 bean HTTP 请求已得到服务。这 事件在请求完成后发布。此活动仅适用于 使用 Spring 的 Web 应用程序。​​DispatcherServlet​

​ServletRequestHandledEvent​

的子类添加了特定于 Servlet 的上下文信息。​​RequestHandledEvent​

您还可以创建和发布自己的自定义事件。以下示例显示 扩展 Spring 基类的简单类:​​ApplicationEvent​

public class BlockedListEvent extends ApplicationEvent {

private final String address;
private final String content;

public BlockedListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}

// accessor and other methods...
}

若要发布自定义项,请在 上调用该方法。通常,这是通过创建一个实现的类并将其注册为 Spring Bean 来完成的。以下 示例显示了这样的类:​​ApplicationEvent​​​​publishEvent()​​​​ApplicationEventPublisher​​​​ApplicationEventPublisherAware​

public class EmailService implements ApplicationEventPublisherAware {

private List blockedList;
private ApplicationEventPublisher publisher;

public void setBlockedList(List blockedList) {
this.blockedList = blockedList;
}

public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}

public void sendEmail(String address, String content) {
if (blockedList.contains(address)) {
publisher.publishEvent(new BlockedListEvent(this, address, content));
return;
}
// send email...
}
}

在配置时,Spring 容器会检测到实现并自动调用。实际上,传入的参数是 Spring 容器本身。您正在通过其接口与应用程序上下文进行交互。​​EmailService​​​​ApplicationEventPublisherAware​​​​setApplicationEventPublisher()​​​​ApplicationEventPublisher​

要接收自定义,您可以创建一个实现的类并将其注册为 Spring Bean。以下示例 显示这样的类:​​ApplicationEvent​​​​ApplicationListener​

public class BlockedListNotifier implements ApplicationListener {

private String notificationAddress;

public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}

public void onApplicationEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}

请注意,它通常使用您的类型进行参数化 自定义事件(在前面的示例中)。这意味着该方法可以保持类型安全,避免任何向下转换的需要。 您可以根据需要注册任意数量的事件侦听器,但请注意,默认情况下,事件 侦听器同步接收事件。这意味着该方法 阻止,直到所有侦听器完成事件处理。这样做的一个优点 同步和单线程方法是,当侦听器收到事件时,它 如果事务上下文是 可用。如果需要另一种事件发布策略,请参阅 javadoc 对于 Spring 的应用程序事件多播器接口 和SimpleApplicationEventMulticaster实现,用于配置选项。​​ApplicationListener​​​​BlockedListEvent​​​​onApplicationEvent()​​​​publishEvent()​

以下示例显示了用于注册和配置每个 上面的类:




known.spammer@example.org
known.hacker@example.org
john.doe@example.org






把它们放在一起,当憨豆的方法 调用,如果有任何应阻止的电子邮件,则发布 typeis 的自定义事件。Thebean被注册为anand接收,此时它可以 通知相关方。​​sendEmail()​​​​emailService​​​​BlockedListEvent​​​​blockedListNotifier​​​​ApplicationListener​​​​BlockedListEvent​

基于注释的事件侦听器

您可以使用注释在受管 Bean 的任何方法上注册事件侦听器。可以重写如下:​​@EventListener​​​​BlockedListNotifier​

public class BlockedListNotifier {

private String notificationAddress;

public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}

@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}

方法签名再次声明它侦听的事件类型, 但是,这一次,使用灵活的名称并且没有实现特定的侦听器接口。 只要实际事件类型,也可以通过泛型缩小事件类型的范围 解析其实现层次结构中的泛型参数。

如果您的方法应该侦听多个事件,或者如果您想定义它而不使用 参数,事件类型也可以在注释本身上指定。这 以下示例演示如何执行此操作:

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}

还可以使用属性添加其他运行时筛选 定义SpEL表达式的注释,该注释应匹配 实际调用特定事件的方法。​​condition​

以下示例显示了如何重写我们的通知程序,使其仅在事件的属性等于时调用:​​content​​​​my-event​

@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}

每个表达式根据专用上下文进行评估。下表列出了 提供给上下文的项,以便您可以将它们用于条件事件处理:​​SpEL​

Table 8. Event SpEL available metadata

名字

位置

描述

事件

根对象

实际的。​​ApplicationEvent​

​#root.event​​​或​​event​

参数数组

根对象

用于调用方法的参数(作为对象数组)。

​#root.args​​​或者;访问第一个参数等。​​args​​​​args[0]​

参数名称

评估背景

任何方法参数的名称。如果由于某种原因名称不可用 (例如,因为编译的字节码中没有调试信息),单个 参数也可以使用语法 其中代表 参数索引(从 0 开始)。​​#a<#arg>​​​​<#arg>​

​#blEvent​​​或者(您也可以使用OR参数表示法作为别名)​​#a0​​​​#p0​​​​#p<#arg>​

请注意,这使您可以访问基础事件,即使您的方法 签名实际上是指已发布的任意对象。​​#root.event​

如果需要发布一个事件作为处理另一个事件的结果,则可以更改 方法签名以返回应发布的事件,如以下示例所示:

@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}

该方法在它处理的每个方法上都会发布一个新内容。如果需要发布多个事件,可以返回 或事件数组。​​handleBlockedListEvent()​​​​ListUpdateEvent​​​​BlockedListEvent​​​​Collection​

异步侦听器

如果希望特定侦听器异步处理事件,则可以重用常规@Async支持。 以下示例演示如何执行此操作:

@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
// BlockedListEvent is processed in a separate thread
}

使用异步事件时请注意以下限制:

  • 如果异步事件侦听器引发 ,则不会传播到 访客。有关更多详细信息,请参阅AsyncUncaughtExceptionHandler。Exception
  • 异步事件侦听器方法无法通过返回 价值。如果需要发布另一个事件作为处理的结果,请注入应用程序事件发布者以手动发布事件。
对侦听器进行排序

如果需要先调用一个侦听器,然后再调用另一个侦听器,则可以将注释添加到方法声明中,如以下示例所示:​​@Order​

@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
通用事件

您还可以使用泛型来进一步定义事件的结构。请考虑使用 anwhereis 创建的实际实体的类型。例如,您 可以创建以下侦听器定义以仅接收 a:​​EntityCreatedEvent​​​T​​​​EntityCreatedEvent​​​​Person​

@EventListener
public void onPersonCreated(EntityCreatedEvent event) {
// ...
}

由于类型擦除,仅当触发的事件解析泛型时,这才有效 事件侦听器筛选的参数(即类似参数)。​​class PersonCreatedEvent extends EntityCreatedEvent { … }​

在某些情况下,如果所有事件都遵循相同的内容,这可能会变得非常乏味 结构(如前面示例中的事件)。在这种情况下, 你可以实现引导框架超越运行时的内容 环境提供。以下事件演示如何执行此操作:​​ResolvableTypeProvider​

1.15.3. 方便访问低级资源

为了以最佳方式使用和理解应用程序上下文,您应该熟悉 你自己与春天的抽象,如​​参考资料​​中所述。​​Resource​

应用程序上下文是一个,可用于加载对象。 A本质上是JDKclass的一个功能更丰富的版本。 事实上,thewrap 的实现是一个实例,其中 适当。Acan 从几乎任何位置获取低级资源 透明时尚,包括从类路径、文件系统位置、任何位置 可以使用标准 URL 和其他一些变体进行描述。如果资源位置 字符串是一条没有任何特殊前缀的简单路径,这些资源来自哪里 特定于并适合实际的应用程序上下文类型。​​ResourceLoader​​​​Resource​​​​Resource​​​​java.net.URL​​​​Resource​​​​java.net.URL​​​​Resource​

您可以配置部署到应用程序上下文中的 Bean 以实现特殊的 回调接口,,在 自动回调 初始化时间与应用程序上下文本身作为传入。 还可以公开用于访问静态资源的类型属性。 它们像任何其他属性一样注入其中。您可以将这些属性指定为简单路径,并依赖于这些文本的自动转换 字符串到实际对象,当部署 Bean 时。​​ResourceLoaderAware​​​​ResourceLoader​​​​Resource​​​​Resource​​​​String​​​​Resource​

提供给构造函数的一个或多个位置路径实际上是 资源字符串,并以简单形式根据特定 上下文实现。例如对待一个简单的 作为类路径位置的位置路径。您还可以使用位置路径(资源字符串) 使用特殊前缀强制从类路径或 URL 加载定义, 无论实际上下文类型如何。​​ApplicationContext​​​​ClassPathXmlApplicationContext​

1.15.4. 应用程序启动跟踪

管理Spring应用程序的生命周期,并提供丰富的 围绕组件的编程模型。因此,复杂的应用程序可以具有相同的功能 复杂的组件图和启动阶段。​​ApplicationContext​

使用特定指标跟踪应用程序启动步骤有助于了解位置 在启动阶段花费了时间,但它也可以用作更好的方法 从整体上了解上下文生命周期。

(及其子类)与 an 一起检测,它收集有关各个启动阶段的数据:​​AbstractApplicationContext​​​​ApplicationStartup​​​​StartupStep​

  • 应用程序上下文生命周期(基本包扫描、配置类管理)
  • Bean 生命周期(实例化、智能初始化、后处理)
  • 应用程序事件处理

下面是以下检测的示例:​​AnnotationConfigApplicationContext​

// create a startup step and start recording
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages);
// end the current step
scanPackages.end();

应用程序上下文已通过多个步骤进行检测。 记录后,可以使用特定工具收集,显示和分析这些启动步骤。 有关现有启动步骤的完整列表,您可以查看专用附录部分。

默认实现是无操作变体,以实现最小的开销。 这意味着默认情况下,在应用程序启动期间不会收集任何指标。 Spring 框架附带了一个使用 Java Flight Recorder 跟踪启动步骤的实现:要使用此变体,您必须配置它的实例 一旦它被创建出来。​​ApplicationStartup​​​​FlightRecorderApplicationStartup​​​​ApplicationContext​

如果开发人员提供自己的子类,或者他们希望收集更精确的数据,他们也可以使用该基础结构。​​ApplicationStartup​​​​AbstractApplicationContext​

要开始收集自定义,组件可以直接从应用程序上下文中获取实例,使其组件实现, 或要求在任何注射点上的类型。​StartupStep​​​​ApplicationStartup​​​​ApplicationStartupAware​​​​ApplicationStartup​

1.15.5. 方便的应用程序Web 应用程序的上下文实例化

例如,您可以使用 a.当然,您也可以创建实例 通过使用其中一个实现以编程方式。​​ApplicationContext​​​​ContextLoader​​​​ApplicationContext​​​​ApplicationContext​

您可以使用 anby 注册,因为 以下示例显示:​​ApplicationContext​​​​ContextLoaderListener​


contextConfigLocation
/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml



org.springframework.web.context.ContextLoaderListener

侦听器检查参数。如果参数没有 存在,侦听器用作默认值。当 参数确实存在,侦听器通过使用预定义的参数来分隔 分隔符(逗号、分号和空格),并将值用作位置 搜索应用程序上下文。还支持 Ant 样式路径模式。 示例是(对于名称以 和 结尾的所有文件,驻留在目录中)和(对于任何子目录中的所有此类文件)。​​contextConfigLocation​​​​/WEB-INF/applicationContext.xml​​​​String​​​​/WEB-INF/*Context.xml​​​​Context.xml​​​​WEB-INF​​​​/WEB-INF/**/*Context.xml​​​​WEB-INF​

1.15.6. 部署 Springas a Java EE RAR 文件​​ApplicationContext​

可以部署一个 Springas 一个 RAR 文件,封装 上下文及其在 Java EE RAR 部署中所有必需的 Bean 类和库 JAR 单位。这相当于引导独立(仅托管) 在 Java EE 环境中)能够访问 Java EE 服务器设施。RAR 部署 是部署无头 WAR 文件的场景的更自然的替代方法 — 实际上, 一个没有任何 HTTP 入口点的 WAR 文件,仅用于在 Java EE 环境中引导 Spring。​​ApplicationContext​​​​ApplicationContext​​​​ApplicationContext​

RAR 部署非常适合不需要 HTTP 入口点但不需要 HTTP 入口点但 而是仅包含消息终结点和计划作业。在这种情况下,豆子可以 使用应用程序服务器资源,例如 JTA 事务管理器和 JNDI 绑定的 JDBC 实例和 JMS实例,还可以注册 该平台的JMX服务器 - 全部通过Spring的标准事务管理和JNDI。 和JMX支持设施。应用程序组件也可以与应用程序交互 服务器的JCA通过Spring的抽象。​​DataSource​​​​ConnectionFactory​​​​WorkManager​​​​TaskExecutor​

请参阅SpringContextResourceAdapter类的 javadoc,了解 RAR 部署中涉及的配置详细信息。

对于将Spring ApplicationContext简单部署为Java EE RAR文件:

  1. 包 所有应用程序类都放入一个 RAR 文件(这是一个标准 JAR 文件,具有不同的 文件扩展名)。
  2. 将所有必需的库 JAR 添加到 RAR 存档的根目录中。
  3. 添加部署描述符(如SpringContextResourceAdapter 的 javadoc 所示) 以及相应的 Spring XML Bean 定义文件(通常)。META-INF/ra.xmlMETA-INF/applicationContext.xml
  4. 将生成的 RAR 文件拖放到您的 应用程序服务器的部署目录。

1.16. 接口​​BeanFactory​

TheAPI 为 Spring 的 IoC 功能提供了底层基础。 它的具体合约主要用于与Spring的其他部分集成,并且 相关第三方框架及其实现 是更高级别的容器中的密钥委托。​​BeanFactory​​​​DefaultListableBeanFactory​​​​GenericApplicationContext​

​BeanFactory​​相关接口(如,,)是其他框架组件的重要集成点。 通过不需要任何注释甚至反射,它们允许非常有效的 容器与其组件之间的交互。应用程序级 bean 可以 使用相同的回调接口,但通常更喜欢声明性依赖项 而是通过注释或编程配置进行注入。​​BeanFactoryAware​​​​InitializingBean​​​​DisposableBean​

请注意,coreAPI 级别及其实现不会对配置格式或任何 要使用的组件注释。所有这些口味都是通过扩展进入的 (如桑德)和 对共享对象进行操作作为核心元数据表示形式。 这就是Spring的容器如此灵活和可扩展的本质。​​BeanFactory​​​​DefaultListableBeanFactory​​​​XmlBeanDefinitionReader​​​​AutowiredAnnotationBeanPostProcessor​​​​BeanDefinition​

1.16.1.或?​​BeanFactory​​​​ApplicationContext​

本节介绍容器级别之间的差异以及对引导的影响。​​BeanFactory​​​​ApplicationContext​

除非你有充分的理由不这样做,否则你应该使用 an,它的子类作为自定义引导的常见实现。这些是主要条目 指向 Spring 的核心容器,用于所有常见目的:加载配置 文件, 触发类路径扫描, 以编程方式注册 Bean 定义 和带注释的类,以及(从 5.0 开始)注册函数式 Bean 定义。​​ApplicationContext​​​​GenericApplicationContext​​​​AnnotationConfigApplicationContext​

因为 a 包含 a 的所有功能,所以它是 通常建议在平原上,除非已满 需要控制 Bean 加工。在一个(如实现)中,检测到几种 bean 按照约定(即按 Bean 名称或按 Bean 类型 — 特别是后处理器), 而普通人对任何特殊的豆子都不可知。​​ApplicationContext​​​​BeanFactory​​​​BeanFactory​​​​ApplicationContext​​​​GenericApplicationContext​​​​DefaultListableBeanFactory​

对于许多扩展容器功能,例如注释处理和 AOP 代理, BeanPostProcessor扩展点是必不可少的。 如果您仅使用普通处理器,则此类后处理器不会 默认情况下被检测并激活。这种情况可能会令人困惑,因为 您的 Bean 配置实际上没有任何问题。相反,在这种情况下, 容器需要通过其他设置完全引导。​​DefaultListableBeanFactory​

下表列出了 and 接口和实现提供的功能。​​BeanFactory​​​​ApplicationContext​

Table 9. Feature Matrix

特征

​BeanFactory​

​ApplicationContext​

Bean 实例化/连接

是的

是的

集成的生命周期管理

是的

自动注册​​BeanPostProcessor​

是的

自动注册​​BeanFactoryPostProcessor​

是的

便捷的访问(用于国际化)​​MessageSource​

是的

内置发布机制​​ApplicationEvent​

是的

要显式注册 Bean 后处理器, 您需要以编程方式调用,如以下示例所示:​​DefaultListableBeanFactory​​​​addBeanPostProcessor​

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions

// now register any needed BeanPostProcessor instances
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
factory.addBeanPostProcessor(new MyBeanPostProcessor());

// now start using the factory

要应用于平原, 您需要调用 itsMethod,如以下示例所示:​​BeanFactoryPostProcessor​​​​DefaultListableBeanFactory​​​​postProcessBeanFactory​

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));

// bring in some property values from a Properties file
PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));

// now actually do the replacement
cfg.postProcessBeanFactory(factory);

在这两种情况下,显式注册步骤都不方便,即 为什么各种变体比普通的 Spring-Spring支持的应用程序更受欢迎,尤其是当 依靠实例进行扩展 典型企业设置中的容器功能。​​ApplicationContext​​​​DefaultListableBeanFactory​​​​BeanFactoryPostProcessor​​​​BeanPostProcessor​

2. 资源

本章介绍 Spring 如何处理资源以及如何在 春天。它包括以下主题:

  • 介绍
  • 资源接口
  • 内置资源实现
  • 资源加载器接口
  • 资源模式解析器接口
  • 资源加载器感知接口
  • 作为依赖项的资源
  • 应用程序上下文和资源路径

2.1. 简介

Java 的标准类和各种 URL 前缀的标准处理程序, 不幸的是,不足以满足所有获得低级资源的机会。为 例如,没有可用于访问 需要从类路径或相对于 a 获取的资源。虽然可以为专用前缀注册新的处理程序(类似于前缀的现有处理程序,例如),但这通常是 相当复杂,界面仍然缺乏一些理想的功能, 例如用于检查所指向的资源是否存在的方法。​​java.net.URL​​​​URL​​​​ServletContext​​​​URL​​​​http:​​​​URL​

2.2. 界面​​Resource​

位于包装中的弹簧接口是 旨在成为更强大的接口,用于抽象对低级资源的访问。这 以下列表提供了界面的概述。有关更多详细信息,请参阅资源javadoc。​​Resource​​​​org.springframework.core.io.​​​​Resource​

public interface Resource extends InputStreamSource {

boolean exists();

boolean isReadable();

boolean isOpen();

boolean isFile();

URL getURL() throws IOException;

URI getURI() throws IOException;

File getFile() throws IOException;

ReadableByteChannel readableChannel() throws IOException;

long contentLength() throws IOException;

long lastModified() throws IOException;

Resource createRelative(String relativePath) throws IOException;

String getFilename();

String getDescription();
}

如接口的定义所示,它扩展了接口。以下清单显示了接口的定义:​​Resource​​​​InputStreamSource​​​​InputStreamSource​

public interface InputStreamSource {

InputStream getInputStream() throws IOException;
}

界面中一些最重要的方法是:​​Resource​

  • ​getInputStream()​​:查找并打开资源,返回 从资源读取。预计每次调用都会返回一个新的。调用方负责关闭流。InputStreamInputStream
  • ​exists()​​:返回指示此资源是否实际存在于 物理形式。boolean
  • ​isOpen()​​:返回指示此资源是否表示句柄 与开放的溪流。如果,无法多次读取并且 必须只读取一次,然后关闭以避免资源泄漏。返回 所有常用的资源实现,但 除外。booleantrueInputStreamfalseInputStreamResource
  • ​getDescription()​​:返回此资源的说明,用于错误 使用资源时的输出。这通常是完全限定的文件名或 资源的实际 URL。

其他方法允许您获取表示 资源(如果基础实现兼容并支持 功能)。​​URL​​​​File​

接口的某些实现还实现了扩展的WritableResource接口 支持写入的资源。​​Resource​

Spring 本身广泛使用抽象,作为 需要资源时的许多方法签名。某些 Spring API 中的其他方法 (例如各种实现的构造函数)以朴素或简单的形式用于创建适合 该上下文实现或通过路径上的特殊前缀,让 调用方指定必须创建和使用特定实现。​​Resource​​​​ApplicationContext​​​​String​​​​Resource​​​​String​​​​Resource​

虽然接口在 Spring 和 Spring 中大量使用,但它实际上是 非常方便在您自己的代码中单独用作通用实用程序类,以便访问 资源,即使你的代码不知道或不关心 Spring 的任何其他部分。 虽然这会将您的代码与 Spring 耦合,但它实际上只将其耦合到这一小组 实用程序类,作为更有能力的替代和可以是 被认为等同于您用于此目的的任何其他库。​​Resource​​​​URL​

2.3. 内置实现​​Resource​

Spring 包括几个内置的实现:​​Resource​

  • ​​UrlResource​​
  • ​​ClassPathResource​​
  • ​​FileSystemResource​​
  • ​​PathResource​​
  • ​​ServletContextResource​​
  • ​​InputStreamResource​​
  • ​​ByteArrayResource​​

有关 Spring 中可用的实现的完整列表,请参阅 ​​资源​​javadoc 的“所有已知的实现类”部分。​​Resource​

2.3.1. ​​UrlResource​

​UrlResource​​包装 aand 可用于访问任何对象 通常可通过 URL 访问,例如文件、HTTPS 目标、FTP 目标和 别人。所有 URL 都有一个标准化的表示形式,以便适当的 标准化前缀用于指示一种 URL 类型与另一种 URL 类型。这包括用于访问文件系统路径,用于通过 HTTPS协议,用于通过FTP访问资源等。​​java.net.URL​​​​String​​​​file:​​​​https:​​​​ftp:​

由 Java 代码显式使用构造函数创建的 Ais 但在调用采用用于表示路径的参数的 API 方法时,通常会隐式创建。对于后一种情况,JavaBeans最终决定创建哪种类型的of。如果路径字符串包含 众所周知(对属性编辑器,即)前缀(例如),它创建一个 适合该前缀。但是,如果它无法识别 前缀,它假定字符串是标准 URL 字符串并创建一个。​​UrlResource​​​​UrlResource​​​​String​​​​PropertyEditor​​​​Resource​​​​classpath:​​​​Resource​​​​UrlResource​

2.3.2. ​​ClassPathResource​

此类表示应从类路径获取的资源。它使用 线程上下文类装入器、给定类装入器或给定的类 加载资源。

此实现支持解析为 aif 类路径 资源驻留在文件系统中,但对于驻留在 jar 并且尚未扩展(通过 servlet 引擎或任何环境) 到文件系统。为了解决这个问题,各种实现始终支持 分辨率为 A。​​Resource​​​​java.io.File​​​​Resource​​​​java.net.URL​

AI 由 Java 代码显式使用构造函数创建,但在调用采用用于表示路径的参数的 API 方法时,通常是隐式创建的。对于后一种情况,JavaBeans识别字符串路径上的特殊前缀和 在这种情况下创建。​​ClassPathResource​​​​ClassPathResource​​​​String​​​​PropertyEditor​​​​classpath:​​​​ClassPathResource​

2.3.3. ​​FileSystemResource​

这是句柄的实现。它还支持句柄,应用Spring的标准基于字符串的路径。 转换,但通过 API 执行所有操作。对于纯基支持,请使用 arather.support resolution as aand as a。​​Resource​​​​java.io.File​​​​java.nio.file.Path​​​​java.nio.file.Files​​​​java.nio.path.Path​​​​PathResource​​​​FileSystemResource​​​​File​​​​URL​

2.3.4. ​​PathResource​

这是句柄的实现,执行所有 通过 API 进行操作和转换。它支持分辨率作为 aand 由于 AAAND 还有效地实现了 extendedinterface.is 一个纯粹的替代方案 不同的行为。​​Resource​​​​java.nio.file.Path​​​​Path​​​​File​​​​URL​​​​WritableResource​​​​PathResource​​​​java.nio.path.Path​​​​FileSystemResource​​​​createRelative​

2.3.5. ​​ServletContextResource​

这是对解释 相关 Web 应用程序的根目录中的相对路径。​​Resource​​​​ServletContext​

它始终支持流访问和 URL 访问,但仅允许访问 当 Web 应用程序存档展开并且资源在物理上位于 文件系统。它是否已扩展并在文件系统上或被访问 直接从JAR或其他地方(如数据库)实际上是 依赖于 Servlet 容器。​​java.io.File​

2.3.6. ​​InputStreamResource​

Anis aimplementation为一个给定的。它 仅当没有适用的特定实现时,才应使用。在 特别是,在可能的情况下首选任何基于文件的实现。​​InputStreamResource​​​​Resource​​​​InputStream​​​​Resource​​​​ByteArrayResource​​​​Resource​

与其他实现相比,这是 已打开的资源。因此,它返回自。如果出现以下情况,请勿使用它 您需要将资源描述符保留在某个位置,或者如果您需要读取流 多次。​​Resource​​​​true​​​​isOpen()​

2.3.7. ​​ByteArrayResource​

这是给定字节数组的实现。它为给定的字节数组创建 a。​​Resource​​​​ByteArrayInputStream​

它对于从任何给定的字节数组加载内容很有用,而不必求助于 一次性使用。​​InputStreamResource​

2.4. 界面​​ResourceLoader​

该接口旨在由可以返回的对象实现 (即加载)实例。以下清单显示了接口定义:​​ResourceLoader​​​​Resource​​​​ResourceLoader​

public interface ResourceLoader {

Resource getResource(String location);

ClassLoader getClassLoader();
}

所有应用程序上下文都实现接口。因此,所有 应用程序上下文可用于获取实例。​​ResourceLoader​​​​Resource​

调用特定应用程序上下文和位置路径时 指定没有特定的前缀,你得到的 atype 是 适用于该特定应用程序上下文。例如,假设以下内容 针对实例运行的代码片段:​​getResource()​​​​Resource​​​​ClassPathXmlApplicationContext​

Resource template = ctx.getResource("some/resource/path/myTemplate.txt");

针对 a,该代码返回 a。如果 对 ainstance 运行相同的方法,它会 返回 A。对于 a,它将返回 a。它同样会为每个上下文返回适当的对象。​​ClassPathXmlApplicationContext​​​​ClassPathResource​​​​FileSystemXmlApplicationContext​​​​FileSystemResource​​​​WebApplicationContext​​​​ServletContextResource​

因此,您可以以适合特定应用程序的方式加载资源 上下文。

另一方面,您也可以强制使用,无论 应用程序上下文类型,通过指定特殊前缀,如下所示 示例显示:​​ClassPathResource​​​​classpath:​

Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");

同样,您可以通过指定任何标准前缀来强制使用 ato。以下示例使用和前缀:​​UrlResource​​​​java.net.URL​​​​file​​​​https​

Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");

下表总结了将对象转换为对象的策略:​​String​​​​Resource​

Table 10. Resource strings

前缀

解释

类路径:

​classpath:com/myapp/config.xml​

从类路径加载。

文件:

​file:///data/config.xml​

从文件系统加载为 a。另请参阅文件系统资源警告​。​​URL​

https:

​https://myserver/logo.png​

加载为 a。​​URL​

(无)

​/data/config.xml​

取决于标的物。​​ApplicationContext​

2.5. 界面​​ResourcePatternResolver​

接口是接口的扩展 它定义了解析位置模式的策略(例如,Ant 样式路径) 模式)进入对象。​​ResourcePatternResolver​​​​ResourceLoader​​​​Resource​

public interface ResourcePatternResolver extends ResourceLoader {

String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

Resource[] getResources(String locationPattern) throws IOException;
}

如上所示,此接口还定义了特殊资源前缀 对于类路径中的所有匹配资源。请注意,资源位置为 在这种情况下,预期为没有占位符的路径 — 例如,。JAR 文件或类路径中的不同目录可以 包含具有相同路径和相同名称的多个文件。有关更多详细信息,请参阅应用程序上下文构造函数资源路径中的通配符及其小节 在通配符支持上,带有源前缀。​​classpath*:​​​​classpath*:/config/beans.xml​​​​classpath*:​

可以检查传入的(例如,通过ResourceLoaderAware语义提供的传入)是否 它也实现了这个扩展接口。​​ResourceLoader​

​PathMatchingResourcePatternResolver​​是一个可用的独立实现 在阿南德之外也用于 填充豆 properties.is 能够 将指定的资源位置路径解析为一个或多个匹配对象。 源路径可以是与目标具有一对一映射的简单路径,也可以包含特殊前缀和/或内部 Ant 风格的正则表达式(使用 Spring 的实用程序匹配)。后者都是有效的 通配符。​​ApplicationContext​​​​ResourceArrayPropertyEditor​​​​Resource[]​​​​PathMatchingResourcePatternResolver​​​​Resource​​​​Resource​​​​classpath*:​​​​org.springframework.util.AntPathMatcher​

2.6. 界面​​ResourceLoaderAware​

接口是一个特殊的回调接口,用于标识 希望提供参考的组件。以下列表 显示了接口的定义:​​ResourceLoaderAware​​​​ResourceLoader​​​​ResourceLoaderAware​

public interface ResourceLoaderAware {

void setResourceLoader(ResourceLoader resourceLoader);
}

当类实现和部署到应用程序上下文中时 (作为 Spring 管理的 bean),它被应用程序识别为 上下文。然后,应用程序上下文调用, 提供自身作为参数(请记住,Spring 中的所有应用程序上下文都实现了 界面)。​​ResourceLoaderAware​​​​ResourceLoaderAware​​​​setResourceLoader(ResourceLoader)​​​​ResourceLoader​

由于 anis a,bean 还可以实现接口并使用提供的应用程序上下文直接到 加载资源。但是,一般来说,如果您只需要使用专用界面,最好使用专用界面。代码将仅与资源加载耦合 接口(可以被认为是一个实用程序接口),而不是整个 Springinterface。​​ApplicationContext​​​​ResourceLoader​​​​ApplicationContextAware​​​​ResourceLoader​​​​ApplicationContext​

在应用程序组件中,您还可以依赖自动布线 实现接口的替代方法。传统和自动配线模式(如自动配线协作者中所述) 能够为 构造函数参数或 分别是二传手方法参数。实现更大的灵活性(包括能够 自动连线字段和多参数方法),请考虑使用基于注释的 自动接线功能。在这种情况下,它们会自动连接到一个字段中, 构造函数参数,或期望类型尽可能长的方法参数 因为有问题的字段、构造函数或方法带有注释。 有关详细信息,请参阅使用@Autowired。​​ResourceLoader​​​​ResourceLoaderAware​​​​constructor​​​​byType​​​​ResourceLoader​​​​ResourceLoader​​​​ResourceLoader​​​​@Autowired​

2.7. 资源作为依赖关系

如果 Bean 本身要通过某种排序确定并提供资源路径 在动态过程中,Bean 使用 theorInterface 加载资源可能是有意义的。例如,考虑加载 某种模板,其中所需的特定资源取决于 用户的角色。如果资源是静态的,则完全消除接口(或接口)的使用是有意义的,具有 Bean 公开了它需要的属性,并期望它们被注入其中。​​ResourceLoader​​​​ResourcePatternResolver​​​​ResourceLoader​​​​ResourcePatternResolver​​​​Resource​

然后注入这些属性变得微不足道的是,所有应用程序上下文 注册并使用特殊的JavaBeans,它可以转换路径 到对象。例如,下面的类具有类型的属性。​​PropertyEditor​​​​String​​​​Resource​​​​MyBean​​​​template​​​​Resource​

package example;

public class MyBean {

private Resource template;

public setTemplate(Resource template) {
this.template = template;
}

// ...
}

在 XML 配置文件中,可以使用简单的 该资源的字符串,如以下示例所示:​​template​



请注意,资源路径没有前缀。因此,由于应用程序上下文 本身将用作,资源通过 a、a 或 a 加载,具体取决于 应用程序上下文的确切类型。​​ResourceLoader​​​​ClassPathResource​​​​FileSystemResource​​​​ServletContextResource​

如果需要强制使用特定类型,可以使用前缀。这 以下两个示例演示如何强制 Aand A( 后者用于访问文件系统中的文件):​​Resource​​​​ClassPathResource​​​​UrlResource​

如果重构类以用于注释驱动的配置,则 路径可以存储在名为 — 例如, 在提供给 Spring 的属性文件中(参见环境抽象)。然后,可以使用属性占位符通过注释引用模板路径(请参阅使用@Value)。春天会 检索模板路径的值作为字符串,并特殊遗嘱 将字符串转换为要注入到构造函数中的对象。 下面的示例演示如何实现此目的。​​MyBean​​​​myTemplate.txt​​​​template.path​​​​Environment​​​​@Value​​​​PropertyEditor​​​​Resource​​​​MyBean​

@Component
public class MyBean {

private final Resource template;

public MyBean(@Value("${template.path}") Resource template) {
this.template = template;
}

// ...
}

如果我们想支持在同一路径下发现多个模板 类路径中的位置(例如,在类路径中的多个 JAR 中)我们可以 使用 specialprefix 和通配符将 akey 定义为。如果我们按如下方式重新定义类, Spring 会将模板路径模式转换为对象数组 可以注入到构造函数中。​​classpath*:​​​​templates.path​​​​classpath*:/config/templates/*.txt​​​​MyBean​​​​Resource​​​​MyBean​

爪哇岛

科特林

@Component
public class MyBean {

private final Resource[] templates;

public MyBean(@Value("${templates.path}") Resource[] templates) {
this.templates = templates;
}

// ...
}

2.8. 应用程序上下文和资源路径

本节介绍如何使用资源(包括快捷方式)创建应用程序上下文 适用于 XML、如何使用通配符以及其他详细信息。

2.8.1. 构建应用程序上下文

应用程序上下文构造函数(用于特定应用程序上下文类型)通常 将字符串或字符串数组作为资源的位置路径,例如 构成上下文定义的 XML 文件。

当这样的位置路径没有前缀时,从 该路径和用于加载 Bean 定义取决于并且适用于 特定的应用程序上下文。例如,请考虑以下示例,该示例将创建一个:​​Resource​​​​ClassPathXmlApplicationContext​

ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");

Bean 定义是从类路径加载的,因为 ais 使用。但是,请考虑以下示例,该示例将创建一个:​​ClassPathResource​​​​FileSystemXmlApplicationContext​

ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml");

现在,从文件系统位置加载 Bean 定义(在本例中,相对于 当前工作目录)。

请注意,在 位置路径覆盖默认类型“已创建”以装入 Bean 定义。请考虑以下示例:​​classpath​​​​Resource​

ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");

使用 从类路径加载 Bean 定义。 但是,它仍然是一个。如果随后将其用作 ,则任何不带前缀的路径仍被视为文件系统路径。​​FileSystemXmlApplicationContext​​​​FileSystemXmlApplicationContext​​​​ResourceLoader​

构造实例 — 快捷方式​​ClassPathXmlApplicationContext​

公开许多构造函数以启用 方便的实例化。基本思想是,您只能提供一个字符串数组 仅包含 XML 文件本身的文件名(不带前导路径) 信息),并提供 a。然后派生 来自所提供类的路径信息。​​ClassPathXmlApplicationContext​​​​Class​​​​ClassPathXmlApplicationContext​

请考虑以下目录布局:

com/
example/
services.xml
repositories.xml
MessengerService.class

以下示例显示了 ainstance 如何由 在文件名和(位于 类路径)可以实例化:​​ClassPathXmlApplicationContext​​​​services.xml​​​​repositories.xml​

ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "repositories.xml"}, MessengerService.class);

请参阅ClassPathXmlApplicationContextjavadoc 以获取有关各种构造函数的详细信息。

2.8.2. 应用程序上下文构造函数资源路径中的通配符

应用程序上下文构造函数值中的资源路径可以是简单路径(如 如前所示),每个目标都有一个到目标器的一对一映射, 或者,可以包含特殊前缀或内部 Ant 样式模式 (通过使用 Spring 的实用程序进行匹配)。后者都是有效的 通配符。​​Resource​​​​classpath*:​​​​PathMatcher​

此机制的一个用途是当您需要执行组件样式的应用程序组装时。都 组件可以将上下文定义片段发布到已知位置路径,并且, 当使用前缀相同的路径创建最终应用程序上下文时,将自动选取所有组件片段。​​classpath*:​

请注意,此通配符特定于在应用程序上下文中使用资源路径 构造函数(或直接使用实用程序类层次结构时)并且 在施工时解决。它与类型本身无关。 您不能使用前缀来构造实际值,因为 一个资源一次只指向一个资源。​​PathMatcher​​​​Resource​​​​classpath*:​​​​Resource​

蚂蚁风格图案

路径位置可以包含 Ant 样式模式,如以下示例所示:

/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml

当路径位置包含 Ant 样式模式时,解析器将遵循更复杂的模式 尝试解析通配符的过程。它产生一条通往 最后一个非通配符段并从中获取 URL。如果此网址不是网址或 特定于容器的变体(例如在 WebLogic、WebSphere 等中), AIS从中获取并用于通过遍历通配符来解析通配符 文件系统。对于 jar URL,解析器要么从中获取 a,要么手动解析 jar URL,然后遍历 用于解析通配符的 JAR 文件的内容。​​Resource​​​​jar:​​​​zip:​​​​wsjar​​​​java.io.File​​​​java.net.JarURLConnection​

对可移植性的影响

如果指定的路径已经是 URL(隐式,因为基是文件系统的路径,或者显式地),则通配符保证 以完全便携的方式工作。​​file​​​​ResourceLoader​

如果指定的路径是位置,则解析程序必须获取最后一个 通过调用的非通配符路径段 URL。从此 只是路径的一个节点(不是末尾的文件),它实际上是未定义的(在javadoc中)在这种情况下返回的URL类型。在实践中, 它始终表示目录(类路径资源 解析为文件系统位置)或某种 jar URL(类路径资源所在的位置 解析为 JAR 位置)。尽管如此,此操作仍存在可移植性问题。​​classpath​​​​Classloader.getResource()​​​​ClassLoader​​​​java.io.File​

如果为最后一个非通配符段获取了 jar URL,则解析器必须能够 从中获取或手动解析 jar URL,以便能够 遍历 JAP 的内容并解析通配符。这在大多数环境中都有效 但在其他方面失败,我们强烈建议资源的通配符解析 来自 jar 在您依赖它之前,请在您的特定环境中进行彻底测试。​​java.net.JarURLConnection​

前缀​​classpath*:​

构造基于 XML 的应用程序上下文时,位置字符串可以使用 特殊前缀,如以下示例所示:​​classpath*:​

ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");

此特殊前缀指定与给定名称匹配的所有类路径资源 必须获得(在内部,这基本上是通过调用 to 发生的),然后合并以形成最终应用程序 上下文定义。​​ClassLoader.getResources(…)​

您还可以将前缀与模式组合在 位置路径的其余部分(例如,)。在此 在这种情况下,解决策略相当简单:Acall是 在最后一个非通配符路径段上使用,以获取 类装入器层次结构,然后,关闭每个资源,相同的分辨率 前面描述的策略用于通配符子路径。​​classpath*:​​​​PathMatcher​​​​classpath*:META-INF/*-beans.xml​​​​ClassLoader.getResources()​​​​PathMatcher​

与通配符有关的其他注意事项

请注意,当与 Ant 风格的模式结合使用时,只能工作 在模式开始之前至少有一个根目录,除非实际 目标文件驻留在文件系统中。这意味着模式(例如可能不会从jar文件的根目录中检索文件,而只是 从展开目录的根目录。​​classpath*:​​​​classpath*:*.xml​

Spring 检索类路径条目的能力源于 JDK 的方法,该方法只返回 空字符串(指示要搜索的潜在根)。Spring 评估 jar 文件中的运行时配置和清单 以及,但这并不能保证导致可移植行为。​​ClassLoader.getResources()​​​​URLClassLoader​​​​java.class.path​

蚂蚁风格的模式与资源不能保证找到匹配 资源(如果要搜索的根包在多个类路径位置中可用)。 请考虑以下资源位置示例:​​classpath:​

com/mycompany/package1/service-context.xml

现在考虑一个 Ant 风格的路径,有人可能会使用它来尝试查找该文件:

classpath:com/mycompany/**/service-context.xml

这样的资源可能只存在于类路径中的一个位置,但是当路径如 前面的示例用于尝试解决它,解析器在(第一个) 返回的网址。如果此基本包节点存在于 多个位置,所需的资源可能不存在于第一个位置 找到位置。因此,在这种情况下,您应该更喜欢使用 相同的 Ant 样式模式,它搜索包含基本包的所有类路径位置:​​getResource("com/mycompany");​​​​ClassLoader​​​​classpath*:​​​​com.mycompany​​​​classpath*:com/mycompany/**/service-context.xml​

2.8.3.注意事项​​FileSystemResource​

A那不附加到一个(那 是,当 AI 不是实际时)对待 绝对和相对路径如您所期望。相对路径相对于 当前工作目录,而绝对路径相对于 文件系统。​​FileSystemResource​​​​FileSystemApplicationContext​​​​FileSystemApplicationContext​​​​ResourceLoader​

然而,出于向后兼容性(历史)的原因,当是时,这会发生变化。强制所有附加实例 将所有位置路径视为相对路径,无论它们是否以前导斜杠开头。 实际上,这意味着以下示例是等效的:​​FileSystemApplicationContext​​​​ResourceLoader​​​​FileSystemApplicationContext​​​​FileSystemResource​

ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");

以下示例也是等效的(即使它们不同是有意义的,作为一个 情况是相对的,另一个是绝对的):

FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");

实际上,如果您需要真正的绝对文件系统路径,则应避免使用 绝对路径与索兰德 强制使用 aby 使用 URL 前缀。以下示例 演示如何执行此操作:​​FileSystemResource​​​​FileSystemXmlApplicationContext​​​​UrlResource​​​​file:​

// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:///conf/context.xml");

3. 验证、数据绑定和类型转换

将验证视为业务逻辑有利有弊,Spring 提供 一种不排除其中任何一个的验证(和数据绑定)设计。 具体而言,验证不应绑定到 Web 层,而应易于本地化, 并且应该可以插入任何可用的验证器。考虑到这些关切, 弹簧提供了既基本又非常可用的契约 在应用程序的每一层中。​​Validator​

数据绑定对于让用户输入动态绑定到域非常有用 应用程序的模型(或用于处理用户输入的任何对象)。春天 提供了恰如其分的名字来做到这一点。和 弥补 包装,主要用于但不 仅限于 Web 图层。​​DataBinder​​​​Validator​​​​DataBinder​​​​validation​

这是Spring框架中的一个基本概念,并且在很多中使用 的地方。但是,您可能不需要直接使用。但是,由于这是参考文档,因此我们认为某些解释 可能是有序的。我们在本章中解释,因为,如果你是 要使用它,您最有可能在尝试将数据绑定到对象时这样做。​​BeanWrapper​​​​BeanWrapper​​​​BeanWrapper​

Spring's和较低级别都使用实现来解析和格式化属性值。Theandtypes是JavaBeans规范的一部分,也是 在本章中解释。Spring 3 引入了一个软件包,它提供了一个 通用类型转换工具,以及更高级别的“格式”包 设置 UI 字段值的格式。您可以将这些包用作实现的更简单的替代方法。本章还将讨论它们。​​DataBinder​​​​BeanWrapper​​​​PropertyEditorSupport​​​​PropertyEditor​​​​PropertyEditorSupport​​​​core.convert​​​​PropertyEditorSupport​

Spring 通过设置基础结构和适配器支持 Java Bean 验证 春天自己的合同。应用程序可以全局启用一次 Bean 验证, 如Java Bean 验证中所述,并将其专门用于所有验证 需要。在 Web 层中,应用程序可以进一步注册每个控制器本地 Spring实例,如配置DataBinder 中所述,它可以 对于插入自定义验证逻辑很有用。​​Validator​​​​Validator​​​​DataBinder​

3.1. 使用 Spring 的验证器接口进行验证

Spring 具有可用于验证对象的接口。该接口通过使用对象来工作,以便在验证时, 验证程序可以向对象报告验证失败。​​Validator​​​​Validator​​​​Errors​​​​Errors​

请考虑以下小型数据对象示例:

public class Person {

private String name;
private int age;

// the usual getters and setters...
}

下一个示例通过实现 以下两种接口方法:​​Person​​​​org.springframework.validation.Validator​

  • ​supports(Class)​​:这可以验证所提供的实例吗?ValidatorClass
  • ​validate(Object, org.springframework.validation.Errors)​​:验证给定对象 并且,在出现验证错误的情况下,将那些注册到给定对象中。Errors

实现 ay 相当简单,特别是当你知道 Spring 框架也提供的辅助类时。以下 示例实现for实例:​​Validator​​​​ValidationUtils​​​​Validator​​​​Person​

public class PersonValidator implements Validator {

/**
* This Validator validates only Person instances
*/
public boolean supports(Class clazz) {
return Person.class.equals(clazz);
}

public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
Person p = (Person) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}

类上的方法用于 如果属性为空字符串,则拒绝该属性。看看ValidationUtilsjavadoc 以查看除了前面显示的示例之外,它还提供了哪些功能。​​static​​​​rejectIfEmpty(..)​​​​ValidationUtils​​​​name​​​​null​

虽然当然可以实现单个类来验证每个 在富对象中的嵌套对象中,最好封装验证 每个嵌套对象类在其自己的实现中的逻辑。一个简单的 “富”对象的示例是由两个属性(第一个和第二个名称)和一个 complexObject.objects 组成的。 可以独立于对象使用,因此已实现区别。如果您希望重用包含的逻辑 在课堂内无需诉诸复制和粘贴,您可以 依赖注入或实例化你的, 如以下示例所示:​​Validator​​​​Validator​​​​Customer​​​​String​​​​Address​​​​Address​​​​Customer​​​​AddressValidator​​​​CustomerValidator​​​​AddressValidator​​​​AddressValidator​​​​CustomerValidator​

public class CustomerValidator implements Validator {

private final Validator addressValidator;

public CustomerValidator(Validator addressValidator) {
if (addressValidator == null) {
throw new IllegalArgumentException("The supplied [Validator] is " +
"required and must not be null.");
}
if (!addressValidator.supports(Address.class)) {
throw new IllegalArgumentException("The supplied [Validator] must " +
"support the validation of [Address] instances.");
}
this.addressValidator = addressValidator;
}

/**
* This Validator validates Customer instances, and any subclasses of Customer too
*/
public boolean supports(Class clazz) {
return Customer.class.isAssignableFrom(clazz);
}

public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
Customer customer = (Customer) target;
try {
errors.pushNestedPath("address");
ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
} finally {
errors.popNestedPath();
}
}
}

验证错误将报告给传递给验证器的对象。在这种情况下 Spring Web MVC,您可以使用标签来检查错误消息,但是 您也可以自己检查对象。有关 它提供的方法可以在javadoc中找到。​​Errors​​​​​​Errors​

3.2. 将代码解析为错误消息

我们介绍了数据绑定和验证。本节介绍输出相应的消息 到验证错误。在上一节所示的示例中, 我们拒绝了Theandfields。如果我们想使用 a 输出错误消息,我们可以使用我们在拒绝字段时提供的错误代码来实现 (在本例中为“姓名”和“年龄”)。当您调用(直接或间接地,通过使用 例如,类)或其他方法之一 从接口,底层实现不仅注册了代码 传入,但也注册了许多额外的错误代码。确定接口注册哪些错误代码。默认情况下,使用,它(例如)不仅注册消息 使用您提供的代码,但也注册包含您传递的字段名称的消息 到拒绝方法。因此,如果您通过使用拒绝字段, 除了代码,Spring 还寄存沙(第一个包含字段名称,第二个包含类型 的领域)。这样做是为了方便开发人员确定错误消息。​​name​​​​age​​​​MessageSource​​​​ValidationUtils​​​​rejectValue​​​​reject​​​​Errors​​​​MessageCodesResolver​​​​Errors​​​​DefaultMessageCodesResolver​​​​rejectValue("age", "too.darn.old")​​​​too.darn.old​​​​too.darn.old.age​​​​too.darn.old.age.int​

可以找到有关和默认策略的更多信息 在 MessageCodesResolver 和DefaultMessageCodesResolver 的 javadoc 中, 分别。​​MessageCodesResolver​

3.3. Bean 操作和​​BeanWrapper​

该软件包遵循JavaBeans标准。 JavaBean是一个具有默认无参数构造函数的类,它遵循 一种命名约定,其中(例如)命名的属性将 有一个二传手方法和一个吸气手方法。为 有关 JavaBeans 和规范的更多信息,请参阅JavaBeans。​​org.springframework.beans​​​​bingoMadness​​​​setBingoMadness(..)​​​​getBingoMadness()​

bean 包中一个非常重要的类是接口及其 相应的实现 ()。正如引用自javadoc的,提供了设置和获取属性值的功能(单独或在 批量),获取属性描述符,并查询属性以确定它们是否 可读或可写。此外,还提供对嵌套属性的支持, 启用将子属性上的属性设置为无限深度。它还支持添加标准JavaBeansand的能力,而无需在目标类中支持代码。 最后但并非最不重要的一点是,提供对设置索引属性的支持。 通常不由应用程序代码直接使用,而由和使用。​​BeanWrapper​​​​BeanWrapperImpl​​​​BeanWrapper​​​​BeanWrapper​​​​BeanWrapper​​​​PropertyChangeListeners​​​​VetoableChangeListeners​​​​BeanWrapper​​​​BeanWrapper​​​​DataBinder​​​​BeanFactory​

作品的方式部分由它的名字表示:它包裹着一个豆子 对该 Bean 执行操作,例如设置和检索属性。​​BeanWrapper​

3.3.1. 设置和获取基本属性和嵌套属性

设置和获取属性是通过 的 and重载方法变体完成的。请参阅他们的 Javadoc 详。下表显示了这些约定的一些示例:​​setPropertyValue​​​​getPropertyValue​​​​BeanWrapper​

Table 11. Examples of properties

表达

解释

​name​

指示与理论方法对应的属性。​​name​​​​getName()​​​​isName()​​​​setName(..)​

​account.name​

指示对应于 (例如)理论方法。​​name​​​​account​​​​getAccount().setName()​​​​getAccount().getName()​

​account[2]​

指示索引属性的第三个元素。索引属性 可以是类型、或其他自然顺序集合。​​account​​​​array​​​​list​

​account[COMPANYNAME]​

指示由属性的键索引的映射条目的值。​​COMPANYNAME​​​​account​​​​Map​

(如果您不打算使用 直接的。如果您只使用 and 和它们的默认实现,则应跳到属性编辑器部分。​​BeanWrapper​​​​DataBinder​​​​BeanFactory​

以下两个示例类使用 get 和 set 性能:​​BeanWrapper​

public class Company {

private String name;
private Employee managingDirector;

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public Employee getManagingDirector() {
return this.managingDirector;
}

public void setManagingDirector(Employee managingDirector) {
this.managingDirector = managingDirector;
}
}
public class Employee {

private String name;

private float salary;

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public float getSalary() {
return salary;
}

public void setSalary(float salary) {
this.salary = salary;
}
}

以下代码片段显示了如何检索和操作某些 实例化和的属性:​​Company​​​​Employee​

BeanWrapper company = new BeanWrapperImpl(new Company());
// setting the company name..
company.setPropertyValue("name", "Some Company Inc.");
// ... can also be done like this:
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);

// ok, let's create the director and tie it to the company:
BeanWrapper jim = new BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());

// retrieving the salary of the managingDirector through the company
Float salary = (Float) company.getPropertyValue("managingDirector.salary");

3.3.2. 内置实现​​PropertyEditor​

弹簧使用a的概念来实现anand a之间的转换。它可以很方便 以不同于对象本身的方式表示属性。例如,a可以用人类可读的方式表示(如:),而 我们仍然可以将人类可读的形式转换回原始日期(或者,甚至 更好的是,将以人类可读形式输入的任何日期转换回对象)。这 行为可以通过注册类型的自定义编辑器来实现。在 aor 上注册自定义编辑器, 或者,在特定的 IoC 容器中(如上一章所述),给出它 有关如何将属性转换为所需类型的知识。有关更多信息,请参阅 Oracle 的java.beans 包的 javadoc。​​PropertyEditor​​​​Object​​​​String​​​​Date​​​​String​​​​'2007-14-09'​​​​Date​​​​java.beans.PropertyEditor​​​​BeanWrapper​​​​PropertyEditor​

在 Spring 中使用属性编辑的几个示例:

  • 在 bean 上设置属性是通过使用实现来完成的。 当您使用您声明的某个 Bean 的属性值时 在 XML 文件中,Spring (如果相应属性的 setter 具有 aparameter)用于尝试将参数解析为 aobject。PropertyEditorStringClassClassEditorClass
  • 在 Spring 的 MVC 框架中解析 HTTP 请求参数是通过使用各种类型来完成的 的实现,您可以在所有子类中手动绑定。PropertyEditorCommandController

Spring 有许多内置的实现,让生活变得轻松。 它们都位于包装中。默认情况下,大多数(但不是全部,如下表所示)由注册者注册。如果属性编辑器可以以某种方式进行配置,则可以 仍然注册您自己的变体以覆盖默认变体。下表描述了 Spring 提供的各种实现:​​PropertyEditor​​​​org.springframework.beans.propertyeditors​​​​BeanWrapperImpl​​​​PropertyEditor​

表 12.内置实现​​PropertyEditor​


解释

​ByteArrayPropertyEditor​

字节数组的编辑器。将字符串转换为其相应的字节 交涉。默认注册者。​​BeanWrapperImpl​

​ClassEditor​

将表示类的字符串分析为实际类,反之亦然。当一个 找不到类,阿尼斯扔了。默认情况下,注册者。​​IllegalArgumentException​​​​BeanWrapperImpl​

​CustomBooleanEditor​

属性的可自定义属性编辑器。默认情况下,可以通过将注册的自定义实例注册为 自定义编辑器。​​Boolean​​​​BeanWrapperImpl​

​CustomCollectionEditor​

集合的属性编辑器,将任何源转换为给定的目标类型。​​Collection​​​​Collection​

​CustomDateEditor​

可定制的属性编辑器,支持自定义。不 默认注册。必须根据需要使用适当的格式进行用户注册。​​java.util.Date​​​​DateFormat​

​CustomNumberEditor​

任何子类的可自定义属性编辑器,例如,,,或。默认情况下,注册者但可以由 将其自定义实例注册为自定义编辑器。​​Number​​​​Integer​​​​Long​​​​Float​​​​Double​​​​BeanWrapperImpl​

​FileEditor​

将字符串解析为对象。默认情况下,注册者。​​java.io.File​​​​BeanWrapperImpl​

​InputStreamEditor​

单向属性编辑器,可以采用字符串并生成(通过 中间和)可以直接将属性设置为字符串。请注意,默认用法不会关闭 为你。默认情况下,注册者。​​ResourceEditor​​​​Resource​​​​InputStream​​​​InputStream​​​​InputStream​​​​BeanWrapperImpl​

​LocaleEditor​

可以将字符串解析为对象,反之亦然(字符串格式与方法相同)。还接受空格作为分隔符,作为下划线的替代项。 默认情况下,注册者。​​Locale​​​​[language]_[country]_[variant]​​​​toString()​​​​Locale​​​​BeanWrapperImpl​

​PatternEditor​

可以将字符串解析为对象,反之亦然。​​java.util.regex.Pattern​

​PropertiesEditor​

可以将字符串(使用类的javadoc中定义的格式格式化)转换为对象。默认情况下,已注册 由。​​java.util.Properties​​​​Properties​​​​BeanWrapperImpl​

​StringTrimmerEditor​

修剪字符串的属性编辑器。(可选)允许转换空字符串 变成一个值。默认情况下未注册 — 必须由用户注册。​​null​

​URLEditor​

可以将 URL 的字符串表示形式解析为实际对象。 默认情况下,注册者。​​URL​​​​BeanWrapperImpl​

弹簧使用设置属性的搜索路径 可能需要的编辑器。搜索路径还包括, 包括诸如、和大多数类型的实现 基元类型。另请注意,标准的 JavaBeans 基础架构 自动发现类(无需注册它们) 显式)如果它们与它们处理的类位于同一包中并且具有相同的 名称为该类,附加。例如,可以有以下内容 类和包结构,这足以使类 识别并用作 for 类型属性。​​java.beans.PropertyEditorManager​​​​sun.bean.editors​​​​PropertyEditor​​​​Font​​​​Color​​​​PropertyEditor​​​​Editor​​​​SomethingEditor​​​​PropertyEditor​​​​Something​

com
chank
pop
Something
SomethingEditor // the PropertyEditor for the Something class

请注意,您也可以在此处使用标准的JavaBeans机制。 (此处在一定程度上描述)。这 下面的示例使用该机制使用关联类的属性显式注册一个或多个实例:​​BeanInfo​​​​BeanInfo​​​​PropertyEditor​

com
chank
pop
Something
SomethingBeanInfo // the BeanInfo for the Something class

引用类的以下 Java 源代码 关联 a与类的属性:​​SomethingBeanInfo​​​​CustomNumberEditor​​​​age​​​​Something​

public class SomethingBeanInfo extends SimpleBeanInfo {

public PropertyDescriptor[] getPropertyDescriptors() {
try {
final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true);
PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Something.class) {
@Override
public PropertyEditor createPropertyEditor(Object bean) {
return numberPE;
}
};
return new PropertyDescriptor[] { ageDescriptor };
}
catch (IntrospectionException ex) {
throw new Error(ex.toString());
}
}
}
注册其他自定义实现​​PropertyEditor​

当将 bean 属性设置为字符串值时,Spring IoC 容器最终使用 标准JavaBeans实现,用于将这些字符串转换为复杂类型的 财产。Spring 预注册了许多自定义实现(例如,到 将表示为字符串的类名转换为对象)。此外 Java的标准JavaBeanslookup机制允许afor类被适当地命名,并与类放在同一个包中。 它提供支持,以便可以自动找到它。​​PropertyEditor​​​​PropertyEditor​​​​Class​​​​PropertyEditor​​​​PropertyEditor​

如果需要注册其他习俗,有几种机制是 可用。最手动的方法,通常不方便或 推荐,是使用接口的方法,假设你有偏好。 另一个(稍微方便一些)机制是使用特殊的豆子工厂 后处理器调用。虽然你可以使用bean工厂后处理器 通过实现,具有 嵌套属性设置,因此我们强烈建议您将它与 一起使用,在那里您可以以类似于任何其他 Bean 和 可以自动检测和应用的地方。​​PropertyEditors​​​​registerCustomEditor()​​​​ConfigurableBeanFactory​​​​BeanFactory​​​​CustomEditorConfigurer​​​​BeanFactory​​​​CustomEditorConfigurer​​​​ApplicationContext​

请注意,所有 Bean 工厂和应用程序上下文都会自动使用许多 内置属性编辑器,通过使用 ATO进行 处理属性转换。寄存器的标准属性编辑器在上一节中列出。 此外,还覆盖或添加其他编辑器来处理 以适合特定应用程序上下文类型的方式进行资源查找。​​BeanWrapper​​​​BeanWrapper​​​​ApplicationContext​

标准 JavaBeans 实例用于转换属性值 表示为属性的实际复杂类型的字符串。您可以使用豆厂后处理器方便地添加 支持对 AN 的其他实例。​​PropertyEditor​​​​CustomEditorConfigurer​​​​PropertyEditor​​​​ApplicationContext​

考虑以下示例,该示例定义了一个调用的用户类 另一个调用的类,它需要设置为属性:​​ExoticType​​​​DependsOnExoticType​​​​ExoticType​

package example;

public class ExoticType {

private String name;

public ExoticType(String name) {
this.name = name;
}
}

public class DependsOnExoticType {

private ExoticType type;

public void setType(ExoticType type) {
this.type = type;
}
}

正确设置后,我们希望能够将类型属性分配为 字符串,转换为实际实例。以下 Bean 定义显示了如何设置此关系:​​PropertyEditor​​​​ExoticType​



实现可能类似于以下内容:​​PropertyEditor​

// converts string representation to ExoticType object
package example;

public class ExoticTypeEditor extends PropertyEditorSupport {

public void setAsText(String text) {
setValue(new ExoticType(text.toUpperCase()));
}
}

最后,下面的示例演示如何使用 newwith 注册,然后可以根据需要使用它:​​CustomEditorConfigurer​​​​PropertyEditor​​​​ApplicationContext​







用​​PropertyEditorRegistrar​

向 Spring 容器注册属性编辑器的另一种机制是 创建和使用 a。此接口在以下情况下特别有用 您需要在几种不同情况下使用同一组属性编辑器。 您可以编写相应的注册器并在每种情况下重用它.实例与调用的接口结合使用,该接口由 Spring(and) 实现。实例特别方便 与(此处所述)一起使用时,它公开了一个属性 已添加调用实例 以这种方式可以轻松共享和 弹簧 MVC 控制器。此外,它避免了自定义同步的需要 编辑器:Ais 希望为每次创建 Bean 尝试创建新实例。​​PropertyEditorRegistrar​​​​PropertyEditorRegistrar​​​​PropertyEditorRegistry​​​​BeanWrapper​​​​DataBinder​​​​PropertyEditorRegistrar​​​​CustomEditorConfigurer​​​​setPropertyEditorRegistrars(..)​​​​PropertyEditorRegistrar​​​​CustomEditorConfigurer​​​​DataBinder​​​​PropertyEditorRegistrar​​​​PropertyEditor​

以下示例演示如何创建自己的实现:​​PropertyEditorRegistrar​

package com.foo.editors.spring;

public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {

public void registerCustomEditors(PropertyEditorRegistry registry) {

// it is expected that new PropertyEditor instances are created
registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());

// you could register as many custom property editors as are required here...
}
}

另请参阅示例实现。请注意,在方法的实现中,它如何创建每个属性编辑器的新实例。​​org.springframework.beans.support.ResourceEditorRegistrar​​​​PropertyEditorRegistrar​​​​registerCustomEditors(..)​

下一个示例演示如何配置 aand 注入实例 我们的进入它:​​CustomEditorConfigurer​​​​CustomPropertyEditorRegistrar​









class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>

最后(有点偏离本章的重点)对于那些你们 使用Spring 的 MVC Web 框架,使用 ain 与数据绑定 Web 控制器结合使用可以非常方便。以下 示例使用 ain 实现 anmethod:​​PropertyEditorRegistrar​​​​PropertyEditorRegistrar​​​​@InitBinder​

@Controller
public class RegisterUserController {

private final PropertyEditorRegistrar customPropertyEditorRegistrar;

RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
this.customPropertyEditorRegistrar = propertyEditorRegistrar;
}

@InitBinder
void initBinder(WebDataBinder binder) {
this.customPropertyEditorRegistrar.registerCustomEditors(binder);
}

// other methods related to registering a User
}

这种注册方式可以导致简洁的代码(实现 的方法只有一行长),并允许将公共注册代码封装在一个类中,然后在尽可能多的控制器之间共享 根据需要。​​PropertyEditor​​​​@InitBinder​​​​PropertyEditor​

3.4. 弹簧类型转换

Spring 3 引入了一个提供常规类型转换的包 系统。系统定义一个SPI来实现类型转换逻辑和一个API 在运行时执行类型转换。在 Spring 容器中,您可以使用此系统 作为转换外部化 Bean 属性值的实现的替代方法 字符串到所需的属性类型。您还可以在 需要类型转换的应用程序。​​core.convert​​​​PropertyEditor​

3.4.1. 转换器 SPI

实现类型转换逻辑的 SPI 很简单,并且是强类型的,如下所示 接口定义显示:

package org.springframework.core.convert.converter;

public interface Converter {

T convert(S source);
}

要创建自己的转换器,请实现接口并参数化为要转换的类型以及要转换为的类型。您也可以透明地应用这样的 转换器,如果集合或数组需要 转换为数组或集合,前提是委托数组或集合 转换器也已注册(默认情况下注册)。​​Converter​​​​S​​​​T​​​​S​​​​T​​​​DefaultConversionService​

对于每个调用 to,源参数保证不为 null。如果转换失败,您可能会引发任何未经检查的异常。具体来说,它应该抛出 anto 报告无效的源值。 注意确保您的实现是线程安全的。​​convert(S)​​​​Converter​​​​IllegalArgumentException​​​​Converter​

包中提供了几种转换器实现,作为 一种便利。其中包括从字符串到数字和其他常见类型的转换器。 下面的清单显示了类,这是一个典型的实现:​​core.convert.support​​​​StringToInteger​​​​Converter​

package org.springframework.core.convert.support;

final class StringToInteger implements Converter {

public Integer convert(String source) {
return Integer.valueOf(source);
}
}

3.4.2. 使用​​ConverterFactory​

当您需要集中整个类层次结构的转换逻辑时 (例如,转换 FromtoObjects 时),可以实现,如以下示例所示:​​String​​​​Enum​​​​ConverterFactory​

package org.springframework.core.convert.converter;

public interface ConverterFactory {

Converter getConverter(Class targetType);
}

将 S 参数化为要转换的类型,将 R 参数化为定义的基本类型 可以转换为的类的范围。然后实施, 其中 T 是 R 的子类。​​getConverter(Class)​

以你为例:​​StringToEnumConverterFactory​

package org.springframework.core.convert.support;

final class StringToEnumConverterFactory implements ConverterFactory {

public Converter getConverter(Class targetType) {
return new StringToEnumConverter(targetType);
}

private final class StringToEnumConverter implements Converter {

private Class enumType;

public StringToEnumConverter(Class enumType) {
this.enumType = enumType;
}

public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}

3.4.3. 使用​​GenericConverter​

当您需要复杂的实现时,请考虑使用接口。使用更灵活但键入较少的签名 比,支持在多个源和 目标类型。此外,还提供可用的源和目标字段 实现转化逻辑时可以使用的上下文。这样的上下文让 类型转换由字段批注或 字段签名。以下清单显示了以下接口定义:​​Converter​​​​GenericConverter​​​​Converter​​​​GenericConverter​​​​GenericConverter​​​​GenericConverter​

package org.springframework.core.convert.converter;

public interface GenericConverter {

public Set getConvertibleTypes();

Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

要实现 a,已返回支持的 源→目标类型对。然后实现以包含转换逻辑。源提供 访问保存正在转换的值的源字段。目标提供对要在其中设置转换值的目标字段的访问。​​GenericConverter​​​​getConvertibleTypes()​​​​convert(Object, TypeDescriptor, TypeDescriptor)​​​​TypeDescriptor​​​​TypeDescriptor​

ais 一个在 Java 数组之间转换的转换器的一个很好的例子 和一个集合。这种内省声明的字段 用于解析集合的元素类型的目标集合类型。这让每个 元素在 集合在目标字段上设置。​​GenericConverter​​​​ArrayToCollectionConverter​

用​​ConditionalGenericConverter​

有时,您希望 a仅在特定条件成立时才运行。为 例如,您可能希望仅在存在特定批注时才运行 在目标字段中,或者您可能希望仅在特定方法运行时运行 (例如 amethod)在目标上定义,class.is 与接口的联合,允许您定义此类自定义匹配条件:​​Converter​​​​Converter​​​​Converter​​​​static valueOf​​​​ConditionalGenericConverter​​​​GenericConverter​​​​ConditionalConverter​

public interface ConditionalConverter {

boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

ais anthat 转换的一个很好的例子 在持久性实体标识符和实体引用之间。仅当目标实体类型声明静态查找器方法(例如,)时,此类匹配才可能匹配。您可以在实现中执行这样的查找器方法检查。​​ConditionalGenericConverter​​​​IdToEntityConverter​​​​IdToEntityConverter​​​​findAccount(Long)​​​​matches(TypeDescriptor, TypeDescriptor)​

3.4.4. 接口​​ConversionService​

​ConversionService​​定义一个统一的 API,用于在 运行。转换器通常在以下外观界面后面运行:

package org.springframework.core.convert;

public interface ConversionService {

boolean canConvert(Class sourceType, Class targetType);

T convert(Object source, Class targetType);

boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

大多数实现也实现,其中 提供用于注册转换器的 SPI。在内部,实现委托给其注册的转换器以执行类型转换逻辑。​​ConversionService​​​​ConverterRegistry​​​​ConversionService​

在 thepackage.is 适用于 在大多数环境中使用。提供方便的工厂 创建通用配置。​​ConversionService​​​​core.convert.support​​​​GenericConversionService​​​​ConversionServiceFactory​​​​ConversionService​

3.4.5. 配置​​ConversionService​

Ais 一个无状态对象,设计用于在应用程序实例化 启动,然后在多个线程之间共享。在 Spring 应用程序中,您通常 为每个 Spring 容器 (OR) 配置 a实例。 弹簧拾起它,并在键入时使用它 转换需要由框架执行。您也可以将其注入到任何 bean 中并直接调用它。​​ConversionService​​​​ConversionService​​​​ApplicationContext​​​​ConversionService​​​​ConversionService​

要使用 Spring 注册默认值,请添加以下 Bean 定义 与 Anof:​​ConversionService​​​​id​​​​conversionService​

    class="org.springframework.context.support.ConversionServiceFactoryBean"/>

默认值可以在字符串、数字、枚举、集合、 地图和其他常见类型。用您的补充或覆盖默认转换器 自己的自定义转换器,设置属性。属性值可以实现 任何,,或接口。​​ConversionService​​​​converters​​​​Converter​​​​ConverterFactory​​​​GenericConverter​

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





在Spring MVC应用程序中使用a也很常见。请参阅Spring MVC章节中的转换和格式化。​​ConversionService​

在某些情况下,您可能希望在转换过程中应用格式。有关使用的详细信息,请参阅FormatterRegistrySPI。​​FormattingConversionServiceFactoryBean​

3.4.6. 以编程方式使用​​ConversionService​

要以编程方式使用 ainstance,您可以注入对 就像你对任何其他豆子一样。以下示例演示如何执行此操作:​​ConversionService​

@Service
public class MyService {

public MyService(ConversionService conversionService) {
this.conversionService = conversionService;
}

public void doIt() {
this.conversionService.convert(...)
}
}

对于大多数用例,您可以使用指定的方法,但是 不适用于更复杂的类型,例如参数化元素的集合。 例如,如果要以编程方式将 aof转换为 aof, 您需要提供源和目标类型的正式定义。​​convert​​​​targetType​​​​List​​​​Integer​​​​List​​​​String​

幸运的是,提供了各种选项来使操作变得简单, 如以下示例所示:​​TypeDescriptor​

DefaultConversionService cs = new DefaultConversionService();

List input = ...
cs.convert(input,
TypeDescriptor.forObject(input), // List type descriptor
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));

请注意,自动注册以下转换器: 适用于大多数环境。这包括集合转换器、标量 转换器和基本到转换器。您可以注册相同的转换器 与 anyby 在类上使用静态方法。​​DefaultConversionService​​​​Object​​​​String​​​​ConverterRegistry​​​​addDefaultConverters​​​​DefaultConversionService​

值类型的转换器对数组和集合重用,因此 无需创建特定的转换器即可从 AOF 转换为 AOF,前提是标准集合处理是合适的。​​Collection​​​​S​​​​Collection​​​​T​

3.5. 弹簧字段格式化

如上一节所述,core.convert是一个 通用型转换系统。它提供了一个统一的 API 作为 以及用于从一种类型实现转换逻辑的强类型SPI。 到另一个。Spring 容器使用此系统绑定 Bean 属性值。在 此外,Spring 表达式语言 (SpEL) 和使用此系统来 绑定字段值。例如,当 SpEL 需要强制 ato ato 完成尝试,系统执行强制。​​ConversionService​​​​Converter​​​​DataBinder​​​​Short​​​​Long​​​​expression.setValue(Object bean, Object value)​​​​core.convert​

现在考虑典型客户端环境的类型转换要求,例如 网络或桌面应用程序。在此类环境中,通常转换自以支持客户端回发过程,以及转换回 查看渲染过程。此外,您经常需要本地化值。越多 通用SPI不满足此类格式要求 径直。为了直接解决这些问题,Spring 3 引入了一个方便的 SPI,它 为客户端环境提供简单而可靠的实现替代方案。​​String​​​​String​​​​String​​​​core.convert​​​​Converter​​​​Formatter​​​​PropertyEditor​

通常,当您需要实现通用类型时,可以使用 SPI。 转换逻辑 — 例如,用于在 A 和 A 之间进行转换。 当您在客户端环境(如 Web 应用程序),并且需要解析和打印本地化的字段值。为两个 SPI 提供统一的类型转换 API。​​Converter​​​​java.util.Date​​​​Long​​​​Formatter​​​​ConversionService​

3.5.1. SPI​​Formatter​

实现字段格式逻辑的 SPI 很简单且是强类型的。这 以下清单显示了接口定义:​​Formatter​​​​Formatter​

package org.springframework.format;

public interface Formatter extends Printer, Parser {
}

​Formatter​​从和构建块接口扩展。这 以下清单显示了这两个接口的定义:​​Printer​​​​Parser​

public interface Printer {

String print(T fieldValue, Locale locale);
}
import java.text.ParseException;

public interface Parser {

T parse(String clientValue, Locale locale) throws ParseException;
}

若要创建自己的接口,请实现前面所示的接口。 参数化为要格式化的对象类型,例如,。实现打印实例的操作 显示在客户端区域设置中。实现从客户端区域设置返回的格式化表示形式中解析实例的操作。你应该抛出一个或一个解析尝试失败。拿 注意确保您的实现是线程安全的。​​Formatter​​​​Formatter​​​​T​​​​java.util.Date​​​​print()​​​​T​​​​parse()​​​​T​​​​Formatter​​​​ParseException​​​​IllegalArgumentException​​​​Formatter​

为了方便起见,子包提供了几种实现。 该包提供,和格式使用a的对象。 该包提供 ato 格式对象 一个。​​format​​​​Formatter​​​​number​​​​NumberStyleFormatter​​​​CurrencyStyleFormatter​​​​PercentStyleFormatter​​​​Number​​​​java.text.NumberFormat​​​​datetime​​​​DateFormatter​​​​java.util.Date​​​​java.text.DateFormat​

下面是一个示例实现:​​DateFormatter​​​​Formatter​

package org.springframework.format.datetime;

public final class DateFormatter implements Formatter {

private String pattern;

public DateFormatter(String pattern) {
this.pattern = pattern;
}

public String print(Date date, Locale locale) {
if (date == null) {
return "";
}
return getDateFormat(locale).format(date);
}

public Date parse(String formatted, Locale locale) throws ParseException {
if (formatted.length() == 0) {
return null;
}
return getDateFormat(locale).parse(formatted);
}

protected DateFormat getDateFormat(Locale locale) {
DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
dateFormat.setLenient(false);
return dateFormat;
}
}

Spring 团队欢迎社区驱动的贡献。请参阅GitHub 要贡献的问题。​​Formatter​

3.5.2. 注释驱动的格式化

可以按字段类型或注记配置字段格式。绑定 对 A 的注释,实现。以下 列表显示了接口的定义:​​Formatter​​​​AnnotationFormatterFactory​​​​AnnotationFormatterFactory​

要创建实现,请执行以下操作:

  1. 将参数化 A 作为要关联的字段 格式逻辑 — 例如。annotationTypeorg.springframework.format.annotation.DateTimeFormat
  2. 已返回可以使用批注的字段类型。getFieldTypes()
  3. 返回 a以打印带批注字段的值。getPrinter()Printer
  4. Havereturn ato parse afor an annotated field。getParser()ParserclientValue

以下示例实现将注释绑定到格式化程序,以使数字样式或模式 指定:​​AnnotationFormatterFactory​​​​@NumberFormat​

public final class NumberFormatAnnotationFormatterFactory
implements AnnotationFormatterFactory {

public Set> getFieldTypes() {
return new HashSet>(asList(new Class[] {
Short.class, Integer.class, Long.class, Float.class,
Double.class, BigDecimal.class, BigInteger.class }));
}

public Printer getPrinter(NumberFormat annotation, Class fieldType) {
return configureFormatterFrom(annotation, fieldType);
}

public Parser getParser(NumberFormat annotation, Class fieldType) {
return configureFormatterFrom(annotation, fieldType);
}

private Formatter configureFormatterFrom(NumberFormat annotation, Class fieldType) {
if (!annotation.pattern().isEmpty()) {
return new NumberStyleFormatter(annotation.pattern());
} else {
Style style = annotation.style();
if (style == Style.PERCENT) {
return new PercentStyleFormatter();
} else if (style == Style.CURRENCY) {
return new CurrencyStyleFormatter();
} else {
return new NumberStyleFormatter();
}
}
}
}

要触发格式设置,您可以使用@NumberFormat对字段进行批注,如下所示 示例显示:

格式注释 API

包中存在可移植格式注释 API。您可以使用 to 格式化字段,例如 and、andto format、,(用于毫秒时间戳)以及 JSR-310。​​org.springframework.format.annotation​​​​@NumberFormat​​​​Number​​​​Double​​​​Long​​​​@DateTimeFormat​​​​java.util.Date​​​​java.util.Calendar​​​​Long​​​​java.time​

以下示例用于将 aas 格式化为 ISO 日期 (年-月-日):​​@DateTimeFormat​​​​java.util.Date​

3.5.3. SPI​​FormatterRegistry​

这是一个用于注册格式化程序的SPI,并 converters.is 适用于 大多数环境。您可以通过编程或声明方式配置此变体 作为春豆,例如通过使用。因为这个 实现也实现,可以直接配置它 用于 Spring's and the Spring Expression Language (SpEL)。​​FormatterRegistry​​​​FormattingConversionService​​​​FormatterRegistry​​​​FormattingConversionServiceFactoryBean​​​​ConversionService​​​​DataBinder​

以下清单显示了 SPI:​​FormatterRegistry​

如前面的清单所示,您可以按字段类型或批注注册格式化程序。

SPI 允许您集中配置格式规则,而不是 在控制器之间复制此类配置。例如,您可能希望 强制所有日期字段都以某种方式格式化,或者字段具有特定的格式 批注以某种方式格式化。使用共享,您可以定义 这些规则一次,只要需要格式化,就会应用它们。​​FormatterRegistry​​​​FormatterRegistry​

3.5.4. SPI​​FormatterRegistrar​

​FormatterRegistrar​​是一个 SPI,用于通过 格式化程序注册表。以下清单显示了其接口定义:

在注册多个相关转换器时很有用,并且 给定格式类别(如日期格式)的格式化程序。它也可以是 在声明性注册不足的情况下很有用 - 例如,当格式化程序 需要在不同于其拥有者的特定字段类型下编制索引,当 注册 A/对。下一节提供了有关以下内容的更多信息 转换器和格式化程序注册。​​FormatterRegistrar​​​​​​Printer​​​​Parser​

3.5.5. 在 Spring MVC 中配置格式化

请参阅Spring MVC章节中的转换和格式化。

3.6. 配置全局日期和时间格式

默认情况下,未批注的日期和时间字段转换自 字符串通过使用样式。如果您愿意,可以通过以下方式更改此设置 定义您自己的全局格式。​​@DateTimeFormat​​​​DateFormat.SHORT​

为此,请确保 Spring 不会注册默认格式化程序。相反,请注册 在以下人员的帮助下手动格式化程序:

例如,以下 Java 配置注册全局格式:​​yyyyMMdd​

@Configuration
public class AppConfig {

@Bean
public FormattingConversionService conversionService() {

// Use the DefaultFormattingConversionService but do not register defaults
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);

// Ensure @NumberFormat is still supported
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());

// Register JSR-310 date conversion with a specific global format
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
registrar.registerFormatters(conversionService);

// Register date conversion with a specific global format
DateFormatterRegistrar registrar = new DateFormatterRegistrar();
registrar.setFormatter(new DateFormatter("yyyyMMdd"));
registrar.registerFormatters(conversionService);

return conversionService;
}
}

如果您更喜欢基于 XML 的配置,则可以使用 a.以下示例演示如何执行此操作:​​FormattingConversionServiceFactoryBean​

请注意,在 Web 中配置日期和时间格式时,还需要注意其他事项 应用。请参阅WebMVC转换和格式化或WebFlux 转换和格式化。

版本 5.3.23

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