32. 消息发送

Spring Framework 提供了与消息系统集成的广泛支持,从使用 JmsTemplate 简化使用 JMS API 到异步接收消息的完整基础设施。Spring AMQP 为高级消息队列协议提供了类似的特征集。Spring Boot 还为 RabbitTemplate 和 RabbitMQ 提供了自动配置选项。Spring WebSocket 本身包括对 STOMP 消息传递的支持,并且 Spring Boot 通过 starters 和少量的自动配置来支持它。Spring Boot 也支持 Apache Kafka。

32.1 JMS

javax.jms.ConnectionFactory 接口提供了一种创建 javax.jms.Connection 的标准方法,用于与 JMS 代理进行交互。虽然 Spring 需要一个 ConnectionFactory 来与 JMS 一起工作,但通常不需要直接使用它,而可以依赖于更高级别的消息抽象。(详见 Spring Framework 参考文档的相关部分。)Spring Boot 还自动配置必要的基础结构来发送和接收消息。

32.1.1 ActiveMQ 支持

当 ActiveMQ 在类路径上可用时,Spring Boot 可以配置 ConnectionFactory。如果代理存在,则会自动启动和配置嵌入式代理(如果没有通过配置指定代理 URL)。

[Note] Note

如果你使用 spring-boot-starter-activemq,则提供了连接或嵌入 ActiveMQ 实例的必要依赖,以及与 JMS 集成的 Spring 基础设施。

ActiveMQ 配置由 Spring 中的外部配置属性 spring.activemq.* 控制。例如,你可以在 application.properties 声明以下部分:

spring.activemq.broker-url=tcp://192.168.1.210:9876
spring.activemq.user=admin
spring.activemq.password=secret

默认情况下,CachingConnectionFactory 将本地 ConnectionFactory 封装为可以通过 Spring 中的外部配置属性 spring.jms.* 控制的合理设置。

spring.jms.cache.session-cache-size=5

如果你愿意使用本地池,可以通过向 org.apache.activemq:activemq-jms-pool 添加依赖项并相应地配置 PooledConnectionFactory 来实现这一点,如下面的示例所示:

spring.activemq.pool.enabled=true
spring.activemq.pool.max-connections=50
[Tip] Tip

请参阅更多支持选项的 ActiveMQProperties。你还可以注册任意数量的 bean,以实现 ActiveMQConnectionFactoryCustomizer 用于更高级的自定义。

默认情况下,ActiveMQ 创建一个目的地,如果它还不存在,那么目的地是根据其提供的名称来解决的。

32.1.2 Artemis 支持

当 Spring Boot 检测到 Artemis 在类路径上可用时,它会自动配置 ConnectionFactory。如果代理存在,则会自动启动和配置嵌入代理(除非已显式设置了模式属性)。所支持的模式是 embedded(明确表示需要嵌入代理,并且代理在类路径上不可用时,应该发生错误)和 native(使用 netty 传输协议连接到代理)。当后者被配置时,Spring Boot 配置连接到本地机器上运行的代理的 ConnectionFactory,并具有默认设置。

[Note] Note

如果你使用 spring-boot-starter-artemis,则提供连接到现有 Artemis 实例所必要的依赖项,以及与 JMS 集成的 Spring 基础设施。将 org.apache.activemq:artemis-jms-server 添加到应用程序中来使你使用嵌入式模式。

Artemis 配置由 spring.artemis.* 中的外部配置属性控制。例如,你可以在 application.properties 中添加以下部分:

spring.artemis.mode=native
spring.artemis.host=192.168.1.210
spring.artemis.port=9876
spring.artemis.user=admin
spring.artemis.password=secret

在嵌入代理时,可以选择是否启用持久性,并列出应该使用的目的地。这些可以被指定为逗号分隔的列表,用默认选项创建它们,或者可以定义 org.apache.activemq.artemis.jms.server.config.JMSQueueConfiguration 或 org.apache.activemq.artemis.jms.server.config.TopicConfiguration 类型的 bean,分别用于高级队列和主题配置。

默认情况下,CachingConnectionFactory 将本地 ConnectionFactory 封装为可以通过 spring.jms.* 的外部配置属性控制的敏感设置。

spring.jms.cache.session-cache-size=5

