Merge branch '6.1.x'
# Conflicts: # spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java
This commit is contained in:
@@ -409,6 +409,9 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to stop bean '" + beanName + "'", ex);
|
||||
}
|
||||
if (bean instanceof SmartLifecycle) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,35 @@ public @interface Scheduled {
|
||||
*/
|
||||
String zone() default "";
|
||||
|
||||
/**
|
||||
* Execute the annotated method with a fixed period between invocations.
|
||||
* <p>The time unit is milliseconds by default but can be overridden via
|
||||
* {@link #timeUnit}.
|
||||
* @return the period
|
||||
*/
|
||||
long fixedRate() default -1;
|
||||
|
||||
/**
|
||||
* Execute the annotated method with a fixed period between invocations.
|
||||
* <p>The duration String can be in several formats:
|
||||
* <ul>
|
||||
* <li>a plain integer — which is interpreted to represent a duration in
|
||||
* milliseconds by default unless overridden via {@link #timeUnit()} (prefer
|
||||
* using {@link #fixedDelay()} in that case)</li>
|
||||
* <li>any of the known {@link org.springframework.format.annotation.DurationFormat.Style
|
||||
* DurationFormat.Style}: the {@link org.springframework.format.annotation.DurationFormat.Style#ISO8601 ISO8601}
|
||||
* style or the {@link org.springframework.format.annotation.DurationFormat.Style#SIMPLE SIMPLE} style
|
||||
* — using the {@link #timeUnit()} as fallback if the string doesn't contain an explicit unit</li>
|
||||
* <li>one of the above, with Spring-style "${...}" placeholders as well as SpEL expressions</li>
|
||||
* </ul>
|
||||
* @return the period as a String value — for example a placeholder,
|
||||
* or a {@link org.springframework.format.annotation.DurationFormat.Style#ISO8601 java.time.Duration} compliant value
|
||||
* or a {@link org.springframework.format.annotation.DurationFormat.Style#SIMPLE simple format} compliant value
|
||||
* @since 3.2.2
|
||||
* @see #fixedRate()
|
||||
*/
|
||||
String fixedRateString() default "";
|
||||
|
||||
/**
|
||||
* Execute the annotated method with a fixed period between the end of the
|
||||
* last invocation and the start of the next.
|
||||
@@ -143,13 +172,13 @@ public @interface Scheduled {
|
||||
* last invocation and the start of the next.
|
||||
* <p>The duration String can be in several formats:
|
||||
* <ul>
|
||||
* <li>a plain integer — which is interpreted to represent a duration in
|
||||
* milliseconds by default unless overridden via {@link #timeUnit()} (prefer
|
||||
* using {@link #fixedDelay()} in that case)</li>
|
||||
* <li>any of the known {@link org.springframework.format.annotation.DurationFormat.Style
|
||||
* DurationFormat.Style}: the {@link org.springframework.format.annotation.DurationFormat.Style#ISO8601 ISO8601}
|
||||
* style or the {@link org.springframework.format.annotation.DurationFormat.Style#SIMPLE SIMPLE} style
|
||||
* — using the {@link #timeUnit()} as fallback if the string doesn't contain an explicit unit</li>
|
||||
* <li>a plain integer — which is interpreted to represent a duration in
|
||||
* milliseconds by default unless overridden via {@link #timeUnit()} (prefer
|
||||
* using {@link #fixedDelay()} in that case)</li>
|
||||
* <li>any of the known {@link org.springframework.format.annotation.DurationFormat.Style
|
||||
* DurationFormat.Style}: the {@link org.springframework.format.annotation.DurationFormat.Style#ISO8601 ISO8601}
|
||||
* style or the {@link org.springframework.format.annotation.DurationFormat.Style#SIMPLE SIMPLE} style
|
||||
* — using the {@link #timeUnit()} as fallback if the string doesn't contain an explicit unit</li>
|
||||
* </ul>
|
||||
* <p><b>NOTE: With virtual threads, fixed rates and cron triggers are recommended
|
||||
* over fixed delays.</b> Fixed-delay tasks operate on a single scheduler thread
|
||||
@@ -162,35 +191,6 @@ public @interface Scheduled {
|
||||
*/
|
||||
String fixedDelayString() default "";
|
||||
|
||||
/**
|
||||
* Execute the annotated method with a fixed period between invocations.
|
||||
* <p>The time unit is milliseconds by default but can be overridden via
|
||||
* {@link #timeUnit}.
|
||||
* @return the period
|
||||
*/
|
||||
long fixedRate() default -1;
|
||||
|
||||
/**
|
||||
* Execute the annotated method with a fixed period between invocations.
|
||||
* <p>The duration String can be in several formats:
|
||||
* <ul>
|
||||
* <li>a plain integer — which is interpreted to represent a duration in
|
||||
* milliseconds by default unless overridden via {@link #timeUnit()} (prefer
|
||||
* using {@link #fixedDelay()} in that case)</li>
|
||||
* <li>any of the known {@link org.springframework.format.annotation.DurationFormat.Style
|
||||
* DurationFormat.Style}: the {@link org.springframework.format.annotation.DurationFormat.Style#ISO8601 ISO8601}
|
||||
* style or the {@link org.springframework.format.annotation.DurationFormat.Style#SIMPLE SIMPLE} style
|
||||
* — using the {@link #timeUnit()} as fallback if the string doesn't contain an explicit unit</li>
|
||||
* <li>one of the above, with Spring-style "${...}" placeholders as well as SpEL expressions</li>
|
||||
* </ul>
|
||||
* @return the period as a String value — for example a placeholder,
|
||||
* or a {@link org.springframework.format.annotation.DurationFormat.Style#ISO8601 java.time.Duration} compliant value
|
||||
* or a {@link org.springframework.format.annotation.DurationFormat.Style#SIMPLE simple format} compliant value
|
||||
* @since 3.2.2
|
||||
* @see #fixedRate()
|
||||
*/
|
||||
String fixedRateString() default "";
|
||||
|
||||
/**
|
||||
* Number of units of time to delay before the first execution of a
|
||||
* {@link #fixedRate} or {@link #fixedDelay} task.
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.net.URLClassLoader;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.FileSystemNotFoundException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
@@ -979,7 +980,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
.formatted(rootPath.toAbsolutePath(), subPattern));
|
||||
}
|
||||
|
||||
try (Stream<Path> files = Files.walk(rootPath)) {
|
||||
try (Stream<Path> files = Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)) {
|
||||
files.filter(isMatchingFile).sorted().map(FileSystemResource::new).forEach(result::add);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
||||
@@ -160,6 +160,7 @@ public abstract class RouterFunctions {
|
||||
*/
|
||||
public static RouterFunction<ServerResponse> resource(RequestPredicate predicate, Resource resource,
|
||||
BiConsumer<Resource, HttpHeaders> headersConsumer) {
|
||||
|
||||
return resources(new PredicateResourceLookupFunction(predicate, resource), headersConsumer);
|
||||
}
|
||||
|
||||
@@ -197,6 +198,7 @@ public abstract class RouterFunctions {
|
||||
*/
|
||||
public static RouterFunction<ServerResponse> resources(String pattern, Resource location,
|
||||
BiConsumer<Resource, HttpHeaders> headersConsumer) {
|
||||
|
||||
return resources(resourceLookupFunction(pattern, location), headersConsumer);
|
||||
}
|
||||
|
||||
@@ -240,7 +242,9 @@ public abstract class RouterFunctions {
|
||||
* @return a router function that routes to resources
|
||||
* @since 6.1
|
||||
*/
|
||||
public static RouterFunction<ServerResponse> resources(Function<ServerRequest, Optional<Resource>> lookupFunction, BiConsumer<Resource, HttpHeaders> headersConsumer) {
|
||||
public static RouterFunction<ServerResponse> resources(Function<ServerRequest, Optional<Resource>> lookupFunction,
|
||||
BiConsumer<Resource, HttpHeaders> headersConsumer) {
|
||||
|
||||
return new ResourcesRouterFunction(lookupFunction, headersConsumer);
|
||||
}
|
||||
|
||||
@@ -250,12 +254,12 @@ public abstract class RouterFunctions {
|
||||
* can be used to change the {@code PathPatternParser} properties from the defaults, for instance to change
|
||||
* {@linkplain PathPatternParser#setCaseSensitive(boolean) case sensitivity}.
|
||||
* @param routerFunction the router function to change the parser in
|
||||
* @param parser the parser to change to.
|
||||
* @param parser the parser to change to
|
||||
* @param <T> the type of response returned by the handler function
|
||||
* @return the change router function
|
||||
*/
|
||||
public static <T extends ServerResponse> RouterFunction<T> changeParser(RouterFunction<T> routerFunction,
|
||||
PathPatternParser parser) {
|
||||
public static <T extends ServerResponse> RouterFunction<T> changeParser(
|
||||
RouterFunction<T> routerFunction, PathPatternParser parser) {
|
||||
|
||||
Assert.notNull(routerFunction, "RouterFunction must not be null");
|
||||
Assert.notNull(parser, "Parser must not be null");
|
||||
@@ -1151,7 +1155,6 @@ public abstract class RouterFunctions {
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.route(this.predicate, this.handlerFunction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1173,13 +1176,10 @@ public abstract class RouterFunctions {
|
||||
return this.predicate.nest(serverRequest)
|
||||
.map(nestedRequest -> {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(
|
||||
String.format(
|
||||
"Nested predicate \"%s\" matches against \"%s\"",
|
||||
this.predicate, serverRequest));
|
||||
logger.trace(String.format("Nested predicate \"%s\" matches against \"%s\"",
|
||||
this.predicate, serverRequest));
|
||||
}
|
||||
Optional<HandlerFunction<T>> result =
|
||||
this.routerFunction.route(nestedRequest);
|
||||
Optional<HandlerFunction<T>> result = this.routerFunction.route(nestedRequest);
|
||||
if (result.isPresent() && nestedRequest != serverRequest) {
|
||||
// new attributes map from nestedRequest.attributes() can be composed of the old attributes,
|
||||
// which means that clearing the old attributes will remove those values from new attributes as well
|
||||
@@ -1202,7 +1202,6 @@ public abstract class RouterFunctions {
|
||||
this.routerFunction.accept(visitor);
|
||||
visitor.endNested(this.predicate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1212,11 +1211,11 @@ public abstract class RouterFunctions {
|
||||
|
||||
private final BiConsumer<Resource, HttpHeaders> headersConsumer;
|
||||
|
||||
|
||||
public ResourcesRouterFunction(Function<ServerRequest, Optional<Resource>> lookupFunction,
|
||||
BiConsumer<Resource, HttpHeaders> headersConsumer) {
|
||||
Assert.notNull(lookupFunction, "Function must not be null");
|
||||
Assert.notNull(headersConsumer, "HeadersConsumer must not be null");
|
||||
|
||||
Assert.notNull(lookupFunction, "Lookup function must not be null");
|
||||
Assert.notNull(headersConsumer, "Headers consumer must not be null");
|
||||
this.lookupFunction = lookupFunction;
|
||||
this.headersConsumer = headersConsumer;
|
||||
}
|
||||
@@ -1284,5 +1283,4 @@ public abstract class RouterFunctions {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -28,10 +29,12 @@ import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import jakarta.validation.executable.ExecutableValidator;
|
||||
import jakarta.validation.metadata.BeanDescriptor;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.MessageSourceResolvable;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
@@ -91,6 +94,8 @@ class MethodValidationTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
LocaleContextHolder.setDefaultLocale(Locale.UK);
|
||||
|
||||
LocalValidatorFactoryBean validatorBean = new LocalValidatorFactoryBean();
|
||||
validatorBean.afterPropertiesSet();
|
||||
this.jakartaValidator = new InvocationCountingValidator(validatorBean);
|
||||
@@ -120,6 +125,11 @@ class MethodValidationTests {
|
||||
return handlerAdapter;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void reset() {
|
||||
LocaleContextHolder.setDefaultLocale(null);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void modelAttribute() {
|
||||
|
||||
@@ -22,14 +22,15 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.jsp.tagext.Tag;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.format.annotation.NumberFormat;
|
||||
import org.springframework.format.annotation.NumberFormat.Style;
|
||||
import org.springframework.format.number.PercentStyleFormatter;
|
||||
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
|
||||
@@ -49,6 +50,8 @@ class EvalTagTests extends AbstractTagTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
LocaleContextHolder.setDefaultLocale(Locale.UK);
|
||||
|
||||
context = createPageContext();
|
||||
FormattingConversionServiceFactoryBean factory = new FormattingConversionServiceFactoryBean();
|
||||
factory.afterPropertiesSet();
|
||||
@@ -58,6 +61,11 @@ class EvalTagTests extends AbstractTagTests {
|
||||
tag.setPageContext(context);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void reset() {
|
||||
LocaleContextHolder.setDefaultLocale(null);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void printScopedAttributeResult() throws Exception {
|
||||
@@ -81,13 +89,12 @@ class EvalTagTests extends AbstractTagTests {
|
||||
|
||||
@Test
|
||||
void printFormattedScopedAttributeResult() throws Exception {
|
||||
PercentStyleFormatter formatter = new PercentStyleFormatter();
|
||||
tag.setExpression("bean.formattable");
|
||||
int action = tag.doStartTag();
|
||||
assertThat(action).isEqualTo(Tag.EVAL_BODY_INCLUDE);
|
||||
action = tag.doEndTag();
|
||||
assertThat(action).isEqualTo(Tag.EVAL_PAGE);
|
||||
assertThat(((MockHttpServletResponse) context.getResponse()).getContentAsString()).isEqualTo(formatter.print(new BigDecimal(".25"), Locale.getDefault()));
|
||||
assertThat(((MockHttpServletResponse) context.getResponse()).getContentAsString()).isEqualTo("25%");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user