diff --git a/framework-docs/modules/ROOT/pages/integration/scheduling.adoc b/framework-docs/modules/ROOT/pages/integration/scheduling.adoc index 886f2941aa..415e043920 100644 --- a/framework-docs/modules/ROOT/pages/integration/scheduling.adoc +++ b/framework-docs/modules/ROOT/pages/integration/scheduling.adoc @@ -380,6 +380,12 @@ Notice that the methods to be scheduled must have void returns and must not acce arguments. If the method needs to interact with other objects from the application context, those would typically have been provided through dependency injection. +`@Scheduled` can be used as a repeatable annotation. If several scheduled declarations +are found on the same method, each of them will be processed independently, with a +separate trigger firing for each of them. As a consequence, such co-located schedules +may overlap and execute multiple times in parallel or in immediate succession. +Please make sure that your specified cron expressions etc do not accidentally overlap. + [NOTE] ==== As of Spring Framework 4.3, `@Scheduled` methods are supported on beans of any scope. diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java index 0eec31358a..b9ba2081c0 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java @@ -29,8 +29,8 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar; /** * Annotation that marks a method to be scheduled. Exactly one of the - * {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes must be - * specified. + * {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes + * must be specified. * *
The annotated method must not accept arguments. It will typically have * a {@code void} return type; if not, the returned value will be ignored @@ -56,7 +56,10 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar; * or {@link EnableScheduling @EnableScheduling} annotation. * *
This annotation can be used as a {@linkplain Repeatable repeatable} - * annotation. + * annotation. If several scheduled declarations are found on the same method, + * each of them will be processed independently, with a separate trigger firing + * for each of them. As a consequence, such co-located schedules may overlap + * and execute multiple times in parallel or in immediate succession. * *
This annotation may be used as a meta-annotation to create custom
* composed annotations with attribute overrides.
diff --git a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java
index 695c760d6f..2fb7bed7f9 100644
--- a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java
+++ b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java
@@ -354,7 +354,7 @@ public class MBeanExporterTests extends AbstractMBeanServerTests {
exporter.setBeans(beansToExport);
exporter.setServer(getServer());
exporter.setBeanFactory(factory);
- exporter.setAutodetectMode(MBeanExporter.AUTODETECT_NONE);
+ exporter.setAutodetect(false);
// MBean has a bad ObjectName, so if said MBean is autodetected, an exception will be thrown...
start(exporter);
}
@@ -524,18 +524,16 @@ public class MBeanExporterTests extends AbstractMBeanServerTests {
}
@Test
- void notRunningInBeanFactoryAndPassedBeanNameToExport() throws Exception {
+ void notRunningInBeanFactoryAndPassedBeanNameToExport() {
Map If no custom translator is provided, a default
- * {@link SQLErrorCodeSQLExceptionTranslator} is used
- * which examines the SQLException's vendor-specific error code.
+ * A {@link SQLErrorCodeSQLExceptionTranslator} used by default if a user-provided
+ * `sql-error-codes.xml` file has been found in the root of the classpath. Otherwise,
+ * {@link SQLExceptionSubclassTranslator} serves as the default translator as of 6.0.
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
- * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
+ * @see org.springframework.jdbc.support.SQLExceptionSubclassTranslator
*/
public void setExceptionTranslator(SQLExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator;
}
/**
- * Return the exception translator for this instance.
- * Creates a default {@link SQLErrorCodeSQLExceptionTranslator}
- * for the specified DataSource if none set, or a
- * {@link SQLStateSQLExceptionTranslator} in case of no DataSource.
- * @see #getDataSource()
+ * Return the exception translator to use for this instance,
+ * creating a default if necessary.
+ * @see #setExceptionTranslator
*/
public SQLExceptionTranslator getExceptionTranslator() {
SQLExceptionTranslator exceptionTranslator = this.exceptionTranslator;
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java
index bda750f472..e03987499a 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,8 +59,8 @@ public class JdbcTransactionManager extends DataSourceTransactionManager {
/**
- * Create a new JdbcTransactionManager instance.
- * A DataSource has to be set to be able to use it.
+ * Create a new {@code JdbcTransactionManager} instance.
+ * A {@code DataSource} has to be set to be able to use it.
* @see #setDataSource
*/
public JdbcTransactionManager() {
@@ -68,7 +68,7 @@ public class JdbcTransactionManager extends DataSourceTransactionManager {
}
/**
- * Create a new JdbcTransactionManager instance.
+ * Create a new {@code JdbcTransactionManager} instance.
* @param dataSource the JDBC DataSource to manage transactions for
*/
public JdbcTransactionManager(DataSource dataSource) {
@@ -79,13 +79,15 @@ public class JdbcTransactionManager extends DataSourceTransactionManager {
/**
- * Specify the database product name for the DataSource that this transaction manager
- * uses. This allows to initialize an SQLErrorCodeSQLExceptionTranslator without
- * obtaining a Connection from the DataSource to get the meta-data.
+ * Specify the database product name for the {@code DataSource} that this
+ * transaction manager operates on.
+ * This allows for initializing a {@link SQLErrorCodeSQLExceptionTranslator} without
+ * obtaining a {@code Connection} from the {@code DataSource} to get the meta-data.
* @param dbName the database product name that identifies the error codes entry
- * @see JdbcAccessor#setDatabaseProductName
+ * @see #setExceptionTranslator
* @see SQLErrorCodeSQLExceptionTranslator#setDatabaseProductName
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
+ * @see JdbcAccessor#setDatabaseProductName
*/
public void setDatabaseProductName(String dbName) {
if (SQLErrorCodeSQLExceptionTranslator.hasUserProvidedErrorCodesFile()) {
@@ -97,22 +99,22 @@ public class JdbcTransactionManager extends DataSourceTransactionManager {
}
/**
- * Set the exception translator for this instance.
- * If no custom translator is provided, a default
- * {@link SQLErrorCodeSQLExceptionTranslator} is used
- * which examines the SQLException's vendor-specific error code.
- * @see JdbcAccessor#setExceptionTranslator
+ * Set the exception translator for this transaction manager.
+ * A {@link SQLErrorCodeSQLExceptionTranslator} used by default if a user-provided
+ * `sql-error-codes.xml` file has been found in the root of the classpath. Otherwise,
+ * {@link SQLExceptionSubclassTranslator} serves as the default translator as of 6.0.
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
+ * @see org.springframework.jdbc.support.SQLExceptionSubclassTranslator
+ * @see JdbcAccessor#setExceptionTranslator
*/
public void setExceptionTranslator(SQLExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator;
}
/**
- * Return the exception translator for this instance.
- * Creates a default {@link SQLErrorCodeSQLExceptionTranslator}
- * for the specified DataSource if none set.
- * @see #getDataSource()
+ * Return the exception translator to use for this instance,
+ * creating a default if necessary.
+ * @see #setExceptionTranslator
*/
public SQLExceptionTranslator getExceptionTranslator() {
SQLExceptionTranslator exceptionTranslator = this.exceptionTranslator;
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java
index 058348f7ec..eb5fdcd250 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,6 +51,8 @@ import org.springframework.lang.Nullable;
* Falls back to a standard {@link SQLStateSQLExceptionTranslator} if the JDBC
* driver does not actually expose JDBC 4 compliant {@code SQLException} subclasses.
*
+ * This translator serves as the default translator as of 6.0.
+ *
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 2.5
@@ -72,7 +74,7 @@ public class SQLExceptionSubclassTranslator extends AbstractFallbackSQLException
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
}
if (ex instanceof SQLTransactionRollbackException) {
- if ("40001".equals(ex.getSQLState())) {
+ if (SQLStateSQLExceptionTranslator.indicatesCannotAcquireLock(ex.getSQLState())) {
return new CannotAcquireLockException(buildMessage(task, sql, ex), ex);
}
return new PessimisticLockingFailureException(buildMessage(task, sql, ex), ex);
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java
index f8d64b9485..5954618b3e 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java
@@ -39,11 +39,16 @@ import org.springframework.lang.Nullable;
* does not require special initialization (no database vendor detection, etc.).
* For more precise translation, consider {@link SQLErrorCodeSQLExceptionTranslator}.
*
+ * This translator is commonly used as a {@link #setFallbackTranslator fallback}
+ * behind a primary translator such as {@link SQLErrorCodeSQLExceptionTranslator} or
+ * {@link SQLExceptionSubclassTranslator}.
+ *
* @author Rod Johnson
* @author Juergen Hoeller
* @author Thomas Risberg
* @see java.sql.SQLException#getSQLState()
* @see SQLErrorCodeSQLExceptionTranslator
+ * @see SQLExceptionSubclassTranslator
*/
public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLExceptionTranslator {
@@ -111,7 +116,7 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
}
else if (PESSIMISTIC_LOCKING_FAILURE_CODES.contains(classCode)) {
- if ("40001".equals(sqlState)) {
+ if (indicatesCannotAcquireLock(sqlState)) {
return new CannotAcquireLockException(buildMessage(task, sql, ex), ex);
}
return new PessimisticLockingFailureException(buildMessage(task, sql, ex), ex);
@@ -148,9 +153,10 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException
return sqlState;
}
+
/**
* Check whether the given SQL state (and the associated error code in case
- * of a generic SQL state value) indicate a duplicate key exception:
+ * of a generic SQL state value) indicate a {@link DuplicateKeyException}:
* either SQL state 23505 as a specific indication, or the generic SQL state
* 23000 with well-known vendor codes (1 for Oracle, 1062 for MySQL/MariaDB,
* 2601/2627 for MS SQL Server).
@@ -163,4 +169,13 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException
(errorCode == 1 || errorCode == 1062 || errorCode == 2601 || errorCode == 2627)));
}
+ /**
+ * Check whether the given SQL state indicates a {@link CannotAcquireLockException},
+ * with SQL state 40001 as a specific indication.
+ * @param sqlState the SQL state value
+ */
+ static boolean indicatesCannotAcquireLock(@Nullable String sqlState) {
+ return "40001".equals(sqlState);
+ }
+
}