如果你愿意使用本地池,可以通过向 org.apache.activemq:activemq-jms-pool 添加依赖项并相应地配置 PooledConnectionFactory 来实现这一点,如下面的示例所示:

spring.artemis.pool.enabled=true
spring.artemis.pool.max-connections=50

更多支持的选项请参阅 ArtemisProperties。

不涉及 JNDI 查找,并且使用 Artemis 配置中的 name 属性或通过配置提供的名称来解决目的地对它们的名称。

32.1.3 使用 JNDI 连接工厂

如果在应用服务器中运行应用程序,Spring Boot 将尝试使用 JNDI 定位 JMS ConnectionFactory。默认情况下,检查 java:/JmsXA 和 java:/XAConnectionFactory 位置。如果需要指定另一个位置,可以使用 spring.jms.jndi-name 属性,如下面的示例所示:

spring.jms.jndi-name=java:/MyConnectionFactory

32.1.4 发送消息

Spring 的 JmsTemplate 是自动配置的,你可以直接将它自动存储到你自己的 bean 中,如下面的示例所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

	private final JmsTemplate jmsTemplate;

	@Autowired
	public MyBean(JmsTemplate jmsTemplate) {
		this.jmsTemplate = jmsTemplate;
	}

	// ...

}
[Note] Note

JmsMessagingTemplate 可以以类似的方式注入。如果定义了 DestinationResolver 或 MessageConverter bean,它将自动关联到自动配置的 JmsTemplate。

32.1.5 接收消息

当 JMS 基础结构存在时,任何 bean 都可以用 @JmsListener 注解来创建监听器端点。如果没有定义 JmsListenerContainerFactory,默认自动配置一个。如果定义了 DestinationResolver 或 MessageConverter bean,它将自动关联到默认工厂。

默认情况下,默认的工厂是事务性的。如果运行在存在 JtaTransactionManager 的基础结构中,则默认情况下它与监听器容器关联。如果没有,则启用 sessionTransacted 标志。在后一种情况下,你可以将本地数据存储事务与传入消息的处理相关联,通过在监听器方法(或其委托)中添加 @Transactional。这确保一旦本地事务完成,传入消息就被确认。这还包括发送已在同一 JMS 会话上执行的响应消息。

下面的组件在 someQueue 目的地上创建监听器端点:

@Component
public class MyBean {

	@JmsListener(destination = "someQueue")
	public void processMessage(String content) {
		// ...
	}

}
[Tip] Tip

更多细节请参阅 @EnableJms 的 Javadoc。

如果你需要创建更多的 JmsListenerContainerFactory 实例,或者想要重写默认值,Spring Boot 提供了一个 DefaultJmsListenerContainerFactoryConfigurer 以便你可以使用它初始化一个与自动配置的设置相同的 DefaultJmsListenerContainerFactory。

例如,下面的示例使用特定 MessageConverter 暴露另一个工厂:

@Configuration
static class JmsConfiguration {

	@Bean
	public DefaultJmsListenerContainerFactory myFactory(
			DefaultJmsListenerContainerFactoryConfigurer configurer) {
		DefaultJmsListenerContainerFactory factory =
				new DefaultJmsListenerContainerFactory();
		configurer.configure(factory, connectionFactory());
		factory.setMessageConverter(myMessageConverter());
		return factory;
	}

}

然后,你可以在任何 @JmsListener 注解方法中使用该工厂,如下:

@Component
public class MyBean {

	@JmsListener(destination = "someQueue", containerFactory="myFactory")
	public void processMessage(String content) {
		// ...
	}

}

32.2 AMQP

高级消息队列协议(AMQP)是一个平台无关的、有线级协议的面向消息的中间件。Spring AMQP 项目将核心 Spring 概念应用于开发基于 AMQP 的消息解决方案。Spring Boot 通过 RabbitMQ 为 AMQP 提供了许多便利,包括 spring-boot-starter-amqp “Starter”。

32.2.1 RabbitMQ 支持

RabbitMQ 是基于 AMQP 协议的轻量级的、可靠的、可扩展的、可移植的消息代理。Spring 使用 RabbitMQ 通过 AMQP 协议进行通信。

RabbitMQ 配置是由外部配置 spring.rabbitmq.* 属性控制的。例如,你可以在 application.properties 中声明以下部分:

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=secret

如果上下文中存在一个 ConnectionNameStrategy bean,它将被自动用于由自动配置的 ConnectionFactory 创建的名称连接。有关更多支持的选项,请参阅 RabbitProperties。

