From f0472ea1c09e3308627746b62b91d620d16a6608 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 23 Mar 2026 20:11:54 +0100 Subject: [PATCH 1/3] Polishing (includes nullable declaration for internal type array) --- .../org/springframework/util/TypeUtils.java | 47 +++++++------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/TypeUtils.java b/spring-core/src/main/java/org/springframework/util/TypeUtils.java index 5fa6ae0214a..ed0c4ee369d 100644 --- a/spring-core/src/main/java/org/springframework/util/TypeUtils.java +++ b/spring-core/src/main/java/org/springframework/util/TypeUtils.java @@ -38,15 +38,16 @@ import org.springframework.lang.Contract; */ public abstract class TypeUtils { - private static final Type[] IMPLICIT_LOWER_BOUNDS = { null }; + private static final @Nullable Type[] IMPLICIT_LOWER_BOUNDS = { null }; private static final Type[] IMPLICIT_UPPER_BOUNDS = { Object.class }; + /** * Check if the right-hand side type may be assigned to the left-hand side * type following the Java generics rules. - * @param lhsType the target type (left-hand side (LHS) type) - * @param rhsType the value type (right-hand side (RHS) type) that should + * @param lhsType the target type (left-hand side type) + * @param rhsType the value type (right-hand side type) that should * be assigned to the target type * @return {@code true} if {@code rhsType} is assignable to {@code lhsType} * @see ClassUtils#isAssignable(Class, Class) @@ -55,37 +56,34 @@ public abstract class TypeUtils { Assert.notNull(lhsType, "Left-hand side type must not be null"); Assert.notNull(rhsType, "Right-hand side type must not be null"); - // all types are assignable to themselves and to class Object + // All types are assignable to themselves and to class Object. if (lhsType.equals(rhsType) || Object.class == lhsType) { return true; } if (lhsType instanceof Class lhsClass) { - // just comparing two classes + // Just comparing two classes... if (rhsType instanceof Class rhsClass) { return ClassUtils.isAssignable(lhsClass, rhsClass); } if (rhsType instanceof ParameterizedType rhsParameterizedType) { Type rhsRaw = rhsParameterizedType.getRawType(); - - // a parameterized type is always assignable to its raw class type + // A parameterized type is always assignable to its raw class type. if (rhsRaw instanceof Class rhRawClass) { return ClassUtils.isAssignable(lhsClass, rhRawClass); } } else if (lhsClass.isArray() && rhsType instanceof GenericArrayType rhsGenericArrayType) { Type rhsComponent = rhsGenericArrayType.getGenericComponentType(); - return isAssignable(lhsClass.componentType(), rhsComponent); } } - // parameterized types are only assignable to other parameterized types and class types + // Parameterized types are only assignable to other parameterized types and class types. if (lhsType instanceof ParameterizedType lhsParameterizedType) { if (rhsType instanceof Class rhsClass) { Type lhsRaw = lhsParameterizedType.getRawType(); - if (lhsRaw instanceof Class lhsClass) { return ClassUtils.isAssignable(lhsClass, rhsClass); } @@ -97,7 +95,6 @@ public abstract class TypeUtils { if (lhsType instanceof GenericArrayType lhsGenericArrayType) { Type lhsComponent = lhsGenericArrayType.getGenericComponentType(); - if (rhsType instanceof Class rhsClass && rhsClass.isArray()) { return isAssignable(lhsComponent, rhsClass.componentType()); } @@ -122,7 +119,6 @@ public abstract class TypeUtils { Type[] lhsTypeArguments = lhsType.getActualTypeArguments(); Type[] rhsTypeArguments = rhsType.getActualTypeArguments(); - if (lhsTypeArguments.length != rhsTypeArguments.length) { return false; } @@ -130,7 +126,6 @@ public abstract class TypeUtils { for (int size = lhsTypeArguments.length, i = 0; i < size; ++i) { Type lhsArg = lhsTypeArguments[i]; Type rhsArg = rhsTypeArguments[i]; - if (!lhsArg.equals(rhsArg) && !(lhsArg instanceof WildcardType wildcardType && isAssignable(wildcardType, rhsArg))) { return false; @@ -141,17 +136,14 @@ public abstract class TypeUtils { } private static boolean isAssignable(WildcardType lhsType, Type rhsType) { - Type[] lUpperBounds = getUpperBounds(lhsType); - - Type[] lLowerBounds = getLowerBounds(lhsType); + @Nullable Type[] lUpperBounds = getUpperBounds(lhsType); + @Nullable Type[] lLowerBounds = getLowerBounds(lhsType); if (rhsType instanceof WildcardType rhsWcType) { - // both the upper and lower bounds of the right-hand side must be - // completely enclosed in the upper and lower bounds of the left- - // hand side. - Type[] rUpperBounds = getUpperBounds(rhsWcType); - - Type[] rLowerBounds = getLowerBounds(rhsWcType); + // Both the upper and lower bounds of the right-hand side must be + // completely enclosed in the upper and lower bounds of the left-hand side. + @Nullable Type[] rUpperBounds = getUpperBounds(rhsWcType); + @Nullable Type[] rLowerBounds = getLowerBounds(rhsWcType); for (Type lBound : lUpperBounds) { for (Type rBound : rUpperBounds) { @@ -159,7 +151,6 @@ public abstract class TypeUtils { return false; } } - for (Type rBound : rLowerBounds) { if (!isAssignableBound(lBound, rBound)) { return false; @@ -173,7 +164,6 @@ public abstract class TypeUtils { return false; } } - for (Type rBound : rLowerBounds) { if (!isAssignableBound(rBound, lBound)) { return false; @@ -187,7 +177,6 @@ public abstract class TypeUtils { return false; } } - for (Type lBound : lLowerBounds) { if (!isAssignableBound(rhsType, lBound)) { return false; @@ -198,17 +187,15 @@ public abstract class TypeUtils { return true; } - private static Type[] getLowerBounds(WildcardType wildcardType) { + private static @Nullable Type[] getLowerBounds(WildcardType wildcardType) { Type[] lowerBounds = wildcardType.getLowerBounds(); - - // supply the implicit lower bound if none are specified + // Supply the implicit lower bound if none are specified. return (lowerBounds.length == 0 ? IMPLICIT_LOWER_BOUNDS : lowerBounds); } private static Type[] getUpperBounds(WildcardType wildcardType) { Type[] upperBounds = wildcardType.getUpperBounds(); - - // supply the implicit upper bound if none are specified + // Supply the implicit upper bound if none are specified. return (upperBounds.length == 0 ? IMPLICIT_UPPER_BOUNDS : upperBounds); } From 86924d067b7b95c6fa5e3bdc0a0c6bd80863fec1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 23 Mar 2026 20:12:40 +0100 Subject: [PATCH 2/3] Add registerManagedResource variant with bean key argument Closes gh-36520 --- .../jmx/export/MBeanExporter.java | 22 ++++++++++++-- .../export/MBeanExporterOperationsTests.java | 29 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java index 3081c000fc6..aee9e398b08 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java @@ -471,11 +471,29 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo @Override public ObjectName registerManagedResource(Object managedResource) throws MBeanExportException { + return registerManagedResource(managedResource, (String) null); + } + + /** + * Register the supplied resource with JMX. If the resource is not a valid MBean already, + * Spring will generate a management interface for it. The exact interface generated will + * depend on the implementation and its configuration. This call also generates an + * {@link ObjectName} for the managed resource and returns this to the caller. + * @param managedResource the resource to expose via JMX + * @param beanKey the corresponding bean key for {@link ObjectNamingStrategy} purposes + * (making the generated {@code ObjectName} unique without the need for an identity key) + * @return the {@link ObjectName} under which the resource was exposed + * @throws MBeanExportException if Spring is unable to generate an {@link ObjectName} + * or register the MBean + * @since 7.0.7 + * @see ObjectNamingStrategy#getObjectName(Object, String) + */ + public ObjectName registerManagedResource(Object managedResource, @Nullable String beanKey) throws MBeanExportException { Assert.notNull(managedResource, "Managed resource must not be null"); ObjectName objectName; try { - objectName = getObjectName(managedResource, null); - if (this.ensureUniqueRuntimeObjectNames) { + objectName = getObjectName(managedResource, beanKey); + if (beanKey == null && this.ensureUniqueRuntimeObjectNames) { objectName = JmxUtils.appendIdentityToObjectName(objectName, managedResource); } } diff --git a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterOperationsTests.java b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterOperationsTests.java index 82e5ba85ab1..626facebb3b 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterOperationsTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterOperationsTests.java @@ -67,6 +67,35 @@ class MBeanExporterOperationsTests extends AbstractMBeanServerTests { assertThat(infoFromServer).isEqualTo(info); } + @Test + void registerManagedResourceWithUserSuppliedBeanKey() throws Exception { + ObjectName objectName = ObjectNameManager.getInstance("spring:name=Foo"); + + JmxTestBean bean = new JmxTestBean(); + bean.setName("Rob Harrop"); + + MBeanExporter exporter = new MBeanExporter(); + exporter.setServer(getServer()); + exporter.registerManagedResource(bean, "spring:name=Foo"); + + String name = (String) getServer().getAttribute(objectName, "Name"); + assertThat(bean.getName()).as("Incorrect name on MBean").isEqualTo(name); + } + + @Test + void registerExistingMBeanWithUserSuppliedBeanKey() throws Exception { + ObjectName objectName = ObjectNameManager.getInstance("spring:name=Foo"); + ModelMBeanInfo info = new ModelMBeanInfoSupport("myClass", "myDescription", null, null, null, null); + RequiredModelMBean bean = new RequiredModelMBean(info); + + MBeanExporter exporter = new MBeanExporter(); + exporter.setServer(getServer()); + exporter.registerManagedResource(bean, "spring:name=Foo"); + + MBeanInfo infoFromServer = getServer().getMBeanInfo(objectName); + assertThat(infoFromServer).isEqualTo(info); + } + @Test void registerManagedResourceWithGeneratedObjectName() throws Exception { final ObjectName objectNameTemplate = ObjectNameManager.getInstance("spring:type=Test"); From a01dd1cd79b278ddf7ea8de367cbabae9c26b0fa Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 23 Mar 2026 20:13:03 +0100 Subject: [PATCH 3/3] Upgrade to Tomcat 11.0.20, Hibernate ORM 7.2.7, Protobuf 4.34.1 --- framework-platform/framework-platform.gradle | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/framework-platform/framework-platform.gradle b/framework-platform/framework-platform.gradle index 4643f09804e..4ff9f4bbf21 100644 --- a/framework-platform/framework-platform.gradle +++ b/framework-platform/framework-platform.gradle @@ -31,7 +31,7 @@ dependencies { api("com.google.code.findbugs:findbugs:3.0.1") api("com.google.code.findbugs:jsr305:3.0.2") api("com.google.code.gson:gson:2.13.2") - api("com.google.protobuf:protobuf-java-util:4.34.0") + api("com.google.protobuf:protobuf-java-util:4.34.1") api("com.h2database:h2:2.4.240") api("com.jayway.jsonpath:json-path:2.10.0") api("com.networknt:json-schema-validator:1.5.3") @@ -96,10 +96,10 @@ dependencies { api("org.apache.httpcomponents.client5:httpclient5:5.6") api("org.apache.httpcomponents.core5:httpcore5-reactive:5.4.2") api("org.apache.poi:poi-ooxml:5.5.1") - api("org.apache.tomcat.embed:tomcat-embed-core:11.0.18") - api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.18") - api("org.apache.tomcat:tomcat-util:11.0.18") - api("org.apache.tomcat:tomcat-websocket:11.0.18") + api("org.apache.tomcat.embed:tomcat-embed-core:11.0.20") + api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.20") + api("org.apache.tomcat:tomcat-util:11.0.20") + api("org.apache.tomcat:tomcat-websocket:11.0.20") api("org.aspectj:aspectjrt:1.9.25") api("org.aspectj:aspectjtools:1.9.25") api("org.aspectj:aspectjweaver:1.9.25") @@ -120,7 +120,7 @@ dependencies { api("org.glassfish:jakarta.el:4.0.2") api("org.graalvm.sdk:graal-sdk:22.3.1") api("org.hamcrest:hamcrest:3.0") - api("org.hibernate.orm:hibernate-core:7.2.6.Final") + api("org.hibernate.orm:hibernate-core:7.2.7.Final") api("org.hibernate.validator:hibernate-validator:9.1.0.Final") api("org.hsqldb:hsqldb:2.7.4") api("org.htmlunit:htmlunit:4.21.0")