集成

1. REST 端点

Spring Framework 提供了两种调用 REST 端点的选择:spring-doc.cadn.net.cn

从 5.0 开始,RestTemplate处于维护模式,只有少量请求今后将接受更改和错误。请考虑使用 WebClient,它提供了更现代的 API 和支持同步、异步和流式处理场景。

1.1.RestTemplate

RestTemplate提供高于 HTTP 客户端库的更高级别的 API。它使在一行中轻松调用 REST 端点。它公开了以下几组重载的方法:spring-doc.cadn.net.cn

表 1.RestTemplate 方法
方法组 描述

getForObjectspring-doc.cadn.net.cn

通过 GET 检索表示。spring-doc.cadn.net.cn

getForEntityspring-doc.cadn.net.cn

检索一个ResponseEntity(即状态、标头和正文)使用 GET。spring-doc.cadn.net.cn

headForHeadersspring-doc.cadn.net.cn

使用 HEAD 检索资源的所有标头。spring-doc.cadn.net.cn

postForLocationspring-doc.cadn.net.cn

使用 POST 创建新资源并返回Location标头。spring-doc.cadn.net.cn

postForObjectspring-doc.cadn.net.cn

使用 POST 创建新资源,并从响应中返回表示形式。spring-doc.cadn.net.cn

postForEntityspring-doc.cadn.net.cn

使用 POST 创建新资源,并从响应中返回表示形式。spring-doc.cadn.net.cn

putspring-doc.cadn.net.cn

使用 PUT 创建或更新资源。spring-doc.cadn.net.cn

patchForObjectspring-doc.cadn.net.cn

使用 PATCH 更新资源,并从响应中返回表示。请注意,JDKHttpURLConnection不支持PATCH,但 ApacheHttpComponents 和其他 Components。spring-doc.cadn.net.cn

deletespring-doc.cadn.net.cn

使用 DELETE 删除指定 URI 处的资源。spring-doc.cadn.net.cn

optionsForAllowspring-doc.cadn.net.cn

使用 ALLOW 检索资源允许的 HTTP 方法。spring-doc.cadn.net.cn

exchangespring-doc.cadn.net.cn

前面方法的更通用(和不那么固执己见)的版本,提供额外的需要时的灵活性。它接受一个RequestEntity(包括 HTTP 方法、URL、标头、和正文作为输入)并返回一个ResponseEntity.spring-doc.cadn.net.cn

这些方法允许使用ParameterizedTypeReference而不是Class指定具有泛型的响应类型。spring-doc.cadn.net.cn

executespring-doc.cadn.net.cn

执行请求的最通用方式,完全控制请求通过回调接口进行准备和响应提取。spring-doc.cadn.net.cn

1.1.1. 初始化

默认构造函数使用java.net.HttpURLConnection以执行请求。 您可以 切换到不同的 HTTP 库,实现ClientHttpRequestFactory. 内置支持以下内容:spring-doc.cadn.net.cn

例如,要切换到 Apache HttpComponents,您可以使用以下命令:spring-doc.cadn.net.cn

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());

ClientHttpRequestFactory公开特定于底层HTTP 客户端库的配置选项 — 例如,用于凭据、连接池和其他详细信息。spring-doc.cadn.net.cn

请注意,java.netHTTP 请求的实现可能会在以下情况下引发异常访问表示错误的响应状态(例如 401)。如果这是一个问题,请切换到另一个 HTTP 客户端库。
URI

许多RestTemplate方法接受 URI 模板和 URI 模板变量, 要么作为String变量参数,或作为Map<String,String>.spring-doc.cadn.net.cn

以下示例使用String变量参数:spring-doc.cadn.net.cn

String result = restTemplate.getForObject(
        "https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");

以下示例使用Map<String, String>:spring-doc.cadn.net.cn

Map<String, String> vars = Collections.singletonMap("hotel", "42");

String result = restTemplate.getForObject(
        "https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);

请记住,URI 模板会自动编码,如以下示例所示:spring-doc.cadn.net.cn

restTemplate.getForObject("https://example.com/hotel list", String.class);

// Results in request to "https://example.com/hotel%20list"

您可以使用uriTemplateHandler属性RestTemplate自定义 URI 被编码。或者,您可以准备一个java.net.URI并将其传递给 这RestTemplate接受URI.spring-doc.cadn.net.cn

有关使用 URI 和编码 URI 的更多详细信息,请参阅 URI 链接spring-doc.cadn.net.cn

您可以使用exchange()指定请求标头的方法,如以下示例所示:spring-doc.cadn.net.cn

String uriTemplate = "https://example.com/hotels/{hotel}";
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42);

RequestEntity<Void> requestEntity = RequestEntity.get(uri)
        .header("MyRequestHeader", "MyValue")
        .build();

ResponseEntity<String> response = template.exchange(requestEntity, String.class);

String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();

您可以通过许多RestTemplate返回ResponseEntity.spring-doc.cadn.net.cn

1.1.2. 身体

传入和返回的对象RestTemplate方法与原始方法转换 在HttpMessageConverter.spring-doc.cadn.net.cn

在 POST 上,输入对象被序列化为请求正文,如以下示例所示:spring-doc.cadn.net.cn

URI location = template.postForLocation("https://example.com/people", person);

无需显式设置请求的 Content-Type 标头。在大多数情况下, 您可以根据源找到兼容的消息转换器Object类型,并选择 消息转换器相应地设置内容类型。如有必要,您可以使用exchange方法显式提供Content-Typerequest 标头,并且在 turn 时,会影响选择的消息转换器。spring-doc.cadn.net.cn

在 GET 上,响应的正文被反序列化为输出Object,如以下示例所示:spring-doc.cadn.net.cn

Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);

Accept不需要显式设置请求的标头。在大多数情况下, 可以根据预期的响应类型找到兼容的消息转换器,其中 然后有助于填充Accept页眉。如有必要,您可以使用exchange提供Accept标头。spring-doc.cadn.net.cn

默认情况下,RestTemplate注册所有内置消息转换器,具体取决于有助于的类路径检查 以确定存在哪些可选转换库。您还可以设置消息 显式使用的转换器。spring-doc.cadn.net.cn

1.1.3. 消息转换

spring-web模块包含HttpMessageConverter读取合同和 通过InputStreamOutputStream.HttpMessageConverter实例在客户端使用(例如,在RestTemplate) 和 在服务器端(例如,在 Spring MVC REST 控制器中)。spring-doc.cadn.net.cn

框架中提供了主要媒体 (MIME) 类型的具体实现并且默认情况下,在RestTemplate在客户端和RequestMappingHandlerAdapter(请参阅配置消息转换器)。spring-doc.cadn.net.cn

的实现HttpMessageConverter将在以下部分中介绍。对于所有转换器,都会使用默认媒体类型,但您可以通过将supportedMediaTypesbean 属性。下表描述了每个实现:spring-doc.cadn.net.cn

表 2.HttpMessageConverter 实现
消息转换器 描述

StringHttpMessageConverterspring-doc.cadn.net.cn

HttpMessageConverter可以读写的实现StringHTTP 中的实例request 和 response。默认情况下,此转换器支持所有文本媒体类型 (text/*)并使用Content-Typetext/plain.spring-doc.cadn.net.cn

FormHttpMessageConverterspring-doc.cadn.net.cn

HttpMessageConverter可以从 HTTP 读取和写入表单数据的实现 请求和响应。默认情况下,此转换器读取和写入application/x-www-form-urlencoded媒体类型。表单数据从中读取并写入MultiValueMap<String, String>.转换器还可以写入(但不能读取)多部分 从MultiValueMap<String, Object>.默认情况下,multipart/form-data是 支持。从 Spring Framework 5.2 开始,可以支持 写入表单数据。请参阅 javadocFormHttpMessageConverter了解更多详情。spring-doc.cadn.net.cn

ByteArrayHttpMessageConverterspring-doc.cadn.net.cn

HttpMessageConverter可以从 HTTP 请求和响应。默认情况下,此转换器支持所有媒体类型 () 并使用*/*Content-Typeapplication/octet-stream.您可以覆盖此 通过将supportedMediaTypes属性和覆盖getContentType(byte[]).spring-doc.cadn.net.cn

MarshallingHttpMessageConverterspring-doc.cadn.net.cn

HttpMessageConverter可以使用 Spring 的MarshallerUnmarshaller抽象org.springframework.oxm包。 此转换器需要一个MarshallerUnmarshaller在可以使用之前。您可以注入这些 通过构造函数或 bean 属性。默认情况下,此转换器支持text/xmlapplication/xml.spring-doc.cadn.net.cn

MappingJackson2HttpMessageConverterspring-doc.cadn.net.cn

HttpMessageConverter可以使用 Jackson 的ObjectMapper.您可以根据需要使用 Jackson 的 提供了注释。当您需要进一步控制时(对于自定义 JSON 需要为特定类型提供序列化器/反序列化器),可以注入自定义ObjectMapper通过ObjectMapper财产。默认情况下,此 转换器支持application/json.spring-doc.cadn.net.cn

MappingJackson2XmlHttpMessageConverterspring-doc.cadn.net.cn

HttpMessageConverter可以使用 Jackson XML 扩展的XmlMapper.您可以使用 JAXB 根据需要自定义 XML 映射 或Jackson提供的注释。当您需要进一步控制时(对于自定义 XML 需要为特定类型提供序列化器/反序列化器),可以注入自定义XmlMapper通过ObjectMapper财产。默认情况下,此 转换器支持application/xml.spring-doc.cadn.net.cn

SourceHttpMessageConverterspring-doc.cadn.net.cn

HttpMessageConverter可以读写的实现javax.xml.transform.Source来自 HTTP 请求和响应。只DOMSource,SAXSourceStreamSource被支持。默认情况下,此转换器支持text/xmlapplication/xml.spring-doc.cadn.net.cn

BufferedImageHttpMessageConverterspring-doc.cadn.net.cn

HttpMessageConverter可以读写的实现java.awt.image.BufferedImage来自 HTTP 请求和响应。该转换器读取 并写入 Java I/O API 支持的媒体类型。spring-doc.cadn.net.cn

1.1.4. Jackson JSON 视图

您可以指定 Jackson JSON 视图来仅序列化对象属性的子集,如以下示例所示:spring-doc.cadn.net.cn

MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);

RequestEntity<MappingJacksonValue> requestEntity =
    RequestEntity.post(new URI("https://example.com/user")).body(value);

ResponseEntity<String> response = template.exchange(requestEntity, String.class);

1.1.5. 多部分

要发送多部分数据,您需要提供一个MultiValueMap<String, Object>谁的价值观 可能是一个Object对于部件内容,则Resource对于文件部件,或HttpEntity为 带有标题的部件内容。例如:spring-doc.cadn.net.cn

MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();

parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));

在大多数情况下,您不必指定Content-Type对于每个部分。内容 类型是根据HttpMessageConverter选择序列化 它,或者,在Resource基于文件扩展名。如有必要,您可以 显式提供MediaType使用HttpEntity包装纸。spring-doc.cadn.net.cn

一旦MultiValueMap准备就绪,您可以将其传递给RestTemplate,如下图所示:spring-doc.cadn.net.cn

MultiValueMap<String, Object> parts = ...;
template.postForObject("https://example.com/upload", parts, Void.class);

如果MultiValueMap包含至少一种非Stringvalue,则Content-Type已设置 自multipart/form-data通过FormHttpMessageConverter.如果MultiValueMapStringContent-Type默认为application/x-www-form-urlencoded. 如有必要,Content-Type也可以显式设置。spring-doc.cadn.net.cn

1.2. 使用AsyncRestTemplate(已弃用)

AsyncRestTemplate已弃用。适用于您可能考虑使用AsyncRestTemplate,请改用 WebClientspring-doc.cadn.net.cn

2. 远程处理和 Web 服务

Spring 为使用各种技术的远程处理提供支持。 远程支持简化了远程服务的开发,实现了 通过 Java 接口和对象作为输入和输出。目前,Spring 支持 以下远程处理技术:spring-doc.cadn.net.cn

从 Spring Framework 5.3 开始,现在不推荐使用对多种远程处理技术的支持 出于安全原因和更广泛的行业支持。支持基础设施将被移除 来自 Spring Framework 的下一个主要版本。

以下远程处理技术现已弃用,不会被替换:spring-doc.cadn.net.cn

  • 远程方法调用 (RMI):通过使用RmiProxyFactoryBeanRmiServiceExporter,Spring 支持传统的 RMI(java.rmi.Remoteinterfaces 和java.rmi.RemoteException)和通过 RMI 进行透明远程处理 调用器(使用任何 Java 接口)。spring-doc.cadn.net.cn

  • Spring HTTP Invoker(已弃用):Spring 提供了一种特殊的远程处理策略,允许 通过 HTTP 进行 Java 序列化,支持任何 Java 接口(作为 RMI 调用者这样做)。相应的支持类是HttpInvokerProxyFactoryBeanHttpInvokerServiceExporter.spring-doc.cadn.net.cn

  • Hessian:通过使用 Spring 的HessianProxyFactoryBeanHessianServiceExporter,您可以通过 Caucho 提供的基于 HTTP 的轻量级二进制协议。spring-doc.cadn.net.cn

  • JMS(已弃用):通过 JMS 进行远程处理,作为底层协议,通过JmsInvokerServiceExporterJmsInvokerProxyFactoryBeanspring-jms模块。spring-doc.cadn.net.cn

在讨论 Spring 的远程处理功能时,我们使用以下域 型号及相应服务:spring-doc.cadn.net.cn

public class Account implements Serializable {

    private String name;

    public String getName(){
        return name;
    }

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

    public void insertAccount(Account account);

    public List<Account> getAccounts(String name);
}
// the implementation doing nothing at the moment
public class AccountServiceImpl implements AccountService {

    public void insertAccount(Account acc) {
        // do something...
    }

    public List<Account> getAccounts(String name) {
        // do something...
    }
}

本节首先使用 RMI 将服务公开给远程客户端,并稍微讨论一下 关于使用 RMI 的缺点。然后继续使用一个使用 Hessian 作为 协议。spring-doc.cadn.net.cn

2.1. AMQP

Spring AMQP 项目支持通过 AMQP 作为底层协议进行远程处理。 有关更多详细信息,请访问 Spring AMQP 参考的 Spring Remoting 部分。spring-doc.cadn.net.cn

未对远程接口实施自动检测。spring-doc.cadn.net.cn

远程接口没有自动检测已实现的接口的主要原因是为了避免为远程调用者打开太多大门。目标对象可能实现内部回调接口,例如InitializingBeanDisposableBean哪个不想暴露给来电者。spring-doc.cadn.net.cn

提供具有目标实现的所有接口的代理通常无关紧要 在当地案例中。但是,导出远程服务时,应公开特定的 服务接口,具有用于远程使用的特定作。除了内部 回调接口,目标可能会实现多个业务接口,只有 其中一个用于远程曝光。出于这些原因,我们需要这样的 服务接口。spring-doc.cadn.net.cn

这是配置便利性和意外风险之间的权衡 内部方法的曝光。总是指定服务接口并不过分 努力并让您在特定方法的受控暴露方面处于安全状态。spring-doc.cadn.net.cn

2.2. 选择技术时的注意事项

这里介绍的每一项技术都有其缺点。在选择技术时, 您应该仔细考虑您的需求、您公开的服务以及您 通过电线发送。spring-doc.cadn.net.cn

使用 RMI 时,无法通过 HTTP 协议访问对象, 除非您通过隧道传输 RMI 流量。RMI 是一个相当重量级的协议,因为它 支持全对象序列化,这在使用复杂数据模型时非常重要 这需要通过网络序列化。但是,RMI-JRMP 与 Java 客户端绑定。是的 Java 到 Java 的远程处理解决方案。spring-doc.cadn.net.cn