[Tip] Tip

更多细节请参阅了解 AMQP,RabbitMQ 使用的协议

32.2.2 发送 Message

Spring 的 AmqpTemplate 和 AmqpAdmin 是自动配置的,你可以直接将他们自动存储到你自己的 bean 中,如下面的示例所示;

import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

	private final AmqpAdmin amqpAdmin;
	private final AmqpTemplate amqpTemplate;

	@Autowired
	public MyBean(AmqpAdmin amqpAdmin, AmqpTemplate amqpTemplate) {
		this.amqpAdmin = amqpAdmin;
		this.amqpTemplate = amqpTemplate;
	}

	// ...

}
[Note] Note

可以使用类似的方法注入 RabbitMessagingTemplate。如果定义了 MessageConverter bean,则自动将其关联到自动配置的 AmqpTemplate。

如果需要,任何定义为 bean 的 org.springframework.amqp.core.Queue,将被自动用于在 RabbitMQ 实例上声明相应的队列。

若要重试操作,你可以在 AmqpTemplate 上启用重试(例如,在代理连接丢失的情况下)。默认情况下,重试被禁用。

32.2.3 接收 Message

当 Rabbit 基础设施存在时,任何 bean 都可以使用 @RabbitListener 注解来创建监听器端点。如果没有 bean 定义 RabbitListenerContainerFactory,默认的 SimpleRabbitListenerContainerFactory 将被自动配置,并且你可以使用 spring.rabbitmq.listener.type 属性切换到直接的容器。如果定义了 MessageConverter 或 MessageRecoverer bean,它将自动与默认工厂关联。

下面的示例组件在 someQueue 队列上创建监听器端点:

@Component
public class MyBean {

	@RabbitListener(queues = "someQueue")
	public void processMessage(String content) {
		// ...
	}

}
[Tip] Tip

更多细节请参阅 @EnableRabbit 的 Javadoc。

如果你需要创建更多的 RabbitListenerContainerFactory 实例或想重写默认值,Spring Boot 提供了一个 SimpleRabbitListenerContainerFactoryConfigurer 和 DirectRabbitListenerContainerFactoryConfigurer,以便你使用与自动配置工厂相同的设置来初始化一个 SimpleRabbitListenerContainerFactory 和 DirectRabbitListenerContainerFactory。

[Tip] Tip

你选择哪种容器类型并不重要。这两个 bean 是通过自动配置暴露出来的。

例如,下面的配置类使用特定的 MessageConverter 暴露另一个工厂:

@Configuration
static class RabbitConfiguration {

	@Bean
	public SimpleRabbitListenerContainerFactory myFactory(
			SimpleRabbitListenerContainerFactoryConfigurer configurer) {
		SimpleRabbitListenerContainerFactory factory =
				new SimpleRabbitListenerContainerFactory();
		configurer.configure(factory, connectionFactory);
		factory.setMessageConverter(myMessageConverter());
		return factory;
	}

}

然后,你可以在任何 @RabbitListener 注解的方法中使用该工厂,如下:

@Component
public class MyBean {

	@RabbitListener(queues = "someQueue", containerFactory="myFactory")
	public void processMessage(String content) {
		// ...
	}

}

你可以启用重试来处理监听器抛出异常的情况。默认情况下,使用 RejectAndDontRequeueRecoverer,但可以定义你自己的 MessageRecoverer。如果代理被配置为这样,当重试被耗尽时则消息被拒绝,并丢弃或路由到死信交换。默认情况下,重试被禁用。

[Important] 重点

默认情况下,如果重试被禁用,并且监听器抛出异常,则将无限期重试传递。你可以通过两种方式修改此行为:将 defaultRequeueRejected 属性设置为 false,以便尝试零重新传递,或抛出 AmqpRejectAndDontRequeueException 来通知消息应该被拒绝。后者是启用重试时所使用的机制,并且达到最大的交付尝试次数。

32.3 Apache Kafka 支持

Apache Kafka 是通过 spring-kafka 项目提供的自动配置来支持的。

Kafka 配置是由 spring.kafka.* 的外部配置属性控制的。例如,你可以在 application.properties 中添加以下部分:

spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=myGroup
[Tip] Tip

