diff --git a/spring-aop/src/test/java/org/springframework/aop/support/AopUtilsTests.java b/spring-aop/src/test/java/org/springframework/aop/support/AopUtilsTests.java index be4c92bd98..fb4d1ed6f7 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/AopUtilsTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/AopUtilsTests.java @@ -17,16 +17,19 @@ package org.springframework.aop.support; import java.lang.reflect.Method; +import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; +import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.interceptor.ExposeInvocationInterceptor; import org.springframework.aop.target.EmptyTargetSource; import org.springframework.aop.testfixture.interceptor.NopInterceptor; import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.ResolvableType; import org.springframework.core.testfixture.io.SerializationTestUtils; import org.springframework.lang.Nullable; import org.springframework.util.ReflectionUtils; @@ -37,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Rod Johnson * @author Chris Beams * @author Sebastien Deleuze + * @author Juergen Hoeller */ class AopUtilsTests { @@ -99,4 +103,36 @@ class AopUtilsTests { assertThat(result).isEqualTo(name); } + @Test // gh-32365 + void mostSpecificMethodBetweenJdkProxyAndTarget() throws Exception { + Class proxyClass = new ProxyFactory(new WithInterface()).getProxyClass(getClass().getClassLoader()); + Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithInterface.class); + assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class); + } + + @Test // gh-32365 + void mostSpecificMethodBetweenCglibProxyAndTarget() throws Exception { + Class proxyClass = new ProxyFactory(new WithoutInterface()).getProxyClass(getClass().getClassLoader()); + Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithoutInterface.class); + assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class); + } + + + interface ProxyInterface { + + void handle(List list); + } + + static class WithInterface implements ProxyInterface { + + public void handle(List list) { + } + } + + static class WithoutInterface { + + public void handle(List list) { + } + } + } 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 d250e220b2..03b82e1b87 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 @@ -224,10 +224,8 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC 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/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java index f20aba5585..9d8f801590 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 class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd * 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 28a1288ae3..9eb00934de 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 @@ import static org.assertj.core.api.Assertions.assertThat; */ class DataBinderConstructTests { - @Test void dataClassBinding() { MapValueResolver valueResolver = new MapValueResolver(Map.of("param1", "value1", "param2", "true")); @@ -78,7 +77,7 @@ class DataBinderConstructTests { 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 51c6ec66c4..e34cbe78b2 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 @@ class DataBinderTests { .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 @@ class DataBinderTests { 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 @@ class DataBinderTests { .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 e9cd60103e..cc33a5162f 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 @@ class MethodValidationProxyTests { 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/BridgeMethodResolver.java b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java index cc1f351175..b37b120868 100644 --- a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -17,6 +17,7 @@ package org.springframework.core; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; @@ -86,7 +87,10 @@ public final class BridgeMethodResolver { * @see org.springframework.util.ClassUtils#getMostSpecificMethod */ public static Method getMostSpecificMethod(Method bridgeMethod, @Nullable Class targetClass) { - if (targetClass != null && !bridgeMethod.getDeclaringClass().isAssignableFrom(targetClass)) { + if (targetClass != null && + !ClassUtils.getUserClass(bridgeMethod.getDeclaringClass()).isAssignableFrom(targetClass) && + !Proxy.isProxyClass(bridgeMethod.getDeclaringClass())) { + // From a different class hierarchy, and not a JDK or CGLIB proxy either -> return as-is. return bridgeMethod; } 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 8218407da6..d08b14fb32 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -710,7 +710,7 @@ public class ResolvableType implements Serializable { * @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) { @@ -742,7 +742,7 @@ public class ResolvableType implements Serializable { * generic is returned. *

If 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() @@ -845,7 +845,7 @@ public class ResolvableType implements Serializable { * 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 b6f9d7d1a8..9f2e3ca361 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.assertThatExceptionOfType; 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 class MockMvcWebConnectionTests { 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 class MockMvcWebConnectionTests { @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 d8784fd10c..dd3473780b 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 org.springframework.mock.web.MockHttpServletResponse; 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 class MockWebResponseBuilderTests { } - // --- constructor - @Test public void constructorWithNullWebRequest() { assertThatIllegalArgumentException().isThrownBy(() -> @@ -66,12 +63,10 @@ public class MockWebResponseBuilderTests { @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 class MockWebResponseBuilderTests { .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 2106d757c9..9555ab52af 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 2c8a738b4f..23869a8b33 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-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java index 7f83a46359..9404d8a7c8 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 abstract class AbstractHttpMessageConverter implements HttpMessageConv public OutputStream getBody() { return outputStream; } - @Override public HttpHeaders getHeaders() { return headers; @@ -304,8 +303,7 @@ public abstract class AbstractHttpMessageConverter implements HttpMessageConv * 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