如果您需要基于 HTTP 的远程处理,但也依赖于 Java 序列化。它与 RMI 调用者共享基本基础设施,但使用 HTTP 作为传输。请注意,HTTP 调用程序不仅限于 Java 到 Java 的远程处理 而且也适用于客户端和服务器端的 Spring。(后者也适用于 Spring 的非 RMI 接口的 RMI 调用器。spring-doc.cadn.net.cn

Hessian 在异构环境中运行时可能会提供显着的价值, 因为它们明确允许非 Java 客户端。但是,非 Java 支持仍然是 有限。已知问题包括 Hibernate 对象的序列化与 延迟初始化的集合。如果您有这样的数据模型,请考虑使用 RMI 或 HTTP 调用器而不是 Hessian 调用器。spring-doc.cadn.net.cn

JMS 可用于提供服务集群并让 JMS 代理 照顾负载平衡、发现和自动故障转移。默认情况下,Java 序列化是 用于 JMS 远程处理,但 JMS 提供程序可以使用不同的机制来处理 线格式,例如 XStream 让服务器在其他 技术。spring-doc.cadn.net.cn

最后但并非最不重要的一点是,EJB 比 RMI 具有优势,因为它支持标准 基于角色的身份验证和授权以及远程事务传播。是的 可以获取 RMI 调用器或 HTTP 调用器来支持安全上下文传播作为 好吧,尽管这不是 core Spring 提供的。Spring 仅提供合适的挂钩 用于插入第三方或定制解决方案。spring-doc.cadn.net.cn

2.3. Java Web 服务

Spring 为标准 Java Web 服务 API 提供全面支持:spring-doc.cadn.net.cn

除了在 Spring Core 中对 JAX-WS 的库存支持外,Spring 产品组合还 具有 Spring Web Services 的功能,这是一个解决方案 契约优先、文档驱动的 Web 服务 — 强烈推荐用于构建现代、 面向未来的 Web 服务。spring-doc.cadn.net.cn

2.3.1. 使用 JAX-WS 公开基于 Servlet 的 Web 服务

Spring 为 JAX-WS servlet 端点实现提供了一个方便的基类:SpringBeanAutowiringSupport.为了揭露我们的AccountService,我们扩展了 Spring 的SpringBeanAutowiringSupportclass 并在这里实现我们的业务逻辑,通常 将调用委托给业务层。我们使用 Spring 的@Autowired注释来表达对 Spring 管理的 bean 的依赖关系。以下示例 显示我们的类扩展SpringBeanAutowiringSupport:spring-doc.cadn.net.cn

/**
 * JAX-WS compliant AccountService implementation that simply delegates
 * to the AccountService implementation in the root web application context.
 *
 * This wrapper class is necessary because JAX-WS requires working with dedicated
 * endpoint classes. If an existing service needs to be exported, a wrapper that
 * extends SpringBeanAutowiringSupport for simple Spring bean autowiring (through
 * the @Autowired annotation) is the simplest JAX-WS compliant way.
 *
 * This is the class registered with the server-side JAX-WS implementation.
 * In the case of a Java EE server, this would simply be defined as a servlet
 * in web.xml, with the server detecting that this is a JAX-WS endpoint and reacting
 * accordingly. The servlet name usually needs to match the specified WS service name.
 *
 * The web service engine manages the lifecycle of instances of this class.
 * Spring bean references will just be wired in here.
 */
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

@WebService(serviceName="AccountService")
public class AccountServiceEndpoint extends SpringBeanAutowiringSupport {

    @Autowired
    private AccountService biz;

    @WebMethod
    public void insertAccount(Account acc) {
        biz.insertAccount(acc);
    }

    @WebMethod
    public Account[] getAccounts(String name) {
        return biz.getAccounts(name);
    }
}

我们AccountServiceEndpoint需要在与 Spring 相同的 Web 应用程序中运行 上下文以允许访问 Spring 的设施。在 Java 中默认情况下是这种情况 EE 环境,使用 JAX-WS servlet 端点部署的标准契约。 有关详细信息,请参阅各种 Java EE Web 服务教程。spring-doc.cadn.net.cn

2.3.2. 使用 JAX-WS 导出独立 Web 服务

Oracle JDK 附带的内置 JAX-WS 提供程序支持 Web 的公开 服务,使用JDK中也包含的内置HTTP服务器。Spring的SimpleJaxWsServiceExporter检测所有@WebService-Spring的带注释的豆子 应用程序上下文,并通过默认的 JAX-WS 服务器(JDK HTTP 服务器)。spring-doc.cadn.net.cn

在此场景中,端点实例被定义和管理为 Spring Bean 他们自己。它们已向 JAX-WS 引擎注册,但它们的生命周期将 Spring 应用程序上下文。这意味着您可以应用 Spring 功能 (例如显式依赖注入)到端点实例。注释驱动 注入@Autowired也有效。以下示例演示如何 定义这些 bean:spring-doc.cadn.net.cn

<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter">
    <property name="baseAddress" value="http://localhost:8080/"/>
</bean>

<bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint">
    ...
</bean>

...

AccountServiceEndpoint可以但不必派生自 Spring 的SpringBeanAutowiringSupport, 因为此示例中的端点是完全由 Spring 管理的 bean。这意味着 端点实现可以如下所示(没有声明任何超类——以及 Spring 的@Autowired仍遵循配置注释):spring-doc.cadn.net.cn

@WebService(serviceName="AccountService")
public class AccountServiceEndpoint {

    @Autowired
    private AccountService biz;

    @WebMethod
    public void insertAccount(Account acc) {
        biz.insertAccount(acc);
    }

    @WebMethod
    public List<Account> getAccounts(String name) {
        return biz.getAccounts(name);
    }
}

2.3.3. 使用 JAX-WS RI 的 Spring 支持导出 Web 服务

Oracle 的 JAX-WS RI 是作为 GlassFish 项目的一部分开发的,提供 Spring 支持 作为其 JAX-WS Commons 项目的一部分。这允许将 JAX-WS 端点定义为 Spring 管理的 bean,类似于上一节中讨论的独立模式——但这次是在 Servlet 环境中。spring-doc.cadn.net.cn

这在 Java EE 环境中是不可移植的。它主要用于非 EE 将 JAX-WS RI 嵌入到 Web 应用程序中的环境,例如 Tomcat。

与导出基于 Servlet 的端点的标准样式的区别在于 端点实例本身的生命周期由 Spring 管理,并且 只有一个 JAX-WS servlet 在web.xml.使用标准 Java EE 样式(作为 如前所示),每个服务端点都有一个 servlet 定义,每个端点 通常委托给 Spring bean(通过使用@Autowired,如前所述)。spring-doc.cadn.net.cn

有关设置和使用风格的详细信息,请参阅 https://jax-ws-commons.java.net/spring/spring-doc.cadn.net.cn

2.3.4. 使用 JAX-WS 访问 Web 服务

Spring 提供了两个工厂 bean 来创建 JAX-WS Web 服务代理,即LocalJaxWsServiceFactoryBeanJaxWsPortProxyFactoryBean. 前者可以仅返回一个 JAX-WS 服务类供我们使用。后者是成熟的版本,可以返回实现我们的业务服务接口的代理。在下面的示例中,我们使用JaxWsPortProxyFactoryBeanAccountService端点(再次):spring-doc.cadn.net.cn

<bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
    <property name="serviceInterface" value="example.AccountService"/> (1)
    <property name="wsdlDocumentUrl" value="http://localhost:8888/AccountServiceEndpoint?WSDL"/>
    <property name="namespaceUri" value="https://example/"/>
    <property name="serviceName" value="AccountService"/>
    <property name="portName" value="AccountServiceEndpointPort"/>
</bean>
1 哪里serviceInterface是客户使用的业务界面。

wsdlDocumentUrl是 WSDL 文件的 URL。Spring 在启动时需要它来创建 JAX-WS 服务。namespaceUri对应于targetNamespace在 .wsdl 文件。serviceName对应于 .wsdl 文件中的服务名称。portName对应于 .wsdl 文件中的端口名。spring-doc.cadn.net.cn

访问 Web 服务很容易,因为我们有一个 bean 工厂,可以将其公开为一个名为AccountService.以下示例显示了我们如何连接 Spring:spring-doc.cadn.net.cn

<bean id="client" class="example.AccountClientImpl">
    ...
    <property name="service" ref="accountWebService"/>
</bean>

从客户端代码中,我们可以像普通类一样访问 Web 服务, 如以下示例所示:spring-doc.cadn.net.cn

public class AccountClientImpl {

    private AccountService service;

    public void setService(AccountService service) {
        this.service = service;
    }

    public void foo() {
        service.insertAccount(...);
    }
}
以上稍微简化了,因为 JAX-WS 需要端点接口 以及要注释的实现类@WebService,@SOAPBinding等。 附注。这意味着您不能(轻松)使用纯 Java 接口和 实现类作为 JAX-WS 端点工件;你需要对它们进行注释 因此首先。查看 JAX-WS 文档以获取有关这些需求的详细信息。

2.4. RMI(已弃用)

从 Spring Framework 5.3 开始,RMI 支持已被弃用,并且不会被替换。

通过使用 Spring 对 RMI 的支持,您可以通过 RMI 基础设施。完成此设置后,您基本上拥有了类似的配置 远程 EJB,除了没有对安全性的标准支持 上下文传播或远程事务传播。Spring 确实提供了钩子 使用 RMI 调用器时,此类附加调用上下文,因此,例如,您可以 插入安全框架或自定义安全凭据。spring-doc.cadn.net.cn

2.4.1. 使用RmiServiceExporter

使用RmiServiceExporter,我们可以公开 AccountService 对象的接口 作为 RMI 对象。可以使用以下命令访问该接口RmiProxyFactoryBean,或通过 在传统 RMI 服务的情况下,普通 RMI。这RmiServiceExporter明确地 支持通过 RMI 调用器公开任何非 RMI 服务。spring-doc.cadn.net.cn

我们首先必须在 Spring 容器中设置我们的服务。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<bean id="accountService" class="example.AccountServiceImpl">
    <!-- any additional properties, maybe a DAO? -->
</bean>

接下来,我们必须使用RmiServiceExporter. 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
    <!-- does not necessarily have to be the same name as the bean to be exported -->
    <property name="serviceName" value="AccountService"/>
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
    <!-- defaults to 1099 -->
    <property name="registryPort" value="1199"/>
</bean>

在前面的示例中,我们覆盖了 RMI 注册表的端口。通常,您的应用程序 server 还维护一个 RMI 注册表,明智的做法是不要干扰该注册表。 此外,服务名称用于绑定服务。因此,在前面的示例中, 服务绑定于'rmi://HOST:1199/AccountService'.稍后我们使用此 URL 链接 客户端的服务。spring-doc.cadn.net.cn

servicePort属性已被省略(默认为 0)。这意味着 匿名端口用于与服务通信。

2.4.2. 在客户端的服务中链接

我们的客户端是一个简单的对象,它使用AccountService管理帐户, 如以下示例所示:spring-doc.cadn.net.cn

public class SimpleObject {

    private AccountService accountService;

    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    // additional methods using the accountService
}

为了在客户端上链接服务,我们创建一个单独的 Spring 容器, 以包含以下简单对象和服务链接配置位:spring-doc.cadn.net.cn

<bean class="example.SimpleObject">
    <property name="accountService" ref="accountService"/>
</bean>

<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
    <property name="serviceUrl" value="rmi://HOST:1199/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

这就是我们在客户端上支持远程帐户服务所需做的全部工作。Spring 透明地创建调用者,并通过RmiServiceExporter.在客户端,我们使用RmiProxyFactoryBean.spring-doc.cadn.net.cn

2.5. 使用 Hessian 通过 HTTP 远程调用服务(已弃用)

从 Spring Framework 5.3 开始,Hessian 支持已被弃用,不会被替换。

Hessian 提供基于 HTTP 的二进制远程处理协议。它由 Caucho 开发, 您可以在 https://www.caucho.com/ 找到有关 Hessian 本身的更多信息。spring-doc.cadn.net.cn

2.5.1. 黑森州

Hessian 通过 HTTP 进行通信,并使用自定义 servlet 进行通信。通过使用 Spring 的DispatcherServlet原则(见 webmvc.html),我们可以连接这样的 servlet 来公开您的服务。首先,我们必须在应用程序中创建一个新的 servlet, 如下摘录自web.xml:spring-doc.cadn.net.cn

<servlet>
    <servlet-name>remoting</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>remoting</servlet-name>
    <url-pattern>/remoting/*</url-pattern>
</servlet-mapping>

如果您熟悉 Spring 的DispatcherServlet原则,你可能 知道现在你必须创建一个名为remoting-servlet.xml(在 servlet 名称之后)WEB-INF目录。 下一节将使用应用程序上下文。spring-doc.cadn.net.cn

或者,考虑使用 Spring 的更简单的HttpRequestHandlerServlet.这样做 允许您在根应用程序上下文中嵌入远程导出器定义(通过 默认,在WEB-INF/applicationContext.xml),具有单独的 servlet 定义 指向特定的导出器 bean。在这种情况下,每个 servlet 名称都需要与 其目标出口商。spring-doc.cadn.net.cn

2.5.2. 使用HessianServiceExporter

在新创建的应用程序上下文中,称为remoting-servlet.xml,我们创建一个HessianServiceExporter导出我们的服务,如以下示例所示:spring-doc.cadn.net.cn

<bean id="accountService" class="example.AccountServiceImpl">
    <!-- any additional properties, maybe a DAO? -->
</bean>

<bean name="/AccountService" class="org.springframework.remoting.caucho.HessianServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

现在我们已准备好在客户端链接服务。没有显式处理程序映射指定(将请求 URL 映射到服务上),因此我们使用BeanNameUrlHandlerMapping使用。 因此,服务被导出到通过其 bean 名称指示的 URL在包含的DispatcherServlet实例的映射(如前面定义):https://HOST:8080/remoting/AccountService.spring-doc.cadn.net.cn

或者,您可以创建一个HessianServiceExporter在根应用程序上下文中(例如, 在WEB-INF/applicationContext.xml),如以下示例所示:spring-doc.cadn.net.cn

<bean name="accountExporter" class="org.springframework.remoting.caucho.HessianServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

在后一种情况下,您应该在web.xml, 具有相同的最终结果:导出器被映射到/remoting/AccountService. 请注意,servlet 名称需要与目标导出器的 bean 名称匹配。以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<servlet>
    <servlet-name>accountExporter</servlet-name>
    <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>accountExporter</servlet-name>
    <url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping>

2.5.3. 在客户端的服务中链接

通过使用HessianProxyFactoryBean,我们可以在客户端链接服务。一样 原则与 RMI 示例一样适用。我们创建一个单独的豆工厂或 应用程序上下文,并提及以下 bean,其中SimpleObject是通过使用 这AccountService以管理帐户,如以下示例所示:spring-doc.cadn.net.cn

<bean class="example.SimpleObject">
    <property name="accountService" ref="accountService"/>
</bean>

<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
    <property name="serviceUrl" value="https://remotehost:8080/remoting/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

2.5.4. 将 HTTP 基本身份验证应用于通过 Hessian 公开的服务

Hessian 的优点之一是我们可以轻松应用 HTTP 基本身份验证, 因为这两种协议都是基于 HTTP 的。您的正常 HTTP 服务器安全机制可以 通过使用web.xml例如,安全功能。通常 无需在此处使用每用户安全凭据。相反,您可以使用您定义的共享凭据 在HessianProxyFactoryBean级别(类似于 JDBCDataSource),如以下示例所示:spring-doc.cadn.net.cn

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors" ref="authorizationInterceptor"/>
</bean>

<bean id="authorizationInterceptor"
        class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor">
    <property name="authorizedRoles" value="administrator,operator"/>
</bean>

在前面的示例中,我们明确提到了BeanNameUrlHandlerMapping并设置 拦截器,仅允许管理员和操作员调用 此应用程序上下文。spring-doc.cadn.net.cn

前面的示例未显示一种灵活的安全基础结构。为 就安全性而言,更多选项,请查看 Spring Security 项目 在 https://spring.io/projects/spring-security/

2.6. Spring HTTP Invoker(已弃用)

从 Spring Framework 5.3 开始,HTTP Invoker 支持已被弃用,并且不会被替换。

与 Hessian 相反,Spring HTTP 调用器都是使用自己的 slim 序列化机制,并使用标准的 Java 序列化 通过 HTTP 公开服务的机制。如果你的论点 返回类型是复杂类型,无法使用序列化 Hessian 使用的机制(有关更多注意事项,请参阅下一节 您选择远程处理技术)。spring-doc.cadn.net.cn

在后台,Spring 使用 JDK 提供的标准设施或 阿帕奇HttpComponents执行 HTTP 调用。如果您需要更多 高级且易于使用的功能,请使用后者。有关详细信息,请参阅 hc.apache.org/httpcomponents-client-ga/spring-doc.cadn.net.cn

注意由于不安全的 Java 反序列化而导致的漏洞: 纵的输入流可能会导致服务器上执行不需要的代码 在反序列化步骤期间。因此,不要公开 HTTP 调用程序 端点到不受信任的客户端。相反,仅在您自己的服务之间公开它们。 通常,我们强烈建议改用任何其他消息格式(例如 JSON)。spring-doc.cadn.net.cn

如果您担心 Java 序列化导致的安全漏洞, 考虑核心 JVM 级别的通用序列化过滤器机制, 最初是为 JDK 9 开发的,但同时向后移植到 JDK 8、7 和 6。请参阅 https://blogs.oracle.com/java-platform-group/entry/incoming_filter_serialization_data_ahttps://openjdk.java.net/jeps/290spring-doc.cadn.net.cn

2.6.1. 公开服务对象

为服务对象设置 HTTP 调用器基础设施与 你也会用 Hessian 做同样的事情。正如 Hessian 支持提供的那样HessianServiceExporter,Spring 的 HttpInvoker 支持提供了org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter.spring-doc.cadn.net.cn

要公开AccountService(前面提到的)在 Spring Web MVC 中DispatcherServlet,则需要在 dispatcher 的应用程序上下文,如以下示例所示:spring-doc.cadn.net.cn

<bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

这样的导出器定义通过DispatcherServlet实例的标准 映射设施,如黑森州部分所述。spring-doc.cadn.net.cn

或者,您可以创建一个HttpInvokerServiceExporter在根应用程序上下文中 (例如,在'WEB-INF/applicationContext.xml'),如以下示例所示:spring-doc.cadn.net.cn

<bean name="accountExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

此外,您可以在web.xml,替换为 servlet 名称与目标导出器的 bean 名称匹配,如以下示例所示:spring-doc.cadn.net.cn

<servlet>
    <servlet-name>accountExporter</servlet-name>
    <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>accountExporter</servlet-name>
    <url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping>

2.6.2. 在客户端的服务中链接

同样,从客户端链接服务的方式非常相似 当您使用黑森语时。通过使用代理,Spring 可以将您的调用转换为 HTTP POST 请求指向导出服务的 URL。以下示例 显示如何配置此排列:spring-doc.cadn.net.cn

<bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
    <property name="serviceUrl" value="https://remotehost:8080/remoting/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

如前所述,您可以选择要使用的 HTTP 客户端。默认情况下,HttpInvokerProxy使用 JDK 的 HTTP 功能,但您也可以使用 ApacheHttpComponents客户端通过将httpInvokerRequestExecutor财产。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<property name="httpInvokerRequestExecutor">
    <bean class="org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor"/>
</property>

2.7. JMS(已弃用)

从 Spring Framework 5.3 开始,JMS 远程支持已被弃用,并且不会被替换。

您还可以使用 JMS 作为底层通信来透明地公开服务 协议。Spring Framework 中的 JMS 远程支持非常基本。它发送 并在same thread并且在同一个非事务性Session. 因此,吞吐量取决于实现。请注意,这些单线程 非事务约束仅适用于 Spring 的 JMS 远程处理支持。 有关 Spring 对基于 JMS 的消息传递的丰富支持的信息,请参阅 JMS(Java 消息服务)。spring-doc.cadn.net.cn

服务器端和客户端都使用以下接口:spring-doc.cadn.net.cn

package com.foo;

public interface CheckingAccountService {

    public void cancelAccount(Long accountId);
}

在服务器端使用上述接口的以下简单实现:spring-doc.cadn.net.cn

package com.foo;

public class SimpleCheckingAccountService implements CheckingAccountService {

    public void cancelAccount(Long accountId) {
        System.out.println("Cancelling account [" + accountId + "]");
    }
}

以下配置文件包含共享的 JMS 基础架构 Bean 在客户端和服务器上:spring-doc.cadn.net.cn

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

    <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://ep-t43:61616"/>
    </bean>

    <bean id="queue" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg value="mmm"/>
    </bean>

</beans>

2.7.1. 服务器端配置

在服务器上,您需要公开使用JmsInvokerServiceExporter,如以下示例所示:spring-doc.cadn.net.cn

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

    <bean id="checkingAccountService"
            class="org.springframework.jms.remoting.JmsInvokerServiceExporter">
        <property name="serviceInterface" value="com.foo.CheckingAccountService"/>
        <property name="service">
            <bean class="com.foo.SimpleCheckingAccountService"/>
        </property>
    </bean>

    <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destination" ref="queue"/>
        <property name="concurrentConsumers" value="3"/>
        <property name="messageListener" ref="checkingAccountService"/>
    </bean>

</beans>
package com.foo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Server {

    public static void main(String[] args) throws Exception {
        new ClassPathXmlApplicationContext("com/foo/server.xml", "com/foo/jms.xml");
    }
}

2.7.2. 客户端配置

客户端只需要创建一个客户端代理来实现商定的 接口 (CheckingAccountService).spring-doc.cadn.net.cn

以下示例定义了可以注入到其他客户端对象中的 Bean (代理负责通过 JMS 将调用转发到服务器端对象):spring-doc.cadn.net.cn

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

    <bean id="checkingAccountService"
            class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean">
        <property name="serviceInterface" value="com.foo.CheckingAccountService"/>
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="queue" ref="queue"/>
    </bean>

</beans>
package com.foo;

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

public class Client {

    public static void main(String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("com/foo/client.xml", "com/foo/jms.xml");
        CheckingAccountService service = (CheckingAccountService) ctx.getBean("checkingAccountService");
        service.cancelAccount(new Long(10));
    }
}

3. Enterprise JavaBeans (EJB) 集成

作为一个轻量级容器,Spring 通常被认为是 EJB 的替代品。我们确实相信 对于许多(如果不是大多数)应用程序和用例来说,Spring 作为一个容器,结合了 凭借其在事务、ORM 和 JDBC 访问领域的丰富支持功能, 是比通过 EJB 容器实现等效功能更好的选择 和 EJB。spring-doc.cadn.net.cn

但是,请务必注意,使用 Spring 并不妨碍您使用 EJB。 事实上,Spring 使访问 EJB 和实现 EJB 和功能变得更加容易 在他们内部。此外,使用 Spring 访问 EJB 提供的服务允许 这些服务的实现以稍后在本地 EJB 之间透明地切换, 远程 EJB 或 POJO(普通旧 Java 对象)变体,而无需客户端代码 被改变。spring-doc.cadn.net.cn

在本章中,我们将探讨 Spring 如何帮助您访问和实现 EJB。 Spring 在访问无状态会话 Bean (SLSB) 时提供特定的价值,因此我们开始通过讨论这个主题。spring-doc.cadn.net.cn

3.1. 访问 EJB

本节介绍如何访问 EJB。spring-doc.cadn.net.cn

3.1.1. 概念

要在本地或远程无状态会话 Bean 上调用方法,客户机代码必须 通常执行 JNDI 查找以获取(本地或远程)EJB Home 对象,然后使用 一个create方法调用以获取实际的(本地或远程)EJB 对象。 然后在 EJB 上调用一个或多个方法。spring-doc.cadn.net.cn

为了避免重复的低级代码,许多 EJB 应用程序使用 Service Locator 和 业务委托模式。这些比在整个过程中喷洒 JNDI 查找要好 客户端代码,但它们通常的实现有明显的缺点:spring-doc.cadn.net.cn

  • 通常,使用 EJB 的代码依赖于 Service Locator 或 Business Delegate 单例, 使其难以测试。spring-doc.cadn.net.cn

  • 对于没有业务委托使用的服务定位器模式, 应用程序代码最终仍然必须调用create()EJB 主页上的方法 并处理由此产生的异常。因此,它仍然与 EJB API 和 EJB 编程模型的复杂性。spring-doc.cadn.net.cn

  • 实现业务委托模式通常会导致大量代码 复制,我们必须编写许多调用同一方法的方法 在 EJB 上。spring-doc.cadn.net.cn

Spring 方法是允许创建和使用代理对象(通常 在 Spring 容器中配置),它们充当无代码业务委托。你需要 不在 手动编码的业务委托,除非您实际上在此类代码中添加了真正的价值。spring-doc.cadn.net.cn

3.1.2. 访问本地 SLSB

假设我们有一个需要使用本地 EJB 的 Web 控制器。我们遵循最好 练习并使用 EJB 业务方法接口模式,以便 EJB 的本地 接口扩展了非特定于 EJB 的业务方法接口。我们称之为 业务方法接口MyComponent.以下示例显示了这样的接口:spring-doc.cadn.net.cn

public interface MyComponent {
    ...
}

使用业务方法接口模式的主要原因之一是确保 本地接口中的方法签名与 Bean 实现之间的同步 class 是自动的。另一个原因是它后来使我们更容易 如果有意义,请切换到服务的 POJO(普通旧 Java 对象)实现 这样做。我们还需要实现本地主界面,并提供一个 实现类SessionBeanMyComponent商 methods 接口。现在,我们需要做的唯一 Java 编码来连接我们的 Web 层 控制器给 EJB 实现是公开一个类型为MyComponent在控制器上。这会将引用保存为 控制器。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

private MyComponent myComponent;

public void setMyComponent(MyComponent myComponent) {
    this.myComponent = myComponent;
}

我们随后可以在控制器的任何业务方法中使用这个实例变量。 现在,假设我们从 Spring 容器中获取控制器对象,我们可以 (在同一上下文中)配置一个LocalStatelessSessionProxyFactoryBean实例 这是 EJB 代理对象。我们配置代理并将myComponent控制器的属性,其中包含以下配置条目:spring-doc.cadn.net.cn

<bean id="myComponent"
        class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
    <property name="jndiName" value="ejb/myBean"/>
    <property name="businessInterface" value="com.mycom.MyComponent"/>
</bean>

<bean id="myController" class="com.mycom.myController">
    <property name="myComponent" ref="myComponent"/>
</bean>

很多工作都在幕后进行,这要归功于 Spring AOP 框架,尽管您不会被迫使用 AOP 概念来享受结果。 这myComponentbean 定义为 EJB 创建一个代理,它实现了业务 method 接口。EJB 本地主页在启动时被缓存,因此只有一个 JNDI 查找。每次调用 EJB 时,代理都会调用classname方法 本地 EJB 并在 EJB 上调用相应的业务方法。spring-doc.cadn.net.cn

myControllerbean 定义将myComponent控制器的属性 class 添加到 EJB 代理。spring-doc.cadn.net.cn

或者(最好在许多此类代理定义的情况下),考虑使用 这<jee:local-slsb>Spring 的“jee”命名空间中的配置元素。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<jee:local-slsb id="myComponent" jndi-name="ejb/myBean"
        business-interface="com.mycom.MyComponent"/>

<bean id="myController" class="com.mycom.myController">
    <property name="myComponent" ref="myComponent"/>
</bean>

这种 EJB 访问机制极大地简化了应用程序代码。Web 层 代码(或其他 EJB 客户机代码)不依赖于 EJB 的使用。自 将此 EJB 引用替换为 POJO 或模拟对象或其他测试存根,我们可以 将myComponentbean 定义,而无需更改一行 Java 代码。 此外,我们不必编写一行 JNDI 查找或其他 EJB 管道 代码作为我们应用程序的一部分。spring-doc.cadn.net.cn

实际应用中的基准测试和经验表明,性能开销 这种方法(涉及目标 EJB 的反射调用)是最小的,并且 在典型使用中是检测不到的。请记住,我们不想制作 无论如何,对 EJB 的细粒度调用,因为与 EJB 相关的成本是存在的 应用程序服务器中的基础设施。spring-doc.cadn.net.cn

关于 JNDI 查找,有一个警告。在 bean 容器中,此类是 通常最好用作单例(没有理由将其作为原型)。 但是,如果该 Bean 容器预先实例化单例(就像各种 XML 一样ApplicationContext变体),如果加载了 bean 容器,则可能会遇到问题 在 EJB 容器装入目标 EJB 之前。这是因为 JNDI 查找是 在init()方法,然后缓存,但 EJB 没有 尚未绑定在目标位置。解决方案是不要预先实例化它 factory 对象,但允许它在首次使用时创建。在 XML 容器中,您可以控制 通过使用lazy-init属性。spring-doc.cadn.net.cn

虽然大多数 Spring 用户不感兴趣,但那些这样做的人 使用 EJB 的编程 AOP 工作可能需要查看LocalSlsbInvokerInterceptor.spring-doc.cadn.net.cn

3.1.3. 访问远程 SLSB

访问远程 EJB 与访问本地 EJB 基本相同,不同之处在于SimpleRemoteStatelessSessionProxyFactoryBean<jee:remote-slsb>配置 元素。当然,无论有没有 Spring,远程调用语义都适用:一个 有时必须调用另一台计算机中另一个 VM 中的对象的方法 在使用场景和故障处理方面受到不同的处理。spring-doc.cadn.net.cn

与非 Spring 方法相比,Spring 的 EJB 客户端支持又增加了一个优势。 通常,EJB 客户端代码很容易来回切换是有问题的 在本地或远程调用 EJB 之间。这是因为远程接口方法 必须声明他们投掷RemoteException,客户端代码必须处理这个问题, 而本地接口方法则不需要。为需要的本地 EJB 编写的客户端代码 要移动到远程 EJB,通常必须修改 EJB 以添加对远程的处理 异常,以及为需要移动到本地的远程 EJB 编写的客户端代码 EJB 可以保持不变,但对远程进行大量不必要的处理 异常或修改以删除该代码。使用 Spring 远程 EJB proxy,则可以不声明任何抛出的RemoteException在您的业务方法接口和实现 EJB 代码时,具有相同的远程接口(除了它确实抛出RemoteException),并依靠代理动态处理两者 接口,就好像它们是一样的。也就是说,客户端代码不必处理 检查RemoteException类。任何实际的RemoteException在 EJB 调用作为未选中的RemoteAccessException类,其中 是RuntimeException.然后,您可以随意切换目标服务 在本地 EJB 或远程 EJB(甚至是普通 Java 对象)实现之间,没有 客户代码知道或关心。当然,这是可选的:没有 阻止您声明RemoteException在您的业务界面中。spring-doc.cadn.net.cn

3.1.4. 访问 EJB 2.x SLSB 与 EJB 3 SLSB

通过 Spring 访问 EJB 2.x 会话 Bean 和 EJB 3 会话 Bean 主要是 透明。Spring 的 EJB 访问器,包括<jee:local-slsb><jee:remote-slsb>设施,在运行时透明地适应实际组件。 如果找到,它们会处理主界面(EJB 2.x 样式)或执行直接组件 调用,如果没有可用的主界面(EJB 3 样式)。spring-doc.cadn.net.cn

注: 对于 EJB 3 会话 Bean,您可以有效地使用JndiObjectFactoryBean / <jee:jndi-lookup>同样,由于完全可用的组件引用是公开的 那里的普通 JNDI 查找。定义显式<jee:local-slsb><jee:remote-slsb>查找提供了一致且更明确的 EJB 访问配置。spring-doc.cadn.net.cn

4. JMS(Java 消息服务)

Spring提供了一个JMS集成框架,该框架在许多方面简化了JMS API的使用 与 Spring 的 JDBC API 集成方式相同。spring-doc.cadn.net.cn

JMS 大致可以分为两个功能领域,即生产和 消息的消耗。这JmsTemplateclass 用于消息生成,而 同步消息接收。用于类似于 Java EE 的异步接收 消息驱动的 bean 样式,Spring 提供了许多消息侦听器容器,这些容器 您可以使用来创建消息驱动的 POJO (MDP)。Spring 还提供了一种声明式方式 以创建消息侦听器。spring-doc.cadn.net.cn

org.springframework.jms.corepackage 提供了使用 JMS。它包含 JMS 模板类,通过处理 创建和释放资源,就像JdbcTemplate为 JDBC 做。这 Spring模板类通用的设计原则是提供辅助方法来 执行常见作,为了更复杂地使用,委托 处理任务到用户实现的回调接口。JMS 模板遵循 相同的设计。这些类提供了各种发送消息的便捷方法, 同步使用消息,并将 JMS 会话和消息生产者公开给 用户。spring-doc.cadn.net.cn

org.springframework.jms.supportpackage 提供JMSException译本 功能性。翻译将选中的JMSExceptionhierarchy 设置为 未检查异常的镜像层次结构。如果有特定于提供程序的 被选中的javax.jms.JMSExceptionexist,则此异常包装在 猖獗UncategorizedJmsException.spring-doc.cadn.net.cn

org.springframework.jms.support.converterpackage 提供了一个MessageConverter抽象,用于在 Java 对象和 JMS 消息之间进行转换。spring-doc.cadn.net.cn

org.springframework.jms.support.destinationpackage 提供了各种策略用于管理 JMS 目标,例如为目标提供服务定位器存储在 JNDI 中。spring-doc.cadn.net.cn

org.springframework.jms.annotationpackage 提供了必要的基础设施通过使用@JmsListener.spring-doc.cadn.net.cn

org.springframework.jms.configpackage 为jms命名空间以及 Java 配置支持来配置侦听器容器和创建侦听器端点。spring-doc.cadn.net.cn

最后,org.springframework.jms.connectionpackage 提供了 这ConnectionFactory适用于独立应用。它还包含一个 Spring 的PlatformTransactionManager对于 JMS(狡猾地命名为JmsTransactionManager).这允许将 JMS 无缝集成为事务性 资源添加到 Spring 的事务管理机制中。spring-doc.cadn.net.cn

从 Spring Framework 5 开始,Spring 的 JMS 包完全支持 JMS 2.0,并且需要 JMS 2.0 API 在运行时存在。我们建议使用与 JMS 2.0 兼容的提供程序。spring-doc.cadn.net.cn

如果您碰巧在系统中使用较旧的消息代理,您可以尝试升级到 JMS 2.0 兼容驱动程序,适用于您现有的代理生成。或者,您也可以 尝试针对基于 JMS 1.1 的驱动程序运行,只需将 JMS 2.0 API jar 放在 classpath,但仅对驱动程序使用 JMS 1.1 兼容的 API。Spring 的 JMS 支持 默认情况下遵循 JMS 1.1 约定,因此通过相应的配置,它确实如此 支持这样的场景。但是,请仅在过渡方案中考虑这一点。spring-doc.cadn.net.cn

4.1. 使用 Spring JMS

本节介绍如何使用 Spring 的 JMS 组件。spring-doc.cadn.net.cn

4.1.1. 使用JmsTemplate

JmsTemplateclass 是 JMS 核心包中的中心类。它简化了 JMS,因为它在发送或 同步接收消息。spring-doc.cadn.net.cn

使用JmsTemplate只需要实现回调接口,即可为它们提供 明确定义的高层合同。这MessageCreator回调接口会创建一个 消息Session由调用代码提供JmsTemplate.自 允许更复杂的 JMS API 使用,SessionCallback提供 JMS 会话,以及ProducerCallback公开一个SessionMessageProducer双。spring-doc.cadn.net.cn

JMS API 公开了两种类型的发送方法,一种采用交付模式、优先级、 以及作为服务质量 (QOS) 参数的生存时间,以及不采用 QOS 的参数 参数并使用默认值。因为JmsTemplate有很多发送方法, 将 QOS 参数设置为 bean 属性 避免发送方法数量重复。同样,的超时值 同步接收调用是通过使用setReceiveTimeout财产。spring-doc.cadn.net.cn

一些 JMS 提供程序允许通过 配置ConnectionFactory.这具有调用MessageProducer实例的send方法 (send(Destination destination, Message message)) 使用与 JMS 规范中指定的 QOS 默认值不同的 QOS 默认值。挨次 为了提供对 QOS 值的一致管理,请JmsTemplate因此,必须是 通过设置布尔属性,专门启用了使用自己的 QOS 值isExplicitQosEnabledtrue.spring-doc.cadn.net.cn

为方便起见,JmsTemplate还公开了一个基本的请求-回复作,该作允许 用于发送消息并等待作为 作。spring-doc.cadn.net.cn

的实例JmsTemplate一旦配置,类是线程安全的。这是 重要,因为这意味着您可以配置JmsTemplate然后安全地将此共享引用注入多个协作者。成为 clear,则JmsTemplate是有状态的,因为它维护对ConnectionFactory,但这种状态不是对话状态。

从 Spring Framework 4.1 开始,JmsMessagingTemplate建立在JmsTemplate并提供与消息传递抽象的集成——即org.springframework.messaging.Message.这样,您就可以创建消息 以通用方式发送。spring-doc.cadn.net.cn

4.1.2. 连接

JmsTemplate需要引用ConnectionFactory.这ConnectionFactory是 JMS 规范的一部分,用作使用 JMS 的入口点。它 被客户机应用程序用作工厂来创建与 JMS 的连接 provider 并封装各种配置参数,其中许多是 特定于提供商,例如 SSL 配置选项。spring-doc.cadn.net.cn

在 EJB 中使用 JMS 时,提供商提供 JMS 接口的实现 以便他们可以参与声明式事务管理并执行池化 连接和会议。为了使用此实现,Java EE 容器 通常要求您将 JMS 连接工厂声明为resource-ref里面 EJB 或 Servlet 部署描述符。要确保将这些特性与JmsTemplate在 EJB 中,客户端应用程序应确保它引用托管的实现ConnectionFactory.spring-doc.cadn.net.cn

缓存消息传递资源

标准 API 涉及创建许多中间对象。要发送消息,请执行执行以下“API”遍历:spring-doc.cadn.net.cn

ConnectionFactory->Connection->Session->MessageProducer->send

ConnectionFactorySend作,三中级 对象被创建和销毁。优化资源使用并增加 performance,Spring 提供了两个ConnectionFactory.spring-doc.cadn.net.cn

SingleConnectionFactory

Spring 提供了ConnectionFactory接口SingleConnectionFactory,返回相同的Connection在所有createConnection()调用并忽略对close().这对于测试和 独立环境,以便同一连接可用于多个JmsTemplate可以跨越任意数量事务的调用。SingleConnectionFactory引用标准ConnectionFactory这通常来自 JNDI。spring-doc.cadn.net.cn

CachingConnectionFactory

CachingConnectionFactory扩展了SingleConnectionFactory并添加Session,MessageProducerMessageConsumer实例。 初始缓存大小设置为1.您可以使用sessionCacheSize属性设置为 增加缓存的会话数。请注意,实际缓存的会话数 大于该数字,因为会话是根据其确认模式缓存的, 因此,最多可以有四个缓存的会话实例(每种确认模式一个) 什么时候sessionCacheSize设置为 1。MessageProducerMessageConsumer实例 缓存在它们拥有的会话中,并且还考虑了唯一的属性 缓存时生产者和消费者。MessageProducer 根据其 目的地。MessageConsumers 基于由目标、选择器、 noLocal 传递标志和持久订阅名称(如果创建持久使用者)。spring-doc.cadn.net.cn

用于临时队列和主题的 MessageProducers 和 MessageConsumers (TemporaryQueue/TemporaryTopic) 永远不会被缓存。不幸的是,WebLogic JMS 发生了 要在其常规目标实现上实现临时队列/主题接口, 错误地指示其任何目标都无法缓存。请使用其他连接 pool/cache 在 WebLogic 上,或自定义CachingConnectionFactory用于 WebLogic 目的。spring-doc.cadn.net.cn

4.1.3. 目标管理

目的地,如ConnectionFactoryinstances 是可以存储的 JMS 管理对象 并在 JNDI 中检索。配置 Spring 应用程序上下文时,您可以使用 JNDI公司JndiObjectFactoryBean工厂类或<jee:jndi-lookup>执行依赖关系 注入到对象对 JMS 目标的引用上。然而,这种策略 如果应用程序中有大量目的地,或者如果 是 JMS 提供程序独有的高级目标管理功能。示例 此类高级目标管理包括创建动态目标或 支持目标的分层命名空间。这JmsTemplate将目标名称的解析委托给实现DestinationResolver接口。DynamicDestinationResolver是默认值实现使用JmsTemplate并适应解析动态目的地。 一个JndiDestinationResolver还提供作为服务定位器JNDI 中包含的目标,并可选择回退到DynamicDestinationResolver.spring-doc.cadn.net.cn

通常,JMS 应用程序中使用的目标仅在运行时已知,因此,因此,在部署应用程序时无法以管理方式创建。 这是 通常是因为交互的系统组件之间存在共享的应用程序逻辑根据众所周知的命名约定在运行时创建目标。 甚至 尽管动态目标的创建不是 JMS 规范的一部分,但大多数提供商都提供了此功能。动态目标是使用用户定义的名称创建的,这将它们与临时目标区分开来,并且通常未在 JNDI 中注册。用于创建动态目标的 API 因提供商而异到提供商,因为与目标相关的属性是特定于提供商的。但是,提供商有时会做出的一个简单的实现选择是忽略 JMS 规范中的警告并使用TopicSession createTopic(String topicName)QueueSession createQueue(String queueName)方法,以创建具有默认目标属性的新目标。取决于 在提供商实施方面,DynamicDestinationResolver然后还可以创建一个 物理目标,而不是仅解析一个目标。spring-doc.cadn.net.cn

布尔属性pubSubDomain用于配置JmsTemplate跟 了解正在使用的 JMS 域。默认情况下,此属性的值为 false,表示点对点域Queues,将被使用。此属性 (使用JmsTemplate) 通过 的实现DestinationResolver接口。spring-doc.cadn.net.cn

您还可以配置JmsTemplate通过 财产defaultDestination.默认目标为发送和接收 不引用特定目标的作。spring-doc.cadn.net.cn

4.1.4. 消息监听器容器

JMS 消息在 EJB 世界中最常见的用途之一是驱动消息驱动 Beans(MDB)。Spring 提供了一种解决方案,以某种方式创建消息驱动的 POJO (MDP) 这不会将用户绑定到 EJB 容器。(有关详细信息,请参阅异步接收:消息驱动的 POJO 报道了 Spring 的 MDP 支持。从 Spring Framework 4.1 开始,端点方法可以是 注释为@JmsListener— 有关更多详细信息,请参阅注释驱动的侦听器端点spring-doc.cadn.net.cn

消息侦听器容器用于从 JMS 消息队列接收消息,并且 驱动MessageListener被注入其中。侦听器容器是 负责消息接收的所有线程,并分派到侦听器中,以便 加工。消息侦听器容器是 MDP 和 消息传递提供商,并负责注册以接收消息,参与 事务、资源获取和释放、异常转换等。这 允许您编写(可能复杂的)业务逻辑 与接收消息(并可能响应消息)相关联,以及委托 样板 JMS 基础设施对框架的关注。spring-doc.cadn.net.cn

有两个标准的 JMS 消息监听器容器与 Spring 打包在一起,每个容器都带有 其专业功能集。spring-doc.cadn.net.cn

SimpleMessageListenerContainer

此消息侦听器容器是两种标准风格中较简单的一种。它创造了 启动时固定数量的 JMS 会话和使用者,使用 标准 JMSMessageConsumer.setMessageListener()方法,并将其留在 JMS 中 provider 来执行侦听器回调。此变体不允许动态适应 到运行时需求或参与外部管理的交易。 在兼容性方面,它非常接近独立 JMS 的精神 规范,但通常与 Java EE 的 JMS 限制不兼容。spring-doc.cadn.net.cn

SimpleMessageListenerContainer不允许对外参与 托管事务,它确实支持本机 JMS 事务。要启用此功能, 您可以切换sessionTransactedflag 到true或者,在 XML 命名空间中,将acknowledge属性设置为transacted.侦听器抛出的异常随后导致 回滚,并重新传递消息。或者,考虑使用CLIENT_ACKNOWLEDGE模式,它在异常情况下也提供重新传递,但 不使用 transactedSession实例,因此不包括任何其他Session交易协议中的作(例如发送响应消息)。
默认值AUTO_ACKNOWLEDGE模式不提供适当的可靠性保证。 当侦听器执行失败时,消息可能会丢失(因为提供者会自动 在侦听器调用后确认每条消息,没有要传播到的例外 提供者)或当侦听器容器关闭时(您可以通过将 这acceptMessagesWhileStopping标志)。确保在以下情况下使用事务会话 可靠性需求(例如,用于可靠的队列处理和持久主题订阅)。
DefaultMessageListenerContainer

在大多数情况下使用此消息侦听器容器。与SimpleMessageListenerContainer,此容器变体允许动态适应以适应运行时需求,并能够参与外部管理的事务。当配置JtaTransactionManager. 因此,处理可能会利用 XA 事务 语义学。 这个侦听器容器在对JMS 提供程序的低要求、高级功能(例如参与外部管理的事务)以及与 Java EE 环境的兼容性之间取得了良好的平衡。spring-doc.cadn.net.cn

您可以自定义容器的缓存级别。请注意,如果未启用缓存,将为每个消息接收创建一个新连接和一个新会话。将此与高负载的非持久订阅相结合可能会导致消息丢失。确保在这种情况下使用适当的缓存级别。spring-doc.cadn.net.cn

当代理出现故障时,此容器还具有可恢复的功能。默认情况下, 一个简单的BackOff实现每五秒重试一次。您可以指定 一个习惯BackOff实现更细粒度的恢复选项。看ExponentialBackOff举个例子。spring-doc.cadn.net.cn

与它的兄弟姐妹一样 (SimpleMessageListenerContainer),DefaultMessageListenerContainer支持本机 JMS 事务并允许 自定义确认模式。如果对你的方案可行,这是非常 推荐而不是外部管理的交易——也就是说,如果您能忍受 偶尔出现重复消息,以防 JVM 死亡。自定义重复消息 业务逻辑中的检测步骤可以涵盖此类情况,例如, 以业务实体存在检查或协议表检查的形式。 任何此类安排都比替代方案效率高得多: 使用 XA 事务包装整个处理(通过配置DefaultMessageListenerContainer使用JtaTransactionManager) 来覆盖 JMS 消息的接收以及业务逻辑的执行 消息监听器(包括数据库作等)。
默认值AUTO_ACKNOWLEDGE模式不提供适当的可靠性保证。 当侦听器执行失败时,消息可能会丢失(因为提供者会自动 在侦听器调用后确认每条消息,没有要传播到的例外 提供者)或当侦听器容器关闭时(您可以通过将 这acceptMessagesWhileStopping标志)。确保在以下情况下使用事务会话 可靠性需求(例如,用于可靠的队列处理和持久主题订阅)。

4.1.5. 事务管理

Spring 提供了一个JmsTransactionManager管理单个 JMS 的事务ConnectionFactory.这允许 JMS 应用程序利用托管事务 Spring 的功能,如数据访问一章的事务管理部分所述。 这JmsTransactionManager执行本地资源事务,绑定 JMS 来自指定ConnectionFactory到线程。JmsTemplate自动检测此类事务资源并运行 相应地对他们进行。spring-doc.cadn.net.cn

在 Java EE 环境中,ConnectionFactorypools 连接和会话实例, 因此,这些资源可以在事务之间有效地重用。在独立环境中, 使用 Spring 的SingleConnectionFactory导致共享 JMSConnection跟 每个事务都有自己的独立Session.或者,考虑使用 特定于提供程序的池化适配器,例如 ActiveMQ 的PooledConnectionFactory类。spring-doc.cadn.net.cn

您还可以使用JmsTemplate使用JtaTransactionManager以及支持 XA 的 JMSConnectionFactory执行分布式事务。请注意,这需要 使用 JTA 事务管理器以及正确配置的 XA ConnectionFactory。 (检查您的 Java EE 服务器或 JMS 提供者的文档。spring-doc.cadn.net.cn

在托管和非托管事务环境中重用代码可能会造成混淆 使用 JMS API 创建SessionConnection.这是因为 JMS API 只有一个工厂方法来创建Session,并且它需要 事务和确认模式。在托管环境中,设置这些值是 环境事务基础设施的责任,因此这些价值观 被提供商的 JMS 连接包装器忽略。当您使用JmsTemplate在非托管环境中,您可以通过使用 性能sessionTransactedsessionAcknowledgeMode.当您使用PlatformTransactionManagerJmsTemplate,则模板始终被赋予一个 事务性 JMSSession.spring-doc.cadn.net.cn

4.2. 发送消息

JmsTemplate包含许多发送消息的便捷方法。 发送 方法使用javax.jms.Destination对象和其他使用String在 JNDI 查找中。 这send方法 不取 destination 参数使用默认 destination。spring-doc.cadn.net.cn

以下示例使用MessageCreator回调以从 提供Session对象:spring-doc.cadn.net.cn

import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.Session;

import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.core.JmsTemplate;

public class JmsQueueSender {

    private JmsTemplate jmsTemplate;
    private Queue queue;

    public void setConnectionFactory(ConnectionFactory cf) {
        this.jmsTemplate = new JmsTemplate(cf);
    }

    public void setQueue(Queue queue) {
        this.queue = queue;
    }

    public void simpleSend() {
        this.jmsTemplate.send(this.queue, new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                return session.createTextMessage("hello queue world");
            }
        });
    }
}

在前面的示例中,JmsTemplate是通过传递对ConnectionFactory. 作为替代方案,零参数构造函数和connectionFactory,可用于在JavaBean 样式(使用BeanFactory或纯 Java 代码)。或者,考虑派生自 Spring 的JmsGatewaySupport便利基类,它为 JMS 配置提供预构建的 bean 属性。spring-doc.cadn.net.cn

send(String destinationName, MessageCreator creator)方法允许您发送消息,使用目标的字符串名称。如果这些名称在 JNDI 中注册,您应该将destinationResolver模板的属性转换为JndiDestinationResolver.spring-doc.cadn.net.cn

如果您创建了JmsTemplate并指定了默认目的地,则send(MessageCreator c)向该目标发送消息。spring-doc.cadn.net.cn

4.2.1. 使用消息转换器

为了方便发送域模型对象,JmsTemplate有 将 Java 对象作为消息数据参数的各种 send 方法 内容。重载的方法convertAndSend()receiveAndConvert()方法JmsTemplate将转换过程委托给MessageConverter接口。该接口定义了一个简单的合约,用于在 Java 对象和 JMS 消息。默认实现 (SimpleMessageConverter)支持转换 之间StringTextMessage,byte[]BytesMessagejava.util.MapMapMessage.通过使用转换器,您和您的应用程序代码可以专注于 通过 JMS 发送或接收的业务对象,并且与 如何将其表示为 JMS 消息的详细信息。spring-doc.cadn.net.cn

沙盒当前包括一个MapMessageConverter,它使用反射来转换 在 JavaBean 和MapMessage.您可能使用的其他常用实现选项 实现自己是使用现有 XML 封送包(例如 JAXB 或 XStream)创建TextMessage表示对象。spring-doc.cadn.net.cn

适应邮件的属性、标头和正文的设置,而这些设置不能 通用封装在转换器类中,则MessagePostProcessor接口 允许您在邮件转换后但在发送之前访问邮件。这 以下示例演示如何修改消息标头和属性java.util.Map转换为消息:spring-doc.cadn.net.cn

public void sendWithConversion() {
    Map map = new HashMap();
    map.put("Name", "Mark");
    map.put("Age", new Integer(47));
    jmsTemplate.convertAndSend("testQueue", map, new MessagePostProcessor() {
        public Message postProcessMessage(Message message) throws JMSException {
            message.setIntProperty("AccountID", 1234);
            message.setJMSCorrelationID("123-00001");
            return message;
        }
    });
}

这将产生以下形式的消息:spring-doc.cadn.net.cn

MapMessage={
    Header={
        ... standard headers ...
        CorrelationID={123-00001}
    }
    Properties={
        AccountID={Integer:1234}
    }
    Fields={
        Name={String:Mark}
        Age={Integer:47}
    }
}

4.2.2. 使用SessionCallbackProducerCallback

虽然发送作涵盖了许多常见的使用方案,但有时您可能 想要在 JMS 上执行多个作SessionMessageProducer.这SessionCallbackProducerCallback公开 JMSSessionSession / MessageProducer对,分别。这execute()方法JmsTemplate跑 这些回调方法。spring-doc.cadn.net.cn

4.3. 接收消息

这描述了如何在 Spring 中使用 JMS 接收消息。spring-doc.cadn.net.cn

4.3.1. 同步接收

虽然 JMS 通常与异步处理相关联,但您可以同步消费消息。重载的receive(..)方法提供了这个 功能性。 在同步接收期间,调用线程会阻塞,直到消息变得可用。这可能是一个危险的作,因为调用线程可以可能被无限期地阻止。 这receiveTimeout属性指定多长时间接收者应该等待,然后放弃等待消息。spring-doc.cadn.net.cn

4.3.2. 异步接收:消息驱动的 POJO

Spring 还通过使用@JmsListener注释,并提供一个开放的基础设施来以编程方式注册端点。 到目前为止,这是设置异步接收器最方便的方法。 有关更多详细信息,请参阅启用侦听器端点注释

以类似于 EJB 世界中的消息驱动 Bean (MDB) 的方式,消息驱动 POJO (MDP) 充当 JMS 消息的接收器。一个限制(但请参阅MessageListenerAdapter) 是它必须实现 这javax.jms.MessageListener接口。请注意,如果您的 POJO 收到消息 在多个线程上,确保实现是线程安全的非常重要。spring-doc.cadn.net.cn

以下示例显示了 MDP 的简单实现:spring-doc.cadn.net.cn

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class ExampleListener implements MessageListener {

    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            try {
                System.out.println(((TextMessage) message).getText());
            }
            catch (JMSException ex) {
                throw new RuntimeException(ex);
            }
        }
        else {
            throw new IllegalArgumentException("Message must be of type TextMessage");
        }
    }
}

一旦你实现了MessageListener,是时候创建一个消息监听器了 容器。spring-doc.cadn.net.cn

以下示例显示如何定义和配置其中一个消息侦听器 随 Spring 一起提供的容器(在本例中,DefaultMessageListenerContainer):spring-doc.cadn.net.cn

<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener"/>

<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="destination"/>
    <property name="messageListener" ref="messageListener"/>
</bean>

请参阅各种消息侦听器容器(所有容器都实现 MessageListenerContainer)的 Spring javadoc 了解每个实现支持的功能的完整描述。spring-doc.cadn.net.cn

4.3.3. 使用SessionAwareMessageListener接口

SessionAwareMessageListenerinterface 是一个特定于 Spring 的接口,它提供 与 JMS 类似的合同MessageListener接口,但也给出了消息处理 方法访问 JMSSession从中,Message被收到。 以下列表显示了SessionAwareMessageListener接口:spring-doc.cadn.net.cn

package org.springframework.jms.listener;

public interface SessionAwareMessageListener {

    void onMessage(Message message, Session session) throws JMSException;
}

您可以选择让 MDP 实现此接口(优先于 JMS系统MessageListener接口),如果您希望您的 MDP 能够响应任何 收到的消息(通过使用SessiononMessage(Message, Session)方法)。Spring 附带的所有消息侦听器容器实现 支持实现MessageListenerSessionAwareMessageListener接口。实现SessionAwareMessageListener请注意,它们随后与 Spring 联系在一起 通过界面。是否使用它的选择完全取决于您 作为应用程序开发人员或架构师。spring-doc.cadn.net.cn

请注意,onMessage(..)方法SessionAwareMessageListener接口抛出JMSException.与标准 JMS 相比MessageListener接口,当使用SessionAwareMessageListener接口,它是 客户端代码处理任何引发的异常的责任。spring-doc.cadn.net.cn

4.3.4. 使用MessageListenerAdapter

MessageListenerAdapterclass 是 Spring 异步 消息传递支持。简而言之,它允许您将几乎任何类公开为 MDP (尽管有一些限制)。spring-doc.cadn.net.cn

请考虑以下接口定义:spring-doc.cadn.net.cn

public interface MessageDelegate {

    void handleMessage(String message);

    void handleMessage(Map message);

    void handleMessage(byte[] message);

    void handleMessage(Serializable message);
}

请注意,尽管该接口既没有扩展MessageListener也不是SessionAwareMessageListener接口,您仍然可以通过使用MessageListenerAdapter类。另请注意各种消息处理方法 根据各种内容强类型Message他们可以的类型 接收和处理。spring-doc.cadn.net.cn

现在考虑以下MessageDelegate接口:spring-doc.cadn.net.cn

public class DefaultMessageDelegate implements MessageDelegate {
    // implementation elided for clarity...
}

特别是,请注意前面的MessageDelegate接口(DefaultMessageDelegateclass) 根本没有 JMS 依赖项。它确实是一个POJO,我们可以通过以下配置将其制作成 MDP:spring-doc.cadn.net.cn

<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <constructor-arg>
        <bean class="jmsexample.DefaultMessageDelegate"/>
    </constructor-arg>
</bean>

<!-- and this is the message listener container... -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="destination"/>
    <property name="messageListener" ref="messageListener"/>
</bean>

下一个示例显示了另一个只能处理接收 JMS 的 MDPTextMessage消息。请注意消息处理方法的实际调用方式receiveMessageListenerAdapter默认为handleMessage),但它是可配置的(正如本节后面所见)。通知 还有receive(..)方法被强类型化为仅接收和响应 JMSTextMessage消息。 以下列表显示了TextMessageDelegate接口:spring-doc.cadn.net.cn

public interface TextMessageDelegate {

    void receive(TextMessage message);
}

以下列表显示了一个实现TextMessageDelegate接口:spring-doc.cadn.net.cn

public class DefaultTextMessageDelegate implements TextMessageDelegate {
    // implementation elided for clarity...
}

服务员的配置MessageListenerAdapter则如下所示:spring-doc.cadn.net.cn

<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <constructor-arg>
        <bean class="jmsexample.DefaultTextMessageDelegate"/>
    </constructor-arg>
    <property name="defaultListenerMethod" value="receive"/>
    <!-- we don't want automatic message context extraction -->
    <property name="messageConverter">
        <null/>
    </property>
</bean>

请注意,如果messageListener接收 JMSMessage类型的 以外TextMessageIllegalStateException被抛出(随后吞下)。的另一个功能MessageListenerAdapterclass 是自动发回响应的能力Message如果处理程序方法返回non-void 值。考虑以下接口和类:spring-doc.cadn.net.cn

public interface ResponsiveTextMessageDelegate {

    // notice the return type...
    String receive(TextMessage message);
}
public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate {
    // implementation elided for clarity...
}

如果您使用DefaultResponsiveTextMessageDelegate结合MessageListenerAdapter,则从执行 这'receive(..)'方法(在默认配置中)转换为TextMessage. 由此产生的TextMessage然后发送到Destination(如果一个存在)在 JMS 中定义Reply-To原始的属性Message或 违约Destination设置在MessageListenerAdapter(如果已配置)。如果没有Destination找到,则InvalidDestinationException被抛出(请注意,此异常不会被吞噬并向上传播调用堆栈)。spring-doc.cadn.net.cn

4.3.5. 处理事务中的消息

在事务中调用消息侦听器只需要重新配置侦听器容器。spring-doc.cadn.net.cn

您可以通过sessionTransacted旗 在侦听器容器定义上。然后,每个消息侦听器调用都会运行在活动的 JMS 事务中,如果侦听器执行失败,则消息接收会回滚。发送响应消息(通过SessionAwareMessageListener) 是 同一本地事务的一部分,但任何其他资源作(例如 数据库访问)独立运行。这通常需要重复的消息 侦听器实现中的检测,以涵盖数据库处理 已提交,但消息处理未能提交。spring-doc.cadn.net.cn

考虑以下 bean 定义:spring-doc.cadn.net.cn

<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="destination"/>
    <property name="messageListener" ref="messageListener"/>
    <property name="sessionTransacted" value="true"/>
</bean>

要参与外部管理的交易,您需要配置一个 事务管理器,并使用支持外部托管的侦听器容器 事务(通常,DefaultMessageListenerContainer).spring-doc.cadn.net.cn

要配置消息侦听器容器以参与 XA 事务,您需要 配置JtaTransactionManager(默认情况下,它委托给 Java EE 服务器的事务子系统)。请注意,底层 JMSConnectionFactory需要 具有 XA 能力并在您的 JTA 交易协调员处正确注册。(检查您的 Java EE 服务器对 JNDI 资源的配置。这也允许消息接收 因为(例如)数据库访问是同一事务的一部分(具有统一提交 语义,以牺牲 XA 事务日志开销为代价)。spring-doc.cadn.net.cn

以下 bean 定义创建事务管理器:spring-doc.cadn.net.cn

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

然后我们需要将其添加到我们之前的容器配置中。容器 处理剩下的。以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="destination"/>
    <property name="messageListener" ref="messageListener"/>
    <property name="transactionManager" ref="transactionManager"/> (1)
</bean>
1 我们的交易经理。

4.4. 对 JCA 消息端点的支持

从 2.5 版开始,Spring 还提供了对基于 JCA 的支持MessageListener容器。这JmsMessageEndpointManager尝试 自动确定ActivationSpec来自提供者的ResourceAdapter类名。因此,通常可以提供 Spring 的泛型JmsActivationSpecConfig,如以下示例所示:spring-doc.cadn.net.cn

<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
    <property name="resourceAdapter" ref="resourceAdapter"/>
    <property name="activationSpecConfig">
        <bean class="org.springframework.jms.listener.endpoint.JmsActivationSpecConfig">
            <property name="destinationName" value="myQueue"/>
        </bean>
    </property>
    <property name="messageListener" ref="myMessageListener"/>
</bean>

或者,您可以设置一个JmsMessageEndpointManager使用给定的ActivationSpec对象。这ActivationSpec对象也可能来自 JNDI 查找 (使用<jee:jndi-lookup>).以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
    <property name="resourceAdapter" ref="resourceAdapter"/>
    <property name="activationSpec">
        <bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
            <property name="destination" value="myQueue"/>
            <property name="destinationType" value="javax.jms.Queue"/>
        </bean>
    </property>
    <property name="messageListener" ref="myMessageListener"/>
</bean>

使用 Spring 的ResourceAdapterFactoryBean,您可以配置目标ResourceAdapter本地,如以下示例所示:spring-doc.cadn.net.cn

<bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean">
    <property name="resourceAdapter">
        <bean class="org.apache.activemq.ra.ActiveMQResourceAdapter">
            <property name="serverUrl" value="tcp://localhost:61616"/>
        </bean>
    </property>
    <property name="workManager">
        <bean class="org.springframework.jca.work.SimpleTaskWorkManager"/>
    </property>
</bean>

指定的WorkManager还可以指向特定于环境的线程池——通常通过SimpleTaskWorkManager实例的asyncTaskExecutor财产。考虑 为所有ResourceAdapter实例,如果您碰巧遇到 使用多个适配器。spring-doc.cadn.net.cn

在某些环境(例如 WebLogic 9 或更高版本)中,您可以改为获取整个ResourceAdapter对象 来自 JNDI(通过使用<jee:jndi-lookup>). 基于 Spring 的消息然后,侦听器可以与服务器托管的ResourceAdapter,其中还使用server 的内置WorkManager.spring-doc.cadn.net.cn

Spring还提供了一个不与JMS绑定的通用JCA消息端点管理器:org.springframework.jca.endpoint.GenericMessageEndpointManager. 此组件允许使用任何消息侦听器类型(例如 JMSMessageListener) 和任何特定于提供者ActivationSpec对象。 请参阅 JCA 提供商的文档,以了解连接器的实际功能,并查看GenericMessageEndpointManagerjavadoc 用于特定于 Spring 的配置详细信息。spring-doc.cadn.net.cn

基于 JCA 的消息端点管理与 EJB 2.1 消息驱动的 Bean 非常相似。它使用相同的底层资源提供者契约。与 EJB 2.1 MDB 一样,您也可以在 Spring 上下文中使用任何消息侦听器接口,由您的 JCA 提供程序支持。尽管如此,Spring 还是为 JMS 提供了明确的“便利”支持,因为 JMS 是与 JCA 端点管理契约一起使用的最常见端点 API。

4.5. 注释驱动的侦听器端点

异步接收消息的最简单方法是使用带注释的侦听器端点基础设施。简而言之,它允许您将托管bean 的方法公开为 JMS 侦听器端点。以下示例显示了如何使用它:spring-doc.cadn.net.cn

@Component
public class MyService {

    @JmsListener(destination = "myDestination")
    public void processOrder(String data) { ... }
}

前面示例的思想是,每当消息在javax.jms.Destination myDestinationprocessOrder方法被调用相应地(在本例中,使用 JMS 消息的内容,类似于 什么MessageListenerAdapter提供)。spring-doc.cadn.net.cn

带注释的端点基础结构创建消息侦听器容器 在后台,通过使用JmsListenerContainerFactory. 这样的容器不会针对应用程序上下文注册,但可以轻松 通过使用JmsListenerEndpointRegistry豆。spring-doc.cadn.net.cn

@JmsListener是 Java 8 上的可重复注释,因此您可以关联 通过添加额外的@JmsListener声明给它。

4.5.1. 启用侦听器端点注释

启用对@JmsListener注释,您可以添加@EnableJms到其中一个 你@Configuration类,如以下示例所示:spring-doc.cadn.net.cn

@Configuration
@EnableJms
public class AppConfig {

    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory());
        factory.setDestinationResolver(destinationResolver());
        factory.setSessionTransacted(true);
        factory.setConcurrency("3-10");
        return factory;
    }
}

默认情况下,基础架构会查找名为jmsListenerContainerFactory作为工厂用于创建消息侦听器容器的源。在这个 case(并忽略 JMS 基础设施设置),您可以调用processOrder核心池大小为三个线程,最大池大小为十个线程的方法。spring-doc.cadn.net.cn

您可以自定义侦听器容器工厂以用于每个注释,也可以 通过实现JmsListenerConfigurer接口。 仅当至少注册了一个端点而没有特定的 集装箱工厂。请参阅实现JmsListenerConfigurer了解详细信息和示例。spring-doc.cadn.net.cn

如果您更喜欢 XML 配置,可以使用<jms:annotation-driven>元素,如以下示例所示:spring-doc.cadn.net.cn

<jms:annotation-driven/>

<bean id="jmsListenerContainerFactory"
        class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destinationResolver" ref="destinationResolver"/>
    <property name="sessionTransacted" value="true"/>
    <property name="concurrency" value="3-10"/>
</bean>

4.5.2. 编程端点注册

JmsListenerEndpoint提供 JMS 端点的模型,并负责配置 该模型的容器。基础结构允许您以编程方式配置终结点 除了JmsListener注解。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

    @Override
    public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
        SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
        endpoint.setId("myJmsEndpoint");
        endpoint.setDestination("anotherQueue");
        endpoint.setMessageListener(message -> {
            // processing
        });
        registrar.registerEndpoint(endpoint);
    }
}

在前面的示例中,我们使用SimpleJmsListenerEndpoint,它提供了实际的MessageListener调用。但是,您也可以构建自己的端点变体 来描述自定义调用机制。spring-doc.cadn.net.cn

请注意,您可以跳过使用@JmsListener完全 并通过JmsListenerConfigurer.spring-doc.cadn.net.cn

4.5.3. 带注释的端点方法签名

到目前为止,我们一直在注入一个简单的String在我们的端点中,但它实际上可以 具有非常灵活的方法签名。在下面的示例中,我们重写它以注入Order跟 自定义标头:spring-doc.cadn.net.cn

@Component
public class MyService {

    @JmsListener(destination = "myDestination")
    public void processOrder(Order order, @Header("order_type") String orderType) {
        ...
    }
}

您可以在 JMS 侦听器端点中注入的主要元素如下:spring-doc.cadn.net.cn

  • 原始的javax.jms.Message或其任何子类(前提是 与传入邮件类型匹配)。spring-doc.cadn.net.cn

  • javax.jms.Session用于选择访问本机 JMS API(例如,用于发送 自定义回复)。spring-doc.cadn.net.cn

  • org.springframework.messaging.Message表示传入的 JMS 消息。 请注意,此消息同时包含自定义标头和标准标头(如定义 由JmsHeaders).spring-doc.cadn.net.cn

  • @Header-annotated 方法参数来提取特定的标头值,包括 标准 JMS 标头。spring-doc.cadn.net.cn

  • 一个@Headers-带注释的参数,也必须可以分配给java.util.Map为 访问所有标头。spring-doc.cadn.net.cn

  • 非受支持类型之一的非注释元素 (MessageSession)被视为有效载荷。您可以通过注释来明确这一点 参数@Payload.您还可以通过添加额外的@Valid.spring-doc.cadn.net.cn

注入 Spring 的能力Message抽象对受益特别有用 从存储在传输特定消息中的所有信息中,而不依赖于 传输特定的 API。以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@JmsListener(destination = "myDestination")
public void processOrder(Message<Order> order) { ... }

方法参数的处理由DefaultMessageHandlerMethodFactory,您可以 进一步自定义以支持其他方法参数。您可以自定义转换和验证 那里也有支持。spring-doc.cadn.net.cn

例如,如果我们想确保我们的Order在处理之前有效,我们可以 使用@Valid并配置必要的验证器,如以下示例所示:spring-doc.cadn.net.cn

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

    @Override
    public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
        registrar.setMessageHandlerMethodFactory(myJmsHandlerMethodFactory());
    }

    @Bean
    public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {
        DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
        factory.setValidator(myValidator());
        return factory;
    }
}

4.5.4. 响应管理

现有支持MessageListenerAdapter已经让你的方法具有非void返回类型。在这种情况下,结果 调用封装在javax.jms.Message,在指定的目标中发送 在JMSReplyTo原始消息的标头或配置在 听众。现在,您可以使用@SendTo的注释 消息传递抽象。spring-doc.cadn.net.cn

假设我们的processOrder方法现在应该返回一个OrderStatus,我们可以写它 自动发送响应,如以下示例所示:spring-doc.cadn.net.cn

@JmsListener(destination = "myDestination")
@SendTo("status")
public OrderStatus processOrder(Order order) {
    // order processing
    return status;
}
如果您有多个@JmsListener-annotated 方法,也可以将@SendTo类级别的注释以共享默认回复目的地。

如果需要以与传输无关的方式设置其他标头,可以返回Message相反,使用类似于以下的方法:spring-doc.cadn.net.cn

@JmsListener(destination = "myDestination")
@SendTo("status")
public Message<OrderStatus> processOrder(Order order) {
    // order processing
    return MessageBuilder
            .withPayload(status)
            .setHeader("code", 1234)
            .build();
}

如果需要在运行时计算响应目标,可以封装响应 在JmsResponse实例,该实例还提供运行时使用的目标。我们可以重写之前的 示例如下:spring-doc.cadn.net.cn

@JmsListener(destination = "myDestination")
public JmsResponse<Message<OrderStatus>> processOrder(Order order) {
    // order processing
    Message<OrderStatus> response = MessageBuilder
            .withPayload(status)
            .setHeader("code", 1234)
            .build();
    return JmsResponse.forQueue(response, "status");
}

最后,如果您需要为响应指定一些 QoS 值,例如优先级或 Time to live,您可以配置JmsListenerContainerFactory因此 如以下示例所示:spring-doc.cadn.net.cn

@Configuration
@EnableJms
public class AppConfig {

    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory());
        QosSettings replyQosSettings = new QosSettings();
        replyQosSettings.setPriority(2);
        replyQosSettings.setTimeToLive(10000);
        factory.setReplyQosSettings(replyQosSettings);
        return factory;
    }
}

4.6. JMS 命名空间支持

Spring 提供了一个 XML 命名空间来简化 JMS 配置。使用 JMS namespace 元素,您需要引用 JMS 模式,如以下示例所示:spring-doc.cadn.net.cn

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

    <!-- bean definitions here -->

</beans>
1 引用 JMS 模式。

命名空间由三个顶级元素组成:<annotation-driven/>,<listener-container/><jca-listener-container/>.<annotation-driven/>允许使用注释驱动的侦听器端点<listener-container/><jca-listener-container/>定义共享侦听器容器配置,并且可以包含<listener/>子元素。 以下示例显示了两个侦听器的基本配置:spring-doc.cadn.net.cn

<jms:listener-container>

    <jms:listener destination="queue.orders" ref="orderService" method="placeOrder"/>

    <jms:listener destination="queue.confirmations" ref="confirmationLogger" method="log"/>

</jms:listener-container>

前面的示例相当于创建两个不同的侦听器容器 Bean 定义和两个不同的MessageListenerAdapterbean 定义,如图所示 在MessageListenerAdapter.除了显示的属性 在前面的示例中,listener元素可以包含多个可选的。 下表描述了所有可用属性:spring-doc.cadn.net.cn

表 3.JMS <listener> 元素的属性
属性 描述

idspring-doc.cadn.net.cn

托管侦听器容器的 Bean 名称。如果未指定,则 Bean 名称为 自动生成。spring-doc.cadn.net.cn

destination(必填)spring-doc.cadn.net.cn

此侦听器的目标名称,通过DestinationResolver策略。spring-doc.cadn.net.cn

ref(必填)spring-doc.cadn.net.cn

处理程序对象的 bean 名称。spring-doc.cadn.net.cn

methodspring-doc.cadn.net.cn

要调用的处理程序方法的名称。如果ref属性指向MessageListener或弹簧SessionAwareMessageListener,您可以省略此属性。spring-doc.cadn.net.cn

response-destinationspring-doc.cadn.net.cn

要向其发送响应消息的默认响应目标的名称。这是 适用于不携带JMSReplyTo田。这 此目标的类型由监听器容器的response-destination-type属性。请注意,这仅适用于具有 返回值,每个结果对象都转换为响应消息。spring-doc.cadn.net.cn

subscriptionspring-doc.cadn.net.cn

持久订阅的名称(如果有)。spring-doc.cadn.net.cn

selectorspring-doc.cadn.net.cn

此侦听器的可选消息选择器。spring-doc.cadn.net.cn

concurrencyspring-doc.cadn.net.cn

为此侦听器启动的并发会话数或使用者数。此值可以是 表示最大数量的简单数字(例如,5) 或表示较低的范围 以及上限(例如3-5).请注意,指定的最小值只是提示 并且可能会在运行时被忽略。默认值是容器提供的值。spring-doc.cadn.net.cn

<listener-container/>元素还接受几个可选属性。这 允许自定义各种策略(例如,taskExecutordestinationResolver)以及基本的 JMS 设置和资源引用。通过使用这些属性,您可以定义高度自定义的侦听器容器,同时仍然受益于命名空间的便利性。spring-doc.cadn.net.cn

您可以自动公开此类设置,例如JmsListenerContainerFactory由 指定id的 bean 通过factory-id属性 如以下示例所示:spring-doc.cadn.net.cn

<jms:listener-container connection-factory="myConnectionFactory"
        task-executor="myTaskExecutor"
        destination-resolver="myDestinationResolver"
        transaction-manager="myTransactionManager"
        concurrency="10">

    <jms:listener destination="queue.orders" ref="orderService" method="placeOrder"/>

    <jms:listener destination="queue.confirmations" ref="confirmationLogger" method="log"/>

</jms:listener-container>

下表描述了所有可用的属性。请参阅类级 javadoc的AbstractMessageListenerContainer及其具体子类,以获取有关各个属性的更多详细信息。javadoc还提供了对事务选择和消息重新传递场景的讨论。spring-doc.cadn.net.cn

表 4.JMS <listener-container> 元素的属性
属性 描述

container-typespring-doc.cadn.net.cn

此侦听器容器的类型。可用选项包括default,simple,default102simple102(默认选项为default).spring-doc.cadn.net.cn

container-classspring-doc.cadn.net.cn

自定义侦听器容器实现类作为完全限定的类名。默认值是 Spring 的标准DefaultMessageListenerContainerSimpleMessageListenerContainer,根据container-type属性。spring-doc.cadn.net.cn

factory-idspring-doc.cadn.net.cn

将此元素定义的设置公开为JmsListenerContainerFactory使用指定的id以便它们可以与其他端点重用。spring-doc.cadn.net.cn

connection-factoryspring-doc.cadn.net.cn

对 JMS 的引用ConnectionFactorybean(默认 bean 名称为connectionFactory).spring-doc.cadn.net.cn

task-executorspring-doc.cadn.net.cn

对Spring的参考TaskExecutor用于 JMS 侦听器调用器。spring-doc.cadn.net.cn

destination-resolverspring-doc.cadn.net.cn

DestinationResolver解决 JMS 的策略Destination实例。spring-doc.cadn.net.cn

message-converterspring-doc.cadn.net.cn

MessageConverter将 JMS 消息转换为侦听器的策略method 参数。默认值是SimpleMessageConverter.spring-doc.cadn.net.cn

error-handlerspring-doc.cadn.net.cn

ErrorHandler处理任何未捕获的异常的策略,这些异常 可能发生在执行MessageListener.spring-doc.cadn.net.cn

destination-typespring-doc.cadn.net.cn

此侦听器的 JMS 目标类型:queue,topic,durableTopic,sharedTopic, 或sharedDurableTopic.这可能会启用pubSubDomain,subscriptionDurablesubscriptionShared容器的属性。默认值为queue(禁用 这三个属性)。spring-doc.cadn.net.cn

response-destination-typespring-doc.cadn.net.cn

响应的 JMS 目标类型:queuetopic.默认值是destination-type属性。spring-doc.cadn.net.cn

client-idspring-doc.cadn.net.cn

此侦听器容器的 JMS 客户端 ID。使用 持久订阅。spring-doc.cadn.net.cn

cachespring-doc.cadn.net.cn

JMS 资源的缓存级别:none,connection,session,consumerauto.默认情况下 (auto),缓存级别有效consumer除非 已指定外部事务管理器 — 在这种情况下,有效的 默认值将为none(假设 Java EE 风格的事务管理,其中给定的 ConnectionFactory 是一个 XA 感知池)。spring-doc.cadn.net.cn

acknowledgespring-doc.cadn.net.cn

本机 JMS 确认模式:auto,client,dups-oktransacted.一个值 之transacted激活本地事务的Session.或者,您可以指定 这transaction-manager属性,见表后面的。默认值为auto.spring-doc.cadn.net.cn

transaction-managerspring-doc.cadn.net.cn

对外部PlatformTransactionManager(通常是基于 XA 的 事务协调器,例如 Spring 的JtaTransactionManager).如果未指定, 使用本机确认(请参阅acknowledge属性)。spring-doc.cadn.net.cn

concurrencyspring-doc.cadn.net.cn

每个侦听器要启动的并发会话数或使用者数。它可以是 表示最大数量的简单数字(例如,5) 或指示 下限和上限(例如,3-5).请注意,指定的最小值只是 提示,并且可能会在运行时被忽略。默认值为1.应将并发限制为1在 主题侦听器的情况或队列排序是否重要。考虑将其提高 一般队列。spring-doc.cadn.net.cn

prefetchspring-doc.cadn.net.cn

要加载到单个会话中的最大消息数。请注意,将此 数量可能会导致并发消费者的饥饿。spring-doc.cadn.net.cn

receive-timeoutspring-doc.cadn.net.cn

用于接收呼叫的超时(以毫秒为单位)。默认值为1000(一 第二)。-1表示没有超时。spring-doc.cadn.net.cn

back-offspring-doc.cadn.net.cn

指定BackOff用于计算恢复间隔的实例 尝试。如果BackOffExecution实现返回BackOffExecution#STOP, 侦听器容器不会进一步尝试恢复。这recovery-interval设置此属性时,将忽略 value 。默认值为FixedBackOff跟 间隔为 5000 毫秒(即 5 秒)。spring-doc.cadn.net.cn

recovery-intervalspring-doc.cadn.net.cn

指定恢复尝试之间的间隔(以毫秒为单位)。它提供了一个方便的 创建FixedBackOff与指定的间隔。为了更多的恢复 选项,请考虑指定一个BackOff实例。默认值为 5000 毫秒 (即五秒)。spring-doc.cadn.net.cn

phasespring-doc.cadn.net.cn

此容器应启动和停止的生命周期阶段。越低 值,则此容器启动得越早,停止得越晚。默认值为Integer.MAX_VALUE,这意味着容器尽可能晚地启动并在 尽快。spring-doc.cadn.net.cn

使用jms模式支持非常相似, 如以下示例所示:spring-doc.cadn.net.cn

<jms:jca-listener-container resource-adapter="myResourceAdapter"
        destination-resolver="myDestinationResolver"
        transaction-manager="myTransactionManager"
        concurrency="10">

    <jms:listener destination="queue.orders" ref="myMessageListener"/>

</jms:jca-listener-container>

下表描述了 JCA 变型的可用配置选项:spring-doc.cadn.net.cn

表 5.JMS <jca-listener-container/> 元素的属性
属性 描述

factory-idspring-doc.cadn.net.cn

将此元素定义的设置公开为JmsListenerContainerFactory使用指定的id以便它们可以与其他端点重用。spring-doc.cadn.net.cn

resource-adapterspring-doc.cadn.net.cn

对 JCA 的引用ResourceAdapterbean(默认 bean 名称为resourceAdapter).spring-doc.cadn.net.cn

activation-spec-factoryspring-doc.cadn.net.cn

JmsActivationSpecFactory.默认值是自动检测 JMS provider 及其ActivationSpec类(参见DefaultJmsActivationSpecFactory).spring-doc.cadn.net.cn

destination-resolverspring-doc.cadn.net.cn

DestinationResolver解决 JMS 的策略Destinations.spring-doc.cadn.net.cn

message-converterspring-doc.cadn.net.cn

MessageConverter将 JMS 消息转换为侦听器的策略 method 参数。默认值为SimpleMessageConverter.spring-doc.cadn.net.cn

destination-typespring-doc.cadn.net.cn

此侦听器的 JMS 目标类型:queue,topic,durableTopic,sharedTopic. 或sharedDurableTopic.这可能会启用pubSubDomain,subscriptionDurable, 和subscriptionShared容器的属性。默认值为queue(禁用 这三个属性)。spring-doc.cadn.net.cn

response-destination-typespring-doc.cadn.net.cn

响应的 JMS 目标类型:queuetopic.默认值是destination-type属性。spring-doc.cadn.net.cn

client-idspring-doc.cadn.net.cn

此侦听器容器的 JMS 客户端 ID。使用时需要指定 持久订阅。spring-doc.cadn.net.cn

acknowledgespring-doc.cadn.net.cn

本机 JMS 确认模式:auto,client,dups-oktransacted.一个值 之transacted激活本地事务的Session.或者,您可以指定 这transaction-manager属性。默认值为auto.spring-doc.cadn.net.cn

transaction-managerspring-doc.cadn.net.cn

对弹簧的引用JtaTransactionManagerjavax.transaction.TransactionManager用于为每个传入消息启动 XA 事务。如果未指定,则使用本机确认(请参阅acknowledge属性)。spring-doc.cadn.net.cn

concurrencyspring-doc.cadn.net.cn

每个侦听器要启动的并发会话或使用者数。它可以是表示最大数量的简单数字(例如5) 或指示 下限和上限(例如,3-5). 请注意,指定的最小值只是一个hint,并且通常在使用 JCA 侦听器容器时在运行时被忽略。默认值为 1。spring-doc.cadn.net.cn

prefetchspring-doc.cadn.net.cn

要加载到单个会话中的最大消息数。请注意,将此 数量可能会导致并发消费者的饥饿。spring-doc.cadn.net.cn

5. JMX

Spring 中的 JMX(Java 管理扩展)支持提供了以下功能:轻松透明地将您的 Spring 应用程序集成到 JMX 基础设施中。spring-doc.cadn.net.cn

JMX?

本章不是对 JMX 的介绍。它没有试图解释为什么您可能想要使用 JMX。如果您是 JMX 的新手,请参阅本章末尾的更多资源spring-doc.cadn.net.cn

具体来说,Spring 的 JMX 支持提供了四个核心特性:spring-doc.cadn.net.cn

这些功能旨在无需将应用程序组件耦合到 Spring 或 JMX 接口和类。事实上,在大多数情况下,您的应用程序 类不需要知道 Spring 或 JMX 就可以利用 Spring JMX 功能。spring-doc.cadn.net.cn

5.1. 将 Bean 导出到 JMX

Spring 的 JMX 框架中的核心类是MBeanExporter.这个类是 负责获取您的 Spring Bean 并将它们注册到 JMXMBeanServer. 例如,考虑以下类:spring-doc.cadn.net.cn

package org.springframework.jmx;

public class JmxTestBean implements IJmxTestBean {

    private String name;
    private int age;
    private boolean isSuperman;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    public String getName() {
        return name;
    }

    public int add(int x, int y) {
        return x + y;
    }

    public void dontExposeMe() {
        throw new RuntimeException();
    }
}

将此 bean 的属性和方法公开为 MBean,您可以配置MBeanExporter您的 配置文件并传入 bean,如以下示例所示:spring-doc.cadn.net.cn

<beans>
    <!-- this bean must not be lazily initialized if the exporting is to happen -->
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean1" value-ref="testBean"/>
            </map>
        </property>
    </bean>
    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>
</beans>

前面配置代码段中的相关 bean 定义是exporter豆。这beans属性告诉MBeanExporter究竟你的豆子必须是哪一个 导出到 JMXMBeanServer.在默认配置中,每个条目的键 在beans Map用作ObjectName对于 相应的条目值。您可以更改此行为,如控制ObjectNameBean 的实例.spring-doc.cadn.net.cn

使用此配置,testBeanbean 在ObjectName bean:name=testBean1.默认情况下,所有public豆子的属性 作为属性公开,并且所有public方法(除了那些继承自Object类)作为作公开。spring-doc.cadn.net.cn

MBeanExporter是一个Lifecyclebean(参见启动和关闭回调)。默认情况下,MBean 在 应用程序生命周期。您可以配置phase在其中 导出发生或通过设置autoStartup旗。

5.1.1. 创建 MBeanServer

上一节中显示的配置假定 应用程序在具有一个(并且只有一个)的环境中运行MBeanServer已经运行。在这种情况下,Spring 会尝试找到正在运行的MBeanServer和 向该服务器(如果有)注册您的 Bean。当您的 应用程序在容器(例如 Tomcat 或 IBM WebSphere)中运行,该容器具有其 有MBeanServer.spring-doc.cadn.net.cn

但是,这种方法在独立环境中或在内部运行时没有用处 不提供MBeanServer.要解决此问题,您可以创建一个MBeanServer实例,通过添加org.springframework.jmx.support.MBeanServerFactoryBeanclass 添加到您的配置中。 您还可以确保特定的MBeanServer通过设置MBeanExporter实例的server属性设置为MBeanServer返回的值MBeanServerFactoryBean,如以下示例所示:spring-doc.cadn.net.cn

<beans>

    <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>

    <!--
    this bean needs to be eagerly pre-instantiated in order for the exporting to occur;
    this means that it must not be marked as lazily initialized
    -->
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean1" value-ref="testBean"/>
            </map>
        </property>
        <property name="server" ref="mbeanServer"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

在前面的示例中,实例MBeanServerMBeanServerFactoryBean并且是 提供给MBeanExporter通过server财产。当您提供自己的MBeanServer实例,MBeanExporter不尝试定位正在运行的MBeanServer并使用提供的MBeanServer实例。为此,要发挥作用 正确地,您的类路径上必须有一个 JMX 实现。spring-doc.cadn.net.cn

5.1.2. 重用现有的MBeanServer

如果未指定服务器,则MBeanExporter尝试自动检测正在运行的MBeanServer.这适用于大多数环境中,其中只有一个MBeanServer实例是 使用。但是,当存在多个实例时,导出器可能会选择错误的服务器。 在这种情况下,您应该使用MBeanServer agentId以指示要使用的实例 使用,如以下示例所示:spring-doc.cadn.net.cn

<beans>
    <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
        <!-- indicate to first look for a server -->
        <property name="locateExistingServerIfPossible" value="true"/>
        <!-- search for the MBeanServer instance with the given agentId -->
        <property name="agentId" value="MBeanServer_instance_agentId>"/>
    </bean>
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="server" ref="mbeanServer"/>
        ...
    </bean>
</beans>

对于现有的MBeanServer具有动态(或未知)agentId通过查找方法检索的,您应该使用 factory-method, 如以下示例所示:spring-doc.cadn.net.cn

<beans>
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="server">
            <!-- Custom MBeanServerLocator -->
            <bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/>
        </property>
    </bean>

    <!-- other beans here -->

</beans>

5.1.3. 延迟初始化的MBean

如果将 Bean 配置为MBeanExporter也配置为 lazy 初始化,则MBeanExporter不违约,避免 实例化 bean。相反,它使用MBeanServer和 将从容器获取 Bean 推迟到对代理的第一次调用 发生。spring-doc.cadn.net.cn

5.1.4. 自动注册 MBean

通过MBeanExporter并且已经有效的 MBean 是 按原样注册MBeanServer没有斯普林的进一步干预。可能会导致 MBean 由MBeanExporter通过将autodetect属性设置为true,如以下示例所示:spring-doc.cadn.net.cn

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="autodetect" value="true"/>
</bean>

<bean name="spring:mbean=true" class="org.springframework.jmx.export.TestDynamicMBean"/>

在前面的示例中,调用spring:mbean=true已经是一个有效的 JMX MBean 并由 Spring 自动注册。默认情况下,自动检测 JMX 的 Bean 注册的 bean 名称用作ObjectName.您可以覆盖此行为, 详见控制ObjectNameBean 的实例.spring-doc.cadn.net.cn

5.1.5. 控制注册行为

考虑以下场景: 弹簧MBeanExporter尝试注册MBean使用MBeanServer通过使用ObjectName bean:name=testBean1.如果MBean实例已在同一个ObjectName,默认行为 是失败(并抛出一个InstanceAlreadyExistsException).spring-doc.cadn.net.cn

您可以精确控制当MBean是 注册MBeanServer.Spring 的 JMX 支持允许三种不同的 注册行为,以控制注册时的注册行为 进程发现MBean已在同一ObjectName. 下表总结了这些注册行为:spring-doc.cadn.net.cn

表 6.注册行为
注册行为 解释

FAIL_ON_EXISTINGspring-doc.cadn.net.cn

这是默认注册行为。如果MBean实例已经被 在相同下注册ObjectNameMBean正在注册的不是 registered,以及InstanceAlreadyExistsException被抛出。现有的MBean不受影响。spring-doc.cadn.net.cn

IGNORE_EXISTINGspring-doc.cadn.net.cn

如果MBean实例已在同一ObjectNameMBean正在注册的未注册。现有的MBean是 不受影响,并且没有Exception被抛出。这在以下设置中很有用: 多个应用程序希望共享一个公共的MBean在共享的MBeanServer.spring-doc.cadn.net.cn

REPLACE_EXISTINGspring-doc.cadn.net.cn

如果MBean实例已在同一ObjectName这 现存MBean之前注册的 是未注册的,而新的MBean在其位置注册(新的MBean有效地取代了 前一个实例)。spring-doc.cadn.net.cn

上表中的值定义为RegistrationPolicy类。 如果要更改默认注册行为,需要将registrationPolicy您的财产MBeanExporter定义到其中之一 值。spring-doc.cadn.net.cn

以下示例演示如何从默认注册更改 行为设置为REPLACE_EXISTING行为:spring-doc.cadn.net.cn

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean1" value-ref="testBean"/>
            </map>
        </property>
        <property name="registrationPolicy" value="REPLACE_EXISTING"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

5.2. 控制 Bean 的管理界面

上一节的示例中, 您对 Bean 的管理界面几乎没有控制权。所有public每个导出的 bean 的属性和方法分别作为 JMX 属性和作公开。为了更细粒度地控制哪些导出的 bean 的属性和方法实际上公开为 JMX 属性和作,Spring JMX 提供了一种全面且可扩展的机制控制 bean 的管理界面。spring-doc.cadn.net.cn

5.2.1. 使用MBeanInfoAssembler接口

在幕后,MBeanExporter委托到org.springframework.jmx.export.assembler.MBeanInfoAssembler接口,即 负责定义每个公开的 Bean 的管理接口。 默认实现org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler, 定义一个公开所有公共属性和方法的管理接口 (如您在前面各节的示例中看到的那样)。Spring 提供了两个 的其他实现MBeanInfoAssembler界面,让您 使用源级元数据控制生成的管理接口 或任何任意接口。spring-doc.cadn.net.cn

5.2.2. 使用源级元数据:Java 注释

通过使用MetadataMBeanInfoAssembler,您可以定义管理接口 使用源级元数据。元数据的读取被封装 通过org.springframework.jmx.export.metadata.JmxAttributeSource接口。 Spring JMX 提供了一个使用 Java 注释的默认实现,即org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource. 您必须配置MetadataMBeanInfoAssembler实现实例为 这JmxAttributeSource接口使其正常运行(没有默认值)。spring-doc.cadn.net.cn

要标记要导出到 JMX 的 bean,您应该使用ManagedResource注解。必须将要公开的每个方法标记为作 使用ManagedOperation注释并标记您希望公开的每个属性 使用ManagedAttribute注解。标记属性时,可以省略 getter 或 setter 的注释,用于创建只写或只读 属性。spring-doc.cadn.net.cn

一个ManagedResource-带注释的 bean 必须是公共的,公开的方法也必须是公共的 作或属性。

以下示例显示了JmxTestBean我们 用于创建 MBeanServerspring-doc.cadn.net.cn

package org.springframework.jmx;

import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedAttribute;

@ManagedResource(
        objectName="bean:name=testBean4",
        description="My Managed Bean",
        log=true,
        logFile="jmx.log",
        currencyTimeLimit=15,
        persistPolicy="OnUpdate",
        persistPeriod=200,
        persistLocation="foo",
        persistName="bar")
public class AnnotationTestBean implements IJmxTestBean {

    private String name;
    private int age;

    @ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15)
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @ManagedAttribute(description="The Name Attribute",
            currencyTimeLimit=20,
            defaultValue="bar",
            persistPolicy="OnUpdate")
    public void setName(String name) {
        this.name = name;
    }

    @ManagedAttribute(defaultValue="foo", persistPeriod=300)
    public String getName() {
        return name;
    }

    @ManagedOperation(description="Add two numbers")
    @ManagedOperationParameters({
        @ManagedOperationParameter(name = "x", description = "The first number"),
        @ManagedOperationParameter(name = "y", description = "The second number")})
    public int add(int x, int y) {
        return x + y;
    }

    public void dontExposeMe() {
        throw new RuntimeException();
    }

}

在前面的示例中,您可以看到JmxTestBeanclass 的ManagedResource注释和这个ManagedResource配置注释 具有一组属性。这些属性可用于配置各个方面 由MBeanExporter并在 greater 中解释 稍后在源级元数据类型中详述。spring-doc.cadn.net.cn

两个agename属性的注释是ManagedAttribute注释,但是,在age属性,则仅标记 getter。 这会导致这两个属性都包含在管理界面中 作为属性,但age属性是只读的。spring-doc.cadn.net.cn

最后,add(int, int)方法标有ManagedOperation属性 而dontExposeMe()方法不是。这会导致管理界面 仅包含一个作 (add(int, int)) 当您使用MetadataMBeanInfoAssembler.spring-doc.cadn.net.cn

以下配置显示了如何配置MBeanExporter使用MetadataMBeanInfoAssembler:spring-doc.cadn.net.cn

<beans>
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="assembler" ref="assembler"/>
        <property name="namingStrategy" ref="namingStrategy"/>
        <property name="autodetect" value="true"/>
    </bean>

    <bean id="jmxAttributeSource"
            class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>

    <!-- will create management interface using annotation metadata -->
    <bean id="assembler"
            class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
        <property name="attributeSource" ref="jmxAttributeSource"/>
    </bean>

    <!-- will pick up the ObjectName from the annotation -->
    <bean id="namingStrategy"
            class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
        <property name="attributeSource" ref="jmxAttributeSource"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.AnnotationTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>
</beans>

在前面的示例中,MetadataMBeanInfoAssemblerbean 已配置了 实例AnnotationJmxAttributeSource类并传递给MBeanExporter通过汇编器属性。这就是利用所需的全部内容 用于 Spring 公开的 MBean 的元数据驱动管理接口。spring-doc.cadn.net.cn

5.2.3. 源级元数据类型

下表描述了可用于 Spring JMX 的源级元数据类型:spring-doc.cadn.net.cn

表7.源级元数据类型
目的 注解 注释类型

标记Class作为 JMX 托管资源。spring-doc.cadn.net.cn

@ManagedResourcespring-doc.cadn.net.cn

spring-doc.cadn.net.cn

将方法标记为 JMX作。spring-doc.cadn.net.cn

@ManagedOperationspring-doc.cadn.net.cn

方法spring-doc.cadn.net.cn

将 getter 或 setter 标记为 JMX 属性的一半。spring-doc.cadn.net.cn

@ManagedAttributespring-doc.cadn.net.cn

方法(仅限 getter 和 setter)spring-doc.cadn.net.cn

定义作参数的描述。spring-doc.cadn.net.cn

@ManagedOperationParameter@ManagedOperationParametersspring-doc.cadn.net.cn

方法spring-doc.cadn.net.cn

下表描述了可用于这些源级别的配置参数 元数据类型:spring-doc.cadn.net.cn

表8.源级元数据参数
参数 描述 适用于

ObjectNamespring-doc.cadn.net.cn

使用者MetadataNamingStrategy以确定ObjectName托管资源的。spring-doc.cadn.net.cn

ManagedResourcespring-doc.cadn.net.cn

descriptionspring-doc.cadn.net.cn

设置资源、属性或作的友好描述。spring-doc.cadn.net.cn

ManagedResource,ManagedAttribute,ManagedOperationManagedOperationParameterspring-doc.cadn.net.cn

currencyTimeLimitspring-doc.cadn.net.cn

设置currencyTimeLimit描述符字段。spring-doc.cadn.net.cn

ManagedResourceManagedAttributespring-doc.cadn.net.cn

defaultValuespring-doc.cadn.net.cn

设置defaultValue描述符字段。spring-doc.cadn.net.cn

ManagedAttributespring-doc.cadn.net.cn

logspring-doc.cadn.net.cn

设置log描述符字段。spring-doc.cadn.net.cn

ManagedResourcespring-doc.cadn.net.cn

logFilespring-doc.cadn.net.cn

设置logFile描述符字段。spring-doc.cadn.net.cn

ManagedResourcespring-doc.cadn.net.cn

persistPolicyspring-doc.cadn.net.cn

设置persistPolicy描述符字段。spring-doc.cadn.net.cn

ManagedResourcespring-doc.cadn.net.cn

persistPeriodspring-doc.cadn.net.cn

设置persistPeriod描述符字段。spring-doc.cadn.net.cn

ManagedResourcespring-doc.cadn.net.cn

persistLocationspring-doc.cadn.net.cn

设置persistLocation描述符字段。spring-doc.cadn.net.cn

ManagedResourcespring-doc.cadn.net.cn

persistNamespring-doc.cadn.net.cn

设置persistName描述符字段。spring-doc.cadn.net.cn

ManagedResourcespring-doc.cadn.net.cn

namespring-doc.cadn.net.cn

设置作参数的显示名称。spring-doc.cadn.net.cn

ManagedOperationParameterspring-doc.cadn.net.cn

indexspring-doc.cadn.net.cn

设置作参数的索引。spring-doc.cadn.net.cn

ManagedOperationParameterspring-doc.cadn.net.cn

5.2.4. 使用AutodetectCapableMBeanInfoAssembler接口

为了进一步简化配置,Spring 包含了AutodetectCapableMBeanInfoAssembler接口,它扩展了MBeanInfoAssembler接口,以添加对 MBean 资源自动检测的支持。如果将MBeanExporter实例为AutodetectCapableMBeanInfoAssembler是的 允许“投票”是否包含用于 JMX 的 bean。spring-doc.cadn.net.cn

唯一的实现AutodetectCapableMBeanInfo接口是 这MetadataMBeanInfoAssembler,它投票以包含任何标记的 Bean 使用ManagedResource属性。在这种情况下,默认方法是使用 bean name 作为ObjectName,这会导致类似于以下内容的配置:spring-doc.cadn.net.cn

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <!-- notice how no 'beans' are explicitly configured here -->
        <property name="autodetect" value="true"/>
        <property name="assembler" ref="assembler"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

    <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
        <property name="attributeSource">
            <bean class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
        </property>
    </bean>

</beans>

请注意,在前面的配置中,没有 bean 传递给MBeanExporter. 但是,JmxTestBean仍已注册,因为它标有ManagedResource属性和MetadataMBeanInfoAssembler检测到这一点并投票将其包含在内。这种方法的唯一问题是JmxTestBean现在有生意了 意义。可以通过更改ObjectName创建,如控制ObjectNameBean 的实例.spring-doc.cadn.net.cn

5.2.5. 使用 Java 接口定义管理接口

除了MetadataMBeanInfoAssembler,Spring 还包括InterfaceBasedMBeanInfoAssembler,这允许您约束方法 基于集合中定义的方法集公开的属性 接口。spring-doc.cadn.net.cn

虽然公开 MBean 的标准机制是使用接口和简单的 命名方案,InterfaceBasedMBeanInfoAssembler通过以下方式扩展此功能 无需命名约定,允许您使用多个接口 并消除 Bean 实现 MBean 接口的需要。spring-doc.cadn.net.cn

考虑以下接口,该接口用于为JmxTestBean我们之前展示的类:spring-doc.cadn.net.cn

public interface IJmxTestBean {

    public int add(int x, int y);

    public long myOperation();

    public int getAge();

    public void setAge(int age);

    public void setName(String name);

    public String getName();

}

此接口定义作为作公开的方法和属性,并且 JMX MBean 上的属性。以下代码显示了如何配置 Spring JMX 以使用 此接口作为管理接口的定义:spring-doc.cadn.net.cn

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean5" value-ref="testBean"/>
            </map>
        </property>
        <property name="assembler">
            <bean class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
                <property name="managedInterfaces">
                    <value>org.springframework.jmx.IJmxTestBean</value>
                </property>
            </bean>
        </property>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

在前面的示例中,InterfaceBasedMBeanInfoAssembler配置为使用IJmxTestBean接口。是的 重要的是要了解由InterfaceBasedMBeanInfoAssembler不需要实现用于生成 JMX 管理的接口 接口。spring-doc.cadn.net.cn

在前面的案例中,IJmxTestBean接口用于构建所有管理 所有 bean 的接口。在许多情况下,这不是理想的行为,您可能 想要为不同的 bean 使用不同的接口。在这种情况下,您可以通过InterfaceBasedMBeanInfoAssembler一个Properties实例通过interfaceMappings属性,其中每个条目的键是 bean 名称,每个条目的值是 要用于该 bean 的接口名称的逗号分隔列表。spring-doc.cadn.net.cn

如果未通过managedInterfacesinterfaceMappings属性,则InterfaceBasedMBeanInfoAssembler反映 并使用该 bean 实现的所有接口来创建 管理界面。spring-doc.cadn.net.cn

5.2.6. 使用MethodNameBasedMBeanInfoAssembler

MethodNameBasedMBeanInfoAssembler允许您指定方法名称的列表 作为属性和作公开给 JMX 的。以下代码显示了一个示例 配置:spring-doc.cadn.net.cn

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
        <map>
            <entry key="bean:name=testBean5" value-ref="testBean"/>
        </map>
    </property>
    <property name="assembler">
        <bean class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler">
            <property name="managedMethods">
                <value>add,myOperation,getName,setName,getAge</value>
            </property>
        </bean>
    </property>
</bean>

在前面的示例中,您可以看到addmyOperation方法公开为 JMX 作,以及getName(),setName(String)getAge()公开为 JMX 属性的适当一半。在前面的代码中,方法映射适用于 向 JMX 公开的 bean。要逐个 Bean 控制方法暴露,您可以使用 这methodMappings属性MethodNameMBeanInfoAssembler将 Bean 名称映射到 方法名称列表。spring-doc.cadn.net.cn

5.3. 控制ObjectNameBean 的实例

在幕后,MBeanExporter委托到ObjectNamingStrategy获取ObjectName实例。 默认情况下,默认实现KeyNamingStrategy使用beans Map作为ObjectName.此外,KeyNamingStrategy可以映射键 的beans MapProperties文件(或多个文件)来解析ObjectName.除了KeyNamingStrategy,Spring 提供了两个额外的ObjectNamingStrategy实现:IdentityNamingStrategy(它构建了一个ObjectName基于 bean 的 JVM 身份)和MetadataNamingStrategy(其中 使用源级元数据获取ObjectName).spring-doc.cadn.net.cn

5.3.1. 阅读ObjectName属性中的实例

您可以配置自己的KeyNamingStrategy实例并将其配置为读取ObjectName实例来自Properties实例,而不是使用 Bean 键。这KeyNamingStrategy尝试在Properties带钥匙 对应于 Bean 键。如果未找到条目,或者如果Properties实例是null,则使用 bean 密钥本身。spring-doc.cadn.net.cn

以下代码显示了KeyNamingStrategy:spring-doc.cadn.net.cn

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="testBean" value-ref="testBean"/>
            </map>
        </property>
        <property name="namingStrategy" ref="namingStrategy"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

    <bean id="namingStrategy" class="org.springframework.jmx.export.naming.KeyNamingStrategy">
        <property name="mappings">
            <props>
                <prop key="testBean">bean:name=testBean1</prop>
            </props>
        </property>
        <property name="mappingLocations">
            <value>names1.properties,names2.properties</value>
        </property>
    </bean>

</beans>

前面的示例配置了KeyNamingStrategy使用Properties实例 从Properties由映射属性定义的实例和 属性文件,位于 mappings 属性定义的路径中。在这个 配置,则testBeanbean 被赋予一个ObjectNamebean:name=testBean1, 由于这是Properties实例,该实例具有对应于 豆钥匙。spring-doc.cadn.net.cn

如果Properties实例,则 bean 键名称用作 这ObjectName.spring-doc.cadn.net.cn

5.3.2. 使用MetadataNamingStrategy

MetadataNamingStrategy使用objectName属性的ManagedResource属性以创建ObjectName.以下代码显示了 配置MetadataNamingStrategy:spring-doc.cadn.net.cn

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="testBean" value-ref="testBean"/>
            </map>
        </property>
        <property name="namingStrategy" ref="namingStrategy"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

    <bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
        <property name="attributeSource" ref="attributeSource"/>
    </bean>

    <bean id="attributeSource"
            class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>

</beans>

如果没有objectName已为ManagedResource属性,则ObjectName使用以下内容创建 format: [fully-qualified-package-name]:type=[short-classname],name=[bean-name]。为 示例,生成的ObjectName因为下面的 bean 将是com.example:type=MyClass,name=myBean:spring-doc.cadn.net.cn

<bean id="myBean" class="com.example.MyClass"/>

5.3.3. 配置基于 Annotation 的 MBean 导出

如果您更喜欢使用基于注释的方法来定义 您的管理界面,一个方便的子类MBeanExporter可用:AnnotationMBeanExporter.定义此子类的实例时,不再需要namingStrategy,assemblerattributeSource配置 因为它始终使用基于 Java 注释的标准元数据(自动检测是 也始终启用)。事实上,与其定义MBeanExporterbean,一个偶数 更简单的语法支持@EnableMBeanExport @Configuration注解 如以下示例所示:spring-doc.cadn.net.cn

@Configuration
@EnableMBeanExport
public class AppConfig {

}

如果您更喜欢基于 XML 的配置,则<context:mbean-export/>元素服务于 相同的用途,如以下列表所示:spring-doc.cadn.net.cn

<context:mbean-export/>

如有必要,可以提供对特定 MBean 的引用serverdefaultDomain属性(属性AnnotationMBeanExporter) 接受替代 生成的 MBean 的值ObjectName域。这用于代替 完全限定的包名称,如上一节 MetadataNamingStrategy 所述,如以下示例所示:spring-doc.cadn.net.cn

@EnableMBeanExport(server="myMBeanServer", defaultDomain="myDomain")
@Configuration
ContextConfiguration {

}

以下示例显示了前面基于注释的示例的 XML 等效项:spring-doc.cadn.net.cn

<context:mbean-export server="myMBeanServer" default-domain="myDomain"/>
不要将基于接口的 AOP 代理与 JMX 的自动检测结合使用 注解。基于接口的代理“隐藏”了目标类,这 还隐藏了 JMX 管理的资源注释。因此,您应该在其中使用目标类代理 case(通过将 'proxy-target-class' 标志设置为<aop:config/>,<tx:annotation-driven/>等等)。否则,您的 JMX Bean 可能会被静默忽略 启动。

5.4. 使用 JSR-160 连接器

对于远程访问,Spring JMX 模块提供了两个FactoryBeanorg.springframework.jmx.support用于创建服务器端和客户端的包 连接。spring-doc.cadn.net.cn

5.4.1. 服务器端连接器

让 Spring JMX 创建、启动和公开 JSR-160JMXConnectorServer,您可以使用 以下配置:spring-doc.cadn.net.cn

<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"/>

默认情况下,ConnectorServerFactoryBean创建一个JMXConnectorServer绑定到service:jmx:jmxmp://localhost:9875.这serverConnectorbean 因此公开了 当地MBeanServer通过本地主机上的 JMXMP 协议端口 9875 发送到客户端。注意 JMXMP 协议被 JSR 160 规范标记为可选。现在 主要的开源 JMX 实现 MX4J 和 JDK 提供的实现 不支持 JMXMP。spring-doc.cadn.net.cn

要指定其他 URL 并注册JMXConnectorServer本身与MBeanServer,您可以使用serviceUrlObjectName属性,分别, 如以下示例所示:spring-doc.cadn.net.cn

<bean id="serverConnector"
        class="org.springframework.jmx.support.ConnectorServerFactoryBean">
    <property name="objectName" value="connector:name=rmi"/>
    <property name="serviceUrl"
            value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector"/>
</bean>

如果ObjectName属性,Spring 会自动注册您的连接器 使用MBeanServer在那个下ObjectName.以下示例显示了完整的 参数,您可以传递给ConnectorServerFactoryBean创建JMXConnector:spring-doc.cadn.net.cn

<bean id="serverConnector"
        class="org.springframework.jmx.support.ConnectorServerFactoryBean">
    <property name="objectName" value="connector:name=iiop"/>
    <property name="serviceUrl"
        value="service:jmx:iiop://localhost/jndi/iiop://localhost:900/myconnector"/>
    <property name="threaded" value="true"/>
    <property name="daemon" value="true"/>
    <property name="environment">
        <map>
            <entry key="someKey" value="someValue"/>
        </map>
    </property>
</bean>

请注意,当您使用基于 RMI 的连接器时,您需要查找服务 (tnameservrmiregistry) 启动,以便完成名称注册。如果你 使用 Spring 通过 RMI 为您导出远程服务,Spring 已经 构建了 RMI 注册表。如果没有,可以使用以下命令轻松启动注册表 配置片段:spring-doc.cadn.net.cn

<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
    <property name="port" value="1099"/>
</bean>

5.4.2. 客户端连接器

要创建MBeanServerConnection到启用了 JSR-160 的远程MBeanServer,您可以使用MBeanServerConnectionFactoryBean,如以下示例所示:spring-doc.cadn.net.cn

<bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean">
    <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/>
</bean>

5.4.3. JMX over Hessian 或 SOAP

JSR-160 允许扩展客户端之间进行通信的方式和服务器。前几节中显示的示例使用了基于 RMI 的强制性实现JSR-160 规范(IIOP 和 JRMP)和(可选)JMXMP 所要求的。通过使用其他提供程序或 JMX 实现(例如 MX4J),您可以通过简单的 HTTP 或 SSL 等方式利用 SOAP 或 Hessian 等协议,如以下示例所示:spring-doc.cadn.net.cn

<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean">
    <property name="objectName" value="connector:name=burlap"/>
    <property name="serviceUrl" value="service:jmx:burlap://localhost:9874"/>
</bean>

在前面的示例中,我们使用了 MX4J 3.0.0。有关详细信息,请参阅官方 MX4J文档。spring-doc.cadn.net.cn

5.5. 通过代理访问 MBean

Spring JMX 允许您创建代理,将调用重新路由到注册在本地或远程MBeanServer. 这些代理为您提供了一个标准的 Java 接口通过它,您可以与 MBean 进行交互。以下代码显示了如何为在本地运行的 MBean 配置proxyMBeanServer:spring-doc.cadn.net.cn

<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
    <property name="objectName" value="bean:name=testBean"/>
    <property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/>
</bean>

在前面的示例中,您可以看到为在ObjectNamebean:name=testBean. 代理实现的接口集由proxyInterfaces属性,并且将方法和属性映射到 MBean 上的作和属性的规则是相同的规则InterfaceBasedMBeanInfoAssembler.spring-doc.cadn.net.cn

MBeanProxyFactoryBean可以创建任何可通过MBeanServerConnection. 默认情况下,本地MBeanServer被定位并使用,但您可以覆盖它并提供MBeanServerConnection指向遥控器MBeanServer以迎合指向远程 MBean 的代理:spring-doc.cadn.net.cn

<bean id="clientConnector"
        class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean">
    <property name="serviceUrl" value="service:jmx:rmi://remotehost:9875"/>
</bean>

<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
    <property name="objectName" value="bean:name=testBean"/>
    <property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/>
    <property name="server" ref="clientConnector"/>
</bean>

在前面的示例中,我们创建了一个MBeanServerConnection指向远程计算机使用MBeanServerConnectionFactoryBean. 这MBeanServerConnection是,则传递给MBeanProxyFactoryBean通过server财产。 代理是created 将所有调用转发给MBeanServer通过这个MBeanServerConnection.spring-doc.cadn.net.cn

5.6. 通知

Spring 的 JMX 产品包括对 JMX 通知的全面支持。spring-doc.cadn.net.cn

5.6.1. 注册通知的侦听器

Spring 的 JMX 支持可以轻松注册任意数量的NotificationListeners具有任意数量的 MBean(这包括由 Spring的MBeanExporter以及通过其他机制注册的 MBean)。为 例如,考虑希望获得通知(通过Notification) 每次目标 MBean 的属性发生变化时。以下内容 示例将通知写入控制台:spring-doc.cadn.net.cn

package com.example;

import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;

public class ConsoleLoggingNotificationListener
        implements NotificationListener, NotificationFilter {

    public void handleNotification(Notification notification, Object handback) {
        System.out.println(notification);
        System.out.println(handback);
    }

    public boolean isNotificationEnabled(Notification notification) {
        return AttributeChangeNotification.class.isAssignableFrom(notification.getClass());
    }

}

以下示例将ConsoleLoggingNotificationListener(在前面的示例中定义)设置为notificationListenerMappings:spring-doc.cadn.net.cn

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean1" value-ref="testBean"/>
            </map>
        </property>
        <property name="notificationListenerMappings">
            <map>
                <entry key="bean:name=testBean1">
                    <bean class="com.example.ConsoleLoggingNotificationListener"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

有了上述配置,每次 JMXNotification广播来自 目标 MBean (bean:name=testBean1)、ConsoleLoggingNotificationListener豆 通过notificationListenerMappings属性是 通知。这ConsoleLoggingNotificationListener然后 bean 可以采取任何作 它认为适当地回应了Notification.spring-doc.cadn.net.cn

您还可以使用直接的 bean 名称作为导出的 bean 和监听器之间的链接, 如以下示例所示:spring-doc.cadn.net.cn

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean1" value-ref="testBean"/>
            </map>
        </property>
        <property name="notificationListenerMappings">
            <map>
                <entry key="testBean">
                    <bean class="com.example.ConsoleLoggingNotificationListener"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

如果您想注册单个NotificationListener实例 封闭MBeanExporterexports,您可以使用特殊的通配符 () 作为*notificationListenerMappings财产 map,如以下示例所示:spring-doc.cadn.net.cn

<property name="notificationListenerMappings">
    <map>
        <entry key="*">
            <bean class="com.example.ConsoleLoggingNotificationListener"/>
        </entry>
    </map>
</property>

如果您需要执行相反的作(即,注册多个不同的侦听器 MBean),您必须改用notificationListenerslist 属性(在 优先于notificationListenerMappings属性)。这一次,而不是 配置NotificationListener对于单个 MBean,我们配置NotificationListenerBean实例。一个NotificationListenerBean封装一个NotificationListenerObjectName(或ObjectNames)它应该成为 在MBeanServer.这NotificationListenerBean还封装 许多其他属性,例如NotificationFilter和任意回手 对象,可用于高级 JMX 通知场景。spring-doc.cadn.net.cn

使用时的配置NotificationListenerBean实例并不疯狂 与前面介绍的内容不同,如以下示例所示:spring-doc.cadn.net.cn

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean1" value-ref="testBean"/>
            </map>
        </property>
        <property name="notificationListeners">
            <list>
                <bean class="org.springframework.jmx.export.NotificationListenerBean">
                    <constructor-arg>
                        <bean class="com.example.ConsoleLoggingNotificationListener"/>
                    </constructor-arg>
                    <property name="mappedObjectNames">
                        <list>
                            <value>bean:name=testBean1</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

前面的示例等效于第一个通知示例。那么,假设 我们希望每次Notification被提出并且 我们还想过滤掉无关的Notifications通过提供NotificationFilter.以下示例实现了这些目标:spring-doc.cadn.net.cn

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean1" value-ref="testBean1"/>
                <entry key="bean:name=testBean2" value-ref="testBean2"/>
            </map>
        </property>
        <property name="notificationListeners">
            <list>
                <bean class="org.springframework.jmx.export.NotificationListenerBean">
                    <constructor-arg ref="customerNotificationListener"/>
                    <property name="mappedObjectNames">
                        <list>
                            <!-- handles notifications from two distinct MBeans -->
                            <value>bean:name=testBean1</value>
                            <value>bean:name=testBean2</value>
                        </list>
                    </property>
                    <property name="handback">
                        <bean class="java.lang.String">
                            <constructor-arg value="This could be anything..."/>
                        </bean>
                    </property>
                    <property name="notificationFilter" ref="customerNotificationListener"/>
                </bean>
            </list>
        </property>
    </bean>

    <!-- implements both the NotificationListener and NotificationFilter interfaces -->
    <bean id="customerNotificationListener" class="com.example.ConsoleLoggingNotificationListener"/>

    <bean id="testBean1" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

    <bean id="testBean2" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="ANOTHER TEST"/>
        <property name="age" value="200"/>
    </bean>

</beans>

(全面讨论什么是回手对象,以及, 确实,多么好NotificationFilter是,请参阅 JMX 的部分 标题为“JMX 通知模型”的规范 (1.2)。spring-doc.cadn.net.cn

5.6.2. 发布通知

Spring 不仅支持注册接收Notifications而且还 用于发布Notifications.spring-doc.cadn.net.cn

本节实际上只与具有 通过MBeanExporter.任何现有的用户定义 MBean 都应 使用标准 JMX API 进行通知发布。

Spring 的 JMX 通知发布支持中的关键接口是NotificationPublisher接口(在org.springframework.jmx.export.notification包)。任何将要成为的豆子 通过MBeanExporter实例可以实现相关的NotificationPublisherAware接口以访问NotificationPublisher实例。这NotificationPublisherAware接口提供NotificationPublisher通过简单的 setter 方法到实现 bean, 然后 Bean 可以使用它来发布Notifications.spring-doc.cadn.net.cn

如 javadoc 中所述NotificationPublisher接口,托管 Bean 通过NotificationPublisher机制不负责通知监听器的状态管理。Spring 的 JMX 支持负责处理所有 JMX 基础设施问题。作为应用程序开发人员,您需要做的就是实现NotificationPublisherAware接口,然后使用 提供NotificationPublisher实例。 请注意,NotificationPublisher在向MBeanServer.spring-doc.cadn.net.cn

使用NotificationPublisher实例非常简单。你创建一个 JMXNotification实例(或适当的Notification子类)、使用与要published 的事件相关的数据填充通知,并调用sendNotification(Notification)NotificationPublisher实例,传入Notification.spring-doc.cadn.net.cn

在以下示例中,导出的JmxTestBean发布一个NotificationEvent每次add(int, int)作被调用:spring-doc.cadn.net.cn

package org.springframework.jmx;

import org.springframework.jmx.export.notification.NotificationPublisherAware;
import org.springframework.jmx.export.notification.NotificationPublisher;
import javax.management.Notification;

public class JmxTestBean implements IJmxTestBean, NotificationPublisherAware {

    private String name;
    private int age;
    private boolean isSuperman;
    private NotificationPublisher publisher;

    // other getters and setters omitted for clarity

    public int add(int x, int y) {
        int answer = x + y;
        this.publisher.sendNotification(new Notification("add", this, 0));
        return answer;
    }

    public void dontExposeMe() {
        throw new RuntimeException();
    }

    public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
        this.publisher = notificationPublisher;
    }

}

NotificationPublisher界面和让它全部工作的机制是其中之一 Spring 的 JMX 支持的更好功能。然而,它确实带有 将您的类耦合到 Spring 和 JMX。与往常一样,这里的建议是 务实。如果您需要NotificationPublisher和 您可以接受与 Spring 和 JMX 的耦合,然后这样做。spring-doc.cadn.net.cn

5.7. 更多资源

本节包含指向有关 JMX 的更多资源的链接:spring-doc.cadn.net.cn

6. 电子邮件

本节介绍如何使用 Spring Framework 发送电子邮件。spring-doc.cadn.net.cn

库依赖项

以下 JAR 需要位于应用程序的类路径上才能使用 Spring Framework 的电子邮件支持:spring-doc.cadn.net.cn

该库可在 Web 上免费获得 - 例如,在 Maven Central 中作为com.sun.mail:jakarta.mail.请确保使用最新的 1.6.x 版本(使用 这javax.mailpackage 命名空间)而不是 Jakarta Mail 2.0(使用jakarta.mailpackage 命名空间)。请参阅v1.xJakarta Mail API 存储库的分支。spring-doc.cadn.net.cn

Spring Framework 提供了一个有用的实用程序库,用于发送屏蔽的电子邮件 您从底层邮件系统的具体情况中了解并负责 代表客户端进行低级资源处理。spring-doc.cadn.net.cn

org.springframework.mailpackage 是 Spring 的根级包 Framework 的电子邮件支持。发送电子邮件的中央界面是MailSender接口。一个简单的值对象,用于封装简单邮件的属性,例如 如fromto(以及许多其他)是SimpleMailMessage类。这个包 还包含已检查异常的层次结构,这些异常提供更高级别的 对较低级别邮件系统异常的抽象,根异常是MailException.有关富邮件异常层次结构的更多信息,请参阅 javadocspring-doc.cadn.net.cn

org.springframework.mail.javamail.JavaMailSender界面添加专用 JavaMail 功能,例如 MIME 消息支持MailSender接口 (它继承自它)。JavaMailSender还提供了一个名为org.springframework.mail.javamail.MimeMessagePreparator用于准备MimeMessage.spring-doc.cadn.net.cn

6.1. 用法

假设我们有一个名为OrderManager,如以下示例所示:spring-doc.cadn.net.cn

public interface OrderManager {

    void placeOrder(Order order);

}

进一步假设我们有一个要求,规定带有 需要生成订单号并将其发送给下相关订单的客户。spring-doc.cadn.net.cn

6.1.1. 基本MailSenderSimpleMailMessage用法

以下示例演示如何使用MailSenderSimpleMailMessage发送 当有人下订单时发送电子邮件:spring-doc.cadn.net.cn

import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;

public class SimpleOrderManager implements OrderManager {

    private MailSender mailSender;
    private SimpleMailMessage templateMessage;

    public void setMailSender(MailSender mailSender) {
        this.mailSender = mailSender;
    }

    public void setTemplateMessage(SimpleMailMessage templateMessage) {
        this.templateMessage = templateMessage;
    }

    public void placeOrder(Order order) {

        // Do the business calculations...

        // Call the collaborators to persist the order...

        // Create a thread safe "copy" of the template message and customize it
        SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
        msg.setTo(order.getCustomer().getEmailAddress());
        msg.setText(
            "Dear " + order.getCustomer().getFirstName()
                + order.getCustomer().getLastName()
                + ", thank you for placing order. Your order number is "
                + order.getOrderNumber());
        try {
            this.mailSender.send(msg);
        }
        catch (MailException ex) {
            // simply log it and go on...
            System.err.println(ex.getMessage());
        }
    }

}

以下示例显示了上述代码的 Bean 定义:spring-doc.cadn.net.cn

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host" value="mail.mycompany.example"/>
</bean>

<!-- this is a template message that we can pre-load with default state -->
<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
    <property name="from" value="[email protected]"/>
    <property name="subject" value="Your order"/>
</bean>

<bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager">
    <property name="mailSender" ref="mailSender"/>
    <property name="templateMessage" ref="templateMessage"/>
</bean>

6.1.2. 使用JavaMailSenderMimeMessagePreparator

本节介绍OrderManager使用MimeMessagePreparator回调接口。在以下示例中,mailSender属性的类型为JavaMailSender以便我们能够使用 JavaMailMimeMessage类:spring-doc.cadn.net.cn

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import javax.mail.internet.MimeMessage;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessagePreparator;

public class SimpleOrderManager implements OrderManager {

    private JavaMailSender mailSender;

    public void setMailSender(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }

    public void placeOrder(final Order order) {
        // Do the business calculations...
        // Call the collaborators to persist the order...

        MimeMessagePreparator preparator = new MimeMessagePreparator() {
            public void prepare(MimeMessage mimeMessage) throws Exception {
                mimeMessage.setRecipient(Message.RecipientType.TO,
                        new InternetAddress(order.getCustomer().getEmailAddress()));
                mimeMessage.setFrom(new InternetAddress("[email protected]"));
                mimeMessage.setText("Dear " + order.getCustomer().getFirstName() + " " +
                        order.getCustomer().getLastName() + ", thanks for your order. " +
                        "Your order number is " + order.getOrderNumber() + ".");
            }
        };

        try {
            this.mailSender.send(preparator);
        }
        catch (MailException ex) {
            // simply log it and go on...
            System.err.println(ex.getMessage());
        }
    }

}
邮件代码是一个跨领域问题,很可能成为 重构为自定义的 Spring AOP 方面,然后可以 在OrderManager目标。

Spring Framework 的邮件支持随标准 JavaMail 实现一起提供。 有关更多信息,请参阅相关的 javadoc。spring-doc.cadn.net.cn

6.2. 使用 JavaMailMimeMessageHelper

在处理 JavaMail 消息时非常方便的类是org.springframework.mail.javamail.MimeMessageHelper,这可以保护您免受 必须使用详细的 JavaMail API。使用MimeMessageHelper是的 创建MimeMessage,如以下示例所示:spring-doc.cadn.net.cn

// of course you would use DI in any real-world cases
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setTo("[email protected]");
helper.setText("Thank you for ordering!");

sender.send(message);

6.2.1. 发送附件和内联资源

多部分电子邮件允许附件和内联资源。示例 内联资源包括要在邮件中使用的图像或样式表,但 您不希望显示为附件。spring-doc.cadn.net.cn

附件

以下示例向您展示如何使用MimeMessageHelper发送电子邮件 使用单个 JPEG 图像附件:spring-doc.cadn.net.cn

JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();

// use the true flag to indicate you need a multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("[email protected]");

helper.setText("Check out this image!");

// let's attach the infamous windows Sample file (this time copied to c:/)
FileSystemResource file = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addAttachment("CoolImage.jpg", file);

sender.send(message);
内联资源

以下示例向您展示如何使用MimeMessageHelper发送电子邮件 使用内联图像:spring-doc.cadn.net.cn

JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();

// use the true flag to indicate you need a multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("[email protected]");

// use the true flag to indicate the text included is HTML
helper.setText("<html><body><img src='cid:identifier1234'></body></html>", true);

// let's include the infamous windows Sample file (this time copied to c:/)
FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addInline("identifier1234", res);

sender.send(message);
内联资源将添加到MimeMessage通过使用指定的Content-ID (identifier1234在上面的例子中)。添加文本的顺序 资源非常重要。请务必先添加文本,然后再添加 资源。如果你反过来做,它就行不通了。

6.2.2. 使用模板库创建电子邮件内容

前面部分中所示的示例中的代码显式创建了电子邮件的内容 通过使用诸如message.setText(..).这对于简单的情况来说很好,而且它 在上述示例的上下文中是可以的,其目的是向您展示 API 的基础知识。spring-doc.cadn.net.cn

但是,在典型的企业应用程序中,开发人员通常不会创建内容 出于多种原因,使用前面显示的方法的电子邮件:spring-doc.cadn.net.cn

通常,解决这些问题所采取的方法是使用模板库(例如 作为 FreeMarker)来定义电子邮件内容的显示结构。这将留下您的代码 仅负责创建要在电子邮件模板中呈现的数据,并且 发送电子邮件。当您的电子邮件内容 甚至变得中等复杂,并且,使用 Spring Framework 的支持类 FreeMarker,它变得很容易做到。spring-doc.cadn.net.cn

7. 任务执行和调度

Spring Framework 为异步执行和调度提供了抽象 任务与TaskExecutorTaskScheduler接口。Spring也 功能 支持线程池或委托给 CommonJ 在应用程序服务器环境中。最终,这些 通用接口背后的实现抽象化了 Java 之间的差异 SE 5、Java SE 6 和 Java EE 环境。spring-doc.cadn.net.cn

Spring 还具有集成类,以支持使用Timer(从 1.3 开始成为 JDK 的一部分)和 Quartz 调度程序 ( https://www.quartz-scheduler.org/)。 您可以使用FactoryBean带有可选的引用TimerTrigger实例。此外,为两者提供便利班 Quartz 调度程序和Timer可用,允许您调用 现有目标对象(类似于法线MethodInvokingFactoryBean作)。spring-doc.cadn.net.cn

7.1. SpringTaskExecutor抽象化

执行器是线程池概念的 JDK 名称。“执行者”命名是 由于无法保证底层实现是 实际上是一个游泳池。执行器可以是单线程的,甚至可以是同步的。Spring的 抽象隐藏了 Java SE 和 Java EE 环境之间的实现细节。spring-doc.cadn.net.cn

Spring的TaskExecutor接口与java.util.concurrent.Executor接口。事实上,最初,它存在的主要原因是抽象化 使用线程池时需要 Java 5。该接口具有单一方法 (execute(Runnable task))接受任务执行,基于语义 以及线程池的配置。spring-doc.cadn.net.cn

TaskExecutor最初是为了给其他 Spring 组件一个抽象而创建的 用于在需要时进行线程池。组件,例如ApplicationEventMulticaster, JMS 的AbstractMessageListenerContainer,和 Quartz 集成都使用TaskExecutor抽象到池线程。但是,如果您的 Bean 需要线程池 行为,你也可以根据自己的需要使用这个抽象。spring-doc.cadn.net.cn

7.1.1.TaskExecutor类型

Spring 包括许多预构建的TaskExecutor. 很可能,您永远不需要实现自己的。 Spring提供的变体如下:spring-doc.cadn.net.cn

  • SyncTaskExecutor: 此实现不会异步运行调用。相反,每个 调用发生在调用线程中。它主要用于以下情况 不需要多线程的地方,例如在简单的测试用例中。spring-doc.cadn.net.cn

  • SimpleAsyncTaskExecutor: 此实现不会重用任何线程。相反,它启动了一个新线程 对于每个调用。但是,它确实支持阻止的并发限制 在释放槽位之前超过限制的任何调用。如果你 正在寻找真正的池化,请参阅ThreadPoolTaskExecutor,在此列表的后面。spring-doc.cadn.net.cn

  • ConcurrentTaskExecutor: 此实现是java.util.concurrent.Executor实例。 还有另一种选择(ThreadPoolTaskExecutor)公开Executor配置参数作为 bean 属性。很少需要使用ConcurrentTaskExecutor径直。但是,如果ThreadPoolTaskExecutor莫 足够灵活,满足您的需求,ConcurrentTaskExecutor是一种替代方案。spring-doc.cadn.net.cn

  • ThreadPoolTaskExecutor: 这种实现是最常用的。它公开了配置一个java.util.concurrent.ThreadPoolExecutor并将其包装在TaskExecutor. 如果您需要适应不同类型的java.util.concurrent.Executor我们 建议使用ConcurrentTaskExecutor相反。spring-doc.cadn.net.cn

  • WorkManagerTaskExecutor: 此实现使用 CommonJWorkManager作为其支持服务提供者并且是用于设置基于 CommonJ 的线程池的中央便利类在 Spring 应用程序上下文中的 WebLogic 或 WebSphere 上集成。spring-doc.cadn.net.cn

  • DefaultManagedTaskExecutor: 此实现使用 JNDI 获取的ManagedExecutorService在 JSR-236 中 兼容的运行时环境(例如 Java EE 7+ 应用程序服务器), 为此目的替换 CommonJ WorkManager。spring-doc.cadn.net.cn

7.1.2. 使用TaskExecutor

Spring的TaskExecutor实现用作简单的 JavaBeans。在以下示例中, 我们定义了一个使用ThreadPoolTaskExecutor异步打印 发出一组消息:spring-doc.cadn.net.cn

import org.springframework.core.task.TaskExecutor;

public class TaskExecutorExample {

    private class MessagePrinterTask implements Runnable {

        private String message;

        public MessagePrinterTask(String message) {
            this.message = message;
        }

        public void run() {
            System.out.println(message);
        }
    }

    private TaskExecutor taskExecutor;

    public TaskExecutorExample(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    public void printMessages() {
        for(int i = 0; i < 25; i++) {
            taskExecutor.execute(new MessagePrinterTask("Message" + i));
        }
    }
}

如您所见,与其从池中检索线程并自己执行它, 您将Runnable到队列。然后,TaskExecutor使用其内部规则来 决定何时运行任务。spring-doc.cadn.net.cn

要配置规则,请TaskExecutoruses,我们公开了简单的 bean 属性:spring-doc.cadn.net.cn

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="5"/>
    <property name="maxPoolSize" value="10"/>
    <property name="queueCapacity" value="25"/>
</bean>

<bean id="taskExecutorExample" class="TaskExecutorExample">
    <constructor-arg ref="taskExecutor"/>
</bean>

7.2. SpringTaskScheduler抽象化

除了TaskExecutor抽象,Spring 3.0 引入了一个TaskScheduler使用各种方法来调度任务在未来的某个时间点运行。 以下列表显示了TaskScheduler接口定义:spring-doc.cadn.net.cn

public interface TaskScheduler {

    ScheduledFuture schedule(Runnable task, Trigger trigger);

    ScheduledFuture schedule(Runnable task, Instant startTime);

    ScheduledFuture schedule(Runnable task, Date startTime);

    ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);

    ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);

    ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);

    ScheduledFuture scheduleAtFixedRate(Runnable task, long period);

    ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);

    ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);

    ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);

    ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}

最简单的方法是名为schedule只需要一个RunnableDate. 这会导致任务在指定时间后运行一次。所有其他方法 能够安排任务重复运行。固定速率和固定延迟 方法用于简单的定期执行,但接受Trigger是 更加灵活。spring-doc.cadn.net.cn

7.2.1.Trigger接口

Triggerinterface 本质上是受到 JSR-236 的启发,从 Spring 3.0 开始, 尚未正式实施。的基本思想Trigger就是执行力 时间可以根据过去的执行结果甚至任意条件来确定。 如果这些决定确实考虑到了前一次执行的结果, 该信息可在TriggerContext.这Trigger界面本身 非常简单,如以下列表所示:spring-doc.cadn.net.cn

public interface Trigger {

    Date nextExecutionTime(TriggerContext triggerContext);
}

TriggerContext是最重要的部分。它封装了所有 相关数据,并在必要时在未来开放扩展。这TriggerContext是一个接口(一个SimpleTriggerContext实现由 默认)。以下列表显示了以下可用方法Trigger实现。spring-doc.cadn.net.cn

public interface TriggerContext {

    Date lastScheduledExecutionTime();

    Date lastActualExecutionTime();

    Date lastCompletionTime();
}

7.2.2.Trigger实现

Spring 提供了两个Trigger接口。最有趣的一个 是CronTrigger.它支持基于 cron 表达式的任务调度。 例如,以下任务计划在每小时运行 15 分钟后运行,但仅 在工作日朝九晚五的“工作时间”内:spring-doc.cadn.net.cn

scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));

另一个实现是PeriodicTrigger接受固定的 period 、可选的初始延迟值和布尔值,用于指示 period 应解释为固定速率或固定延迟。由于TaskScheduler接口已经定义了以固定速率或 固定延迟,应尽可能直接使用这些方法。的值PeriodicTrigger实现是你可以在依赖于 这Trigger抽象化。例如,允许定期触发器可能很方便, 基于 CRON 的触发器,甚至可以互换使用的自定义触发器实现。 这样的组件可以利用依赖注入,以便你可以配置这样的Triggers外部,因此可以轻松修改或扩展它们。spring-doc.cadn.net.cn

7.2.3.TaskScheduler实现

与 Spring 的TaskExecutor抽象,的主要好处是TaskScheduler安排是应用程序的调度需求与部署分离 环境。当部署到 应用服务器环境,其中线程不应由 应用程序本身。对于此类场景,Spring 提供了一个TimerManagerTaskScheduler将代表TimerManager在 WebLogic 或 WebSphere 上以及最近的DefaultManagedTaskScheduler委托给 JSR-236ManagedScheduledExecutorService在 Java EE 7+ 环境中。两者通常都配置了 JNDI 查找。spring-doc.cadn.net.cn

每当不需要外部线程管理时,一个更简单的替代方法是本地ScheduledExecutorService应用程序中的设置,可以调整通过 Spring 的ConcurrentTaskScheduler. 为了方便起见,Spring 还提供了一个ThreadPoolTaskScheduler,它在内部委托给ScheduledExecutorService提供通用的 bean 样式配置,沿着ThreadPoolTaskExecutor. 这些变体非常适合宽松的本地嵌入线程池设置应用程序服务器环境,尤其是在 Tomcat 和 Jetty 上。spring-doc.cadn.net.cn

7.3. 对调度和异步执行的注释支持

Spring 为任务调度和异步方法提供注释支持 执行。spring-doc.cadn.net.cn

7.3.1. 启用调度注释

启用对@Scheduled@Async注释,您可以添加@EnableScheduling@EnableAsync给你的一个@Configuration类,如以下示例所示:spring-doc.cadn.net.cn

@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}

您可以为您的应用程序选择相关的注释。 例如 如果您只需要支持@Scheduled,您可以省略@EnableAsync. 如需更多细粒度控制,您可以额外实现SchedulingConfigurer接口,则AsyncConfigurer接口,或两者。请参阅SchedulingConfigurerAsyncConfigurerjavadoc 获取完整详细信息。spring-doc.cadn.net.cn

如果您更喜欢 XML 配置,可以使用<task:annotation-driven>元素 如以下示例所示:spring-doc.cadn.net.cn

<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>

请注意,在前面的 XML 中,提供了一个执行器引用来处理那些任务,这些任务对应于具有@Async注释,调度程序提供了用于管理那些用@Scheduled.spring-doc.cadn.net.cn

处理的默认通知方式@Async注释是proxy这允许仅用于通过代理拦截呼叫。同一类中的本地调用不会以这种方式被拦截。对于更高级的拦截模式,请考虑切换到aspectj模式与编译时或加载时编织相结合。

7.3.2.@Scheduled注解

您可以添加@Scheduled注释,以及触发器元数据。 为 例如,以下方法每五秒(5000 毫秒)调用一次,并具有固定延迟,这意味着该周期是从每个前一个调用的完成时间开始测量的。spring-doc.cadn.net.cn

@Scheduled(fixedDelay = 5000)
public void doSomething() {
    // something that should run periodically
}

默认情况下,毫秒将用作固定延迟、固定速率和初始延迟值的时间单位。如果您想使用不同的时间单位,例如秒或分钟,您可以通过timeUnit属性@Scheduled.spring-doc.cadn.net.cn

例如,前面的例子也可以写成这样。spring-doc.cadn.net.cn

@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
    // something that should run periodically
}

如果您需要固定速率执行,可以使用fixedRate属性 注解。以下方法每五秒调用一次(在 每个调用的连续开始时间)。spring-doc.cadn.net.cn

@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
    // something that should run periodically
}

对于固定延迟和固定速率任务,您可以通过指示 在首次执行该方法之前等待的时间量,如下所示fixedRate示例显示。spring-doc.cadn.net.cn

@Scheduled(initialDelay = 1000, fixedRate = 5000)
public void doSomething() {
    // something that should run periodically
}

如果简单的周期性调度不够表达,你可以提供一个 cron 表达式。 以下示例仅在工作日运行:spring-doc.cadn.net.cn

@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
    // something that should run on weekdays only
}
您还可以使用zone属性来指定 cron 所在的时区 表达式已解析。

请注意,要调度的方法必须具有 void 返回,并且不得接受任何 参数。如果该方法需要与应用程序中的其他对象交互 上下文,这些通常是通过依赖注入提供的。spring-doc.cadn.net.cn

从 Spring Framework 4.3 开始,@Scheduled任何范围的 bean 都支持方法。spring-doc.cadn.net.cn

确保您没有初始化同一实例的多个实例@Scheduledannotation 类,除非你确实想调度对每个此类类的回调 实例。与此相关,请确保您不要使用@Configurable在豆上 用@Scheduled并注册为普通春豆 与容器。否则,您将获得双重初始化(一次通过 容器,并通过@Configurable方面),以及每个@Scheduled方法被调用两次。spring-doc.cadn.net.cn

7.3.3.@Async注解

您可以提供@Async注释,以便调用该方法 异步发生。换句话说,调用方在 调用,而该方法的实际执行发生在已 提交给弹簧TaskExecutor.在最简单的情况下,您可以应用注释 到返回void,如以下示例所示:spring-doc.cadn.net.cn

@Async
void doSomething() {
    // this will be run asynchronously
}

与用@Scheduled注释,这些方法可以期待 参数,因为它们是由调用者在运行时以“正常”方式调用的,而不是 而不是从容器管理的计划任务。例如,以下代码是 合法应用@Async注解:spring-doc.cadn.net.cn

@Async
void doSomething(String s) {
    // this will be run asynchronously
}

即使是返回值的方法也可以异步调用。然而,这样的方法 需要有一个Future-typed 返回值。这仍然提供了以下好处 异步执行,以便调用方可以在调用之前执行其他任务get()关于这一点Future.以下示例演示如何使用@Async在方法上 返回一个值:spring-doc.cadn.net.cn

@Async
Future<String> returnSomething(int i) {
    // this will be run asynchronously
}
@Async方法不仅可以声明一个常规的java.util.concurrent.Future返回类型还有 Spring 的org.springframework.util.concurrent.ListenableFuture或者,从 Spring 开始4.2,JDK 8 的java.util.concurrent.CompletableFuture,以便与异步任务进行更丰富的交互,并立即组合进一步的处理步骤。

你不能使用@Async结合生命周期回调,例如@PostConstruct. 要异步初始化 Spring Bean,您当前必须使用一个单独的初始化 Spring Bean,然后调用@Asyncannotated 方法,如以下示例所示:spring-doc.cadn.net.cn

public class SampleBeanImpl implements SampleBean {

    @Async
    void doSomething() {
        // ...
    }

}

public class SampleBeanInitializer {

    private final SampleBean bean;

    public SampleBeanInitializer(SampleBean bean) {
        this.bean = bean;
    }

    @PostConstruct
    public void initialize() {
        bean.doSomething();
    }

}
没有直接的 XML 等效项@Async,因为此类方法应该设计用于异步执行,而不是在外部重新声明为异步。但是,您可以手动设置 Spring 的AsyncExecutionInterceptor与 Spring AOP 一起使用,与自定义切入点相结合。

7.3.4. 执行者资格@Async

默认情况下,当指定@Async在方法上,使用的执行器是 启用异步支持时配置的 即“注释驱动”元素,如果您使用的是 XML 或AsyncConfigurer实施(如果有)。但是,您可以使用value属性的@Async注释,当您需要指示默认值以外的执行器应该是 在执行给定方法时使用。以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@Async("otherExecutor")
void doSomething(String s) {
    // this will be run asynchronously by "otherExecutor"
}

在这种情况下,"otherExecutor"可以是任何ExecutorSpring的豆子 容器,或者它可以是与任何Executor(例如,作为 使用<qualifier>元素或 Spring 的@Qualifier注释)。spring-doc.cadn.net.cn

7.3.5. 使用@Async

@Async方法有一个Future-typed 返回值,易于管理 在方法执行期间抛出的异常,因为此异常是 调用时抛出getFuture结果。使用void返回类型, 但是,异常未捕获且无法传输。您可以提供AsyncUncaughtExceptionHandler来处理此类异常。以下示例显示 如何做到这一点:spring-doc.cadn.net.cn

public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        // handle exception
    }
}

默认情况下,仅记录异常。您可以定义自定义AsyncUncaughtExceptionHandler通过使用AsyncConfigurer<task:annotation-driven/>XML 元素。spring-doc.cadn.net.cn

7.4.taskNamespace

从 3.0 版开始,Spring 包括一个用于配置 XML 命名空间TaskExecutorTaskScheduler实例。它还提供了一种将任务配置为 使用触发器调度。spring-doc.cadn.net.cn

7.4.1. 'scheduler' 元素

以下元素创建了一个ThreadPoolTaskScheduler实例与 指定的线程池大小:spring-doc.cadn.net.cn

<task:scheduler id="scheduler" pool-size="10"/>

id属性用作线程名称的前缀 在游泳池内。这scheduler元素相对简单。如果您不这样做 提供一个pool-size属性,则默认线程池只有一个线程。 调度程序没有其他配置选项。spring-doc.cadn.net.cn

7.4.2.executor元素

下面创建一个ThreadPoolTaskExecutor实例:spring-doc.cadn.net.cn

<task:executor id="executor" pool-size="10"/>

上一节中显示的调度程序一样, 为id属性用作线程名称的前缀 游泳池。就池大小而言,executor元素支持更多 配置选项而不是scheduler元素。一方面,线程池 一个ThreadPoolTaskExecutor本身更具可配置性。不仅仅是单一尺寸, 执行器的线程池可以有不同的核心值和最大大小。 如果您提供单个值,则执行器具有一个固定大小的线程池(核心和 最大尺寸相同)。但是,executor元素的pool-size属性也 接受以下形式的范围min-max.以下示例将最小值设置为5最大值25:spring-doc.cadn.net.cn

<task:executor
        id="executorWithPoolSizeRange"
        pool-size="5-25"
        queue-capacity="100"/>

在前面的配置中,queue-capacity还提供了价值。 线程池的配置还应根据 执行器的队列容量。有关池之间关系的完整描述 大小和队列容量,请参阅文档ThreadPoolExecutor. 主要思想是,当任务提交时,执行器首先尝试使用 如果活动线程数当前小于核心大小,则为空闲线程。 如果已达到核心大小,则任务将添加到队列中,只要其 尚未达到容量。只有这样,如果队列的容量 reached,执行器是否创建超出核心大小的新线程。如果最大尺寸 已到达,则执行器拒绝该任务。spring-doc.cadn.net.cn

默认情况下,队列是无界的,但这很少是所需的配置, 因为它会导致OutOfMemoryErrors如果将足够的任务添加到该队列中,而 所有池线程都处于繁忙状态。此外,如果队列是无界的,则最大大小为 完全没有效果。由于执行器总是在创建新的队列之前尝试队列 线程超过核心大小,则队列必须具有有限的容量,线程池才能 增长超过核心大小(这就是为什么固定大小的池是唯一合理的情况 使用无界队列时)。spring-doc.cadn.net.cn

考虑如上所述,当任务被拒绝时。默认情况下,当 任务被拒绝时,线程池执行器会抛出TaskRejectedException.然而 拒绝策略实际上是可配置的。使用 默认拒绝策略,即AbortPolicy实现。 对于在重负载下可以跳过某些任务的应用程序,您可以改为 配置DiscardPolicyDiscardOldestPolicy.另一种有效的选择 对于需要在重负载下限制提交任务的应用程序来说,是 这CallerRunsPolicy.而不是抛出异常或丢弃任务, 该策略强制调用 submit 方法的线程运行任务本身。 这个想法是,这样的调用方在运行该任务时很忙,无法提交 其他任务立即完成。因此,它提供了一种简单的方法来限制传入的 load 同时保持线程池和队列的限制。通常,这允许 执行器“赶上”它正在处理的任务,从而释放一些 队列和/或池中的容量。您可以从 可用于rejection-policy属性executor元素。spring-doc.cadn.net.cn

以下示例显示了executor元素,其中包含要指定的多个属性 各种行为:spring-doc.cadn.net.cn

<task:executor
        id="executorWithCallerRunsPolicy"
        pool-size="5-25"
        queue-capacity="100"
        rejection-policy="CALLER_RUNS"/>

最后,keep-alive设置确定线程的时间限制(以秒为单位) 在停止之前可能会保持空闲状态。如果线程数超过核心数 当前在池中,在等待此时间而不处理任务后,超出 线程被停止。时间值为零会导致多余的线程停止 在执行任务后立即执行任务,任务队列中没有剩余的后续工作。 以下示例将keep-alive值设置为两分钟:spring-doc.cadn.net.cn

<task:executor
        id="executorWithKeepAlive"
        pool-size="5-25"
        keep-alive="120"/>

7.4.3. 'scheduled-tasks' 元素

Spring 的任务命名空间最强大的特性是支持配置 在 Spring Application Context 中调度的任务。这遵循一种方法 类似于 Spring 中的其他“方法调用器”,例如 JMS 命名空间提供的 用于配置消息驱动的 POJO。基本上,一个ref属性可以指向任何 Spring 管理的对象,以及method属性提供要成为的方法的名称 调用。以下列表显示了一个简单的示例:spring-doc.cadn.net.cn

<task:scheduled-tasks scheduler="myScheduler">
    <task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/>
</task:scheduled-tasks>

<task:scheduler id="myScheduler" pool-size="10"/>

调度器由外部元素引用,每个单独的 任务包括其触发器元数据的配置。在前面的示例中,该 metadata 定义了一个具有固定延迟的周期性触发器,指示 在每个任务执行完成后等待毫秒。另一种选择是fixed-rate,指示该方法应运行多久,而不管运行时间长短 任何先前的执行都需要。此外,对于两者fixed-delayfixed-ratetasks,您可以指定 'initial-delay' 参数,表示等待的毫秒数 在首次执行该方法之前。为了获得更多控制,您可以改为提供cron属性 提供 cron 表达式。 以下示例显示了这些其他选项:spring-doc.cadn.net.cn

<task:scheduled-tasks scheduler="myScheduler">
    <task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/>
    <task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/>
    <task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/>
</task:scheduled-tasks>

<task:scheduler id="myScheduler" pool-size="10"/>

7.5. Cron 表达式

所有 Spring cron 表达式都必须符合相同的格式,无论您是在@Scheduled附注,task:scheduled-tasks元素, 或者其他地方。 格式良好的 cron 表达式,例如 ,由六个空格分隔的时间和日期组成 字段,每个字段都有自己的有效值范围:* * * * * *spring-doc.cadn.net.cn

 ┌───────────── second (0-59)
 │ ┌───────────── minute (0 - 59)
 │ │ ┌───────────── hour (0 - 23)
 │ │ │ ┌───────────── day of the month (1 - 31)
 │ │ │ │ ┌───────────── month (1 - 12) (or JAN-DEC)
 │ │ │ │ │ ┌───────────── day of the week (0 - 7)
 │ │ │ │ │ │          (0 or 7 is Sunday, or MON-SUN)
 │ │ │ │ │ │
 * * * * * *

有一些规则适用:spring-doc.cadn.net.cn

  • 字段可以是星号 (),它始终代表“从头到尾”。 对于“月份中的某一天”或“星期几”字段,问号 (*?) 可以代替 星号。spring-doc.cadn.net.cn

  • 逗号 (,) 用于分隔列表中的项目。spring-doc.cadn.net.cn

  • 用连字符 () 分隔的两个数字表示数字范围。指定的范围包括在内。-spring-doc.cadn.net.cn

  • 在范围(或 )后面指定数字值在该范围内的间隔。*/spring-doc.cadn.net.cn

  • 英文名称也可用于月份和星期几字段。使用特定日期或月份的前三个字母(大小写无关紧要)。spring-doc.cadn.net.cn

  • day-of-month 和 day-of-week 字段可以包含L字符,具有不同的含义spring-doc.cadn.net.cn

    • 在“月日”字段中,L代表当月的最后一天。 如果后跟负偏移量(即L-n),这意味着n每月的最后一天.spring-doc.cadn.net.cn

    • 在星期几字段中,L代表一周的最后一天。 如果以数字或三个字母的名称 (dLDDDL),这意味着一周的最后一天 (dDDD) 在当月.spring-doc.cadn.net.cn

  • day-of-month 字段可以是nW,代表每月中最近的工作日和某一天n. 如果n落在星期六,这得出了它之前的星期五。 如果n落在周日,这会产生之后的星期一,如果n1并落在 星期六(即:1W代表当月的第一个工作日)。spring-doc.cadn.net.cn

  • 如果 day-of-month 字段为LW,表示当月的最后一个工作日spring-doc.cadn.net.cn

  • 星期几字段可以是d#n(或DDD#n),代表n一周中的第 1 天d(或DDD) 在当月.spring-doc.cadn.net.cn

这里有些例子:spring-doc.cadn.net.cn

Cron 表达式 意义

0 0 * * * *spring-doc.cadn.net.cn

每天每小时的顶部spring-doc.cadn.net.cn

*/10 * * * * *spring-doc.cadn.net.cn

每十秒spring-doc.cadn.net.cn

0 0 8-10 * * *spring-doc.cadn.net.cn

每天8点、9点、10点spring-doc.cadn.net.cn

0 0 6,19 * * *spring-doc.cadn.net.cn

每天早上 6:00 和晚上 7:00spring-doc.cadn.net.cn

0 0/30 8-10 * * *spring-doc.cadn.net.cn

每天 8:00、8:30、9:00、9:30、10:00 和 10:30spring-doc.cadn.net.cn

0 0 9-17 * * MON-FRIspring-doc.cadn.net.cn

工作日朝九晚五的小时spring-doc.cadn.net.cn

0 0 0 25 DEC ?spring-doc.cadn.net.cn

每年圣诞节午夜spring-doc.cadn.net.cn

0 0 0 L * *spring-doc.cadn.net.cn

每月最后一天午夜spring-doc.cadn.net.cn

0 0 0 L-3 * *spring-doc.cadn.net.cn

每月倒数第三天午夜spring-doc.cadn.net.cn

0 0 0 * * 5Lspring-doc.cadn.net.cn

每月最后一个星期五午夜spring-doc.cadn.net.cn

0 0 0 * * THULspring-doc.cadn.net.cn

每月最后一个星期四午夜spring-doc.cadn.net.cn

0 0 0 1W * *spring-doc.cadn.net.cn

每月第一个工作日午夜spring-doc.cadn.net.cn

0 0 0 LW * *spring-doc.cadn.net.cn

每月最后一个工作日午夜spring-doc.cadn.net.cn

0 0 0 ? * 5#2spring-doc.cadn.net.cn

每月第二个星期五午夜spring-doc.cadn.net.cn

0 0 0 ? * MON#1spring-doc.cadn.net.cn

每月第一个星期一午夜spring-doc.cadn.net.cn

7.5.1. 宏

诸如0 0 * * * *人类很难解析,因此在出现错误时也很难修复。 为了提高可读性,Spring 支持以下宏,它们表示常用的序列。 您可以使用这些宏而不是六位数值,因此:@Scheduled(cron = "@hourly").spring-doc.cadn.net.cn

意义

@yearly(或@annually)spring-doc.cadn.net.cn

每年一次(0 0 0 1 1 *)spring-doc.cadn.net.cn

@monthlyspring-doc.cadn.net.cn

每月一次(0 0 0 1 * *)spring-doc.cadn.net.cn

@weeklyspring-doc.cadn.net.cn

每周一次(0 0 0 * * 0)spring-doc.cadn.net.cn

@daily(或@midnight)spring-doc.cadn.net.cn

每天一次(0 0 0 * * *),或spring-doc.cadn.net.cn

@hourlyspring-doc.cadn.net.cn

每小时一次,(0 0 * * * *)spring-doc.cadn.net.cn

7.6. 使用 Quartz 调度器

Quartz用途Trigger,JobJobDetail实现各种调度的对象 工作。有关 Quartz 背后的基本概念,请参阅 https://www.quartz-scheduler.org/。为方便起见,Spring 提供了几个 简化在基于 Spring 的应用程序中使用 Quartz 的类。spring-doc.cadn.net.cn

7.6.1. 使用JobDetailFactoryBean

QuartzJobDetail对象包含运行作业所需的所有信息。Spring 提供了一个JobDetailFactoryBean,它为 XML 配置目的提供 bean 样式属性。 请考虑以下示例:spring-doc.cadn.net.cn

<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="example.ExampleJob"/>
    <property name="jobDataAsMap">
        <map>
            <entry key="timeout" value="5"/>
        </map>
    </property>
</bean>

作业详细信息配置包含运行作业所需的所有信息 (ExampleJob). 超时在作业数据映射中指定。作业数据映射可通过JobExecutionContext(在执行时传递给您),但JobDetail还得到 其属性来自映射到作业实例属性的作业数据。因此,在下面的示例中, 这ExampleJob包含名为timeoutJobDetail是否自动应用:spring-doc.cadn.net.cn

package example;

public class ExampleJob extends QuartzJobBean {

    private int timeout;

    /**
     * Setter called after the ExampleJob is instantiated
     * with the value from the JobDetailFactoryBean (5)
     */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
        // do the actual work
    }
}