若要在启动时创建主题,请添加 NewTopic 类型的 bean。如果主题已经存在,则忽略 bean。

更多支持的选项请参阅 KafkaProperties。

32.3.1 发送 Message

Spring 的 KafkaTemplate 是自动配置的,你可以直接在自己的 bean 中自动调用它,如下面的示例所示:

@Component
public class MyBean {

	private final KafkaTemplate kafkaTemplate;

	@Autowired
	public MyBean(KafkaTemplate kafkaTemplate) {
		this.kafkaTemplate = kafkaTemplate;
	}

	// ...

}
[Note] Note

如果定义了一个 RecordMessageConverter bean,它将自动与自动配置的 KafkaTemplate 相关联。

32.3.2 接收 Message

当 Apache Kafka 基础结构存在时,任何 bean 都可以用 @KafkaListener 注解以创建监听器端点。如果没有定义 KafkaListenerContainerFactory,则自动配置一个定义为 spring.kafka.listener.* 关键字的默认工厂。此外,如果定义了一个 RecordMessageConverter bean,它将自动关联到默认工厂。

下面的组件在 someTopic 主题上创建一个监听器端点:

@Component
public class MyBean {

	@KafkaListener(topics = "someTopic")
	public void processMessage(String content) {
		// ...
	}

}

32.3.3 额外的 Kafka 属性

由自动配置支持的属性在附录 A, 常见的应用程序属性中展示。请注意,在大多数情况下,这些属性(连接符的或驼峰式)直接映射到 Apache Kafka 点属性。更多详细信息请参阅 Apache Kafka 文档。

这些属性中的前几个应用于生产者和消费者,但如果希望对每个用户使用不同的值,则可以在生产者或消费者级别指定。Apache Kafka 指定具有 HIGH, MEDIUM, 或 LOW 的重要性的属性。Spring Boot 自动配置支持所有 HIGH 重要性的属性,一些选定的 MEDIUM 和 LOW 的属性,以及任何没有默认值的属性。

只有 Kafka 支持的属性子集可以通过 KafkaProperties 类获得。如果你希望配置具有不直接支持的额外属性的生产者或消费者,请使用以下属性:

spring.kafka.properties.prop.one=first
spring.kafka.admin.properties.prop.two=second
spring.kafka.consumer.properties.prop.three=third
spring.kafka.producer.properties.prop.four=fourth

这就设置了,公共 prop.one Kafka 属性为 first(适用于生产者、消费者和管理员),prop.two admin 属性为 second,prop.three 消费者属性为 third,以及 prop.four 提供者属性为 fourth。

你还可以配置 Spring Kafka JsonDeserializer 如下:

spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.properties.spring.json.value.default.type=com.example.Invoice
spring.kafka.consumer.properties.spring.json.trusted.packages=com.example,org.acme

类似的,你可以在头文件中发送类型信息时禁用 JsonSerializer 的默认行为。

spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.producer.properties.spring.json.add.type.headers=false
[Important] 重点

以这种方式设置的属性重写 Spring Boot 显式支持的任何配置项。

33. 使用 RestTemplate 调用 REST 服务

如果你需要从应用程序调用远程 REST 服务,可以使用 Spring Framework 的 RestTemplate 类。由于 RestTemplate 实例通常需要在使用之前进行自定义,Spring Boot 不提供任何自动配置的单个 RestTemplate bean。但是,它确实自动配置了 RestTemplateBuilder,可以在需要时用于创建 RestTemplate 实例。自动配置的 RestTemplateBuilder 确保将敏感的 HttpMessageConverters 应用到 RestTemplate 实例。

下面的代码展示了一个典型的例子:

@Service
public class MyService {

	private final RestTemplate restTemplate;

	public MyService(RestTemplateBuilder restTemplateBuilder) {
		this.restTemplate = restTemplateBuilder.build();
	}

	public Details someRestCall(String name) {
		return this.restTemplate.getForObject("/{name}/details", Details.class, name);
	}

}
[Tip] Tip

RestTemplateBuilder 包含一些可以用来快速配置 RestTemplate 的有用方法。例如,为了添加基本的权限,可以使用 builder.basicAuthorization("user", "password").build()。

33.1 定制 RestTemplate

定制 RestTemplate 有三种主要的方法,这取决于你希望自定义应用的广度。

