Split integration chapter in smaller documents
This commit splits the integration chapter of the reference documentation in smaller documents for easier maintenance.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
1070
framework-docs/src/docs/asciidoc/integration/cache.adoc
Normal file
1070
framework-docs/src/docs/asciidoc/integration/cache.adoc
Normal file
File diff suppressed because it is too large
Load Diff
302
framework-docs/src/docs/asciidoc/integration/email.adoc
Normal file
302
framework-docs/src/docs/asciidoc/integration/email.adoc
Normal file
@@ -0,0 +1,302 @@
|
||||
[[mail]]
|
||||
= Email
|
||||
|
||||
This section describes how to send email with the Spring Framework.
|
||||
|
||||
.Library dependencies
|
||||
****
|
||||
The following JAR needs to be on the classpath of your application in order to use
|
||||
the Spring Framework's email library:
|
||||
|
||||
* The https://eclipse-ee4j.github.io/mail/[JavaMail / Jakarta Mail 1.6] library
|
||||
|
||||
This library is freely available on the web -- for example, in Maven Central as
|
||||
`com.sun.mail:jakarta.mail`. Please make sure to use the latest 1.6.x version
|
||||
rather than Jakarta Mail 2.0 (which comes with a different package namespace).
|
||||
****
|
||||
|
||||
The Spring Framework provides a helpful utility library for sending email that shields
|
||||
you from the specifics of the underlying mailing system and is responsible for
|
||||
low-level resource handling on behalf of the client.
|
||||
|
||||
The `org.springframework.mail` package is the root level package for the Spring
|
||||
Framework's email support. The central interface for sending emails is the `MailSender`
|
||||
interface. A simple value object that encapsulates the properties of a simple mail such
|
||||
as `from` and `to` (plus many others) is the `SimpleMailMessage` class. This package
|
||||
also contains a hierarchy of checked exceptions that provide a higher level of
|
||||
abstraction over the lower level mail system exceptions, with the root exception being
|
||||
`MailException`. See the {api-spring-framework}/mail/MailException.html[javadoc]
|
||||
for more information on the rich mail exception hierarchy.
|
||||
|
||||
The `org.springframework.mail.javamail.JavaMailSender` interface adds specialized
|
||||
JavaMail features, such as MIME message support to the `MailSender` interface
|
||||
(from which it inherits). `JavaMailSender` also provides a callback interface called
|
||||
`org.springframework.mail.javamail.MimeMessagePreparator` for preparing a `MimeMessage`.
|
||||
|
||||
|
||||
|
||||
[[mail-usage]]
|
||||
== Usage
|
||||
|
||||
Assume that we have a business interface called `OrderManager`, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public interface OrderManager {
|
||||
|
||||
void placeOrder(Order order);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Further assume that we have a requirement stating that an email message with an
|
||||
order number needs to be generated and sent to a customer who placed the relevant order.
|
||||
|
||||
|
||||
[[mail-usage-simple]]
|
||||
=== Basic `MailSender` and `SimpleMailMessage` Usage
|
||||
|
||||
The following example shows how to use `MailSender` and `SimpleMailMessage` to send an
|
||||
email when someone places an order:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
The following example shows the bean definitions for the preceding code:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<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="customerservice@mycompany.example"/>
|
||||
<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>
|
||||
----
|
||||
|
||||
|
||||
[[mail-usage-mime]]
|
||||
=== Using `JavaMailSender` and `MimeMessagePreparator`
|
||||
|
||||
This section describes another implementation of `OrderManager` that uses the `MimeMessagePreparator`
|
||||
callback interface. In the following example, the `mailSender` property is of type
|
||||
`JavaMailSender` so that we are able to use the JavaMail `MimeMessage` class:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.mail.Message;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
import jakarta.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("mail@mycompany.example"));
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: The mail code is a crosscutting concern and could well be a candidate for
|
||||
refactoring into a <<core.adoc#aop, custom Spring AOP aspect>>, which could then
|
||||
be run at appropriate joinpoints on the `OrderManager` target.
|
||||
|
||||
The Spring Framework's mail support ships with the standard JavaMail implementation.
|
||||
See the relevant javadoc for more information.
|
||||
|
||||
|
||||
|
||||
[[mail-javamail-mime]]
|
||||
== Using the JavaMail `MimeMessageHelper`
|
||||
|
||||
A class that comes in pretty handy when dealing with JavaMail messages is
|
||||
`org.springframework.mail.javamail.MimeMessageHelper`, which shields you from
|
||||
having to use the verbose JavaMail API. Using the `MimeMessageHelper`, it is
|
||||
pretty easy to create a `MimeMessage`, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
// 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("test@host.com");
|
||||
helper.setText("Thank you for ordering!");
|
||||
|
||||
sender.send(message);
|
||||
----
|
||||
|
||||
|
||||
[[mail-javamail-mime-attachments]]
|
||||
=== Sending Attachments and Inline Resources
|
||||
|
||||
Multipart email messages allow for both attachments and inline resources. Examples of
|
||||
inline resources include an image or a stylesheet that you want to use in your message but
|
||||
that you do not want displayed as an attachment.
|
||||
|
||||
[[mail-javamail-mime-attachments-attachment]]
|
||||
==== Attachments
|
||||
|
||||
The following example shows you how to use the `MimeMessageHelper` to send an email
|
||||
with a single JPEG image attachment:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
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("test@host.com");
|
||||
|
||||
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);
|
||||
----
|
||||
|
||||
[[mail-javamail-mime-attachments-inline]]
|
||||
==== Inline Resources
|
||||
|
||||
The following example shows you how to use the `MimeMessageHelper` to send an email
|
||||
with an inline image:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
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("test@host.com");
|
||||
|
||||
// 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);
|
||||
----
|
||||
|
||||
WARNING: Inline resources are added to the `MimeMessage` by using the specified `Content-ID`
|
||||
(`identifier1234` in the above example). The order in which you add the text
|
||||
and the resource are very important. Be sure to first add the text and then
|
||||
the resources. If you are doing it the other way around, it does not work.
|
||||
|
||||
|
||||
[[mail-templates]]
|
||||
=== Creating Email Content by Using a Templating Library
|
||||
|
||||
The code in the examples shown in the previous sections explicitly created the content of the email message,
|
||||
by using methods calls such as `message.setText(..)`. This is fine for simple cases, and it
|
||||
is okay in the context of the aforementioned examples, where the intent was to show you
|
||||
the very basics of the API.
|
||||
|
||||
In your typical enterprise application, though, developers often do not create the content
|
||||
of email messages by using the previously shown approach for a number of reasons:
|
||||
|
||||
* Creating HTML-based email content in Java code is tedious and error prone.
|
||||
* There is no clear separation between display logic and business logic.
|
||||
* Changing the display structure of the email content requires writing Java code,
|
||||
recompiling, redeploying, and so on.
|
||||
|
||||
Typically, the approach taken to address these issues is to use a template library (such
|
||||
as FreeMarker) to define the display structure of email content. This leaves your code
|
||||
tasked only with creating the data that is to be rendered in the email template and
|
||||
sending the email. It is definitely a best practice when the content of your email messages
|
||||
becomes even moderately complex, and, with the Spring Framework's support classes for
|
||||
FreeMarker, it becomes quite easy to do.
|
||||
|
||||
1472
framework-docs/src/docs/asciidoc/integration/jms.adoc
Normal file
1472
framework-docs/src/docs/asciidoc/integration/jms.adoc
Normal file
File diff suppressed because it is too large
Load Diff
1375
framework-docs/src/docs/asciidoc/integration/jmx.adoc
Normal file
1375
framework-docs/src/docs/asciidoc/integration/jmx.adoc
Normal file
File diff suppressed because it is too large
Load Diff
517
framework-docs/src/docs/asciidoc/integration/rest-clients.adoc
Normal file
517
framework-docs/src/docs/asciidoc/integration/rest-clients.adoc
Normal file
@@ -0,0 +1,517 @@
|
||||
[[rest-client-access]]
|
||||
= REST Clients
|
||||
|
||||
The Spring Framework provides the following choices for making calls to REST endpoints:
|
||||
|
||||
* <<rest-webclient>> - non-blocking, reactive client w fluent API.
|
||||
* <<rest-resttemplate>> - synchronous client with template method API.
|
||||
* <<rest-http-interface>> - annotated interface with generated, dynamic proxy implementation.
|
||||
|
||||
|
||||
[[rest-webclient]]
|
||||
== `WebClient`
|
||||
|
||||
`WebClient` is a non-blocking, reactive client to perform HTTP requests. It was
|
||||
introduced in 5.0 and offers an alternative to the `RestTemplate`, with support for
|
||||
synchronous, asynchronous, and streaming scenarios.
|
||||
|
||||
`WebClient` supports the following:
|
||||
|
||||
* Non-blocking I/O.
|
||||
* Reactive Streams back pressure.
|
||||
* High concurrency with fewer hardware resources.
|
||||
* Functional-style, fluent API that takes advantage of Java 8 lambdas.
|
||||
* Synchronous and asynchronous interactions.
|
||||
* Streaming up to or streaming down from a server.
|
||||
|
||||
See <<web-reactive.adoc#webflux-client, WebClient>> for more details.
|
||||
|
||||
|
||||
|
||||
|
||||
[[rest-resttemplate]]
|
||||
== `RestTemplate`
|
||||
|
||||
The `RestTemplate` provides a higher level API over HTTP client libraries. It makes it
|
||||
easy to invoke REST endpoints in a single line. It exposes the following groups of
|
||||
overloaded methods:
|
||||
|
||||
NOTE: `RestTemplate` is in maintenance mode, with only requests for minor
|
||||
changes and bugs to be accepted. Please, consider using the
|
||||
<<web-reactive.adoc#webflux-client, WebClient>> instead.
|
||||
|
||||
[[rest-overview-of-resttemplate-methods-tbl]]
|
||||
.RestTemplate methods
|
||||
[cols="1,3"]
|
||||
|===
|
||||
| Method group | Description
|
||||
|
||||
| `getForObject`
|
||||
| Retrieves a representation via GET.
|
||||
|
||||
| `getForEntity`
|
||||
| Retrieves a `ResponseEntity` (that is, status, headers, and body) by using GET.
|
||||
|
||||
| `headForHeaders`
|
||||
| Retrieves all headers for a resource by using HEAD.
|
||||
|
||||
| `postForLocation`
|
||||
| Creates a new resource by using POST and returns the `Location` header from the response.
|
||||
|
||||
| `postForObject`
|
||||
| Creates a new resource by using POST and returns the representation from the response.
|
||||
|
||||
| `postForEntity`
|
||||
| Creates a new resource by using POST and returns the representation from the response.
|
||||
|
||||
| `put`
|
||||
| Creates or updates a resource by using PUT.
|
||||
|
||||
| `patchForObject`
|
||||
| Updates a resource by using PATCH and returns the representation from the response.
|
||||
Note that the JDK `HttpURLConnection` does not support `PATCH`, but Apache
|
||||
HttpComponents and others do.
|
||||
|
||||
| `delete`
|
||||
| Deletes the resources at the specified URI by using DELETE.
|
||||
|
||||
| `optionsForAllow`
|
||||
| Retrieves allowed HTTP methods for a resource by using ALLOW.
|
||||
|
||||
| `exchange`
|
||||
| More generalized (and less opinionated) version of the preceding methods that provides extra
|
||||
flexibility when needed. It accepts a `RequestEntity` (including HTTP method, URL, headers,
|
||||
and body as input) and returns a `ResponseEntity`.
|
||||
|
||||
These methods allow the use of `ParameterizedTypeReference` instead of `Class` to specify
|
||||
a response type with generics.
|
||||
|
||||
| `execute`
|
||||
| The most generalized way to perform a request, with full control over request
|
||||
preparation and response extraction through callback interfaces.
|
||||
|
||||
|===
|
||||
|
||||
[[rest-resttemplate-create]]
|
||||
=== Initialization
|
||||
|
||||
The default constructor uses `java.net.HttpURLConnection` to perform requests. You can
|
||||
switch to a different HTTP library with an implementation of `ClientHttpRequestFactory`.
|
||||
There is built-in support for the following:
|
||||
|
||||
* Apache HttpComponents
|
||||
* Netty
|
||||
* OkHttp
|
||||
|
||||
For example, to switch to Apache HttpComponents, you can use the following:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
|
||||
----
|
||||
|
||||
Each `ClientHttpRequestFactory` exposes configuration options specific to the underlying
|
||||
HTTP client library -- for example, for credentials, connection pooling, and other details.
|
||||
|
||||
TIP: Note that the `java.net` implementation for HTTP requests can raise an exception when
|
||||
accessing the status of a response that represents an error (such as 401). If this is an
|
||||
issue, switch to another HTTP client library.
|
||||
|
||||
[[rest-resttemplate-uri]]
|
||||
==== URIs
|
||||
|
||||
Many of the `RestTemplate` methods accept a URI template and URI template variables,
|
||||
either as a `String` variable argument, or as `Map<String,String>`.
|
||||
|
||||
The following example uses a `String` variable argument:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
String result = restTemplate.getForObject(
|
||||
"https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
|
||||
----
|
||||
|
||||
The following example uses a `Map<String, String>`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
Map<String, String> vars = Collections.singletonMap("hotel", "42");
|
||||
|
||||
String result = restTemplate.getForObject(
|
||||
"https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
|
||||
----
|
||||
|
||||
Keep in mind URI templates are automatically encoded, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
restTemplate.getForObject("https://example.com/hotel list", String.class);
|
||||
|
||||
// Results in request to "https://example.com/hotel%20list"
|
||||
----
|
||||
|
||||
You can use the `uriTemplateHandler` property of `RestTemplate` to customize how URIs
|
||||
are encoded. Alternatively, you can prepare a `java.net.URI` and pass it into one of
|
||||
the `RestTemplate` methods that accepts a `URI`.
|
||||
|
||||
For more details on working with and encoding URIs, see <<web.adoc#mvc-uri-building, URI Links>>.
|
||||
|
||||
[[rest-template-headers]]
|
||||
==== Headers
|
||||
|
||||
You can use the `exchange()` methods to specify request headers, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
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();
|
||||
----
|
||||
|
||||
You can obtain response headers through many `RestTemplate` method variants that return
|
||||
`ResponseEntity`.
|
||||
|
||||
[[rest-template-body]]
|
||||
=== Body
|
||||
|
||||
Objects passed into and returned from `RestTemplate` methods are converted to and from raw
|
||||
content with the help of an `HttpMessageConverter`.
|
||||
|
||||
On a POST, an input object is serialized to the request body, as the following example shows:
|
||||
|
||||
----
|
||||
URI location = template.postForLocation("https://example.com/people", person);
|
||||
----
|
||||
|
||||
You need not explicitly set the Content-Type header of the request. In most cases,
|
||||
you can find a compatible message converter based on the source `Object` type, and the chosen
|
||||
message converter sets the content type accordingly. If necessary, you can use the
|
||||
`exchange` methods to explicitly provide the `Content-Type` request header, and that, in
|
||||
turn, influences what message converter is selected.
|
||||
|
||||
On a GET, the body of the response is deserialized to an output `Object`, as the following example shows:
|
||||
|
||||
----
|
||||
Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);
|
||||
----
|
||||
|
||||
The `Accept` header of the request does not need to be explicitly set. In most cases,
|
||||
a compatible message converter can be found based on the expected response type, which
|
||||
then helps to populate the `Accept` header. If necessary, you can use the `exchange`
|
||||
methods to provide the `Accept` header explicitly.
|
||||
|
||||
By default, `RestTemplate` registers all built-in
|
||||
<<rest-message-conversion, message converters>>, depending on classpath checks that help
|
||||
to determine what optional conversion libraries are present. You can also set the message
|
||||
converters to use explicitly.
|
||||
|
||||
[[rest-message-conversion]]
|
||||
==== Message Conversion
|
||||
[.small]#<<web-reactive.adoc#webflux-codecs, WebFlux>>#
|
||||
|
||||
The `spring-web` module contains the `HttpMessageConverter` contract for reading and
|
||||
writing the body of HTTP requests and responses through `InputStream` and `OutputStream`.
|
||||
`HttpMessageConverter` instances are used on the client side (for example, in the `RestTemplate`) and
|
||||
on the server side (for example, in Spring MVC REST controllers).
|
||||
|
||||
Concrete implementations for the main media (MIME) types are provided in the framework
|
||||
and are, by default, registered with the `RestTemplate` on the client side and with
|
||||
`RequestMappingHandlerAdapter` on the server side (see
|
||||
<<web.adoc#mvc-config-message-converters, Configuring Message Converters>>).
|
||||
|
||||
The implementations of `HttpMessageConverter` are described in the following sections.
|
||||
For all converters, a default media type is used, but you can override it by setting the
|
||||
`supportedMediaTypes` bean property. The following table describes each implementation:
|
||||
|
||||
[[rest-message-converters-tbl]]
|
||||
.HttpMessageConverter Implementations
|
||||
[cols="1,3"]
|
||||
|===
|
||||
| MessageConverter | Description
|
||||
|
||||
| `StringHttpMessageConverter`
|
||||
| An `HttpMessageConverter` implementation that can read and write `String` instances from the HTTP
|
||||
request and response. By default, this converter supports all text media types
|
||||
(`text/{asterisk}`) and writes with a `Content-Type` of `text/plain`.
|
||||
|
||||
| `FormHttpMessageConverter`
|
||||
| An `HttpMessageConverter` implementation that can read and write form data from the HTTP
|
||||
request and response. By default, this converter reads and writes the
|
||||
`application/x-www-form-urlencoded` media type. Form data is read from and written into a
|
||||
`MultiValueMap<String, String>`. The converter can also write (but not read) multipart
|
||||
data read from a `MultiValueMap<String, Object>`. By default, `multipart/form-data` is
|
||||
supported. As of Spring Framework 5.2, additional multipart subtypes can be supported for
|
||||
writing form data. Consult the javadoc for `FormHttpMessageConverter` for further details.
|
||||
|
||||
| `ByteArrayHttpMessageConverter`
|
||||
| An `HttpMessageConverter` implementation that can read and write byte arrays from the
|
||||
HTTP request and response. By default, this converter supports all media types (`{asterisk}/{asterisk}`)
|
||||
and writes with a `Content-Type` of `application/octet-stream`. You can override this
|
||||
by setting the `supportedMediaTypes` property and overriding `getContentType(byte[])`.
|
||||
|
||||
| `MarshallingHttpMessageConverter`
|
||||
| An `HttpMessageConverter` implementation that can read and write XML by using Spring's
|
||||
`Marshaller` and `Unmarshaller` abstractions from the `org.springframework.oxm` package.
|
||||
This converter requires a `Marshaller` and `Unmarshaller` before it can be used. You can inject these
|
||||
through constructor or bean properties. By default, this converter supports
|
||||
`text/xml` and `application/xml`.
|
||||
|
||||
| `MappingJackson2HttpMessageConverter`
|
||||
| An `HttpMessageConverter` implementation that can read and write JSON by using Jackson's
|
||||
`ObjectMapper`. You can customize JSON mapping as needed through the use of Jackson's
|
||||
provided annotations. When you need further control (for cases where custom JSON
|
||||
serializers/deserializers need to be provided for specific types), you can inject a custom `ObjectMapper`
|
||||
through the `ObjectMapper` property. By default, this
|
||||
converter supports `application/json`.
|
||||
|
||||
| `MappingJackson2XmlHttpMessageConverter`
|
||||
| An `HttpMessageConverter` implementation that can read and write XML by using
|
||||
https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML] extension's
|
||||
`XmlMapper`. You can customize XML mapping as needed through the use of JAXB
|
||||
or Jackson's provided annotations. When you need further control (for cases where custom XML
|
||||
serializers/deserializers need to be provided for specific types), you can inject a custom `XmlMapper`
|
||||
through the `ObjectMapper` property. By default, this
|
||||
converter supports `application/xml`.
|
||||
|
||||
| `SourceHttpMessageConverter`
|
||||
| An `HttpMessageConverter` implementation that can read and write
|
||||
`javax.xml.transform.Source` from the HTTP request and response. Only `DOMSource`,
|
||||
`SAXSource`, and `StreamSource` are supported. By default, this converter supports
|
||||
`text/xml` and `application/xml`.
|
||||
|
||||
| `BufferedImageHttpMessageConverter`
|
||||
| An `HttpMessageConverter` implementation that can read and write
|
||||
`java.awt.image.BufferedImage` from the HTTP request and response. This converter reads
|
||||
and writes the media type supported by the Java I/O API.
|
||||
|
||||
|===
|
||||
|
||||
[[rest-template-jsonview]]
|
||||
=== Jackson JSON Views
|
||||
|
||||
You can specify a https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON View]
|
||||
to serialize only a subset of the object properties, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
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);
|
||||
----
|
||||
|
||||
[[rest-template-multipart]]
|
||||
=== Multipart
|
||||
|
||||
To send multipart data, you need to provide a `MultiValueMap<String, Object>` whose values
|
||||
may be an `Object` for part content, a `Resource` for a file part, or an `HttpEntity` for
|
||||
part content with headers. For example:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
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));
|
||||
----
|
||||
|
||||
In most cases, you do not have to specify the `Content-Type` for each part. The content
|
||||
type is determined automatically based on the `HttpMessageConverter` chosen to serialize
|
||||
it or, in the case of a `Resource` based on the file extension. If necessary, you can
|
||||
explicitly provide the `MediaType` with an `HttpEntity` wrapper.
|
||||
|
||||
Once the `MultiValueMap` is ready, you can pass it to the `RestTemplate`, as show below:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
MultiValueMap<String, Object> parts = ...;
|
||||
template.postForObject("https://example.com/upload", parts, Void.class);
|
||||
----
|
||||
|
||||
If the `MultiValueMap` contains at least one non-`String` value, the `Content-Type` is set
|
||||
to `multipart/form-data` by the `FormHttpMessageConverter`. If the `MultiValueMap` has
|
||||
`String` values the `Content-Type` is defaulted to `application/x-www-form-urlencoded`.
|
||||
If necessary the `Content-Type` may also be set explicitly.
|
||||
|
||||
|
||||
[[rest-http-interface]]
|
||||
== HTTP Interface
|
||||
|
||||
The Spring Framework lets you define an HTTP service as a Java interface with annotated
|
||||
methods for HTTP exchanges. You can then generate a proxy that implements this interface
|
||||
and performs the exchanges. This helps to simplify HTTP remote access which often
|
||||
involves a facade that wraps the details of using the underlying HTTP client.
|
||||
|
||||
One, declare an interface with `@HttpExchange` methods:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
interface RepositoryService {
|
||||
|
||||
@GetExchange("/repos/{owner}/{repo}")
|
||||
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
|
||||
|
||||
// more HTTP exchange methods...
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Two, create a proxy that will perform the declared HTTP exchanges:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build();
|
||||
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
|
||||
|
||||
RepositoryService service = factory.createClient(RepositoryService.class);
|
||||
----
|
||||
|
||||
`@HttpExchange` is supported at the type level where it applies to all methods:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
|
||||
interface RepositoryService {
|
||||
|
||||
@GetExchange
|
||||
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
|
||||
|
||||
@PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
void updateRepository(@PathVariable String owner, @PathVariable String repo,
|
||||
@RequestParam String name, @RequestParam String description, @RequestParam String homepage);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
[[rest-http-interface-method-parameters]]
|
||||
=== Method Parameters
|
||||
|
||||
Annotated, HTTP exchange methods support flexible method signatures with the following
|
||||
method parameters:
|
||||
|
||||
[cols="1,2", options="header"]
|
||||
|===
|
||||
| Method argument | Description
|
||||
|
||||
| `URI`
|
||||
| Dynamically set the URL for the request, overriding the annotation's `url` attribute.
|
||||
|
||||
| `HttpMethod`
|
||||
| Dynamically set the HTTP method for the request, overriding the annotation's `method` attribute
|
||||
|
||||
| `@RequestHeader`
|
||||
| Add a request header or mutliple headers. The argument may be a `Map<String, ?>` or
|
||||
`MultiValueMap<String, ?>` with multiple headers, a `Collection<?>` of values, or an
|
||||
individual value. Type conversion is supported for non-String values.
|
||||
|
||||
| `@PathVariable`
|
||||
| Add a variable for expand a placeholder in the request URL. The argument may be a
|
||||
`Map<String, ?>` with multiple variables, or an individual value. Type conversion
|
||||
is supported for non-String values.
|
||||
|
||||
| `@RequestBody`
|
||||
| Provide the body of the request either as an Object to be serialized, or a
|
||||
Reactive Streams `Publisher` such as `Mono`, `Flux`, or any other async type supported
|
||||
through the configured `ReactiveAdapterRegistry`.
|
||||
|
||||
| `@RequestParam`
|
||||
| Add a request parameter or mutliple parameters. The argument may be a `Map<String, ?>`
|
||||
or `MultiValueMap<String, ?>` with multiple parameters, a `Collection<?>` of values, or
|
||||
an individual value. Type conversion is supported for non-String values.
|
||||
|
||||
When `"content-type"` is set to `"application/x-www-form-urlencoded"`, request
|
||||
parameters are encoded in the request body. Otherwise, they are added as URL query
|
||||
parameters.
|
||||
|
||||
| `@RequestPart`
|
||||
| Add a request part, which may be a String (form field), `Resource` (file part),
|
||||
Object (entity to be encoded, e.g. as JSON), `HttpEntity` (part content and headers),
|
||||
a Spring `Part`, or Reactive Streams `Publisher` of any of the above.
|
||||
|
||||
| `@CookieValue`
|
||||
| Add a cookie or mutliple cookies. The argument may be a `Map<String, ?>` or
|
||||
`MultiValueMap<String, ?>` with multiple cookies, a `Collection<?>` of values, or an
|
||||
individual value. Type conversion is supported for non-String values.
|
||||
|
||||
|===
|
||||
|
||||
|
||||
[[rest-http-interface-return-values]]
|
||||
=== Return Values
|
||||
|
||||
Annotated, HTTP exchange methods support the following return values:
|
||||
|
||||
[cols="1,2", options="header"]
|
||||
|===
|
||||
| Method return value | Description
|
||||
|
||||
| `void`, `Mono<Void>`
|
||||
| Perform the given request, and release the response content, if any.
|
||||
|
||||
| `HttpHeaders`, `Mono<HttpHeaders>`
|
||||
| Perform the given request, release the response content, if any, and return the
|
||||
response headers.
|
||||
|
||||
| `<T>`, `Mono<T>`
|
||||
| Perform the given request and decode the response content to the declared return type.
|
||||
|
||||
| `<T>`, `Flux<T>`
|
||||
| Perform the given request and decode the response content to a stream of the declared
|
||||
element type.
|
||||
|
||||
| `ResponseEntity<Void>`, `Mono<ResponseEntity<Void>>`
|
||||
| Perform the given request, and release the response content, if any, and return a
|
||||
`ResponseEntity` with the status and headers.
|
||||
|
||||
| `ResponseEntity<T>`, `Mono<ResponseEntity<T>>`
|
||||
| Perform the given request, decode the response content to the declared return type, and
|
||||
return a `ResponseEntity` with the status, headers, and the decoded body.
|
||||
|
||||
| `Mono<ResponseEntity<Flux<T>>`
|
||||
| Perform the given request, decode the response content to a stream of the declared
|
||||
element type, and return a `ResponseEntity` with the status, headers, and the decoded
|
||||
response body stream.
|
||||
|
||||
|===
|
||||
|
||||
TIP: You can also use any other async or reactive types registered in the
|
||||
`ReactiveAdapterRegistry`.
|
||||
|
||||
|
||||
[[rest-http-interface-exceptions]]
|
||||
=== Exception Handling
|
||||
|
||||
By default, `WebClient` raises `WebClientResponseException` for 4xx and 5xx HTTP status
|
||||
codes. To customize this, you can register a response status handler that applies to all
|
||||
responses performed through the client:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
WebClient webClient = WebClient.builder()
|
||||
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
|
||||
.build();
|
||||
|
||||
WebClientAdapter clientAdapter = WebClientAdapter.forClient(webClient);
|
||||
HttpServiceProxyFactory factory = HttpServiceProxyFactory
|
||||
.builder(clientAdapter).build();
|
||||
----
|
||||
|
||||
For more details and options, such as suppressing error status codes, see the Javadoc of
|
||||
`defaultStatusHandler` in `WebClient.Builder`.
|
||||
967
framework-docs/src/docs/asciidoc/integration/scheduling.adoc
Normal file
967
framework-docs/src/docs/asciidoc/integration/scheduling.adoc
Normal file
@@ -0,0 +1,967 @@
|
||||
[[scheduling]]
|
||||
= Task Execution and Scheduling
|
||||
|
||||
The Spring Framework provides abstractions for the asynchronous execution and scheduling of
|
||||
tasks with the `TaskExecutor` and `TaskScheduler` interfaces, respectively. Spring also
|
||||
features implementations of those interfaces that support thread pools or delegation to
|
||||
CommonJ within an application server environment. Ultimately, the use of these
|
||||
implementations behind the common interfaces abstracts away the differences between Java
|
||||
SE 5, Java SE 6, and Jakarta EE environments.
|
||||
|
||||
Spring also features integration classes to support scheduling with the `Timer`
|
||||
(part of the JDK since 1.3) and the Quartz Scheduler ( https://www.quartz-scheduler.org/[]).
|
||||
You can set up both of those schedulers by using a `FactoryBean` with optional references to
|
||||
`Timer` or `Trigger` instances, respectively. Furthermore, a convenience class for both
|
||||
the Quartz Scheduler and the `Timer` is available that lets you invoke a method of
|
||||
an existing target object (analogous to the normal `MethodInvokingFactoryBean`
|
||||
operation).
|
||||
|
||||
|
||||
|
||||
[[scheduling-task-executor]]
|
||||
== The Spring `TaskExecutor` Abstraction
|
||||
|
||||
Executors are the JDK name for the concept of thread pools. The "`executor`" naming is
|
||||
due to the fact that there is no guarantee that the underlying implementation is
|
||||
actually a pool. An executor may be single-threaded or even synchronous. Spring's
|
||||
abstraction hides implementation details between the Java SE and Jakarta EE environments.
|
||||
|
||||
Spring's `TaskExecutor` interface is identical to the `java.util.concurrent.Executor`
|
||||
interface. In fact, originally, its primary reason for existence was to abstract away
|
||||
the need for Java 5 when using thread pools. The interface has a single method
|
||||
(`execute(Runnable task)`) that accepts a task for execution based on the semantics
|
||||
and configuration of the thread pool.
|
||||
|
||||
The `TaskExecutor` was originally created to give other Spring components an abstraction
|
||||
for thread pooling where needed. Components such as the `ApplicationEventMulticaster`,
|
||||
JMS's `AbstractMessageListenerContainer`, and Quartz integration all use the
|
||||
`TaskExecutor` abstraction to pool threads. However, if your beans need thread pooling
|
||||
behavior, you can also use this abstraction for your own needs.
|
||||
|
||||
|
||||
[[scheduling-task-executor-types]]
|
||||
=== `TaskExecutor` Types
|
||||
|
||||
Spring includes a number of pre-built implementations of `TaskExecutor`.
|
||||
In all likelihood, you should never need to implement your own.
|
||||
The variants that Spring provides are as follows:
|
||||
|
||||
* `SyncTaskExecutor`:
|
||||
This implementation does not run invocations asynchronously. Instead, each
|
||||
invocation takes place in the calling thread. It is primarily used in situations
|
||||
where multi-threading is not necessary, such as in simple test cases.
|
||||
* `SimpleAsyncTaskExecutor`:
|
||||
This implementation does not reuse any threads. Rather, it starts up a new thread
|
||||
for each invocation. However, it does support a concurrency limit that blocks
|
||||
any invocations that are over the limit until a slot has been freed up. If you
|
||||
are looking for true pooling, see `ThreadPoolTaskExecutor`, later in this list.
|
||||
* `ConcurrentTaskExecutor`:
|
||||
This implementation is an adapter for a `java.util.concurrent.Executor` instance.
|
||||
There is an alternative (`ThreadPoolTaskExecutor`) that exposes the `Executor`
|
||||
configuration parameters as bean properties. There is rarely a need to use
|
||||
`ConcurrentTaskExecutor` directly. However, if the `ThreadPoolTaskExecutor` is not
|
||||
flexible enough for your needs, `ConcurrentTaskExecutor` is an alternative.
|
||||
* `ThreadPoolTaskExecutor`:
|
||||
This implementation is most commonly used. It exposes bean properties for
|
||||
configuring a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`.
|
||||
If you need to adapt to a different kind of `java.util.concurrent.Executor`, we
|
||||
recommend that you use a `ConcurrentTaskExecutor` instead.
|
||||
* `DefaultManagedTaskExecutor`:
|
||||
This implementation uses a JNDI-obtained `ManagedExecutorService` in a JSR-236
|
||||
compatible runtime environment (such as a Jakarta EE application server),
|
||||
replacing a CommonJ WorkManager for that purpose.
|
||||
|
||||
|
||||
[[scheduling-task-executor-usage]]
|
||||
=== Using a `TaskExecutor`
|
||||
|
||||
Spring's `TaskExecutor` implementations are used as simple JavaBeans. In the following example,
|
||||
we define a bean that uses the `ThreadPoolTaskExecutor` to asynchronously print
|
||||
out a set of messages:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
As you can see, rather than retrieving a thread from the pool and executing it yourself,
|
||||
you add your `Runnable` to the queue. Then the `TaskExecutor` uses its internal rules to
|
||||
decide when the task gets run.
|
||||
|
||||
To configure the rules that the `TaskExecutor` uses, we expose simple bean properties:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[scheduling-task-scheduler]]
|
||||
== The Spring `TaskScheduler` Abstraction
|
||||
|
||||
In addition to the `TaskExecutor` abstraction, Spring 3.0 introduced a `TaskScheduler`
|
||||
with a variety of methods for scheduling tasks to run at some point in the future.
|
||||
The following listing shows the `TaskScheduler` interface definition:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public interface TaskScheduler {
|
||||
|
||||
ScheduledFuture schedule(Runnable task, Trigger trigger);
|
||||
|
||||
ScheduledFuture schedule(Runnable task, Instant startTime);
|
||||
|
||||
ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
|
||||
|
||||
ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);
|
||||
|
||||
ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
|
||||
|
||||
ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);
|
||||
|
||||
----
|
||||
|
||||
The simplest method is the one named `schedule` that takes only a `Runnable` and an `Instant`.
|
||||
That causes the task to run once after the specified time. All of the other methods
|
||||
are capable of scheduling tasks to run repeatedly. The fixed-rate and fixed-delay
|
||||
methods are for simple, periodic execution, but the method that accepts a `Trigger` is
|
||||
much more flexible.
|
||||
|
||||
|
||||
[[scheduling-trigger-interface]]
|
||||
=== `Trigger` Interface
|
||||
|
||||
The `Trigger` interface is essentially inspired by JSR-236 which, as of Spring 3.0,
|
||||
was not yet officially implemented. The basic idea of the `Trigger` is that execution
|
||||
times may be determined based on past execution outcomes or even arbitrary conditions.
|
||||
If these determinations do take into account the outcome of the preceding execution,
|
||||
that information is available within a `TriggerContext`. The `Trigger` interface itself
|
||||
is quite simple, as the following listing shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public interface Trigger {
|
||||
|
||||
Date nextExecutionTime(TriggerContext triggerContext);
|
||||
}
|
||||
----
|
||||
|
||||
The `TriggerContext` is the most important part. It encapsulates all of
|
||||
the relevant data and is open for extension in the future, if necessary. The
|
||||
`TriggerContext` is an interface (a `SimpleTriggerContext` implementation is used by
|
||||
default). The following listing shows the available methods for `Trigger` implementations.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public interface TriggerContext {
|
||||
|
||||
Date lastScheduledExecutionTime();
|
||||
|
||||
Date lastActualExecutionTime();
|
||||
|
||||
Date lastCompletionTime();
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
[[scheduling-trigger-implementations]]
|
||||
=== `Trigger` Implementations
|
||||
|
||||
Spring provides two implementations of the `Trigger` interface. The most interesting one
|
||||
is the `CronTrigger`. It enables the scheduling of tasks based on
|
||||
<<scheduling-cron-expression,cron expressions>>.
|
||||
For example, the following task is scheduled to run 15 minutes past each hour but only
|
||||
during the 9-to-5 "`business hours`" on weekdays:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
|
||||
----
|
||||
|
||||
The other implementation is a `PeriodicTrigger` that accepts a fixed
|
||||
period, an optional initial delay value, and a boolean to indicate whether the period
|
||||
should be interpreted as a fixed-rate or a fixed-delay. Since the `TaskScheduler`
|
||||
interface already defines methods for scheduling tasks at a fixed rate or with a
|
||||
fixed delay, those methods should be used directly whenever possible. The value of the
|
||||
`PeriodicTrigger` implementation is that you can use it within components that rely on
|
||||
the `Trigger` abstraction. For example, it may be convenient to allow periodic triggers,
|
||||
cron-based triggers, and even custom trigger implementations to be used interchangeably.
|
||||
Such a component could take advantage of dependency injection so that you can configure such `Triggers`
|
||||
externally and, therefore, easily modify or extend them.
|
||||
|
||||
|
||||
[[scheduling-task-scheduler-implementations]]
|
||||
=== `TaskScheduler` implementations
|
||||
|
||||
As with Spring's `TaskExecutor` abstraction, the primary benefit of the `TaskScheduler`
|
||||
arrangement is that an application's scheduling needs are decoupled from the deployment
|
||||
environment. This abstraction level is particularly relevant when deploying to an
|
||||
application server environment where threads should not be created directly by the
|
||||
application itself. For such scenarios, Spring provides a `TimerManagerTaskScheduler`
|
||||
that delegates to a CommonJ `TimerManager` on WebLogic or WebSphere as well as a more recent
|
||||
`DefaultManagedTaskScheduler` that delegates to a JSR-236 `ManagedScheduledExecutorService`
|
||||
in a Jakarta EE environment. Both are typically configured with a JNDI lookup.
|
||||
|
||||
Whenever external thread management is not a requirement, a simpler alternative is
|
||||
a local `ScheduledExecutorService` setup within the application, which can be adapted
|
||||
through Spring's `ConcurrentTaskScheduler`. As a convenience, Spring also provides a
|
||||
`ThreadPoolTaskScheduler`, which internally delegates to a `ScheduledExecutorService`
|
||||
to provide common bean-style configuration along the lines of `ThreadPoolTaskExecutor`.
|
||||
These variants work perfectly fine for locally embedded thread pool setups in lenient
|
||||
application server environments, as well -- in particular on Tomcat and Jetty.
|
||||
|
||||
|
||||
|
||||
[[scheduling-annotation-support]]
|
||||
== Annotation Support for Scheduling and Asynchronous Execution
|
||||
|
||||
Spring provides annotation support for both task scheduling and asynchronous method
|
||||
execution.
|
||||
|
||||
|
||||
[[scheduling-enable-annotation-support]]
|
||||
=== Enable Scheduling Annotations
|
||||
|
||||
To enable support for `@Scheduled` and `@Async` annotations, you can add `@EnableScheduling` and
|
||||
`@EnableAsync` to one of your `@Configuration` classes, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
public class AppConfig {
|
||||
}
|
||||
----
|
||||
|
||||
You can pick and choose the relevant annotations for your application. For example,
|
||||
if you need only support for `@Scheduled`, you can omit `@EnableAsync`. For more
|
||||
fine-grained control, you can additionally implement the `SchedulingConfigurer`
|
||||
interface, the `AsyncConfigurer` interface, or both. See the
|
||||
{api-spring-framework}/scheduling/annotation/SchedulingConfigurer.html[`SchedulingConfigurer`]
|
||||
and {api-spring-framework}/scheduling/annotation/AsyncConfigurer.html[`AsyncConfigurer`]
|
||||
javadoc for full details.
|
||||
|
||||
If you prefer XML configuration, you can use the `<task:annotation-driven>` element,
|
||||
as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
|
||||
<task:executor id="myExecutor" pool-size="5"/>
|
||||
<task:scheduler id="myScheduler" pool-size="10"/>
|
||||
----
|
||||
|
||||
Note that, with the preceding XML, an executor reference is provided for handling those
|
||||
tasks that correspond to methods with the `@Async` annotation, and the scheduler
|
||||
reference is provided for managing those methods annotated with `@Scheduled`.
|
||||
|
||||
NOTE: The default advice mode for processing `@Async` annotations is `proxy` which allows
|
||||
for interception of calls through the proxy only. Local calls within the same class
|
||||
cannot get intercepted that way. For a more advanced mode of interception, consider
|
||||
switching to `aspectj` mode in combination with compile-time or load-time weaving.
|
||||
|
||||
|
||||
[[scheduling-annotation-support-scheduled]]
|
||||
=== The `@Scheduled` annotation
|
||||
|
||||
You can add the `@Scheduled` annotation to a method, along with trigger metadata. For
|
||||
example, the following method is invoked every five seconds (5000 milliseconds) with a
|
||||
fixed delay, meaning that the period is measured from the completion time of each
|
||||
preceding invocation.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Scheduled(fixedDelay = 5000)
|
||||
public void doSomething() {
|
||||
// something that should run periodically
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
By default, milliseconds will be used as the time unit for fixed delay, fixed rate, and
|
||||
initial delay values. If you would like to use a different time unit such as seconds or
|
||||
minutes, you can configure this via the `timeUnit` attribute in `@Scheduled`.
|
||||
|
||||
For example, the previous example can also be written as follows.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
|
||||
public void doSomething() {
|
||||
// something that should run periodically
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
If you need a fixed-rate execution, you can use the `fixedRate` attribute within the
|
||||
annotation. The following method is invoked every five seconds (measured between the
|
||||
successive start times of each invocation).
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
|
||||
public void doSomething() {
|
||||
// something that should run periodically
|
||||
}
|
||||
----
|
||||
|
||||
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating the
|
||||
amount of time to wait before the first execution of the method, as the following
|
||||
`fixedRate` example shows.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Scheduled(initialDelay = 1000, fixedRate = 5000)
|
||||
public void doSomething() {
|
||||
// something that should run periodically
|
||||
}
|
||||
----
|
||||
|
||||
If simple periodic scheduling is not expressive enough, you can provide a
|
||||
<<scheduling-cron-expression,cron expression>>.
|
||||
The following example runs only on weekdays:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
@Scheduled(cron="*/5 * * * * MON-FRI")
|
||||
public void doSomething() {
|
||||
// something that should run on weekdays only
|
||||
}
|
||||
----
|
||||
|
||||
TIP: You can also use the `zone` attribute to specify the time zone in which the cron
|
||||
expression is resolved.
|
||||
|
||||
Notice that the methods to be scheduled must have void returns and must not accept any
|
||||
arguments. If the method needs to interact with other objects from the application
|
||||
context, those would typically have been provided through dependency injection.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
As of Spring Framework 4.3, `@Scheduled` methods are supported on beans of any scope.
|
||||
|
||||
Make sure that you are not initializing multiple instances of the same `@Scheduled`
|
||||
annotation class at runtime, unless you do want to schedule callbacks to each such
|
||||
instance. Related to this, make sure that you do not use `@Configurable` on bean
|
||||
classes that are annotated with `@Scheduled` and registered as regular Spring beans
|
||||
with the container. Otherwise, you would get double initialization (once through the
|
||||
container and once through the `@Configurable` aspect), with the consequence of each
|
||||
`@Scheduled` method being invoked twice.
|
||||
====
|
||||
|
||||
|
||||
[[scheduling-annotation-support-async]]
|
||||
=== The `@Async` annotation
|
||||
|
||||
You can provide the `@Async` annotation on a method so that invocation of that method
|
||||
occurs asynchronously. In other words, the caller returns immediately upon
|
||||
invocation, while the actual execution of the method occurs in a task that has been
|
||||
submitted to a Spring `TaskExecutor`. In the simplest case, you can apply the annotation
|
||||
to a method that returns `void`, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Async
|
||||
void doSomething() {
|
||||
// this will be run asynchronously
|
||||
}
|
||||
----
|
||||
|
||||
Unlike the methods annotated with the `@Scheduled` annotation, these methods can expect
|
||||
arguments, because they are invoked in the "`normal`" way by callers at runtime rather
|
||||
than from a scheduled task being managed by the container. For example, the following code is
|
||||
a legitimate application of the `@Async` annotation:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Async
|
||||
void doSomething(String s) {
|
||||
// this will be run asynchronously
|
||||
}
|
||||
----
|
||||
|
||||
Even methods that return a value can be invoked asynchronously. However, such methods
|
||||
are required to have a `Future`-typed return value. This still provides the benefit of
|
||||
asynchronous execution so that the caller can perform other tasks prior to calling
|
||||
`get()` on that `Future`. The following example shows how to use `@Async` on a method
|
||||
that returns a value:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Async
|
||||
Future<String> returnSomething(int i) {
|
||||
// this will be run asynchronously
|
||||
}
|
||||
----
|
||||
|
||||
TIP: `@Async` methods may not only declare a regular `java.util.concurrent.Future` return type
|
||||
but also Spring's `org.springframework.util.concurrent.ListenableFuture` or, as of Spring
|
||||
4.2, JDK 8's `java.util.concurrent.CompletableFuture`, for richer interaction with the
|
||||
asynchronous task and for immediate composition with further processing steps.
|
||||
|
||||
You can not use `@Async` in conjunction with lifecycle callbacks such as
|
||||
`@PostConstruct`. To asynchronously initialize Spring beans, you currently have to use
|
||||
a separate initializing Spring bean that then invokes the `@Async` annotated method on the
|
||||
target, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: There is no direct XML equivalent for `@Async`, since such methods should be designed
|
||||
for asynchronous execution in the first place, not externally re-declared to be asynchronous.
|
||||
However, you can manually set up Spring's `AsyncExecutionInterceptor` with Spring AOP,
|
||||
in combination with a custom pointcut.
|
||||
|
||||
|
||||
[[scheduling-annotation-support-qualification]]
|
||||
=== Executor Qualification with `@Async`
|
||||
|
||||
By default, when specifying `@Async` on a method, the executor that is used is the
|
||||
one <<scheduling-enable-annotation-support, configured when enabling async support>>,
|
||||
i.e. the "`annotation-driven`" element if you are using XML or your `AsyncConfigurer`
|
||||
implementation, if any. However, you can use the `value` attribute of the `@Async`
|
||||
annotation when you need to indicate that an executor other than the default should be
|
||||
used when executing a given method. The following example shows how to do so:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Async("otherExecutor")
|
||||
void doSomething(String s) {
|
||||
// this will be run asynchronously by "otherExecutor"
|
||||
}
|
||||
----
|
||||
|
||||
In this case, `"otherExecutor"` can be the name of any `Executor` bean in the Spring
|
||||
container, or it may be the name of a qualifier associated with any `Executor` (for example, as
|
||||
specified with the `<qualifier>` element or Spring's `@Qualifier` annotation).
|
||||
|
||||
|
||||
[[scheduling-annotation-support-exception]]
|
||||
=== Exception Management with `@Async`
|
||||
|
||||
When an `@Async` method has a `Future`-typed return value, it is easy to manage
|
||||
an exception that was thrown during the method execution, as this exception is
|
||||
thrown when calling `get` on the `Future` result. With a `void` return type,
|
||||
however, the exception is uncaught and cannot be transmitted. You can provide an
|
||||
`AsyncUncaughtExceptionHandler` to handle such exceptions. The following example shows
|
||||
how to do so:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
|
||||
|
||||
@Override
|
||||
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
|
||||
// handle exception
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
By default, the exception is merely logged. You can define a custom `AsyncUncaughtExceptionHandler`
|
||||
by using `AsyncConfigurer` or the `<task:annotation-driven/>` XML element.
|
||||
|
||||
|
||||
|
||||
[[scheduling-task-namespace]]
|
||||
== The `task` Namespace
|
||||
|
||||
As of version 3.0, Spring includes an XML namespace for configuring `TaskExecutor` and
|
||||
`TaskScheduler` instances. It also provides a convenient way to configure tasks to be
|
||||
scheduled with a trigger.
|
||||
|
||||
|
||||
[[scheduling-task-namespace-scheduler]]
|
||||
=== The 'scheduler' Element
|
||||
|
||||
The following element creates a `ThreadPoolTaskScheduler` instance with the
|
||||
specified thread pool size:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<task:scheduler id="scheduler" pool-size="10"/>
|
||||
----
|
||||
|
||||
The value provided for the `id` attribute is used as the prefix for thread names
|
||||
within the pool. The `scheduler` element is relatively straightforward. If you do not
|
||||
provide a `pool-size` attribute, the default thread pool has only a single thread.
|
||||
There are no other configuration options for the scheduler.
|
||||
|
||||
|
||||
[[scheduling-task-namespace-executor]]
|
||||
=== The `executor` Element
|
||||
|
||||
The following creates a `ThreadPoolTaskExecutor` instance:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<task:executor id="executor" pool-size="10"/>
|
||||
----
|
||||
|
||||
As with the scheduler shown in the <<scheduling-task-namespace-scheduler, previous section>>,
|
||||
the value provided for the `id` attribute is used as the prefix for thread names within
|
||||
the pool. As far as the pool size is concerned, the `executor` element supports more
|
||||
configuration options than the `scheduler` element. For one thing, the thread pool for
|
||||
a `ThreadPoolTaskExecutor` is itself more configurable. Rather than only a single size,
|
||||
an executor's thread pool can have different values for the core and the max size.
|
||||
If you provide a single value, the executor has a fixed-size thread pool (the core and
|
||||
max sizes are the same). However, the `executor` element's `pool-size` attribute also
|
||||
accepts a range in the form of `min-max`. The following example sets a minimum value of
|
||||
`5` and a maximum value of `25`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<task:executor
|
||||
id="executorWithPoolSizeRange"
|
||||
pool-size="5-25"
|
||||
queue-capacity="100"/>
|
||||
----
|
||||
|
||||
In the preceding configuration, a `queue-capacity` value has also been provided.
|
||||
The configuration of the thread pool should also be considered in light of the
|
||||
executor's queue capacity. For the full description of the relationship between pool
|
||||
size and queue capacity, see the documentation for
|
||||
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html[`ThreadPoolExecutor`].
|
||||
The main idea is that, when a task is submitted, the executor first tries to use a
|
||||
free thread if the number of active threads is currently less than the core size.
|
||||
If the core size has been reached, the task is added to the queue, as long as its
|
||||
capacity has not yet been reached. Only then, if the queue's capacity has been
|
||||
reached, does the executor create a new thread beyond the core size. If the max size
|
||||
has also been reached, then the executor rejects the task.
|
||||
|
||||
By default, the queue is unbounded, but this is rarely the desired configuration,
|
||||
because it can lead to `OutOfMemoryErrors` if enough tasks are added to that queue while
|
||||
all pool threads are busy. Furthermore, if the queue is unbounded, the max size has
|
||||
no effect at all. Since the executor always tries the queue before creating a new
|
||||
thread beyond the core size, a queue must have a finite capacity for the thread pool to
|
||||
grow beyond the core size (this is why a fixed-size pool is the only sensible case
|
||||
when using an unbounded queue).
|
||||
|
||||
Consider the case, as mentioned above, when a task is rejected. By default, when a
|
||||
task is rejected, a thread pool executor throws a `TaskRejectedException`. However,
|
||||
the rejection policy is actually configurable. The exception is thrown when using
|
||||
the default rejection policy, which is the `AbortPolicy` implementation.
|
||||
For applications where some tasks can be skipped under heavy load, you can instead
|
||||
configure either `DiscardPolicy` or `DiscardOldestPolicy`. Another option that works
|
||||
well for applications that need to throttle the submitted tasks under heavy load is
|
||||
the `CallerRunsPolicy`. Instead of throwing an exception or discarding tasks,
|
||||
that policy forces the thread that is calling the submit method to run the task itself.
|
||||
The idea is that such a caller is busy while running that task and not able to submit
|
||||
other tasks immediately. Therefore, it provides a simple way to throttle the incoming
|
||||
load while maintaining the limits of the thread pool and queue. Typically, this allows
|
||||
the executor to "`catch up`" on the tasks it is handling and thereby frees up some
|
||||
capacity on the queue, in the pool, or both. You can choose any of these options from an
|
||||
enumeration of values available for the `rejection-policy` attribute on the `executor`
|
||||
element.
|
||||
|
||||
The following example shows an `executor` element with a number of attributes to specify
|
||||
various behaviors:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<task:executor
|
||||
id="executorWithCallerRunsPolicy"
|
||||
pool-size="5-25"
|
||||
queue-capacity="100"
|
||||
rejection-policy="CALLER_RUNS"/>
|
||||
----
|
||||
|
||||
Finally, the `keep-alive` setting determines the time limit (in seconds) for which threads
|
||||
may remain idle before being stopped. If there are more than the core number of threads
|
||||
currently in the pool, after waiting this amount of time without processing a task, excess
|
||||
threads get stopped. A time value of zero causes excess threads to stop
|
||||
immediately after executing a task without remaining follow-up work in the task queue.
|
||||
The following example sets the `keep-alive` value to two minutes:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<task:executor
|
||||
id="executorWithKeepAlive"
|
||||
pool-size="5-25"
|
||||
keep-alive="120"/>
|
||||
----
|
||||
|
||||
|
||||
[[scheduling-task-namespace-scheduled-tasks]]
|
||||
=== The 'scheduled-tasks' Element
|
||||
|
||||
The most powerful feature of Spring's task namespace is the support for configuring
|
||||
tasks to be scheduled within a Spring Application Context. This follows an approach
|
||||
similar to other "`method-invokers`" in Spring, such as that provided by the JMS namespace
|
||||
for configuring message-driven POJOs. Basically, a `ref` attribute can point to any
|
||||
Spring-managed object, and the `method` attribute provides the name of a method to be
|
||||
invoked on that object. The following listing shows a simple example:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<task:scheduled-tasks scheduler="myScheduler">
|
||||
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/>
|
||||
</task:scheduled-tasks>
|
||||
|
||||
<task:scheduler id="myScheduler" pool-size="10"/>
|
||||
----
|
||||
|
||||
The scheduler is referenced by the outer element, and each individual
|
||||
task includes the configuration of its trigger metadata. In the preceding example, that
|
||||
metadata defines a periodic trigger with a fixed delay indicating the number of
|
||||
milliseconds to wait after each task execution has completed. Another option is
|
||||
`fixed-rate`, indicating how often the method should be run regardless of how long
|
||||
any previous execution takes. Additionally, for both `fixed-delay` and `fixed-rate` tasks, you can specify an
|
||||
'initial-delay' parameter, indicating the number of milliseconds to wait
|
||||
before the first execution of the method. For more control, you can instead provide a `cron` attribute
|
||||
to provide a <<scheduling-cron-expression,cron expression>>.
|
||||
The following example shows these other options:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
<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"/>
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[scheduling-cron-expression]]
|
||||
== Cron Expressions
|
||||
|
||||
All Spring cron expressions have to conform to the same format, whether you are using them in
|
||||
<<scheduling-annotation-support-scheduled,`@Scheduled` annotations>>,
|
||||
<<scheduling-task-namespace-scheduled-tasks,`task:scheduled-tasks` elements>>,
|
||||
or someplace else.
|
||||
A well-formed cron expression, such as `* * * * * *`, consists of six space-separated time and date
|
||||
fields, each with its own range of valid values:
|
||||
|
||||
|
||||
....
|
||||
┌───────────── 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)
|
||||
│ │ │ │ │ │
|
||||
* * * * * *
|
||||
....
|
||||
|
||||
There are some rules that apply:
|
||||
|
||||
* A field may be an asterisk (`*`), which always stands for "`first-last`".
|
||||
For the day-of-the-month or day-of-the-week fields, a question mark (`?`) may be used instead of an
|
||||
asterisk.
|
||||
* Commas (`,`) are used to separate items of a list.
|
||||
* Two numbers separated with a hyphen (`-`) express a range of numbers.
|
||||
The specified range is inclusive.
|
||||
* Following a range (or `*`) with `/` specifies the interval of the number's value through the range.
|
||||
* English names can also be used for the month and day-of-week fields.
|
||||
Use the first three letters of the particular day or month (case does not matter).
|
||||
* The day-of-month and day-of-week fields can contain a `L` character, which has a different meaning
|
||||
** In the day-of-month field, `L` stands for _the last day of the month_.
|
||||
If followed by a negative offset (that is, `L-n`), it means _``n``th-to-last day of the month_.
|
||||
** In the day-of-week field, `L` stands for _the last day of the week_.
|
||||
If prefixed by a number or three-letter name (`dL` or `DDDL`), it means _the last day of week (`d`
|
||||
or `DDD`) in the month_.
|
||||
* The day-of-month field can be `nW`, which stands for _the nearest weekday to day of the month ``n``_.
|
||||
If `n` falls on Saturday, this yields the Friday before it.
|
||||
If `n` falls on Sunday, this yields the Monday after, which also happens if `n` is `1` and falls on
|
||||
a Saturday (that is: `1W` stands for _the first weekday of the month_).
|
||||
* If the day-of-month field is `LW`, it means _the last weekday of the month_.
|
||||
* The day-of-week field can be `d#n` (or `DDD#n`), which stands for _the ``n``th day of week `d`
|
||||
(or ``DDD``) in the month_.
|
||||
|
||||
Here are some examples:
|
||||
|
||||
|===
|
||||
| Cron Expression | Meaning
|
||||
|
||||
|`0 0 * * * *` | top of every hour of every day
|
||||
|`*/10 * * * * *` | every ten seconds
|
||||
| `0 0 8-10 * * *` | 8, 9 and 10 o'clock of every day
|
||||
| `0 0 6,19 * * *` | 6:00 AM and 7:00 PM every day
|
||||
| `0 0/30 8-10 * * *` | 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day
|
||||
| `0 0 9-17 * * MON-FRI`| on the hour nine-to-five weekdays
|
||||
| `0 0 0 25 DEC ?` | every Christmas Day at midnight
|
||||
| `0 0 0 L * *` | last day of the month at midnight
|
||||
| `0 0 0 L-3 * *` | third-to-last day of the month at midnight
|
||||
| `0 0 0 * * 5L` | last Friday of the month at midnight
|
||||
| `0 0 0 * * THUL` | last Thursday of the month at midnight
|
||||
| `0 0 0 1W * *` | first weekday of the month at midnight
|
||||
| `0 0 0 LW * *` | last weekday of the month at midnight
|
||||
| `0 0 0 ? * 5#2` | the second Friday in the month at midnight
|
||||
| `0 0 0 ? * MON#1` | the first Monday in the month at midnight
|
||||
|===
|
||||
|
||||
=== Macros
|
||||
|
||||
Expressions such as `0 0 * * * *` are hard for humans to parse and are, therefore, hard to fix in case of bugs.
|
||||
To improve readability, Spring supports the following macros, which represent commonly used sequences.
|
||||
You can use these macros instead of the six-digit value, thus: `@Scheduled(cron = "@hourly")`.
|
||||
|
||||
|===
|
||||
|Macro | Meaning
|
||||
|
||||
| `@yearly` (or `@annually`) | once a year (`0 0 0 1 1 *`)
|
||||
| `@monthly` | once a month (`0 0 0 1 * *`)
|
||||
| `@weekly` | once a week (`0 0 0 * * 0`)
|
||||
| `@daily` (or `@midnight`) | once a day (`0 0 0 * * *`), or
|
||||
| `@hourly` | once an hour, (`0 0 * * * *`)
|
||||
|===
|
||||
|
||||
|
||||
|
||||
[[scheduling-quartz]]
|
||||
== Using the Quartz Scheduler
|
||||
|
||||
Quartz uses `Trigger`, `Job`, and `JobDetail` objects to realize scheduling of all kinds
|
||||
of jobs. For the basic concepts behind Quartz, see
|
||||
https://www.quartz-scheduler.org/[]. For convenience purposes, Spring offers a couple of
|
||||
classes that simplify using Quartz within Spring-based applications.
|
||||
|
||||
|
||||
[[scheduling-quartz-jobdetail]]
|
||||
=== Using the `JobDetailFactoryBean`
|
||||
|
||||
Quartz `JobDetail` objects contain all the information needed to run a job. Spring provides a
|
||||
`JobDetailFactoryBean`, which provides bean-style properties for XML configuration purposes.
|
||||
Consider the following example:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
The job detail configuration has all the information it needs to run the job (`ExampleJob`).
|
||||
The timeout is specified in the job data map. The job data map is available through the
|
||||
`JobExecutionContext` (passed to you at execution time), but the `JobDetail` also gets
|
||||
its properties from the job data mapped to properties of the job instance. So, in the following example,
|
||||
the `ExampleJob` contains a bean property named `timeout`, and the `JobDetail`
|
||||
has it applied automatically:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
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
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
All additional properties from the job data map are available to you as well.
|
||||
|
||||
NOTE: By using the `name` and `group` properties, you can modify the name and the group
|
||||
of the job, respectively. By default, the name of the job matches the bean name
|
||||
of the `JobDetailFactoryBean` (`exampleJob` in the preceding example above).
|
||||
|
||||
|
||||
[[scheduling-quartz-method-invoking-job]]
|
||||
=== Using the `MethodInvokingJobDetailFactoryBean`
|
||||
|
||||
Often you merely need to invoke a method on a specific object. By using the
|
||||
`MethodInvokingJobDetailFactoryBean`, you can do exactly this, as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
|
||||
<property name="targetObject" ref="exampleBusinessObject"/>
|
||||
<property name="targetMethod" value="doIt"/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
The preceding example results in the `doIt` method being called on the
|
||||
`exampleBusinessObject` method, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class ExampleBusinessObject {
|
||||
|
||||
// properties and collaborators
|
||||
|
||||
public void doIt() {
|
||||
// do the actual work
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>
|
||||
----
|
||||
|
||||
By using the `MethodInvokingJobDetailFactoryBean`, you need not create one-line jobs
|
||||
that merely invoke a method. You need only create the actual business object and
|
||||
wire up the detail object.
|
||||
|
||||
By default, Quartz Jobs are stateless, resulting in the possibility of jobs interfering
|
||||
with each other. If you specify two triggers for the same `JobDetail`, it is
|
||||
possible that, before the first job has finished, the second one starts. If
|
||||
`JobDetail` classes implement the `Stateful` interface, this does not happen. The second
|
||||
job does not start before the first one has finished. To make jobs resulting from the
|
||||
`MethodInvokingJobDetailFactoryBean` be non-concurrent, set the `concurrent` flag to
|
||||
`false`, as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
NOTE: By default, jobs will run in a concurrent fashion.
|
||||
|
||||
|
||||
[[scheduling-quartz-cron]]
|
||||
=== Wiring up Jobs by Using Triggers and `SchedulerFactoryBean`
|
||||
|
||||
We have created job details and jobs. We have also reviewed the convenience bean that lets
|
||||
you invoke a method on a specific object. Of course, we still need to schedule the
|
||||
jobs themselves. This is done by using triggers and a `SchedulerFactoryBean`. Several
|
||||
triggers are available within Quartz, and Spring offers two Quartz `FactoryBean`
|
||||
implementations with convenient defaults: `CronTriggerFactoryBean` and
|
||||
`SimpleTriggerFactoryBean`.
|
||||
|
||||
Triggers need to be scheduled. Spring offers a `SchedulerFactoryBean` that exposes
|
||||
triggers to be set as properties. `SchedulerFactoryBean` schedules the actual jobs with
|
||||
those triggers.
|
||||
|
||||
The following listing uses both a `SimpleTriggerFactoryBean` and a `CronTriggerFactoryBean`:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
The preceding example sets up two triggers, one running every 50 seconds with a starting delay of 10
|
||||
seconds and one running every morning at 6 AM. To finalize everything, we need to set up the
|
||||
`SchedulerFactoryBean`, as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
|
||||
<property name="triggers">
|
||||
<list>
|
||||
<ref bean="cronTrigger"/>
|
||||
<ref bean="simpleTrigger"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
|
||||
More properties are available for the `SchedulerFactoryBean`, such as the calendars used by the
|
||||
job details, properties to customize Quartz with, and a Spring-provided JDBC DataSource. See
|
||||
the {api-spring-framework}/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean`]
|
||||
javadoc for more information.
|
||||
|
||||
NOTE: `SchedulerFactoryBean` also recognizes a `quartz.properties` file in the classpath,
|
||||
based on Quartz property keys, as with regular Quartz configuration. Please note that many
|
||||
`SchedulerFactoryBean` settings interact with common Quartz settings in the properties file;
|
||||
it is therefore not recommended to specify values at both levels. For example, do not set
|
||||
an "org.quartz.jobStore.class" property if you mean to rely on a Spring-provided DataSource,
|
||||
or specify an `org.springframework.scheduling.quartz.LocalDataSourceJobStore` variant which
|
||||
is a full-fledged replacement for the standard `org.quartz.impl.jdbcjobstore.JobStoreTX`.
|
||||
|
||||
Reference in New Issue
Block a user