作业数据映射中的所有其他属性也可供您使用。spring-doc.cadn.net.cn

通过使用namegroup属性,您可以分别修改作业的名称和组。默认情况下,作业的名称与 bean 名称的JobDetailFactoryBean (exampleJob在上面的示例中)。

7.6.2. 使用MethodInvokingJobDetailFactoryBean

通常,您只需要在特定对象上调用一个方法。通过使用MethodInvokingJobDetailFactoryBean,您可以完全执行此作,如以下示例所示:spring-doc.cadn.net.cn

<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="exampleBusinessObject"/>
    <property name="targetMethod" value="doIt"/>
</bean>

前面的示例导致doItexampleBusinessObject方法,如以下示例所示:spring-doc.cadn.net.cn

public class ExampleBusinessObject {

    // properties and collaborators

    public void doIt() {
        // do the actual work
    }
}
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>

通过使用MethodInvokingJobDetailFactoryBean,则无需创建单行作业 这只是调用一种方法。您只需创建实际业务对象和 连接细节对象。spring-doc.cadn.net.cn

默认情况下,Quartz 作业是无状态的,导致作业可能会干扰 彼此之间。如果为同一触发器指定两个触发器JobDetail是的 可能在第一项作业完成之前,第二项作业开始。如果JobDetail类实现Stateful接口,这不会发生。第二个 作业在第一个完成之前不会开始。要使作业由MethodInvokingJobDetailFactoryBeanbe non-concurrent,则将concurrentflag 到false,如以下示例所示:spring-doc.cadn.net.cn