为了使任何自定义的范围尽可能窄,注入自动配置的 RestTemplateBuilder,然后根据需要调用其方法。每个方法调用都会返回一个新的 RestTemplateBuilder 实例,因此自定义智慧影响生成器的使用。

为了在应用程序范围内进行广泛的定制,使用 RestTemplateCustomizer bean。所有这样的 bean 都会自动注册到自动配置的 RestTemplateBuilder 中,并应用到与之建立的任何模板中。

下面的示例展示了一个定制器,它配置除了 192.168.0.5 以外的所有主机的代理使用:

static class ProxyCustomizer implements RestTemplateCustomizer {

	@Override
	public void customize(RestTemplate restTemplate) {
		HttpHost proxy = new HttpHost("proxy.example.com");
		HttpClient httpClient = HttpClientBuilder.create()
				.setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {

					@Override
					public HttpHost determineProxy(HttpHost target,
							HttpRequest request, HttpContext context)
							throws HttpException {
						if (target.getHostName().equals("192.168.0.5")) {
							return null;
						}
						return super.determineProxy(target, request, context);
					}

				}).build();
		restTemplate.setRequestFactory(
				new HttpComponentsClientHttpRequestFactory(httpClient));
	}

}

最后,最极端的(也是很少使用的)选项是创建自己的 RestTemplateBuilder bean。这样做可以关闭 RestTemplateBuilder 的自动配置,并防止使用任何 RestTemplateCustomizer bean。

34. 使用 WebClient 调用 REST 服务

如果你的类路径上有 Spring WebFlux,也可以选择使用 WebClient 来调用远程 REST 服务。与 RestTemplate 相比,该客户端有更多的函数式感,并且全是响应式。你可以使用 WebClient.create() 创建者来创建你自己的客户端实例。请参阅 WebClient 相关部分

Spring Boot 为你创建并预先配置这样的生成器。例如,客户端 HTTP 编码器以与服务器相同的方式配置(详见 WebFlux HTTP 编码器自动配置)。

下面的代码展示了一个典型的例子:

@Service
public class MyService {

	private final WebClient webClient;

	public MyService(WebClient.Builder webClientBuilder) {
		this.webClient = webClientBuilder.baseUrl("http://example.org").build();
	}

	public Mono<Details> someRestCall(String name) {
		return this.webClient.get().url("/{name}/details", name)
						.retrieve().bodyToMono(Details.class);
	}

}

34.1 定制 WebClient

WebClient 定制有三种主要方法,这取决于你希望自定义应用的广度。

为了使任何自定义的范围尽可能窄,注入自动配置的 WebClient.Builder,然后根据需要调用其方法。WebClient.Builder 实例是有状态的:生成器上的任何更改都反映在随后创建的所有客户端中。如果你想用同一个生成器创建多个客户端,还可以考虑使用 WebClient.Builder other = builder.clone(); 来克隆生成器。

为了对所有 WebClient.Builder 实例进行广泛的应用程序定制,可以声明 WebClientCustomizer bean,并更改注入 WebClient.Builder 为本地的点。

最后,你可以回到原来的 API 并使用 WebClient.create()。在这种情况下,不应用自动配置或 WebClientCustomizer。

35. 校验

只要一个 JSR-303 实现(如 Hibernate 验证器)在类路径上,Bean Validation 1.1 所支持的方法验证功能就会自动启用。这让 bean 中使用 javax.validation 注解的方法,约束其参数和/或它们的返回值。使用此类注解方法的目标类需要在类型级别上使用 @Validated 注解来注解它们的方法,以便对内联约束注解进行搜索。

例如,下面的服务触发第一个参数的验证,确保其大小在8到10之间:

@Service
@Validated
public class MyBean {

	public Archive findByCodeAndAuthor(@Size(min = 8, max = 10) String code,
			Author author) {
		...
	}

}

36. 发送 Email

Spring Framework 通过 JavaMailSender 接口提供了一个简单的发送 email 的抽象,Spring Boot 为它提供了自动配置以及启动模块。

[Tip] Tip

有关如何使用 JavaMailSender 的详细说明,请参阅参考文档。

如果 spring.mail.host 和相关的库(如 spring-boot-starter-mail 所定义的)可用,则创建一个默认的 JavaMailSender,当其不存在时。发送者可以通过 spring.mail 命名空间的配置项进一步定制。有关的详细信息,请参阅 MailProperties。

