From e9110c072980461f17b59456dcb3bcc1fa2bf9a3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 4 Mar 2024 22:48:52 +0100 Subject: [PATCH] Polishing --- ...AbstractFallbackJCacheOperationSource.java | 25 +++++---- .../AnnotationJCacheOperationSource.java | 8 ++- ...anFactoryJCacheOperationSourceAdvisor.java | 3 +- .../interceptor/JCacheOperationSource.java | 4 +- .../JCacheOperationSourcePointcut.java | 4 +- .../AbstractFallbackCacheOperationSource.java | 52 +++++++++---------- .../interceptor/CacheOperationSource.java | 4 +- .../CacheOperationSourcePointcut.java | 14 ++--- .../AsyncAnnotationBeanPostProcessor.java | 6 +-- .../validation/DataBinderConstructTests.java | 3 +- .../validation/DataBinderTests.java | 6 +-- .../MethodValidationProxyTests.java | 3 +- .../springframework/core/ResolvableType.java | 6 +-- .../htmlunit/MockMvcWebConnectionTests.java | 19 +++---- .../htmlunit/MockWebResponseBuilderTests.java | 12 ++--- .../MockMvcHtmlUnitDriverBuilderTests.java | 1 + .../WebConnectionHtmlUnitDriverTests.java | 1 + ...actFallbackTransactionAttributeSource.java | 30 +++-------- .../TransactionAttributeSource.java | 4 +- .../TransactionAttributeSourcePointcut.java | 10 ++-- .../AbstractHttpMessageConverter.java | 6 +-- 21 files changed, 97 insertions(+), 124 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java index 8b20e4b14822..d59bd01a49a4 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2024 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. @@ -29,12 +29,10 @@ import org.springframework.lang.Nullable; /** - * Abstract implementation of {@link JCacheOperationSource} that caches attributes + * Abstract implementation of {@link JCacheOperationSource} that caches operations * for methods and implements a fallback policy: 1. specific target method; * 2. declaring method. * - *

This implementation caches attributes by method after they are first used. - * * @author Stephane Nicoll * @author Juergen Hoeller * @since 4.1 @@ -43,24 +41,25 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOperationSource { /** - * Canonical value held in cache to indicate no caching attribute was - * found for this method and we don't need to look again. + * Canonical value held in cache to indicate no cache operation was + * found for this method, and we don't need to look again. */ - private static final Object NULL_CACHING_ATTRIBUTE = new Object(); + private static final Object NULL_CACHING_MARKER = new Object(); protected final Log logger = LogFactory.getLog(getClass()); - private final Map cache = new ConcurrentHashMap<>(1024); + private final Map operationCache = new ConcurrentHashMap<>(1024); @Override + @Nullable public JCacheOperation getCacheOperation(Method method, @Nullable Class targetClass) { MethodClassKey cacheKey = new MethodClassKey(method, targetClass); - Object cached = this.cache.get(cacheKey); + Object cached = this.operationCache.get(cacheKey); if (cached != null) { - return (cached != NULL_CACHING_ATTRIBUTE ? (JCacheOperation) cached : null); + return (cached != NULL_CACHING_MARKER ? (JCacheOperation) cached : null); } else { JCacheOperation operation = computeCacheOperation(method, targetClass); @@ -68,10 +67,10 @@ public JCacheOperation getCacheOperation(Method method, @Nullable Class ta if (logger.isDebugEnabled()) { logger.debug("Adding cacheable method '" + method.getName() + "' with operation: " + operation); } - this.cache.put(cacheKey, operation); + this.operationCache.put(cacheKey, operation); } else { - this.cache.put(cacheKey, NULL_CACHING_ATTRIBUTE); + this.operationCache.put(cacheKey, NULL_CACHING_MARKER); } return operation; } @@ -84,7 +83,7 @@ private JCacheOperation computeCacheOperation(Method method, @Nullable Class< return null; } - // The method may be on an interface, but we need attributes from the target class. + // The method may be on an interface, but we need metadata from the target class. // If the target class is null, the method will be unchanged. Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AnnotationJCacheOperationSource.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AnnotationJCacheOperationSource.java index b289b14715f4..ba7b5d8e9c19 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AnnotationJCacheOperationSource.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AnnotationJCacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2024 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. @@ -212,10 +212,8 @@ protected String generateDefaultCacheName(Method method) { for (Class parameterType : parameterTypes) { parameters.add(parameterType.getName()); } - - return method.getDeclaringClass().getName() - + '.' + method.getName() - + '(' + StringUtils.collectionToCommaDelimitedString(parameters) + ')'; + return method.getDeclaringClass().getName() + '.' + method.getName() + + '(' + StringUtils.collectionToCommaDelimitedString(parameters) + ')'; } private int countNonNull(Object... instances) { diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/BeanFactoryJCacheOperationSourceAdvisor.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/BeanFactoryJCacheOperationSourceAdvisor.java index 51fda366b04b..54e4dcaefb6b 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/BeanFactoryJCacheOperationSourceAdvisor.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/BeanFactoryJCacheOperationSourceAdvisor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -46,6 +46,7 @@ public class BeanFactoryJCacheOperationSourceAdvisor extends AbstractBeanFactory * Set the cache operation attribute source which is used to find cache * attributes. This should usually be identical to the source reference * set on the cache interceptor itself. + * @see JCacheInterceptor#setCacheOperationSource */ public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) { this.pointcut.setCacheOperationSource(cacheOperationSource); diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSource.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSource.java index 445a7ef82824..2aa8c2ddb9f0 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSource.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2024 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. @@ -34,7 +34,7 @@ public interface JCacheOperationSource { * Return the cache operations for this method, or {@code null} * if the method contains no JSR-107 related metadata. * @param method the method to introspect - * @param targetClass the target class (may be {@code null}, in which case + * @param targetClass the target class (can be {@code null}, in which case * the declaring class of the method must be used) * @return the cache operation for this method, or {@code null} if none found */ diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSourcePointcut.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSourcePointcut.java index 34693866eea2..a61779b05170 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSourcePointcut.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSourcePointcut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -24,7 +24,7 @@ import org.springframework.util.ObjectUtils; /** - * A Pointcut that matches if the underlying {@link JCacheOperationSource} + * A {@code Pointcut} that matches if the underlying {@link JCacheOperationSource} * has an operation for a given method. * * @author Stephane Nicoll diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java index d20993ae27a7..2883826495f8 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2024 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. @@ -32,20 +32,16 @@ import org.springframework.util.ClassUtils; /** - * Abstract implementation of {@link CacheOperation} that caches attributes + * Abstract implementation of {@link CacheOperationSource} that caches operations * for methods and implements a fallback policy: 1. specific target method; * 2. target class; 3. declaring method; 4. declaring class/interface. * - *

Defaults to using the target class's caching attribute if none is - * associated with the target method. Any caching attribute associated with - * the target method completely overrides a class caching attribute. + *

Defaults to using the target class's declared cache operations if none are + * associated with the target method. Any cache operations associated with + * the target method completely override any class-level declarations. * If none found on the target class, the interface that the invoked method * has been called through (in case of a JDK proxy) will be checked. * - *

This implementation caches attributes by method after they are first - * used. If it is ever desirable to allow dynamic changing of cacheable - * attributes (which is very unlikely), caching could be made configurable. - * * @author Costin Leau * @author Juergen Hoeller * @since 3.1 @@ -53,10 +49,10 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource { /** - * Canonical value held in cache to indicate no caching attribute was - * found for this method and we don't need to look again. + * Canonical value held in cache to indicate no cache operation was + * found for this method, and we don't need to look again. */ - private static final Collection NULL_CACHING_ATTRIBUTE = Collections.emptyList(); + private static final Collection NULL_CACHING_MARKER = Collections.emptyList(); /** @@ -71,14 +67,14 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera *

As this base class is not marked Serializable, the cache will be recreated * after serialization - provided that the concrete subclass is Serializable. */ - private final Map> attributeCache = new ConcurrentHashMap<>(1024); + private final Map> operationCache = new ConcurrentHashMap<>(1024); /** - * Determine the caching attribute for this method invocation. - *

Defaults to the class's caching attribute if no method attribute is found. + * Determine the cache operations for this method invocation. + *

Defaults to class-declared metadata if no method-level metadata is found. * @param method the method for the current invocation (never {@code null}) - * @param targetClass the target class for this invocation (may be {@code null}) + * @param targetClass the target class for this invocation (can be {@code null}) * @return {@link CacheOperation} for this method, or {@code null} if the method * is not cacheable */ @@ -90,21 +86,21 @@ public Collection getCacheOperations(Method method, @Nullable Cl } Object cacheKey = getCacheKey(method, targetClass); - Collection cached = this.attributeCache.get(cacheKey); + Collection cached = this.operationCache.get(cacheKey); if (cached != null) { - return (cached != NULL_CACHING_ATTRIBUTE ? cached : null); + return (cached != NULL_CACHING_MARKER ? cached : null); } else { Collection cacheOps = computeCacheOperations(method, targetClass); if (cacheOps != null) { if (logger.isTraceEnabled()) { - logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps); + logger.trace("Adding cacheable method '" + method.getName() + "' with operations: " + cacheOps); } - this.attributeCache.put(cacheKey, cacheOps); + this.operationCache.put(cacheKey, cacheOps); } else { - this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE); + this.operationCache.put(cacheKey, NULL_CACHING_MARKER); } return cacheOps; } @@ -129,7 +125,7 @@ private Collection computeCacheOperations(Method method, @Nullab return null; } - // The method may be on an interface, but we need attributes from the target class. + // The method may be on an interface, but we need metadata from the target class. // If the target class is null, the method will be unchanged. Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); @@ -163,19 +159,19 @@ private Collection computeCacheOperations(Method method, @Nullab /** - * Subclasses need to implement this to return the caching attribute for the + * Subclasses need to implement this to return the cache operations for the * given class, if any. - * @param clazz the class to retrieve the attribute for - * @return all caching attribute associated with this class, or {@code null} if none + * @param clazz the class to retrieve the cache operations for + * @return all cache operations associated with this class, or {@code null} if none */ @Nullable protected abstract Collection findCacheOperations(Class clazz); /** - * Subclasses need to implement this to return the caching attribute for the + * Subclasses need to implement this to return the cache operations for the * given method, if any. - * @param method the method to retrieve the attribute for - * @return all caching attribute associated with this method, or {@code null} if none + * @param method the method to retrieve the cache operations for + * @return all cache operations associated with this method, or {@code null} if none */ @Nullable protected abstract Collection findCacheOperations(Method method); diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java index 02a9b4f41646..7316831e2497 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2024 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. @@ -54,7 +54,7 @@ default boolean isCandidateClass(Class targetClass) { * Return the collection of cache operations for this method, * or {@code null} if the method contains no cacheable annotations. * @param method the method to introspect - * @param targetClass the target class (may be {@code null}, in which case + * @param targetClass the target class (can be {@code null}, in which case * the declaring class of the method must be used) * @return all cache operations for this method, or {@code null} if none found */ diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSourcePointcut.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSourcePointcut.java index e70275aeaed7..6fe6d7c46cfb 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSourcePointcut.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSourcePointcut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -28,7 +28,7 @@ /** * A {@code Pointcut} that matches if the underlying {@link CacheOperationSource} - * has an attribute for a given method. + * has an operation for a given method. * * @author Costin Leau * @author Juergen Hoeller @@ -36,7 +36,7 @@ * @since 3.1 */ @SuppressWarnings("serial") -class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable { +final class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable { @Nullable private CacheOperationSource cacheOperationSource; @@ -78,7 +78,7 @@ public String toString() { * {@link ClassFilter} that delegates to {@link CacheOperationSource#isCandidateClass} * for filtering classes whose methods are not worth searching to begin with. */ - private class CacheOperationSourceClassFilter implements ClassFilter { + private final class CacheOperationSourceClassFilter implements ClassFilter { @Override public boolean matches(Class clazz) { @@ -88,6 +88,7 @@ public boolean matches(Class clazz) { return (cacheOperationSource == null || cacheOperationSource.isCandidateClass(clazz)); } + @Nullable private CacheOperationSource getCacheOperationSource() { return cacheOperationSource; } @@ -95,7 +96,7 @@ private CacheOperationSource getCacheOperationSource() { @Override public boolean equals(@Nullable Object other) { return (this == other || (other instanceof CacheOperationSourceClassFilter that && - ObjectUtils.nullSafeEquals(cacheOperationSource, that.getCacheOperationSource()))); + ObjectUtils.nullSafeEquals(getCacheOperationSource(), that.getCacheOperationSource()))); } @Override @@ -105,9 +106,8 @@ public int hashCode() { @Override public String toString() { - return CacheOperationSourceClassFilter.class.getName() + ": " + cacheOperationSource; + return CacheOperationSourceClassFilter.class.getName() + ": " + getCacheOperationSource(); } - } } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java index f20aba5585d4..9d8f801590eb 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -98,8 +98,8 @@ public AsyncAnnotationBeanPostProcessor() { * applying the corresponding default if a supplier is not resolvable. * @since 5.1 */ - public void configure( - @Nullable Supplier executor, @Nullable Supplier exceptionHandler) { + public void configure(@Nullable Supplier executor, + @Nullable Supplier exceptionHandler) { this.executor = executor; this.exceptionHandler = exceptionHandler; diff --git a/spring-context/src/test/java/org/springframework/validation/DataBinderConstructTests.java b/spring-context/src/test/java/org/springframework/validation/DataBinderConstructTests.java index 28a1288ae312..9eb00934def4 100644 --- a/spring-context/src/test/java/org/springframework/validation/DataBinderConstructTests.java +++ b/spring-context/src/test/java/org/springframework/validation/DataBinderConstructTests.java @@ -38,7 +38,6 @@ */ class DataBinderConstructTests { - @Test void dataClassBinding() { MapValueResolver valueResolver = new MapValueResolver(Map.of("param1", "value1", "param2", "true")); @@ -78,7 +77,7 @@ void dataClassBindingWithMissingParameter() { assertThat(bindingResult.getFieldValue("param3")).isNull(); } - @Test // gh-31821 + @Test // gh-31821 void dataClassBindingWithNestedOptionalParameterWithMissingParameter() { MapValueResolver valueResolver = new MapValueResolver(Map.of("param1", "value1")); DataBinder binder = initDataBinder(NestedDataClass.class); diff --git a/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java b/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java index 51c6ec66c481..e34cbe78b2f3 100644 --- a/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java +++ b/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java @@ -2055,7 +2055,7 @@ void setAutoGrowCollectionLimitAfterInitialization() { .withMessageContaining("DataBinder is already initialized - call setAutoGrowCollectionLimit before other configuration methods"); } - @Test // SPR-15009 + @Test // SPR-15009 void setCustomMessageCodesResolverBeforeInitializeBindingResultForBeanPropertyAccess() { TestBean testBean = new TestBean(); DataBinder binder = new DataBinder(testBean, "testBean"); @@ -2072,7 +2072,7 @@ void setCustomMessageCodesResolverBeforeInitializeBindingResultForBeanPropertyAc assertThat(((BeanWrapper) binder.getInternalBindingResult().getPropertyAccessor()).getAutoGrowCollectionLimit()).isEqualTo(512); } - @Test // SPR-15009 + @Test // SPR-15009 void setCustomMessageCodesResolverBeforeInitializeBindingResultForDirectFieldAccess() { TestBean testBean = new TestBean(); DataBinder binder = new DataBinder(testBean, "testBean"); @@ -2126,7 +2126,7 @@ void callSetMessageCodesResolverTwice() { .withMessageContaining("DataBinder is already initialized with MessageCodesResolver"); } - @Test // gh-24347 + @Test // gh-24347 void overrideBindingResultType() { TestBean testBean = new TestBean(); DataBinder binder = new DataBinder(testBean, "testBean"); diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationProxyTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationProxyTests.java index e9cd60103e14..cc33a5162fca 100644 --- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationProxyTests.java +++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationProxyTests.java @@ -83,8 +83,7 @@ public void testMethodValidationPostProcessor() { context.close(); } - @Test // gh-29782 - @SuppressWarnings("unchecked") + @Test // gh-29782 public void testMethodValidationPostProcessorForInterfaceOnlyProxy() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MethodValidationPostProcessor.class); diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 4baca2752493..e02a2451d172 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -658,7 +658,7 @@ public ResolvableType getNested(int nestingLevel) { * @param nestingLevel the required nesting level, indexed from 1 for the * current type, 2 for the first nested generic, 3 for the second and so on * @param typeIndexesPerLevel a map containing the generic index for a given - * nesting level (may be {@code null}) + * nesting level (can be {@code null}) * @return a {@code ResolvableType} for the nested level, or {@link #NONE} */ public ResolvableType getNested(int nestingLevel, @Nullable Map typeIndexesPerLevel) { @@ -690,7 +690,7 @@ public ResolvableType getNested(int nestingLevel, @Nullable MapIf no generic is available at the specified indexes {@link #NONE} is returned. * @param indexes the indexes that refer to the generic parameter - * (may be omitted to return the first generic) + * (can be omitted to return the first generic) * @return a {@code ResolvableType} for the specified generic, or {@link #NONE} * @see #hasGenerics() * @see #getGenerics() @@ -793,7 +793,7 @@ public Class[] resolveGenerics(Class fallback) { * Convenience method that will {@link #getGeneric(int...) get} and * {@link #resolve() resolve} a specific generic parameter. * @param indexes the indexes that refer to the generic parameter - * (may be omitted to return the first generic) + * (can be omitted to return the first generic) * @return a resolved {@link Class} or {@code null} * @see #getGeneric(int...) * @see #resolve() diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java index b6f9d7d1a8ea..9f2e3ca361b3 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java @@ -31,7 +31,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; - /** * Integration tests for {@link MockMvcWebConnection}. * @@ -64,14 +63,15 @@ public void contextPathExplicit() throws IOException { public void contextPathEmpty() { this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient, "")); // Empty context path (root context) should not match to a URL with a context path - assertThatExceptionOfType(FailingHttpStatusCodeException.class).isThrownBy(() -> - this.webClient.getPage("http://localhost/context/a")) - .satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404)); + assertThatExceptionOfType(FailingHttpStatusCodeException.class) + .isThrownBy(() -> this.webClient.getPage("http://localhost/context/a")) + .satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404)); + this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient)); // No context is the same providing an empty context path - assertThatExceptionOfType(FailingHttpStatusCodeException.class).isThrownBy(() -> - this.webClient.getPage("http://localhost/context/a")) - .satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404)); + assertThatExceptionOfType(FailingHttpStatusCodeException.class) + .isThrownBy(() -> this.webClient.getPage("http://localhost/context/a")) + .satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404)); } @Test @@ -84,8 +84,9 @@ public void forward() throws IOException { @Test public void infiniteForward() { this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient, "")); - assertThatIllegalStateException().isThrownBy(() -> this.webClient.getPage("http://localhost/infiniteForward")) - .withMessage("Forwarded 100 times in a row, potential infinite forward loop"); + assertThatIllegalStateException() + .isThrownBy(() -> this.webClient.getPage("http://localhost/infiniteForward")) + .withMessage("Forwarded 100 times in a row, potential infinite forward loop"); } @Test diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java index d8784fd10ca1..dd3473780b99 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java @@ -32,7 +32,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - /** * Tests for {@link MockWebResponseBuilder}. * @@ -55,8 +54,6 @@ public void setup() throws Exception { } - // --- constructor - @Test public void constructorWithNullWebRequest() { assertThatIllegalArgumentException().isThrownBy(() -> @@ -66,12 +63,10 @@ public void constructorWithNullWebRequest() { @Test public void constructorWithNullResponse() { assertThatIllegalArgumentException().isThrownBy(() -> - new MockWebResponseBuilder(0L, new WebRequest(new URL("http://company.example:80/test/this/here")), null)); + new MockWebResponseBuilder(0L, + new WebRequest(new URL("http://company.example:80/test/this/here")), null)); } - - // --- build - @Test public void buildContent() throws Exception { this.response.getWriter().write("expected content"); @@ -124,8 +119,7 @@ public void buildResponseHeaders() throws Exception { .endsWith("; Secure; HttpOnly"); } - // SPR-14169 - @Test + @Test // SPR-14169 public void buildResponseHeadersNullDomainDefaulted() throws Exception { Cookie cookie = new Cookie("cookieA", "valueA"); this.response.addCookie(cookie); diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java index 2106d757c95d..9555ab52af8d 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java @@ -50,6 +50,7 @@ class MockMvcHtmlUnitDriverBuilderTests { private HtmlUnitDriver driver; + MockMvcHtmlUnitDriverBuilderTests(WebApplicationContext wac) { this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriverTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriverTests.java index 2c8a738b4f90..23869a8b3378 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriverTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriverTests.java @@ -48,6 +48,7 @@ class WebConnectionHtmlUnitDriverTests { @Mock private WebConnection connection; + @BeforeEach void setup() throws Exception { given(this.connection.getResponse(any(WebRequest.class))).willThrow(new IOException("")); diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java index e669818c3624..6c9a243d4d7e 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2024 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. @@ -42,11 +42,6 @@ * If none found on the target class, the interface that the invoked method * has been called through (in case of a JDK proxy) will be checked. * - *