<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="exampleBusinessObject"/>
    <property name="targetMethod" value="doIt"/>
    <property name="concurrent" value="false"/>
</bean>
默认情况下,作业将以并发方式运行。

7.6.3. 使用触发器和SchedulerFactoryBean

我们创建了工作详细信息和工作。我们还审查了让 在特定对象上调用方法。当然,我们还需要调度 工作本身。这是通过使用触发器和SchedulerFactoryBean.几个 触发器在 Quartz 中可用,Spring 提供两个 QuartzFactoryBean具有方便默认值的实现:CronTriggerFactoryBeanSimpleTriggerFactoryBean.spring-doc.cadn.net.cn

需要安排触发器。Spring 提供了一个SchedulerFactoryBean这暴露了 触发器设置为属性。SchedulerFactoryBean使用 那些触发因素。spring-doc.cadn.net.cn

以下列表同时使用SimpleTriggerFactoryBeanCronTriggerFactoryBean:spring-doc.cadn.net.cn

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
    <!-- see the example of method invoking job above -->
    <property name="jobDetail" ref="jobDetail"/>
    <!-- 10 seconds -->
    <property name="startDelay" value="10000"/>
    <!-- repeat every 50 seconds -->
    <property name="repeatInterval" value="50000"/>
</bean>

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="exampleJob"/>
    <!-- run every morning at 6 AM -->
    <property name="cronExpression" value="0 0 6 * * ?"/>