特别是,某些默认的超时值是无限的,并且你可能希望改变这一点,以避免由未响应邮件服务器阻塞线程,如下面的示例所示:

spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=3000
spring.mail.properties.mail.smtp.writetimeout=5000

还可以从 JNDI 配置 JavaMailSender 和现有的 Session:

spring.mail.jndi-name=mail/Session

当设置 jndi-name 时,它优先于所有其它与会话相关的设置。

37. 使用 JTA 实现分布式事务

Spring Boot 通过使用 Atomikos 或 Bitronix 嵌入式事务管理器来支持跨多个 XA 资源的分布式 JTA 事务。JTA 事务在部署到合适的 Java EE 应用服务器时也得到支持。

当检测到 JTA 环境时,Spring 的 JtaTransactionManager 用于管理事务。自动配置的 JMS、数据源和 JPA bean 被升级以支持 XA 事务。你可以使用标准的 Spring 习语,例如 @Transactional,参与分布式事务。如果你位于 JTA 环境中,仍想使用本地事务,可以将 spring.jta.enabled 属性设置为 false,以禁用 JTA 自动配置。

37.1 使用 Atomikos 事务管理器

Atomikos 是一种流行的开源事务管理器,可以嵌入到 Spring Boot 应用程序中。你可以使用 spring-boot-starter-jta-atomikos Starter 来拉入适当的 Atomikos 库。Spring Boot 自动配置 Atomikos,并确保适当的依赖于 Spring bean 设置的设置,用于正确的启动和关机顺序。

默认情况下,Atomikos 事务日志被写入应用程序的主目录(你应用程序 jar 文件驻留的目录)中的事务日志目录。通过在 application.properties 中设置 spring.jta.log-dir 属性,可以自定义该目录的位置。从 spring.jta.atomikos.properties 开始的属性也可以用于定制 Atomikos UserTransactionServiceImp。有关的详细信息,请参阅 AtomikosProperties Javadoc。

[Note] Note

为了确保多个事务管理器能够安全地协调相同的资源管理器,每个 Atomikos 实例必须配置一个唯一 ID。默认情况下,这个 ID 是 Atomikos 正在运行的机器的 IP 地址。为了确保生产中的唯一性,你应该为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,并设置不同的值。

37.2 使用 Bitronix 事务管理器

Bitronix 是一个流行的开源 JTA 事务管理器实现。可以使用 spring-boot-starter-jta-bitronix starter 为项目添加适当的 Bitronix 依赖项。与 Atomikos 一样,Spring Boot 会自动配置 Bitronix,并对你的 bean 进行后期处理,以确保启动和关闭命令是正确的。

默认情况下,Bitronix 事务日志文件(part1.btm 和 part2.btm)被写入你的应用程序主目录的 transaction-logs 目录。可以通过设置 spring.jta.log-dir 属性来自定义该目录的位置。从 spring.jta.bitronix.properties 开始的属性也绑定到 bitronix.tm.Configuration bean,允许完全定制。有关的详细信息请参阅 Bitronix 文档。

[Note] Note

为了确保多个事务管理器能够安全地协调相同的资源管理器,每个 Bitronix 实例必须配置一个唯一 ID。默认情况下,这个 ID 是 Bitronix 正在运行的机器的 IP 地址。为了确保生产中的唯一性,你应该为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,并设置不同的值。

37.3 使用 Narayana 事务管理器

Narayana 是一个流行的开源 JTA 事务管理器实现,由 JBoss 支持。你可以使用 spring-boot-starter-jta-narayana starter 向项目中添加适当的 Narayana 依赖项。与 Atomikos 和 Bitronix 一样,Spring Boot 会自动配置 Narayana, 并对你的 bean 进行后期处理,以确保启动和关闭命令是正确的。

默认情况下,Narayana 事务日志将写入应用程序主目录(你的应用程序 jar 文件驻留的目录)中的 transaction-logs 目录。通过在 application.properties 文件中设置 spring.jta.log-dir 属性,可以自定义该目录的位置。以 spring.jta.narayana.properties 开始的属性也可以用于自定义 Narayana 配置。有关的完整细节请参阅 NarayanaProperties Javadoc。

[Note] Note

为了确保多个事务管理器能够安全地协调相同的资源管理器,每个 Narayana 实例必须配置一个唯一的ID。默认情况下,此 ID 设置为 1。为了确保生产中的唯一性,你应该为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,并设置不同的值。