This implementation caches attributes by method after they are first used. - * If it is ever desirable to allow dynamic changing of transaction attributes - * (which is very unlikely), caching could be made configurable. Caching is - * desirable because of the cost of evaluating rollback rules. - * * @author Rod Johnson * @author Juergen Hoeller * @since 1.1 @@ -95,7 +90,7 @@ public void setEmbeddedValueResolver(StringValueResolver resolver) { * Determine the transaction attribute for this method invocation. *

Defaults to the class's transaction attribute if no method attribute is found. * @param method the method for the current invocation (never {@code null}) - * @param targetClass the target class for this invocation (may be {@code null}) + * @param targetClass the target class for this invocation (can be {@code null}) * @return a TransactionAttribute for this method, or {@code null} if the method * is not transactional */ @@ -106,27 +101,15 @@ public TransactionAttribute getTransactionAttribute(Method method, @Nullable Cla return null; } - // First, see if we have a cached value. Object cacheKey = getCacheKey(method, targetClass); TransactionAttribute cached = this.attributeCache.get(cacheKey); + if (cached != null) { - // Value will either be canonical value indicating there is no transaction attribute, - // or an actual transaction attribute. - if (cached == NULL_TRANSACTION_ATTRIBUTE) { - return null; - } - else { - return cached; - } + return (cached != NULL_TRANSACTION_ATTRIBUTE ? cached : null); } else { - // We need to work it out. TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass); - // Put it in the cache. - if (txAttr == null) { - this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); - } - else { + if (txAttr != null) { String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass); if (txAttr instanceof DefaultTransactionAttribute dta) { dta.setDescriptor(methodIdentification); @@ -137,6 +120,9 @@ public TransactionAttribute getTransactionAttribute(Method method, @Nullable Cla } this.attributeCache.put(cacheKey, txAttr); } + else { + this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); + } return txAttr; } } diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java index 329f53420527..74eb470229f9 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -57,7 +57,7 @@ default boolean isCandidateClass(Class targetClass) { * Return the transaction attribute for the given method, * or {@code null} if the method is non-transactional. * @param method the method to introspect - * @param targetClass the target class (may be {@code null}, + * @param targetClass the target class (can be {@code null}, * in which case the declaring class of the method must be used) * @return the matching transaction attribute, or {@code null} if none found */ diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSourcePointcut.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSourcePointcut.java index 10ac08147ae3..d388850da43b 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSourcePointcut.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSourcePointcut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -77,7 +77,7 @@ public String toString() { * {@link ClassFilter} that delegates to {@link TransactionAttributeSource#isCandidateClass} * for filtering classes whose methods are not worth searching to begin with. */ - private class TransactionAttributeSourceClassFilter implements ClassFilter { + private final class TransactionAttributeSourceClassFilter implements ClassFilter { @Override public boolean matches(Class clazz) { @@ -89,6 +89,7 @@ public boolean matches(Class clazz) { return (transactionAttributeSource == null || transactionAttributeSource.isCandidateClass(clazz)); } + @Nullable private TransactionAttributeSource getTransactionAttributeSource() { return transactionAttributeSource; } @@ -96,7 +97,7 @@ private TransactionAttributeSource getTransactionAttributeSource() { @Override public boolean equals(@Nullable Object other) { return (this == other || (other instanceof TransactionAttributeSourceClassFilter that && - ObjectUtils.nullSafeEquals(transactionAttributeSource, that.getTransactionAttributeSource()))); + ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), that.getTransactionAttributeSource()))); } @Override @@ -106,9 +107,8 @@ public int hashCode() { @Override public String toString() { - return TransactionAttributeSourceClassFilter.class.getName() + ": " + transactionAttributeSource; + return TransactionAttributeSourceClassFilter.class.getName() + ": " + getTransactionAttributeSource(); } - } } diff --git a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java index 7f83a4635928..9404d8a7c869 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -218,7 +218,6 @@ public void writeTo(OutputStream outputStream) throws IOException { public OutputStream getBody() { return outputStream; } - @Override public HttpHeaders getHeaders() { return headers; @@ -304,8 +303,7 @@ protected Long getContentLength(T t, @Nullable MediaType contentType) throws IOE * Indicates whether this message converter can * {@linkplain #write(Object, MediaType, HttpOutputMessage) write} the * given object multiple times. - * - *

Default implementation returns {@code false}. + *

The default implementation returns {@code false}. * @param t the object t * @return {@code true} if {@code t} can be written repeatedly; * {@code false} otherwise