</bean>

前面的示例设置了两个触发器,一个每 50 秒运行一次,启动延迟为 10 每天早上 6 点跑一秒。为了完成所有内容,我们需要设置SchedulerFactoryBean,如以下示例所示:spring-doc.cadn.net.cn

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger"/>
            <ref bean="simpleTrigger"/>
        </list>
    </property>
</bean>

更多属性可用于SchedulerFactoryBean,例如 作业详细信息、用于自定义 Quartz 的属性以及 Spring 提供的 JDBC DataSource。看 这SchedulerFactoryBeanjavadoc 了解更多信息。spring-doc.cadn.net.cn

SchedulerFactoryBean还识别quartz.properties类路径中的文件, 基于 Quartz 属性键,与常规 Quartz 配置一样。请注意,许多SchedulerFactoryBean设置与属性文件中的常见 Quartz 设置交互; 因此,不建议在这两个级别指定值。例如,不要将 一个“org.quartz.jobStore.class”属性,如果你打算依赖 Spring 提供的 DataSource, 或指定org.springframework.scheduling.quartz.LocalDataSourceJobStore变体,其中 是标准的成熟替代品org.quartz.impl.jdbcjobstore.JobStoreTX.

8. 缓存抽象

从 3.1 版开始,Spring Framework 支持透明地将缓存添加到 现有的 Spring 应用程序。与事务支持类似,缓存抽象允许一致地使用各种缓存解决方案 对代码的影响最小。spring-doc.cadn.net.cn