37.4 使用 Java EE 管理的事务管理器

如果将 Spring Boot 应用程序打包为 war 或 ear 文件,并将其部署到 Java EE 应用服务器,则可以使用应用服务器内置的事务管理器。Spring Boot 试图通过查看常见的 JNDI 位置(java:comp/UserTransaction, java:comp/TransactionManager,等等)自动配置事务管理器。如果使用应用服务器提供的事务服务,通常还需要确保所有资源都由服务器管理并在 JNDI 上公开。Spring Boot 试图通过在 JNDI 路径(java:/JmsXA 或 java:/XAConnectionFactory)中寻找 ConnectionFactory 来自动配置 JMS,并且可以使用 spring.datasource.jndi-name 属性来配置 DataSource。

37.5 混合 XA 和 Non-XA JMS 连接

当使用 JTA 时,主 JMS ConnectionFactory bean 是 XA 感知的,参与分布式事务。在某些情况下,你可能希望使用non-XA ConnectionFactory 来处理某些 JMS 消息。例如,JMS 处理逻辑可能比 XA 超时占用更多的时间。

如果你想使用 non-XA ConnectionFactory,可以注入 nonXaJmsConnectionFactory bean,而不是 @Primary jmsConnectionFactory bean。为了一致性,还使用别名为 xaJmsConnectionFactory 的 bean 提供的 jmsConnectionFactory bean。

下面的示例展示了如何注入 ConnectionFactory 实例:

// Inject the primary (XA aware) ConnectionFactory
@Autowired
private ConnectionFactory defaultConnectionFactory;

// Inject the XA aware ConnectionFactory (uses the alias and injects the same as above)
@Autowired
@Qualifier("xaJmsConnectionFactory")
private ConnectionFactory xaConnectionFactory;

// Inject the non-XA aware ConnectionFactory
@Autowired
@Qualifier("nonXaJmsConnectionFactory")
private ConnectionFactory nonXaConnectionFactory;

37.6 支持 Alternative 嵌入式事务管理器

XAConnectionFactoryWrapper 和 XADataSourceWrapper 接口可用于支持可选的嵌入式事务管理器。这些接口负责包装 XAConnectionFactory 和 XADataSource bean,并将它们作为常规 ConnectionFactory 和 DataSource bean,它们透明地登记在分布式事务中。数据源和 JMS 自动配置使用 JTA 变体,前提是你在 ApplicationContext 中注册了 JtaTransactionManager bean 和适当的 XA 包装器 bean。

BitronixXAConnectionFactoryWrapper 和 BitronixXADataSourceWrapper 提供了如何编写 XA包装器的很好的例子。

38. Hazelcast

如果 Hazelcast 在类路径上,并且找到了合适的配置,Spring Boot 会自动配置一个可以在你的应用程序中注入的 HazelcastInstance。

如果你定义了 com.hazelcast.config.Config bean,Spring Boot 会使用它。如果配置定义了实例名称,Spring Boot 尝试定位现有实例而不是创建新实例。

你还可以指定配置使用的 hazelcast.xml 配置文件,如下面的示例所示:

spring.hazelcast.config=classpath:config/my-hazelcast.xml

否则,Spring Boot 尝试从默认位置查找 Hazelcast 配置:工作目录或类路径的根中的 hazelcast.xml。我们还检查是否设置了 hazelcast.config 系统属性。有关的详细信息请参阅 Hazelcast 文档。

如果 hazelcast-client 出现在类路径上,Spring Boot 首先尝试通过检查以下配置选项来创建客户端:

  • 存在的 com.hazelcast.client.config.ClientConfig bean。
  • spring.hazelcast.config 属性定义的配置文件。
  • 存在的 hazelcast.client.config 系统属性。
  • 工作目录或类路径的根上的 hazelcast-client.xml。
[Note] Note

Spring Boot 还 为 Hazelcast 提供的显式缓存支持。如果启用缓存,HazelcastInstance 将自动包装在 CacheManager 实现中。

39. Quartz 调度程序

Spring Boot 为使用 Quartz 调度程序提供了许多便利,包括 spring-boot-starter-quartz “Starter”。如果有可用的 Quartz,调度程序将自动配置(通过 SchedulerFactoryBean 抽象)。

