From 659500bc1f33350157605b32b8b7894a601a4e01 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Sep 2023 17:36:57 +0200 Subject: [PATCH] Polishing --- .../ROOT/pages/integration/scheduling.adoc | 100 +++++++++--------- .../scheduling/config/FixedRateTask.java | 1 - .../scheduling/config/IntervalTask.java | 2 - .../jdbc/core/JdbcTemplateQueryTests.java | 39 ++++++- .../jdbc/core/JdbcTemplateTests.java | 6 +- 5 files changed, 85 insertions(+), 63 deletions(-) diff --git a/framework-docs/modules/ROOT/pages/integration/scheduling.adoc b/framework-docs/modules/ROOT/pages/integration/scheduling.adoc index 132805d3e3b7..889209ebe972 100644 --- a/framework-docs/modules/ROOT/pages/integration/scheduling.adoc +++ b/framework-docs/modules/ROOT/pages/integration/scheduling.adoc @@ -5,16 +5,11 @@ The Spring Framework provides abstractions for the asynchronous execution and sc 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. +implementations behind the common interfaces abstracts away the differences between +Java SE and Jakarta EE environments. -Spring also features integration classes to support scheduling with the `Timer` -(part of the JDK since 1.3) and the https://www.quartz-scheduler.org/[Quartz Scheduler]. -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). +Spring also features integration classes to support scheduling with the +https://www.quartz-scheduler.org/[Quartz Scheduler]. @@ -261,8 +256,8 @@ 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: +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"] ---- @@ -336,7 +331,7 @@ For example, the previous example can also be written as follows. 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). +successive start times of each invocation): [source,java,indent=0,subs="verbatim,quotes"] ---- @@ -346,9 +341,9 @@ successive start times of each invocation). } ---- -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. +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"] ---- @@ -417,8 +412,8 @@ to a method that returns `void`, as the following example shows: 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: +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"] ---- @@ -442,15 +437,15 @@ that returns a value: } ---- -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. +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: +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"] ---- @@ -504,8 +499,8 @@ used when executing a given method. The following example shows how to do so: ---- 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 `` element or Spring's `@Qualifier` annotation). +container, or it may be the name of a qualifier associated with any `Executor` (for example, +as specified with the `` element or Spring's `@Qualifier` annotation). [[scheduling-annotation-support-exception]] @@ -673,14 +668,15 @@ invoked on that object. The following listing shows a simple example: ---- 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 +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 xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression]. +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 +xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression]. The following example shows these other options: [source,xml,indent=0] @@ -703,9 +699,8 @@ The following example shows these other options: All Spring cron expressions have to conform to the same format, whether you are using them in xref:integration/scheduling.adoc#scheduling-annotation-support-scheduled[`@Scheduled` annotations], xref:integration/scheduling.adoc#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: +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: .... @@ -770,9 +765,10 @@ Here are some examples: [[macros]] === 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")`. +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 @@ -789,8 +785,8 @@ You can use these macros instead of the six-digit value, thus: `@Scheduled(cron [[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 the +Quartz uses `Trigger`, `Job`, and `JobDetail` objects to realize scheduling of all +kinds of jobs. For the basic concepts behind Quartz, see the https://www.quartz-scheduler.org/[Quartz Web site]. For convenience purposes, Spring offers a couple of classes that simplify using Quartz within Spring-based applications. @@ -798,9 +794,9 @@ offers a couple of classes that simplify using Quartz within Spring-based applic [[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: +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"] ---- @@ -817,9 +813,9 @@ Consider the following example: 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: +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,quotes",chomp="-packages"] ---- @@ -912,8 +908,8 @@ 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 +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 @@ -944,9 +940,9 @@ The following listing uses both a `SimpleTriggerFactoryBean` and a `CronTriggerF ---- -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: +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"] ---- diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/FixedRateTask.java b/spring-context/src/main/java/org/springframework/scheduling/config/FixedRateTask.java index 99350a1e3bd2..9e4e28190e5d 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/FixedRateTask.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/FixedRateTask.java @@ -56,5 +56,4 @@ public FixedRateTask(Runnable runnable, Duration interval, Duration initialDelay super(task); } - } diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/IntervalTask.java b/spring-context/src/main/java/org/springframework/scheduling/config/IntervalTask.java index a0a44146ba5c..32a2130ef96b 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/IntervalTask.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/IntervalTask.java @@ -99,8 +99,6 @@ public IntervalTask(Runnable runnable, Duration interval, Duration initialDelay) } - - /** * Return how often in milliseconds the task should be executed. * @deprecated as of 6.0, in favor of {@link #getIntervalDuration()} diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateQueryTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateQueryTests.java index 2bf1b7f62077..24da6eeb6718 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateQueryTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateQueryTests.java @@ -66,15 +66,17 @@ public class JdbcTemplateQueryTests { @BeforeEach - public void setUp() throws Exception { + public void setup() throws Exception { given(this.dataSource.getConnection()).willReturn(this.connection); - given(this.resultSet.getMetaData()).willReturn(this.resultSetMetaData); - given(this.resultSetMetaData.getColumnCount()).willReturn(1); - given(this.resultSetMetaData.getColumnLabel(1)).willReturn("age"); given(this.connection.createStatement()).willReturn(this.statement); given(this.connection.prepareStatement(anyString())).willReturn(this.preparedStatement); - given(this.preparedStatement.executeQuery()).willReturn(this.resultSet); + given(this.statement.getConnection()).willReturn(this.connection); given(this.statement.executeQuery(anyString())).willReturn(this.resultSet); + given(this.preparedStatement.getConnection()).willReturn(this.connection); + given(this.preparedStatement.executeQuery()).willReturn(this.resultSet); + given(this.resultSet.getMetaData()).willReturn(this.resultSetMetaData); + given(this.resultSetMetaData.getColumnCount()).willReturn(1); + given(this.resultSetMetaData.getColumnLabel(1)).willReturn("age"); } @@ -89,6 +91,7 @@ public void testQueryForList() throws Exception { assertThat(((Integer) li.get(1).get("age"))).as("Second row is Integer").isEqualTo(12); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -99,6 +102,7 @@ public void testQueryForListWithEmptyResult() throws Exception { assertThat(li).as("All rows returned").isEmpty(); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -111,6 +115,7 @@ public void testQueryForListWithSingleRowAndColumn() throws Exception { assertThat(((Integer) li.get(0).get("age"))).as("First row is Integer").isEqualTo(11); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -123,6 +128,7 @@ public void testQueryForListWithIntegerElement() throws Exception { assertThat(li.get(0)).as("Element is Integer").isEqualTo(11); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -134,6 +140,7 @@ public void testQueryForMapWithSingleRowAndColumn() throws Exception { assertThat((Integer) map.get("age")).as("Wow is Integer").isEqualTo(11); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -145,6 +152,7 @@ public void testQueryForObjectThrowsIncorrectResultSizeForMoreThanOneRow() throw this.template.queryForObject(sql, String.class)); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -156,6 +164,7 @@ public void testQueryForObjectWithRowMapper() throws Exception { assertThat(o).as("Correct result type").isInstanceOf(Integer.class); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -173,6 +182,7 @@ public void testQueryForStreamWithRowMapper() throws Exception { assertThat(count.get()).isEqualTo(1); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -183,6 +193,7 @@ public void testQueryForObjectWithString() throws Exception { assertThat(this.template.queryForObject(sql, String.class)).isEqualTo("myvalue"); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -193,6 +204,7 @@ public void testQueryForObjectWithBigInteger() throws Exception { assertThat(this.template.queryForObject(sql, BigInteger.class)).isEqualTo(new BigInteger("22")); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -203,6 +215,7 @@ public void testQueryForObjectWithBigDecimal() throws Exception { assertThat(this.template.queryForObject(sql, BigDecimal.class)).isEqualTo(new BigDecimal("22.5")); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -213,6 +226,7 @@ public void testQueryForObjectWithInteger() throws Exception { assertThat(this.template.queryForObject(sql, Integer.class)).isEqualTo(Integer.valueOf(22)); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -224,6 +238,7 @@ public void testQueryForObjectWithIntegerAndNull() throws Exception { assertThat(this.template.queryForObject(sql, Integer.class)).isNull(); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -235,6 +250,7 @@ public void testQueryForInt() throws Exception { assertThat(i).as("Return of an int").isEqualTo(22); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -246,6 +262,7 @@ public void testQueryForIntPrimitive() throws Exception { assertThat(i).as("Return of an int").isEqualTo(22); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -257,6 +274,7 @@ public void testQueryForLong() throws Exception { assertThat(l).as("Return of a long").isEqualTo(87); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -268,6 +286,7 @@ public void testQueryForLongPrimitive() throws Exception { assertThat(l).as("Return of a long").isEqualTo(87); verify(this.resultSet).close(); verify(this.statement).close(); + verify(this.connection).close(); } @Test @@ -290,6 +309,7 @@ private void doTestQueryForListWithArgs(String sql) throws Exception { verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } @Test @@ -301,6 +321,7 @@ public void testQueryForListWithArgsAndEmptyResult() throws Exception { verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } @Test @@ -314,6 +335,7 @@ public void testQueryForListWithArgsAndSingleRowAndColumn() throws Exception { verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } @Test @@ -327,6 +349,7 @@ public void testQueryForListWithArgsAndIntegerElementAndSingleRowAndColumn() thr verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } @Test @@ -339,6 +362,7 @@ public void testQueryForMapWithArgsAndSingleRowAndColumn() throws Exception { verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } @Test @@ -351,6 +375,7 @@ public void testQueryForObjectWithArgsAndRowMapper() throws Exception { verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } @Test @@ -369,6 +394,7 @@ public void testQueryForStreamWithArgsAndRowMapper() throws Exception { verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } @Test @@ -381,6 +407,7 @@ public void testQueryForObjectWithArgsAndInteger() throws Exception { verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } @Test @@ -393,6 +420,7 @@ public void testQueryForIntWithArgs() throws Exception { verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } @Test @@ -405,6 +433,7 @@ public void testQueryForLongWithArgs() throws Exception { verify(this.preparedStatement).setObject(1, 3); verify(this.resultSet).close(); verify(this.preparedStatement).close(); + verify(this.connection).close(); } } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java index d952d473caad..e84290dc4965 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java @@ -94,12 +94,12 @@ public class JdbcTemplateTests { public void setup() throws Exception { given(this.dataSource.getConnection()).willReturn(this.connection); given(this.connection.prepareStatement(anyString())).willReturn(this.preparedStatement); + given(this.connection.prepareCall(anyString())).willReturn(this.callableStatement); + given(this.statement.getConnection()).willReturn(this.connection); + given(this.statement.executeQuery(anyString())).willReturn(this.resultSet); given(this.preparedStatement.executeQuery()).willReturn(this.resultSet); given(this.preparedStatement.executeQuery(anyString())).willReturn(this.resultSet); given(this.preparedStatement.getConnection()).willReturn(this.connection); - given(this.statement.getConnection()).willReturn(this.connection); - given(this.statement.executeQuery(anyString())).willReturn(this.resultSet); - given(this.connection.prepareCall(anyString())).willReturn(this.callableStatement); given(this.callableStatement.getResultSet()).willReturn(this.resultSet); }