在 Spring Framework 4.1 中,缓存抽象得到了显着扩展,并提供了支持 用于 JSR-107 注释和更多自定义选项。spring-doc.cadn.net.cn

8.1. 了解缓存抽象

缓存与缓冲区

术语“缓冲区”和“缓存”往往可以互换使用。但是请注意, 它们代表不同的东西。传统上,缓冲区用作中间体 快速实体和慢速实体之间的数据临时存储。因为一方必须等待 对于另一个(影响性能),缓冲区通过允许整个 数据块,而不是小块移动。数据被写入和读取 仅从缓冲区中进行一次。此外,缓冲区对至少一方可见 那是意识到这一点的。spring-doc.cadn.net.cn

另一方面,根据定义,缓存是隐藏的,双方都不知道 缓存发生。它还提高了性能,但通过让相同的数据 快速阅读多次。spring-doc.cadn.net.cn

您可以在此处找到有关缓冲区和缓存之间差异的进一步说明。spring-doc.cadn.net.cn

从本质上讲,缓存抽象将缓存应用于 Java 方法,从而减少 基于缓存中可用信息的执行次数。也就是说,每次 调用目标方法时,抽象会应用缓存行为来检查 是否已经为给定参数调用了该方法。如果已经 调用时,将返回缓存的结果,而无需调用实际方法。 如果尚未调用该方法,则调用该方法,并缓存结果并 返回给用户,以便下次调用该方法时,缓存的结果为 返回。这样,只能调用昂贵的方法(无论是 CPU 还是 IO 绑定的) 一次给定的参数集,结果重用,而不必实际 再次调用该方法。缓存逻辑是透明应用的,没有任何 干扰调用者。spring-doc.cadn.net.cn