以下类型的 bean 将被自动拾取并与 调度程序 关联:

  • JobDetail: 定义特定的作业。可以使用 JobBuilder API 构建 JobDetail 实例。
  • Calendar。
  • Trigger: 定义何时触发特定作业。

默认情况下,使用内存中的 JobStore。但是,如果 DataSource bean 在应用程序中可用,并且如果 spring.quartz.job-store-type 属性被相应地配置,则可以配置基于 JDBC 的存储,如下面的示例所示:

spring.quartz.job-store-type=jdbc

当使用 JDBC 存储时,可以在启动时初始化该架构,如下面的示例所示:

spring.quartz.jdbc.initialize-schema=always
[Note] Note

默认情况下,通过使用与 Quartz 库一起提供的标准脚本来检测和初始化数据库。还可以通过设置 spring.quartz.jdbc.schema 属性来提供自定义脚本。

Quartz 调度程序配置可以通过使用 Quartz 配置属性(spring.quartz.properties.*)和 SchedulerFactoryBeanCustomizer bean 来定制,这允许编程 SchedulerFactoryBean 自定义。

[Note] Note

特别是,Executor bean 与调度程序无关,因为 Quartz 提供了一种通过 spring.quartz.properties 配置调度程序的方法。如果需要自定义任务执行器,请考虑实现 SchedulerFactoryBeanCustomizer。

作业可以定义设置程序以注入数据映射属性。常规 bean 也可以以类似的方式注入,如下面的例子所示:

public class SampleJob extends QuartzJobBean {

	private MyService myService;

	private String name;

	// Inject "MyService" bean
	public void setMyService(MyService myService) { ... }

	// Inject the "name" job data property
	public void setName(String name) { ... }

	@Override
	protected void executeInternal(JobExecutionContext context)
			throws JobExecutionException {
		...
	}

}

40. Spring 集成

Spring Boot 为使用 Spring 集成提供了许多便利,包括 spring-boot-starter-integration “Starter”。Spring 集成通过消息传递和其它传输(如 HTTP、TCP 等)提供抽象。如果 Spring 集成在类路径上可用,则通过 @EnableIntegration 注解初始化。

Spring Boot 还配置了有附加的 Spring 集成模块触发的一些特性。如果 spring-integration-jmx 也在类路径上,则通过 JMX 发布消息处理统计信息。如果 spring-integration-jdbc 可用,则可以在启动时创建默认数据库集合,如下面的示例所示:

spring.integration.jdbc.initialize-schema=always

更多细节请参阅 IntegrationAutoConfiguration 和 IntegrationProperties 类。

默认情况下,如果一个 Micrometer meterRegistry bean 存在,Spring 集成将由 Micrometer 管理度量。如果希望使用遗留 Spring 集成度量,则在应用程序上下文中添加 DefaultMetricsFactory bean。

41. Spring 会话

Spring Boot 为广泛的数据存储提供了 Spring 会话自动配置。在构建 Servlet web 应用程序时,可以自动配置以下存储区:

  • JDBC
  • Redis
  • Hazelcast
  • MongoDB

当构建一个响应式 Web 应用程序时,可以自动配置下列存储区:

  • Redis
  • MongoDB

如果一个单一的 Spring 会话模块出现在类路径上,Spring Boot 会自动使用该存储实现。如果有多个实现,则必须选择要用来存储会话的 StoreType。例如,使用 JDBC 作为后端存储,可以将应用程序配置如下:

spring.session.store-type=jdbc
[Tip] Tip

你可以通过将 store-type 设置为 none 来禁用 Spring 会话。

每个存储都有特定的附加设置。例如,可以自定义 JDBC 存储的表名称,如下面的示例所示:

spring.session.jdbc.table-name=SESSIONS

若要设置会话的超时,可以使用 spring.session.timeout 属性。如果未设置该属性,则自动配置回落到 server.servlet.session.timeout 的值。

42. JMX 的监控与管理

Java 管理扩展(JMX)提供了一种监控和管理应用程序的标准机制。默认情况下,Spring Boot 创建具有 mbeanServer ID 的 MBeanServer bean,并公开用 Spring JMX 注解注解的任何 bean(@ManagedResource, @ManagedAttribute, 或 @ManagedOperation)。

更多细节请参阅 JmxAutoConfiguration 类。