此方法仅适用于保证返回相同 给定输入(或参数)的输出(结果),无论它被调用多少次。

缓存抽象提供了其他与缓存相关的作,例如 以更新缓存的内容或删除一个或所有条目。如果出现以下情况,这些非常有用 缓存处理在应用程序过程中可能更改的数据。spring-doc.cadn.net.cn

与 Spring Framework 中的其他服务一样,缓存服务是一个抽象 (不是缓存实现),并且需要使用实际存储来存储缓存数据——也就是说,抽象使您不必编写缓存逻辑,但不会 提供实际的数据存储。这种抽象由org.springframework.cache.Cacheorg.springframework.cache.CacheManager接口。spring-doc.cadn.net.cn

Spring 提供了该抽象的一些实现: JDK的java.util.concurrent.ConcurrentMap基于缓存、Ehcache 2.x、 Gemfire 缓存、咖啡因和 JSR-107 兼容缓存(例如 Ehcache 3.x)。有关详细信息,请参阅插入不同的后端缓存 插入其他缓存存储和提供程序。spring-doc.cadn.net.cn

缓存抽象对多线程和 多进程环境,因为此类功能由缓存实现处理。

如果您有一个多进程环境(即部署在多个节点上的应用程序), 您需要相应地配置缓存提供程序。根据您的用例,副本 多个节点上的相同数据就足够了。但是,如果您在 在应用程序过程中,您可能需要启用其他传播机制。spring-doc.cadn.net.cn

缓存特定项直接等效于典型的 get-if-not-found-then-proceed-and-put-eventually 代码块 通过编程缓存交互找到。 不应用锁,多个线程可能会尝试并发加载同一项。 这同样适用于驱逐。如果多个线程正在尝试更新或逐出数据 同时,您可以使用过时的数据。某些缓存提供商提供高级功能 在那个地区。有关更多详细信息,请参阅缓存提供程序的文档。spring-doc.cadn.net.cn

要使用缓存抽象,您需要注意两个方面:spring-doc.cadn.net.cn

8.2. 基于声明性注释的缓存

对于缓存声明,Spring 的缓存抽象提供了一组 Java 注释:spring-doc.cadn.net.cn

8.2.1.@Cacheable注解

顾名思义,您可以使用@Cacheable划分可缓存的方法,即结果存储在缓存中的方法,以便在后续 调用(具有相同的参数),则返回缓存中的值,而没有 必须实际调用该方法。在最简单的形式中,注释声明 需要与带注释的方法关联的缓存的名称,如下所示 示例显示:spring-doc.cadn.net.cn

@Cacheable("books")
public Book findBook(ISBN isbn) {...}

在前面的代码段中,findBook方法与名为books. 每次调用该方法时,都会检查缓存以查看调用是否具有 已经运行,不必重复。而在大多数情况下,只有一个 cache 声明时,注释允许指定多个名称,以便多个 缓存正在使用中。在这种情况下,在调用 method — 如果至少命中一个缓存,则返回关联值。spring-doc.cadn.net.cn

所有其他不包含该值的缓存也会更新,即使 缓存方法实际上没有被调用。

以下示例使用@CacheablefindBook具有多个缓存的方法:spring-doc.cadn.net.cn

@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) {...}
默认密钥生成

由于缓存本质上是键值存储,因此每次调用缓存方法时 需要转换为合适的密钥进行缓存访问。缓存抽象 使用简单的KeyGenerator基于以下算法:spring-doc.cadn.net.cn

这种方法适用于大多数用例,只要参数具有自然键 并实现有效的hashCode()equals()方法。如果不是这样, 你需要改变策略。spring-doc.cadn.net.cn

要提供不同的默认密钥生成器,您需要实现org.springframework.cache.interceptor.KeyGenerator接口。spring-doc.cadn.net.cn

默认密钥生成策略随着 Spring 4.0 的发布而更改。早些时候 Spring 版本使用密钥生成策略,对于多个密钥参数, 仅考虑hashCode()参数而不是equals().这可能会导致 意外的键冲突(有关背景,请参阅 SPR-10237)。新的SimpleKeyGenerator对于此类方案使用复合键。spring-doc.cadn.net.cn

如果要继续使用以前的关键策略,可以配置已弃用的org.springframework.cache.interceptor.DefaultKeyGenerator类或创建自定义 基于哈希KeyGenerator实现。spring-doc.cadn.net.cn

自定义密钥生成声明

由于缓存是通用的,因此目标方法很可能具有各种签名 不能轻易映射到缓存结构之上。这往往会变得显而易见 当目标方法有多个参数,其中只有一些参数适合 缓存(而其余的仅由方法逻辑使用)。请考虑以下示例:spring-doc.cadn.net.cn

@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

乍一看,虽然两人boolean论点会影响这本书的发现方式, 它们对缓存没有用处。此外,如果两者中只有一个很重要怎么办 而另一个则不是?spring-doc.cadn.net.cn

对于此类情况,@Cacheable注释允许您指定键的生成方式 通过其key属性。您可以使用 SpEL 来拾取 感兴趣的参数(或其嵌套属性)、执行作,甚至 调用任意方法,无需编写任何代码或实现任何接口。 这是比默认生成器更推荐的方法,因为方法往往是 随着代码库的增长,签名也大不相同。虽然默认策略可能 它适用于某些方法,很少适用于所有方法。spring-doc.cadn.net.cn

以下示例使用各种 SpEL 声明(如果您不熟悉 SpEL, 帮自己一个忙,阅读 Spring Expression Language):spring-doc.cadn.net.cn

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

前面的片段显示了选择某个参数(其中一个参数)是多么容易 属性,甚至是任意(静态)方法。spring-doc.cadn.net.cn

如果负责生成密钥的算法过于具体,或者它需要 要共享,您可以定义自定义keyGenerator在作上。为此, 指定KeyGeneratorbean 实现,如下所示 示例显示:spring-doc.cadn.net.cn

@Cacheable(cacheNames="books", keyGenerator="myKeyGenerator")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
keykeyGenerator参数互斥且为作 指定两者都会导致异常。
默认缓存解析

缓存抽象使用简单的CacheResolver那 使用配置的CacheManager.spring-doc.cadn.net.cn

要提供不同的默认缓存解析器,您需要实现org.springframework.cache.interceptor.CacheResolver接口。spring-doc.cadn.net.cn

自定义缓存解析

默认缓存分辨率非常适合使用 单CacheManager并且没有复杂的缓存解析要求。spring-doc.cadn.net.cn

对于使用多个缓存管理器的应用程序,您可以将cacheManager用于每个作,如以下示例所示:spring-doc.cadn.net.cn

@Cacheable(cacheNames="books", cacheManager="anotherCacheManager") (1)
public Book findBook(ISBN isbn) {...}
1 指定anotherCacheManager.

您还可以将CacheResolver完全以类似于 替换密钥生成。分辨率为 请求,让实现实际解析 要根据运行时参数使用的缓存。以下示例演示如何 指定一个CacheResolver:spring-doc.cadn.net.cn

@Cacheable(cacheResolver="runtimeCacheResolver") (1)
public Book findBook(ISBN isbn) {...}
1 指定CacheResolver.

从 Spring 4.1 开始,value缓存注释的属性不再 强制的,因为此特定信息可以由CacheResolver无论注释的内容如何。spring-doc.cadn.net.cn

类似于keykeyGeneratorcacheManagercacheResolver参数是互斥的,并且指定两者的作 导致异常,作为自定义CacheManagerCacheResolver实现。这可能不是你所期望的。spring-doc.cadn.net.cn

同步缓存

在多线程环境中,某些作可能会同时调用 相同的参数(通常在启动时)。默认情况下,缓存抽象不会 锁定任何东西,并且相同的值可能会被多次计算,从而违背了目的 缓存。spring-doc.cadn.net.cn

对于这些特定情况,您可以使用sync属性来指示底层 cache 提供程序,用于在计算值时锁定缓存条目。结果, 只有一个线程忙于计算值,而其他线程则被阻塞,直到条目 在缓存中更新。以下示例演示如何使用sync属性:spring-doc.cadn.net.cn

@Cacheable(cacheNames="foos", sync=true) (1)
public Foo executeExpensiveOperation(String id) {...}
1 使用sync属性。
这是一项可选功能,您最喜欢的缓存库可能不支持它。 都CacheManager核心框架提供的实现支持 IT。请参阅 有关更多详细信息,请参阅缓存提供程序的文档。
条件缓存

有时,一个方法可能并不适合始终缓存(例如,它可能 取决于给定的参数)。缓存注释通过condition参数,它采用SpEL计算结果为truefalse.如果true,则该方法被缓存。如果不是,它的行为就好像该方法不是 cached(即,无论缓存中是什么值,每次都会调用该方法 或使用什么参数)。例如,仅当 论点name长度短于 32:spring-doc.cadn.net.cn

@Cacheable(cacheNames="book", condition="#name.length() < 32") (1)
public Book findBook(String name)
1 设置条件@Cacheable.

除了condition参数,您可以使用unless参数来否决 向缓存添加值。与condition,unless表达式被计算 在调用该方法之后。为了扩展前面的例子,也许我们只是 想要缓存平装书,如以下示例所示:spring-doc.cadn.net.cn

@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback") (1)
public Book findBook(String name)
1 使用unless属性来阻止精装本。

缓存抽象支持java.util.Optional返回类型。如果Optional价值 存在,则它将存储在关联的缓存中。如果Optional值不是 目前null将存储在关联的缓存中。#result总是引用业务实体,而不是受支持的包装器,因此可以重写前面的示例 如下:spring-doc.cadn.net.cn

@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result?.hardback")
public Optional<Book> findBook(String name)

请注意#resultstill 指的是Book而不是Optional<Book>.既然可能是null,我们使用 SpEL 的安全导航运算符spring-doc.cadn.net.cn

可用的缓存 SpEL 评估上下文

SpELexpression 根据专用的context. 除了内置参数外,框架还提供了与缓存相关的专用参数 元数据,例如参数名称。下表描述了制作的项目 可用于上下文,以便您可以将它们用于键和条件计算:spring-doc.cadn.net.cn

表 9.缓存 SpEL 可用元数据
名称 位置 描述 示例

methodNamespring-doc.cadn.net.cn

根对象spring-doc.cadn.net.cn

正在调用的方法的名称spring-doc.cadn.net.cn

#root.methodNamespring-doc.cadn.net.cn

methodspring-doc.cadn.net.cn

根对象spring-doc.cadn.net.cn

正在调用的方法spring-doc.cadn.net.cn

#root.method.namespring-doc.cadn.net.cn

targetspring-doc.cadn.net.cn

根对象spring-doc.cadn.net.cn

正在调用的目标对象spring-doc.cadn.net.cn

#root.targetspring-doc.cadn.net.cn

targetClassspring-doc.cadn.net.cn

根对象spring-doc.cadn.net.cn

被调用的目标的类spring-doc.cadn.net.cn

#root.targetClassspring-doc.cadn.net.cn

argsspring-doc.cadn.net.cn

根对象spring-doc.cadn.net.cn

用于调用目标的参数(作为数组)spring-doc.cadn.net.cn

#root.args[0]spring-doc.cadn.net.cn

cachesspring-doc.cadn.net.cn

根对象spring-doc.cadn.net.cn

运行当前方法的缓存集合spring-doc.cadn.net.cn

#root.caches[0].namespring-doc.cadn.net.cn

参数名称spring-doc.cadn.net.cn

评估上下文spring-doc.cadn.net.cn

任何方法参数的名称。如果名称不可用 (可能是由于没有调试信息),参数名称也可以在#a<#arg>哪里#arg代表参数索引(从0).spring-doc.cadn.net.cn

#iban#a0(您也可以使用#p0#p<#arg>符号作为别名)。spring-doc.cadn.net.cn

resultspring-doc.cadn.net.cn

评估上下文spring-doc.cadn.net.cn

方法调用的结果(要缓存的值)。仅适用于unless表达 式cache put表达式(以计算key),或cache evict表达式(当beforeInvocationfalse).对于受支持的包装器(例如Optional),#result指的是实际对象,而不是包装器。spring-doc.cadn.net.cn

#resultspring-doc.cadn.net.cn

8.2.2.@CachePut注解

当需要在不干扰方法执行的情况下更新缓存时, 您可以使用@CachePut注解。也就是说,始终调用该方法,并且其 结果被放入缓存中(根据@CachePut选项)。它支持 与@Cacheable并且应该用于缓存填充而不是 方法流优化。以下示例使用@CachePut注解:spring-doc.cadn.net.cn

@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)
@CachePut@Cacheable同一方法的注释通常是 强烈劝阻,因为他们有不同的行为。虽然后者会导致 方法调用,前者强制调用 以运行缓存更新。这会导致意外行为,并且例外 特定极端情况(例如注释具有将它们排除在每个极端情况下的条件 其他),应避免此类声明。另请注意,此类条件不应依赖于 在结果对象上(即#result变量),因为这些都是预先验证的 确认排除。

8.2.3.@CacheEvict注解

缓存抽象不仅允许填充缓存存储,还允许驱逐。 此过程对于从缓存中删除过时或未使用的数据非常有用。与@Cacheable,@CacheEvict分界执行缓存的方法 逐出(即充当从缓存中删除数据的触发器的方法)。 与它的兄弟姐妹类似,@CacheEvict需要指定一个或多个缓存 ,允许自定义缓存和密钥解析或 条件,并具有额外的参数 (allEntries),指示是否需要执行缓存范围的逐出 而不仅仅是条目驱逐(基于密钥)。以下示例将 来自books缓存:spring-doc.cadn.net.cn

@CacheEvict(cacheNames="books", allEntries=true) (1)
public void loadBooks(InputStream batch)
1 使用allEntries属性从缓存中逐出所有条目。

当需要清除整个缓存区域时,此选项会派上用场。 与其逐出每个条目(这将花费很长时间,因为它效率低下), 所有条目都将在一次作中删除,如前面的示例所示。 请注意,框架会忽略此方案中指定的任何键,因为它不适用 (逐出整个缓存,而不仅仅是一个条目)。spring-doc.cadn.net.cn

您还可以指示逐出是在(默认值)之后还是之前进行 该方法是使用beforeInvocation属性。前者提供了 与其他注释相同的语义:方法成功完成后, 对缓存运行作(在本例中为逐出)。如果方法没有 run(因为它可能被缓存)或引发异常,则不会发生逐出。 后者 (beforeInvocation=true) 会导致逐出始终发生在 方法被调用。这在不需要绑定驱逐的情况下很有用 到方法结果。spring-doc.cadn.net.cn

请注意void方法可以与@CacheEvict- 由于这些方法充当 trigger,则返回值将被忽略(因为它们不与缓存交互)。这是 不是这样@Cacheable将数据添加到缓存或更新缓存中的数据 因此,需要一个结果。spring-doc.cadn.net.cn

8.2.4.@Caching注解

有时,相同类型的多个注释(例如@CacheEvict@CachePut) 需要指定 — 例如,因为条件或键 表达式在不同缓存之间是不同的。@Caching让多个嵌套的@Cacheable,@CachePut@CacheEvict注释在同一方法上使用。 以下示例使用两个@CacheEvict附注:spring-doc.cadn.net.cn

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)

8.2.5.@CacheConfig注解

到目前为止,我们已经看到缓存作提供了许多自定义选项,并且 您可以为每个作设置这些选项。但是,一些自定义选项 如果它们适用于类的所有作,则配置起来可能会很乏味。为 实例,指定要用于 class 可以替换为单个类级定义。这是哪里@CacheConfig发挥作用。以下示例使用@CacheConfig要设置缓存的名称,请执行以下作:spring-doc.cadn.net.cn

@CacheConfig("books") (1)
public class BookRepositoryImpl implements BookRepository {

    @Cacheable
    public Book findBook(ISBN isbn) {...}
}
1 @CacheConfig以设置缓存的名称。

@CacheConfig是一个类级注释,允许共享缓存名称, 自定义KeyGenerator,自定义CacheManager,而自定义CacheResolver. 将此注释放在类上不会打开任何缓存作。spring-doc.cadn.net.cn

作级自定义始终覆盖@CacheConfig. 因此,这为每个缓存作提供了三个级别的自定义:spring-doc.cadn.net.cn

8.2.6. 启用缓存注释

重要的是要注意,即使声明缓存注释不会 自动触发他们的作 - 就像 Spring 中的许多东西一样,该功能必须是 声明性启用(这意味着如果您怀疑缓存是罪魁祸首,您可以 通过仅删除一个配置行而不是 您的代码)。spring-doc.cadn.net.cn

要启用缓存注释,请添加注释@EnableCaching给你的一个@Configuration类:spring-doc.cadn.net.cn

@Configuration
@EnableCaching
public class AppConfig {
}

或者,对于 XML 配置,您可以使用cache:annotation-driven元素:spring-doc.cadn.net.cn

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

        <cache:annotation-driven/>
</beans>

两个cache:annotation-driven元素和@EnableCaching注释可以让您 指定影响缓存行为添加到 通过AOP申请。该配置有意与@Transactional.spring-doc.cadn.net.cn

处理缓存注释的默认通知模式为proxy,这允许 仅用于通过代理拦截呼叫。同一类中的本地调用 不能以这种方式被拦截。对于更高级的拦截模式,请考虑 切换到aspectj模式与编译时或加载时编织相结合。
有关高级自定义(使用 Java 配置)的更多详细信息,这些自定义是 需要实现CachingConfigurer,请参阅 javadoc
表10.缓存注释设置
XML 属性 注释属性 默认值 描述

cache-managerspring-doc.cadn.net.cn

不适用(参见CachingConfigurerjavadoc)spring-doc.cadn.net.cn

cacheManagerspring-doc.cadn.net.cn

要使用的缓存管理器的名称。默认CacheResolver在后面初始化 具有此缓存管理器(或cacheManager如果未设置)。更多 缓存解析的细粒度管理,考虑设置“cache-resolver” 属性。spring-doc.cadn.net.cn

cache-resolverspring-doc.cadn.net.cn

不适用(参见CachingConfigurerjavadoc)spring-doc.cadn.net.cn

一个SimpleCacheResolver使用配置的cacheManager.spring-doc.cadn.net.cn

用于解析后备缓存的 CacheResolver 的 Bean 名称。 此属性不是必需的,只需指定为 'cache-manager' 属性。spring-doc.cadn.net.cn

key-generatorspring-doc.cadn.net.cn

不适用(参见CachingConfigurerjavadoc)spring-doc.cadn.net.cn

SimpleKeyGeneratorspring-doc.cadn.net.cn

要使用的自定义密钥生成器的名称。spring-doc.cadn.net.cn

error-handlerspring-doc.cadn.net.cn

不适用(参见CachingConfigurerjavadoc)spring-doc.cadn.net.cn

SimpleCacheErrorHandlerspring-doc.cadn.net.cn

要使用的自定义缓存错误处理程序的名称。默认情况下,在 缓存相关作将引回客户端。spring-doc.cadn.net.cn

modespring-doc.cadn.net.cn

modespring-doc.cadn.net.cn

proxyspring-doc.cadn.net.cn

默认模式 (proxy)处理要使用 Spring 的 AOP 代理的带注释的 bean框架(遵循代理语义,如前所述,适用于方法调用仅通过代理进入)。替代模式(aspectj) 改为编织 受影响的类使用 Spring 的 AspectJ 缓存方面,修改目标类字节 应用于任何类型的方法调用的代码。AspectJ 编织需要spring-aspects.jar以及启用加载时编织(或编译时编织)。(有关如何设置的详细信息,请参阅 Spring 配置 加载时间编织。spring-doc.cadn.net.cn

proxy-target-classspring-doc.cadn.net.cn

proxyTargetClassspring-doc.cadn.net.cn

falsespring-doc.cadn.net.cn

仅适用于代理模式。控制为其创建缓存代理的类型 类的@Cacheable@CacheEvict附注。如果proxy-target-class属性设置为true,创建基于类的代理。 如果proxy-target-classfalse或者如果省略了该属性,则标准 JDK 创建基于接口的代理。(有关不同代理类型的详细检查,请参阅代理机制。spring-doc.cadn.net.cn

orderspring-doc.cadn.net.cn

orderspring-doc.cadn.net.cn

Ordered.LOWEST_PRECEDENCEspring-doc.cadn.net.cn

定义应用于标注的 bean 的缓存通知的顺序@Cacheable@CacheEvict.(有关相关规则的更多信息 订购 AOP 通知,请参阅通知订购。 没有指定的排序意味着 AOP 子系统确定通知的顺序。spring-doc.cadn.net.cn

<cache:annotation-driven/>寻找@Cacheable/@CachePut/@CacheEvict/@Caching仅在定义它的同一应用程序上下文中的 bean 上。这意味着, 如果你把<cache:annotation-driven/>WebApplicationContext对于一个DispatcherServlet,它只检查控制器中的 Bean,而不是服务中的 Bean。 有关详细信息,请参阅 MVC 部分
方法可见性和缓存注释

使用代理时,应仅将缓存注释应用于具有 公众可见性。如果您确实注释了受保护、私有或包可见的方法 使用这些注释时,不会引发错误,但带注释的方法不会显示 配置的缓存设置。考虑使用 AspectJ(请参阅本节的其余部分) 如果您需要注释非公共方法,因为它会更改字节码本身。spring-doc.cadn.net.cn

Spring 建议您只注释具体类(以及具体 类)替换为@Cache*注释,而不是注释接口。 你当然可以放置一个@Cache*接口(或接口方法)上的注释,但这仅在您使用代理模式(mode="proxy").如果您使用 基于编织的方面 (mode="aspectj"),缓存设置在 接口级声明。
在代理模式(默认)中,只有通过 代理被拦截。这意味着自调用(实际上,在 target 对象调用 target 对象的另一个方法)不会导致实际的 运行时缓存,即使调用的方法标记为@Cacheable.考虑 使用aspectj在这种情况下,模式。此外,代理必须完全初始化为 提供预期的行为,因此您不应在 初始化代码(即@PostConstruct).

8.2.7. 使用自定义注释

自定义注释和 AspectJ

此功能仅适用于基于代理的方法,但可以启用 使用 AspectJ 需要付出一些额外的努力。spring-doc.cadn.net.cn

spring-aspects模块仅定义标准注释的一个方面。 如果您已经定义了自己的注释,则还需要为 那些。检查AnnotationCacheAspect举个例子。spring-doc.cadn.net.cn

缓存抽象允许您使用自己的注释来识别哪种方法 触发缓存填充或驱逐。这作为模板机制非常方便, 因为它消除了重复缓存注释声明的需要,即 如果指定了键或条件,或者如果外部导入 (org.springframework) 在您的代码库中不允许。与其他类似 在构造型注释中,您可以 用@Cacheable,@CachePut,@CacheEvict@CacheConfig作为元注释(即 可以注释其他注释)。在下面的示例中,我们将@Cacheable声明,并带有我们自己的自定义注释:spring-doc.cadn.net.cn

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(cacheNames="books", key="#isbn")
public @interface SlowService {
}

在前面的示例中,我们定义了自己的SlowService注解 它本身被注释为@Cacheable.现在我们可以替换以下代码:spring-doc.cadn.net.cn

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

以下示例显示了自定义注释,我们可以用它替换 前面的代码:spring-doc.cadn.net.cn

@SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

即使@SlowService不是 Spring 注解,则容器会自动选择 在运行时上调其声明并理解其含义。请注意,如前所述,需要启用注解驱动的行为。spring-doc.cadn.net.cn

8.3. JCache (JSR-107) 注释

从 4.1 版本开始,Spring 的缓存抽象完全支持 JCache 标准 (JSR-107) 注释:@CacheResult,@CachePut,@CacheRemove@CacheRemoveAll以及@CacheDefaults,@CacheKey@CacheValue同伴。 即使不将缓存存储迁移到 JSR-107,您也可以使用这些注解。 内部实现使用 Spring 的缓存抽象并提供默认CacheResolverKeyGenerator符合 规范。换句话说,如果您已经在使用 Spring 的缓存抽象, 您可以在不更改缓存存储的情况下切换到这些标准注释 (或配置,就此而言)。spring-doc.cadn.net.cn

8.3.1. 功能摘要

对于熟悉 Spring 缓存注解的用户,下表 描述了 Spring 注解与其 JSR-107 之间的主要区别 同行:spring-doc.cadn.net.cn

表 11.Spring 与 JSR-107 缓存注释
Spring JSR-107型 备注

@Cacheablespring-doc.cadn.net.cn

@CacheResultspring-doc.cadn.net.cn

相当相似。@CacheResult可以缓存特定异常并强制 无论缓存的内容如何,方法的执行。spring-doc.cadn.net.cn

@CachePutspring-doc.cadn.net.cn

@CachePutspring-doc.cadn.net.cn

当 Spring 使用方法调用的结果更新缓存时,JCache 要求将其作为参数传递,并用@CacheValue. 由于这种差异,JCache 允许在 实际方法调用。spring-doc.cadn.net.cn

@CacheEvictspring-doc.cadn.net.cn

@CacheRemovespring-doc.cadn.net.cn

相当相似。@CacheRemove当 方法调用会导致异常。spring-doc.cadn.net.cn

@CacheEvict(allEntries=true)spring-doc.cadn.net.cn

@CacheRemoveAllspring-doc.cadn.net.cn

@CacheRemove.spring-doc.cadn.net.cn

@CacheConfigspring-doc.cadn.net.cn

@CacheDefaultsspring-doc.cadn.net.cn

允许您以类似的方式配置相同的概念。spring-doc.cadn.net.cn

JCache 的概念是javax.cache.annotation.CacheResolver,这是相同的 到Spring的CacheResolver接口,只不过 JCache 只支持一个 缓存。默认情况下,一个简单的实现会根据 name 在注释上声明。需要注意的是,如果没有缓存名称 在注释上指定,则会自动生成默认值。请参阅 javadoc 之@CacheResult#cacheName()了解更多信息。spring-doc.cadn.net.cn

CacheResolver实例由CacheResolverFactory.有可能 为每个缓存作自定义工厂,如以下示例所示:spring-doc.cadn.net.cn

@CacheResult(cacheNames="books", cacheResolverFactory=MyCacheResolverFactory.class) (1)
public Book findBook(ISBN isbn)
1 为此作定制工厂。
对于所有引用的类,Spring 会尝试找到具有给定类型的 bean。 如果存在多个匹配项,则创建一个新实例,并且可以使用常规 bean 生命周期回调,例如依赖注入。

密钥由javax.cache.annotation.CacheKeyGenerator这为 与 Spring 的用途相同KeyGenerator.默认情况下,采用所有方法参数 考虑在内,除非至少有一个参数用@CacheKey.这是 类似于 Spring 的自定义密钥生成 声明。例如,以下是相同的作,其中一个 Spring 的抽象和另一个使用 JCache 的抽象:spring-doc.cadn.net.cn

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@CacheResult(cacheName="books")
public Book findBook(@CacheKey ISBN isbn, boolean checkWarehouse, boolean includeUsed)

您还可以指定CacheKeyResolver在作上,类似于您可以 指定CacheResolverFactory.spring-doc.cadn.net.cn

JCache 可以管理由带注释的方法抛出的异常。这可以防止更新 缓存,但它也可以缓存异常作为失败的指示器,而不是 再次调用该方法。假设InvalidIsbnNotFoundException如果 ISBN 的结构无效。这是永久性的失败(没有一本书可以 使用这样的参数检索)。下面缓存异常,以便进一步 具有相同、无效的 ISBN 的调用会直接抛出缓存异常,而不是 再次调用该方法:spring-doc.cadn.net.cn

@CacheResult(cacheName="books", exceptionCacheName="failures"
            cachedExceptions = InvalidIsbnNotFoundException.class)
public Book findBook(ISBN isbn)

8.3.2. 启用 JSR-107 支持

您无需执行任何特定作即可启用 JSR-107 支持以及 Spring 的 声明式注释支持。双@EnableCachingcache:annotation-driven如果 JSR-107 API 和spring-context-support模块存在于类路径中。spring-doc.cadn.net.cn

根据您的用例,选择基本上是您的。您甚至可以混合和 通过在某些上使用 JSR-107 API 并在 别人。但是,如果这些服务影响相同的缓存,则应使用一致的 以及相同的密钥生成实现。

8.4. 基于声明性 XML 的缓存

如果注释不是一种选择(可能是由于无法访问源代码) 或没有外部代码),您可以使用 XML 进行声明性缓存。因此,而不是 注释缓存方法时,您可以指定目标方法和 缓存指令(类似于声明式事务管理建议)。示例 可以翻译成以下示例:spring-doc.cadn.net.cn

<!-- the service we want to make cacheable -->
<bean id="bookService" class="x.y.service.DefaultBookService"/>

<!-- cache definitions -->
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
    <cache:caching cache="books">
        <cache:cacheable method="findBook" key="#isbn"/>
        <cache:cache-evict method="loadBooks" all-entries="true"/>
    </cache:caching>
</cache:advice>

<!-- apply the cacheable behavior to all BookService interfaces -->
<aop:config>
    <aop:advisor advice-ref="cacheAdvice" pointcut="execution(* x.y.BookService.*(..))"/>
</aop:config>

<!-- cache manager definition omitted -->

在前面的配置中,bookService设置为可缓存。缓存语义 要应用,则封装在cache:advice定义,这会导致findBooks用于将数据放入缓存的方法和loadBooks驱逐方法 数据。这两个定义都不利于books缓存。spring-doc.cadn.net.cn

aop:configdefinition 将缓存通知应用于 使用 AspectJ 切入点表达式进行编程(更多信息可在 Spring 面向方面编程中找到)。在前面的示例中, 所有方法BookService被考虑并应用缓存通知。spring-doc.cadn.net.cn

声明性 XML 缓存支持所有基于注释的模型,因此在 两者应该相当容易。此外,两者都可以在同一应用程序中使用。 基于 XML 的方法不触及目标代码。然而,它本质上是更多的 详细。处理具有重载方法的类时,这些方法针对 缓存,确定正确的方法确实需要额外的努力,因为method论证不是一个好的鉴别器。在这些情况下,您可以使用 AspectJ 切入点 挑选目标方法并应用适当的缓存功能。 但是,通过 XML,可以更轻松地应用包或组或接口范围的缓存 (同样,由于 AspectJ 切入点)并创建类似模板的定义(就像我们所做的那样) 在前面的示例中,通过cache:definitions cache属性)。spring-doc.cadn.net.cn

8.5. 配置缓存存储

缓存抽象提供了多个存储集成选项。要使用它们,您需要 声明适当的CacheManager(控制和管理的实体Cache实例,可用于检索这些实例进行存储)。spring-doc.cadn.net.cn

8.5.1. JDKConcurrentMap基于缓存

基于 JDK 的Cache实现驻留在org.springframework.cache.concurrent包。它允许您使用ConcurrentHashMap作为后盾Cache商店。以下示例显示了如何配置两个缓存:spring-doc.cadn.net.cn

<!-- simple cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
        <set>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="books"/>
        </set>
    </property>
</bean>

前面的代码段使用SimpleCacheManager创建CacheManager对于 两个嵌套的ConcurrentMapCache名为defaultbooks.请注意, 名称直接为每个缓存配置。spring-doc.cadn.net.cn

由于缓存是由应用程序创建的,因此它与其生命周期绑定,使其 适用于基本用例、测试或简单应用程序。缓存扩展良好 并且速度非常快,但它不提供任何管理、持久化能力、 或驱逐合同。spring-doc.cadn.net.cn

8.5.2. 基于 Ehcache 的缓存

Ehcache 3.x 完全符合 JSR-107 标准,不需要专门的支持。

Ehcache 2.x 实现位于org.springframework.cache.ehcache包。同样,要使用它,您需要声明适当的CacheManager. 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<bean id="cacheManager"
        class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>

<!-- EhCache library setup -->
<bean id="ehcache"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="ehcache.xml"/>

此设置在 Spring IoC 中引导 ehcache 库(通过ehcachebean),然后将其连接到专用的CacheManager实现。请注意 整个特定于 Ehcache 的配置是从ehcache.xml.spring-doc.cadn.net.cn

8.5.3. 咖啡因缓存

Caffeine 是 Guava 缓存的 Java 8 重写,其实现位于org.springframework.cache.caffeine打包并提供对多个功能的访问 咖啡因。spring-doc.cadn.net.cn

以下示例配置CacheManager按需创建缓存:spring-doc.cadn.net.cn

<bean id="cacheManager"
        class="org.springframework.cache.caffeine.CaffeineCacheManager"/>

您还可以提供要显式使用的缓存。在这种情况下,只有那些 由经理提供。以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
    <property name="cacheNames">
        <set>
            <value>default</value>
            <value>books</value>
        </set>
    </property>
</bean>

咖啡因CacheManager还支持自定义CaffeineCacheLoader. 有关这些的更多信息,请参阅 Caffeine 文档spring-doc.cadn.net.cn

8.5.4. 基于 GemFire 的缓存

GemFire 是一种面向内存、磁盘支持、弹性可扩展、持续可用、 活动(具有内置的基于模式的订阅通知),全局复制 数据库,并提供功能齐全的边缘缓存。有关如何作的更多信息 使用 GemFire 作为CacheManager(以及更多),请参阅 Spring Data GemFire 参考文档spring-doc.cadn.net.cn

8.5.5. JSR-107 缓存

Spring 的缓存抽象还可以使用符合 JSR-107 的缓存。The JCache 实现位于org.springframework.cache.jcache包。spring-doc.cadn.net.cn

同样,要使用它,您需要声明适当的CacheManager. 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<bean id="cacheManager"
        class="org.springframework.cache.jcache.JCacheCacheManager"
        p:cache-manager-ref="jCacheManager"/>

<!-- JSR-107 cache manager setup  -->
<bean id="jCacheManager" .../>

8.5.6. 处理没有后备存储的缓存

有时,在切换环境或进行测试时,您可能有缓存 声明,而无需配置实际的后备缓存。因为这是一个无效的 配置时,运行时会抛出异常,因为缓存基础设施 找不到合适的店铺。在这种情况下,与其删除 cache 声明(这可能被证明是乏味的),你可以连接一个简单的虚拟缓存,其中 不执行缓存 — 也就是说,它强制每次都调用缓存的方法。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
    <property name="cacheManagers">
        <list>
            <ref bean="jdkCache"/>
            <ref bean="gemfireCache"/>
        </list>
    </property>
    <property name="fallbackToNoOpCache" value="true"/>
</bean>

CompositeCacheManager在前面的链中,多个CacheManager实例和, 通过fallbackToNoOpCache标志,为所有定义添加一个无作缓存,而不是 由配置的缓存管理器处理。也就是说,在 也jdkCachegemfireCache(在示例前面配置)由 无作缓存,它不存储任何信息,导致目标方法 每次都调用。spring-doc.cadn.net.cn

8.6. 插入不同的后端缓存

显然,有很多缓存产品可以用作支持 商店。对于不支持 JSR-107 的那些,您需要提供一个CacheManagerCache实现。这听起来可能比实际更难,因为在实践中,课程 往往是简单的适配器,用于映射 缓存抽象框架,作为ehcache类可以。 最CacheManager类可以使用org.springframework.cache.support包(例如AbstractCacheManager这需要 处理样板代码,只留下实际映射待完成)。spring-doc.cadn.net.cn

8.7. 如何设置TTL/TTI/逐出策略/XXX功能?

直接通过您的缓存提供程序。缓存抽象是一个抽象, 不是缓存实现。您使用的解决方案可能支持各种数据 其他解决方案不支持的策略和不同拓扑(例如 JDK 的ConcurrentHashMap— 在缓存抽象中暴露这一点将毫无用处 因为没有支持)。应控制此类功能 直接通过后备缓存(配置时)或通过其本机 API。spring-doc.cadn.net.cn

9. 附录

9.1. XML模式

附录的这一部分列出了与集成技术相关的 XML 模式。spring-doc.cadn.net.cn

9.1.1.jee图式

jee元素处理与 Java EE(Java Enterprise Edition)配置相关的问题, 例如查找 JNDI 对象和定义 EJB 引用。spring-doc.cadn.net.cn

要使用jeeschema,您需要在顶部有以下前导码 Spring XML 配置文件的。以下代码段中的文本引用了 correct schema,以便jee命名空间可供您使用:spring-doc.cadn.net.cn

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

    <!-- bean definitions here -->

</beans>
<jee:jndi-lookup/>(简单)

以下示例显示如何使用 JNDI 查找数据源,而无需jee图式:spring-doc.cadn.net.cn

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/MyDataSource"/>
</bean>
<bean id="userDao" class="com.foo.JdbcUserDao">
    <!-- Spring will do the cast automatically (as usual) -->
    <property name="dataSource" ref="dataSource"/>
</bean>

以下示例显示如何使用 JNDI 查找具有jee图式:spring-doc.cadn.net.cn

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource"/>

<bean id="userDao" class="com.foo.JdbcUserDao">
    <!-- Spring will do the cast automatically (as usual) -->
    <property name="dataSource" ref="dataSource"/>
</bean>
<jee:jndi-lookup/>(使用单个 JNDI 环境设置)

以下示例显示如何使用 JNDI 查找环境变量,而无需jee:spring-doc.cadn.net.cn

<bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/MyDataSource"/>
    <property name="jndiEnvironment">
        <props>
            <prop key="ping">pong</prop>
        </props>
    </property>
</bean>

以下示例显示如何使用 JNDI 查找环境变量jee:spring-doc.cadn.net.cn

<jee:jndi-lookup id="simple" jndi-name="jdbc/MyDataSource">
    <jee:environment>ping=pong</jee:environment>
</jee:jndi-lookup>
<jee:jndi-lookup/>(使用多个 JNDI 环境设置)

以下示例显示如何使用 JNDI 查找多个环境变量 没有jee:spring-doc.cadn.net.cn

<bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/MyDataSource"/>
    <property name="jndiEnvironment">
        <props>
            <prop key="sing">song</prop>
            <prop key="ping">pong</prop>
        </props>
    </property>
</bean>

以下示例显示如何使用 JNDI 查找多个环境变量jee:spring-doc.cadn.net.cn

<jee:jndi-lookup id="simple" jndi-name="jdbc/MyDataSource">
    <!-- newline-separated, key-value pairs for the environment (standard Properties format) -->
    <jee:environment>
        sing=song
        ping=pong
    </jee:environment>
</jee:jndi-lookup>
<jee:jndi-lookup/>(复合体)

以下示例显示如何使用 JNDI 查找数据源和多个 没有不同的属性jee:spring-doc.cadn.net.cn

<bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/MyDataSource"/>
    <property name="cache" value="true"/>
    <property name="resourceRef" value="true"/>
    <property name="lookupOnStartup" value="false"/>
    <property name="expectedType" value="com.myapp.DefaultThing"/>
    <property name="proxyInterface" value="com.myapp.Thing"/>
</bean>

以下示例显示如何使用 JNDI 查找数据源和多个 不同的属性与jee:spring-doc.cadn.net.cn

<jee:jndi-lookup id="simple"
        jndi-name="jdbc/MyDataSource"
        cache="true"
        resource-ref="true"
        lookup-on-startup="false"
        expected-type="com.myapp.DefaultThing"
        proxy-interface="com.myapp.Thing"/>
<jee:local-slsb/>(简单)

<jee:local-slsb/>元素配置对本地 EJB 无状态会话 Bean 的引用。spring-doc.cadn.net.cn

以下示例显示如何配置对本地 EJB 无状态会话 Bean 的引用 没有jee:spring-doc.cadn.net.cn

<bean id="simple"
        class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
    <property name="jndiName" value="ejb/RentalServiceBean"/>
    <property name="businessInterface" value="com.foo.service.RentalService"/>
</bean>

以下示例显示如何配置对本地 EJB 无状态会话 Bean 的引用 跟jee:spring-doc.cadn.net.cn

<jee:local-slsb id="simpleSlsb" jndi-name="ejb/RentalServiceBean"
        business-interface="com.foo.service.RentalService"/>
<jee:local-slsb/>(复合体)

<jee:local-slsb/>元素配置对本地 EJB 无状态会话 Bean 的引用。spring-doc.cadn.net.cn

以下示例显示如何配置对本地 EJB 无状态会话 Bean 的引用 以及一些没有jee:spring-doc.cadn.net.cn

<bean id="complexLocalEjb"
        class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
    <property name="jndiName" value="ejb/RentalServiceBean"/>
    <property name="businessInterface" value="com.example.service.RentalService"/>
    <property name="cacheHome" value="true"/>
    <property name="lookupHomeOnStartup" value="true"/>
    <property name="resourceRef" value="true"/>
</bean>

以下示例显示了如何配置对本地 EJB 无状态会话 Bean 的引用以及许多属性jee:spring-doc.cadn.net.cn

<jee:local-slsb id="complexLocalEjb"
        jndi-name="ejb/RentalServiceBean"
        business-interface="com.foo.service.RentalService"
        cache-home="true"
        lookup-home-on-startup="true"
        resource-ref="true">
<jee:远程-slsb/>

<jee:remote-slsb/>元素配置对remoteEJB 无状态会话 Bean。spring-doc.cadn.net.cn

以下示例显示如何配置对远程 EJB 无状态会话 Bean 的引用 没有jee:spring-doc.cadn.net.cn

<bean id="complexRemoteEjb"
        class="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean">
    <property name="jndiName" value="ejb/MyRemoteBean"/>
    <property name="businessInterface" value="com.foo.service.RentalService"/>
    <property name="cacheHome" value="true"/>
    <property name="lookupHomeOnStartup" value="true"/>
    <property name="resourceRef" value="true"/>
    <property name="homeInterface" value="com.foo.service.RentalService"/>
    <property name="refreshHomeOnConnectFailure" value="true"/>
</bean>

以下示例显示如何配置对远程 EJB 无状态会话 Bean 的引用 跟jee:spring-doc.cadn.net.cn

<jee:remote-slsb id="complexRemoteEjb"
        jndi-name="ejb/MyRemoteBean"
        business-interface="com.foo.service.RentalService"
        cache-home="true"
        lookup-home-on-startup="true"
        resource-ref="true"
        home-interface="com.foo.service.RentalService"
        refresh-home-on-connect-failure="true">

9.1.2.jms图式

jms元素处理配置与 JMS 相关的 bean,例如 Spring 的消息侦听器容器。这些元素详见 JMS 章节中标题为 JMS 命名空间支持的部分。有关此支持的完整详细信息,请参阅该章节 和jms元素本身。spring-doc.cadn.net.cn

为了完整起见,使用jmsschema,你需要有 Spring XML 配置文件顶部的以下前导码。中的文本 以下代码段引用正确的架构,以便jmsNamespace 可供您:spring-doc.cadn.net.cn

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

    <!-- bean definitions here -->

</beans>

9.1.3. 使用<context:mbean-export/>

9.1.4.cache图式

您可以使用cache元素来启用对 Spring 的@CacheEvict,@CachePut, 和@Caching附注。它还支持基于 XML 的声明性缓存。有关详细信息,请参阅启用缓存注释基于声明性 XML 的缓存spring-doc.cadn.net.cn

要使用cacheschema,您需要在 Spring XML 配置文件的顶部。以下代码片段中的文本引用 正确的架构,以便cache命名空间可供您使用:spring-doc.cadn.net.cn

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

    <!-- bean definitions here -->

</beans>