Compare commits

..

1 Commits

Author SHA1 Message Date
Stéphane Nicoll d3343db2ff Release v6.1.17 2025-02-13 10:21:48 +01:00
77 changed files with 513 additions and 1053 deletions
@@ -11,7 +11,7 @@ runs:
using: composite
steps:
- name: Generate Changelog
uses: spring-io/github-changelog-generator@86958813a62af8fb223b3fd3b5152035504bcb83 #v0.0.12
uses: spring-io/github-changelog-generator@185319ad7eaa75b0e8e72e4b6db19c8b2cb8c4c1 #v0.0.11
with:
config-file: .github/actions/create-github-release/changelog-generator.yml
milestone: ${{ inputs.milestone }}
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
project.getPlugins().apply(CheckstylePlugin.class);
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("10.23.1");
checkstyle.setToolVersion("10.21.2");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
@@ -64,7 +64,7 @@ public class CheckstyleConventions {
NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class);
noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines"));
noHttp.getSource().exclude("**/test-output/**", "**/.settings/**",
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**", "buildSrc/build/**");
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**");
List<String> buildFolders = List.of("bin", "build", "out");
project.allprojects(subproject -> {
Path rootPath = project.getRootDir().toPath();
-1
View File
@@ -5,7 +5,6 @@
"@antora/collector-extension": "1.0.0-alpha.3",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.11.1",
"fast-xml-parser": "4.5.2",
"@springio/asciidoctor-extensions": "1.0.0-alpha.10"
}
}
+10 -10
View File
@@ -9,15 +9,15 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.15.4"))
api(platform("io.micrometer:micrometer-bom:1.12.12"))
api(platform("io.netty:netty-bom:4.1.121.Final"))
api(platform("io.netty:netty-bom:4.1.118.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2023.0.19"))
api(platform("io.projectreactor:reactor-bom:2023.0.15"))
api(platform("io.rsocket:rsocket-bom:1.1.5"))
api(platform("org.apache.groovy:groovy-bom:4.0.27"))
api(platform("org.apache.groovy:groovy-bom:4.0.24"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.assertj:assertj-bom:3.27.3"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.21"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.21"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.16"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.16"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3"))
api(platform("org.junit:junit-bom:5.10.5"))
@@ -101,8 +101,8 @@ dependencies {
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")
api("org.apache.derby:derbytools:10.16.1.1")
api("org.apache.httpcomponents.client5:httpclient5:5.4.4")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.4")
api("org.apache.httpcomponents.client5:httpclient5:5.4.1")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.1")
api("org.apache.poi:poi-ooxml:5.2.5")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.28")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.28")
@@ -116,12 +116,12 @@ dependencies {
api("org.codehaus.jettison:jettison:1.5.4")
api("org.crac:crac:1.4.0")
api("org.dom4j:dom4j:2.1.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.9")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.8")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.10.8")
api("org.ehcache:jcache:1.0.1")
api("org.freemarker:freemarker:2.3.34")
api("org.freemarker:freemarker:2.3.33")
api("org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea")
api("org.glassfish:jakarta.el:4.0.2")
api("org.glassfish.tyrus:tyrus-container-servlet:2.1.3")
@@ -140,7 +140,7 @@ dependencies {
api("org.seleniumhq.selenium:htmlunit-driver:2.70.0")
api("org.seleniumhq.selenium:selenium-java:3.141.59")
api("org.skyscreamer:jsonassert:1.5.3")
api("org.slf4j:slf4j-api:2.0.17")
api("org.slf4j:slf4j-api:2.0.16")
api("org.testng:testng:7.9.0")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.55")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.1.22-SNAPSHOT
version=6.1.17
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -241,7 +241,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
try {
int algorithmicStep = STEP_JOIN_POINT_BINDING;
while (this.numberOfRemainingUnboundArguments > 0 && algorithmicStep < STEP_FINISHED) {
while ((this.numberOfRemainingUnboundArguments > 0) && algorithmicStep < STEP_FINISHED) {
switch (algorithmicStep++) {
case STEP_JOIN_POINT_BINDING -> {
if (!maybeBindThisJoinPoint()) {
@@ -373,8 +373,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
if (this.returningName != null) {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Binding of returning parameter '" + this.returningName +
"' is ambiguous: there are " + this.numberOfRemainingUnboundArguments + " candidates. " +
"Consider compiling with -parameters in order to make declared parameter names available.");
"' is ambiguous: there are " + this.numberOfRemainingUnboundArguments + " candidates.");
}
// We're all set... find the unbound parameter, and bind it.
@@ -486,8 +485,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
*/
private void maybeBindThisOrTargetOrArgsFromPointcutExpression() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments +
" unbound args at this()/target()/args() binding stage, with no way to determine between them");
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
+ " unbound args at this()/target()/args() binding stage, with no way to determine between them");
}
List<String> varNames = new ArrayList<>();
@@ -536,8 +535,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
private void maybeBindReferencePointcutParameter() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments +
" unbound args at reference pointcut binding stage, with no way to determine between them");
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
+ " unbound args at reference pointcut binding stage, with no way to determine between them");
}
List<String> varNames = new ArrayList<>();
@@ -742,9 +741,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
* Simple record to hold the extracted text from a pointcut body, together
* with the number of tokens consumed in extracting it.
*/
private record PointcutBody(int numTokensConsumed, @Nullable String text) {
}
private record PointcutBody(int numTokensConsumed, @Nullable String text) {}
/**
* Thrown in response to an ambiguous binding being detected when
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -35,7 +35,6 @@ import org.springframework.aop.AopInvocationException;
import org.springframework.aop.RawTargetAccess;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.AopUtils;
import org.springframework.aot.AotDetector;
import org.springframework.cglib.core.ClassLoaderAwareGeneratorStrategy;
import org.springframework.cglib.core.CodeGenerationException;
import org.springframework.cglib.core.SpringNamingPolicy;
@@ -202,7 +201,7 @@ class CglibAopProxy implements AopProxy, Serializable {
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setAttemptLoad(enhancer.getUseCache() && AotDetector.useGeneratedArtifacts());
enhancer.setAttemptLoad(true);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -203,6 +203,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
itb.getSpouse();
assertThat(maaif.isMaterialized()).isTrue();
assertThat(imapa.getDeclaredPointcut().getMethodMatcher().matches(TestBean.class.getMethod("getAge"), null)).isTrue();
assertThat(itb.getAge()).as("Around advice must apply").isEqualTo(0);
@@ -300,7 +301,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void bindingWithMultipleArgsDifferentlyOrdered() {
ManyValuedArgs target = new ManyValuedArgs();
ManyValuedArgs mva = createProxy(target, ManyValuedArgs.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new ManyValuedArgs(), "someBean")));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new ManyValuedArgs(), "someBean")));
String a = "a";
int b = 12;
@@ -319,7 +320,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
NotLockable notLockableTarget = new NotLockable();
assertThat(notLockableTarget).isNotInstanceOf(Lockable.class);
NotLockable notLockable1 = createProxy(notLockableTarget, NotLockable.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
assertThat(notLockable1).isInstanceOf(Lockable.class);
Lockable lockable = (Lockable) notLockable1;
assertThat(lockable.locked()).isFalse();
@@ -328,7 +329,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
NotLockable notLockable2Target = new NotLockable();
NotLockable notLockable2 = createProxy(notLockable2Target, NotLockable.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
assertThat(notLockable2).isInstanceOf(Lockable.class);
Lockable lockable2 = (Lockable) notLockable2;
assertThat(lockable2.locked()).isFalse();
@@ -342,19 +343,20 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void introductionAdvisorExcludedFromTargetImplementingInterface() {
assertThat(AopUtils.findAdvisorsThatCanApply(
getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(), "someBean")),
aspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class)).isEmpty();
assertThat(AopUtils.findAdvisorsThatCanApply(getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2);
aspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2);
}
@Test
void introductionOnTargetImplementingInterface() {
CannotBeUnlocked target = new CannotBeUnlocked();
Lockable proxy = createProxy(target, CannotBeUnlocked.class,
// Ensure that we exclude
AopUtils.findAdvisorsThatCanApply(
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class));
assertThat(proxy).isInstanceOf(Lockable.class);
Lockable lockable = proxy;
assertThat(lockable.locked()).as("Already locked").isTrue();
@@ -368,8 +370,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
ArrayList<Object> target = new ArrayList<>();
List<?> proxy = createProxy(target, List.class,
AopUtils.findAdvisorsThatCanApply(
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
List.class));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
List.class));
assertThat(proxy).as("Type pattern must have excluded mixin").isNotInstanceOf(Lockable.class);
}
@@ -377,7 +379,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void introductionBasedOnAnnotationMatch() { // gh-9980
AnnotatedTarget target = new AnnotatedTargetImpl();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean"));
aspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean"));
Object proxy = createProxy(target, AnnotatedTarget.class, advisors);
assertThat(proxy).isInstanceOf(Lockable.class);
Lockable lockable = (Lockable) proxy;
@@ -391,9 +393,9 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeITestBeanModifiable(), "someBean"));
aspectInstanceFactory(new MakeITestBeanModifiable(), "someBean"));
advisors.addAll(getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(), "someBean")));
aspectInstanceFactory(new MakeLockable(), "someBean")));
Modifiable modifiable = (Modifiable) createProxy(target, ITestBean.class, advisors);
assertThat(modifiable).isInstanceOf(Modifiable.class);
@@ -424,7 +426,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
UnsupportedOperationException expectedException = new UnsupportedOperationException();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
assertThat(advisors).as("One advice method was found").hasSize(1);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(itb::getAge);
@@ -437,12 +439,12 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
RemoteException expectedException = new RemoteException();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
assertThat(advisors).as("One advice method was found").hasSize(1);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
assertThatExceptionOfType(UndeclaredThrowableException.class)
.isThrownBy(itb::getAge)
.withCause(expectedException);
.isThrownBy(itb::getAge)
.withCause(expectedException);
}
@Test
@@ -450,7 +452,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
TwoAdviceAspect twoAdviceAspect = new TwoAdviceAspect();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(twoAdviceAspect, "someBean"));
aspectInstanceFactory(twoAdviceAspect, "someBean"));
assertThat(advisors).as("Two advice methods found").hasSize(2);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
itb.setName("");
@@ -464,7 +466,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void afterAdviceTypes() throws Exception {
InvocationTrackingAspect aspect = new InvocationTrackingAspect();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(aspect, "exceptionHandlingAspect"));
aspectInstanceFactory(aspect, "exceptionHandlingAspect"));
Echo echo = createProxy(new Echo(), Echo.class, advisors);
assertThat(aspect.invocations).isEmpty();
@@ -473,7 +475,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
aspect.invocations.clear();
assertThatExceptionOfType(FileNotFoundException.class)
.isThrownBy(() -> echo.echo(new FileNotFoundException()));
.isThrownBy(() -> echo.echo(new FileNotFoundException()));
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
}
@@ -485,6 +487,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(Modifier.isAbstract(aspect.getClass().getSuperclass().getModifiers())).isFalse();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect, "incrementingAspect"));
ITestBean proxy = createProxy(new TestBean("Jane", 42), ITestBean.class, advisors);
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
}
@@ -809,20 +812,20 @@ abstract class AbstractAspectJAdvisorFactoryTests {
invocations.add("before");
}
@After("echo()")
void after() {
invocations.add("after");
}
@AfterReturning(pointcut = "this(target) && execution(* echo(*))", returning = "returnValue")
void afterReturning(JoinPoint joinPoint, Echo target, Object returnValue) {
@AfterReturning("echo()")
void afterReturning() {
invocations.add("after returning");
}
@AfterThrowing(pointcut = "this(target) && execution(* echo(*))", throwing = "exception")
void afterThrowing(JoinPoint joinPoint, Echo target, Throwable exception) {
@AfterThrowing("echo()")
void afterThrowing() {
invocations.add("after throwing");
}
@After("echo()")
void after() {
invocations.add("after");
}
}
@@ -964,7 +967,7 @@ abstract class AbstractMakeModifiable {
class MakeITestBeanModifiable extends AbstractMakeModifiable {
@DeclareParents(value = "org.springframework.beans.testfixture.beans.ITestBean+",
defaultImpl = ModifiableImpl.class)
defaultImpl=ModifiableImpl.class)
static MutableModifiable mixin;
}
@@ -291,7 +291,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
String lastKey = tokens.keys[tokens.keys.length - 1];
if (propValue.getClass().isArray()) {
Class<?> componentType = propValue.getClass().componentType();
Class<?> requiredType = propValue.getClass().componentType();
int arrayIndex = Integer.parseInt(lastKey);
Object oldValue = null;
try {
@@ -299,9 +299,10 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
oldValue = Array.get(propValue, arrayIndex);
}
Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
componentType, ph.nested(tokens.keys.length));
requiredType, ph.nested(tokens.keys.length));
int length = Array.getLength(propValue);
if (arrayIndex >= length && arrayIndex < this.autoGrowCollectionLimit) {
Class<?> componentType = propValue.getClass().componentType();
Object newArray = Array.newInstance(componentType, arrayIndex + 1);
System.arraycopy(propValue, 0, newArray, 0, length);
int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2018 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.
@@ -25,7 +25,6 @@ import org.springframework.lang.Nullable;
* analogous to an InvocationTargetException.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
@SuppressWarnings("serial")
public class MethodInvocationException extends PropertyAccessException {
@@ -42,9 +41,7 @@ public class MethodInvocationException extends PropertyAccessException {
* @param cause the Throwable raised by the invoked method
*/
public MethodInvocationException(PropertyChangeEvent propertyChangeEvent, @Nullable Throwable cause) {
super(propertyChangeEvent,
"Property '" + propertyChangeEvent.getPropertyName() + "' threw exception: " + cause,
cause);
super(propertyChangeEvent, "Property '" + propertyChangeEvent.getPropertyName() + "' threw exception", cause);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -145,6 +145,8 @@ public interface ListableBeanFactory extends BeanFactory {
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beanNamesForTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>This version of {@code getBeanNamesForType} matches all kinds of beans,
* be it singletons, prototypes, or FactoryBeans. In most implementations, the
* result will be the same as for {@code getBeanNamesForType(type, true, true)}.
@@ -174,6 +176,8 @@ public interface ListableBeanFactory extends BeanFactory {
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beanNamesForTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>Bean names returned by this method should always return bean names <i>in the
* order of definition</i> in the backend configuration, as far as possible.
* @param type the generically typed class or interface to match
@@ -206,6 +210,8 @@ public interface ListableBeanFactory extends BeanFactory {
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beanNamesForTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>This version of {@code getBeanNamesForType} matches all kinds of beans,
* be it singletons, prototypes, or FactoryBeans. In most implementations, the
* result will be the same as for {@code getBeanNamesForType(type, true, true)}.
@@ -233,6 +239,8 @@ public interface ListableBeanFactory extends BeanFactory {
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beanNamesForTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>Bean names returned by this method should always return bean names <i>in the
* order of definition</i> in the backend configuration, as far as possible.
* @param type the class or interface to match, or {@code null} for all bean names
@@ -257,24 +265,21 @@ public interface ListableBeanFactory extends BeanFactory {
* subclasses), judging from either bean definitions or the value of
* {@code getObjectType} in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well. Also, it
* <b>suppresses exceptions for beans that are currently in creation in a circular
* reference scenario:</b> typically, references back to the caller of this method.
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
* will get initialized. If the object created by the FactoryBean doesn't match,
* the raw FactoryBean itself will be matched against the type.
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beansOfTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>This version of getBeansOfType matches all kinds of beans, be it
* singletons, prototypes, or FactoryBeans. In most implementations, the
* result will be the same as for {@code getBeansOfType(type, true, true)}.
* <p>The Map returned by this method should always return bean names and
* corresponding bean instances <i>in the order of definition</i> in the
* backend configuration, as far as possible.
* <p><b>Consider {@link #getBeanNamesForType(Class)} with selective {@link #getBean}
* calls for specific bean names in preference to this Map-based retrieval method.</b>
* Aside from lazy instantiation benefits, this also avoids any exception suppression.
* @param type the class or interface to match, or {@code null} for all concrete beans
* @return a Map with the matching beans, containing the bean names as
* keys and the corresponding bean instances as values
@@ -290,9 +295,7 @@ public interface ListableBeanFactory extends BeanFactory {
* subclasses), judging from either bean definitions or the value of
* {@code getObjectType} in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well. Also, it
* <b>suppresses exceptions for beans that are currently in creation in a circular
* reference scenario:</b> typically, references back to the caller of this method.
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
* which means that FactoryBeans will get initialized. If the object created by the
* FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
@@ -301,12 +304,11 @@ public interface ListableBeanFactory extends BeanFactory {
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' {@code beansOfTypeIncludingAncestors}
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>The Map returned by this method should always return bean names and
* corresponding bean instances <i>in the order of definition</i> in the
* backend configuration, as far as possible.
* <p><b>Consider {@link #getBeanNamesForType(Class)} with selective {@link #getBean}
* calls for specific bean names in preference to this Map-based retrieval method.</b>
* Aside from lazy instantiation benefits, this also avoids any exception suppression.
* @param type the class or interface to match, or {@code null} for all concrete beans
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -156,8 +156,6 @@ abstract class BeanDefinitionPropertyValueCodeGeneratorDelegates {
.builder(SuppressWarnings.class)
.addMember("value", "{\"rawtypes\", \"unchecked\"}")
.build());
method.addModifiers(javax.lang.model.element.Modifier.PRIVATE,
javax.lang.model.element.Modifier.STATIC);
method.returns(Map.class);
method.addStatement("$T map = new $T($L)", Map.class,
LinkedHashMap.class, map.size());
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -22,7 +22,6 @@ import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aot.AotDetector;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
@@ -154,7 +153,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanDefinition.getBeanClass());
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setAttemptLoad(AotDetector.useGeneratedArtifacts());
enhancer.setAttemptLoad(true);
if (this.owner instanceof ConfigurableBeanFactory cbf) {
ClassLoader cl = cbf.getBeanClassLoader();
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
@@ -266,7 +265,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
/**
* CGLIB MethodInterceptor to override methods, replacing them with a call
* to a generic {@link MethodReplacer}.
* to a generic MethodReplacer.
*/
private static class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
@@ -278,10 +277,10 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
}
@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
Assert.state(ro != null, "ReplaceOverride not found");
// TODO could cache if a singleton for minor performance optimization
MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
return mr.reimplement(obj, method, args);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -178,13 +178,13 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock.
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock.
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -18,7 +18,6 @@ package org.springframework.beans.factory.aot;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.temporal.ChronoUnit;
@@ -29,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import javax.lang.model.element.Modifier;
@@ -54,7 +54,7 @@ import org.springframework.core.testfixture.aot.generate.value.ExampleClass;
import org.springframework.core.testfixture.aot.generate.value.ExampleClass$$GeneratedBy;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.MethodSpec;
import org.springframework.util.ReflectionUtils;
import org.springframework.javapoet.ParameterizedTypeName;
import static org.assertj.core.api.Assertions.assertThat;
@@ -83,23 +83,14 @@ class BeanDefinitionPropertyValueCodeGeneratorDelegatesTests {
CodeBlock generatedCode = createValueCodeGenerator(generatedClass).generateCode(value);
typeBuilder.set(type -> {
type.addModifiers(Modifier.PUBLIC);
type.addMethod(MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
type.addSuperinterface(
ParameterizedTypeName.get(Supplier.class, Object.class));
type.addMethod(MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC)
.returns(Object.class).addStatement("return $L", generatedCode).build());
});
generationContext.writeGeneratedContent();
TestCompiler.forSystem().with(generationContext).compile(compiled ->
result.accept(getGeneratedCodeReturnValue(compiled, generatedClass), compiled));
}
private static Object getGeneratedCodeReturnValue(Compiled compiled, GeneratedClass generatedClass) {
try {
Object instance = compiled.getInstance(Object.class, generatedClass.getName().reflectionName());
Method get = ReflectionUtils.findMethod(instance.getClass(), "get");
return get.invoke(null);
}
catch (Exception ex) {
throw new RuntimeException("Failed to invoke generated code '%s':".formatted(generatedClass.getName()), ex);
}
result.accept(compiled.getInstance(Supplier.class).get(), compiled));
}
@Nested
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -21,13 +21,12 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.util.ClassUtils;
@@ -44,17 +43,14 @@ class QualifierAnnotationAutowireBeanFactoryTests {
private static final String MARK = "mark";
private final DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
@Test
void testAutowireCandidateDefaultWithIrrelevantDescriptor() throws Exception {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
ConstructorArgumentValues cavs = new ConstructorArgumentValues();
cavs.addGenericArgumentValue(JUERGEN);
RootBeanDefinition rbd = new RootBeanDefinition(Person.class, cavs, null);
lbf.registerBeanDefinition(JUERGEN, rbd);
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN,
new DependencyDescriptor(Person.class.getDeclaredField("name"), false))).isTrue();
@@ -64,12 +60,12 @@ class QualifierAnnotationAutowireBeanFactoryTests {
@Test
void testAutowireCandidateExplicitlyFalseWithIrrelevantDescriptor() throws Exception {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
ConstructorArgumentValues cavs = new ConstructorArgumentValues();
cavs.addGenericArgumentValue(JUERGEN);
RootBeanDefinition rbd = new RootBeanDefinition(Person.class, cavs, null);
rbd.setAutowireCandidate(false);
lbf.registerBeanDefinition(JUERGEN, rbd);
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isFalse();
assertThat(lbf.isAutowireCandidate(JUERGEN,
new DependencyDescriptor(Person.class.getDeclaredField("name"), false))).isFalse();
@@ -77,46 +73,44 @@ class QualifierAnnotationAutowireBeanFactoryTests {
new DependencyDescriptor(Person.class.getDeclaredField("name"), true))).isFalse();
}
@Disabled
@Test
void testAutowireCandidateWithFieldDescriptor() throws Exception {
lbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
cavs1.addGenericArgumentValue(JUERGEN);
RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
person1.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
lbf.registerBeanDefinition(JUERGEN, person1);
ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
cavs2.addGenericArgumentValue(MARK);
RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
lbf.registerBeanDefinition(MARK, person2);
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(
QualifiedTestBean.class.getDeclaredField("qualified"), false);
DependencyDescriptor nonqualifiedDescriptor = new DependencyDescriptor(
QualifiedTestBean.class.getDeclaredField("nonqualified"), false);
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, nonqualifiedDescriptor)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, qualifiedDescriptor)).isTrue();
assertThat(lbf.isAutowireCandidate(MARK, null)).isTrue();
assertThat(lbf.isAutowireCandidate(MARK, nonqualifiedDescriptor)).isTrue();
assertThat(lbf.isAutowireCandidate(MARK, qualifiedDescriptor)).isFalse();
}
@Test
void testAutowireCandidateExplicitlyFalseWithFieldDescriptor() throws Exception {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
ConstructorArgumentValues cavs = new ConstructorArgumentValues();
cavs.addGenericArgumentValue(JUERGEN);
RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null);
person.setAutowireCandidate(false);
person.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
lbf.registerBeanDefinition(JUERGEN, person);
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(
QualifiedTestBean.class.getDeclaredField("qualified"), false);
DependencyDescriptor nonqualifiedDescriptor = new DependencyDescriptor(
QualifiedTestBean.class.getDeclaredField("nonqualified"), false);
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isFalse();
assertThat(lbf.isAutowireCandidate(JUERGEN, nonqualifiedDescriptor)).isFalse();
assertThat(lbf.isAutowireCandidate(JUERGEN, qualifiedDescriptor)).isFalse();
@@ -124,61 +118,56 @@ class QualifierAnnotationAutowireBeanFactoryTests {
@Test
void testAutowireCandidateWithShortClassName() throws Exception {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
ConstructorArgumentValues cavs = new ConstructorArgumentValues();
cavs.addGenericArgumentValue(JUERGEN);
RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null);
person.addQualifier(new AutowireCandidateQualifier(ClassUtils.getShortName(TestQualifier.class)));
lbf.registerBeanDefinition(JUERGEN, person);
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(
QualifiedTestBean.class.getDeclaredField("qualified"), false);
DependencyDescriptor nonqualifiedDescriptor = new DependencyDescriptor(
QualifiedTestBean.class.getDeclaredField("nonqualified"), false);
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, nonqualifiedDescriptor)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, qualifiedDescriptor)).isTrue();
}
@Disabled
@Test
void testAutowireCandidateWithConstructorDescriptor() throws Exception {
lbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
cavs1.addGenericArgumentValue(JUERGEN);
RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
person1.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
lbf.registerBeanDefinition(JUERGEN, person1);
ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
cavs2.addGenericArgumentValue(MARK);
RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
lbf.registerBeanDefinition(MARK, person2);
MethodParameter param = new MethodParameter(QualifiedTestBean.class.getDeclaredConstructor(Person.class), 0);
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(param, false);
param.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
assertThat(param.getParameterName()).isEqualTo("tpb");
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, qualifiedDescriptor)).isTrue();
assertThat(lbf.isAutowireCandidate(MARK, qualifiedDescriptor)).isFalse();
}
@Disabled
@Test
void testAutowireCandidateWithMethodDescriptor() throws Exception {
lbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
cavs1.addGenericArgumentValue(JUERGEN);
RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
person1.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
lbf.registerBeanDefinition(JUERGEN, person1);
ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
cavs2.addGenericArgumentValue(MARK);
RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
lbf.registerBeanDefinition(MARK, person2);
MethodParameter qualifiedParam =
new MethodParameter(QualifiedTestBean.class.getDeclaredMethod("autowireQualified", Person.class), 0);
MethodParameter nonqualifiedParam =
@@ -186,70 +175,37 @@ class QualifierAnnotationAutowireBeanFactoryTests {
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(qualifiedParam, false);
DependencyDescriptor nonqualifiedDescriptor = new DependencyDescriptor(nonqualifiedParam, false);
qualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
nonqualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
assertThat(qualifiedParam.getParameterName()).isEqualTo("tpb");
nonqualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
assertThat(nonqualifiedParam.getParameterName()).isEqualTo("tpb");
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, nonqualifiedDescriptor)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, qualifiedDescriptor)).isTrue();
assertThat(lbf.isAutowireCandidate(MARK, null)).isTrue();
assertThat(lbf.isAutowireCandidate(MARK, nonqualifiedDescriptor)).isTrue();
assertThat(lbf.isAutowireCandidate(MARK, qualifiedDescriptor)).isFalse();
}
@Test
void testAutowireCandidateWithMultipleCandidatesDescriptor() throws Exception {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
cavs1.addGenericArgumentValue(JUERGEN);
RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
person1.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
lbf.registerBeanDefinition(JUERGEN, person1);
ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
cavs2.addGenericArgumentValue(MARK);
RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
person2.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
lbf.registerBeanDefinition(MARK, person2);
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(
new MethodParameter(QualifiedTestBean.class.getDeclaredConstructor(Person.class), 0),
false);
assertThat(lbf.isAutowireCandidate(JUERGEN, qualifiedDescriptor)).isTrue();
assertThat(lbf.isAutowireCandidate(MARK, qualifiedDescriptor)).isTrue();
}
@Test
void autowireBeanByTypeWithQualifierPrecedence() throws Exception {
lbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("testBean", bd);
lbf.registerBeanDefinition("spouse", bd2);
lbf.registerAlias("test", "testBean");
assertThat(lbf.resolveDependency(new DependencyDescriptor(getClass().getDeclaredField("testBean"), true), null))
.isSameAs(lbf.getBean("spouse"));
}
@Test
void autowireBeanByTypeWithQualifierPrecedenceInAncestor() throws Exception {
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
parent.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
parent.registerBeanDefinition("test", bd);
parent.registerBeanDefinition("spouse", bd2);
parent.registerAlias("test", "testBean");
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(parent);
lbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
assertThat(lbf.resolveDependency(new DependencyDescriptor(getClass().getDeclaredField("testBean"), true), null))
.isSameAs(lbf.getBean("spouse"));
}
@SuppressWarnings("unused")
private static class QualifiedTestBean {
@@ -291,8 +247,4 @@ class QualifierAnnotationAutowireBeanFactoryTests {
private @interface TestQualifier {
}
@Qualifier("spouse")
private TestBean testBean;
}
-7
View File
@@ -58,10 +58,3 @@ dependencies {
testRuntimeOnly("org.javamoney:moneta")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine") // for @Inject TCK
}
test {
description = "Runs JUnit Jupiter tests and the @Inject TCK via JUnit Vintage."
useJUnitPlatform {
includeEngines "junit-jupiter", "junit-vintage"
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -191,15 +191,6 @@ final class ConfigurationClass {
return this.beanMethods;
}
boolean hasNonStaticBeanMethods() {
for (BeanMethod beanMethod : this.beanMethods) {
if (!beanMethod.getMetadata().isStatic()) {
return true;
}
}
return false;
}
void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
this.importedResources.put(importedResource, readerClass);
}
@@ -221,7 +212,7 @@ final class ConfigurationClass {
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods")) {
if (hasNonStaticBeanMethods() && this.metadata.isFinal()) {
if (this.metadata.isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
for (BeanMethod beanMethod : this.beanMethods) {
@@ -16,7 +16,6 @@
package org.springframework.context.annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -27,7 +26,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.scope.ScopedProxyFactoryBean;
import org.springframework.aot.AotDetector;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.beans.factory.BeanDefinitionStoreException;
@@ -114,17 +112,12 @@ class ConfigurationClassEnhancer {
try {
// Use original ClassLoader if config class not locally loaded in overriding class loader
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
if (classLoaderMismatch && classLoader instanceof SmartClassLoader smartClassLoader) {
if (classLoader instanceof SmartClassLoader smartClassLoader &&
classLoader != configClass.getClassLoader()) {
classLoader = smartClassLoader.getOriginalClassLoader();
classLoaderMismatch = (classLoader != configClass.getClassLoader());
}
// Use original ClassLoader if config class relies on package visibility
if (classLoaderMismatch && reliesOnPackageVisibility(configClass)) {
classLoader = configClass.getClassLoader();
classLoaderMismatch = false;
}
Enhancer enhancer = newEnhancer(configClass, classLoader);
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
Class<?> enhancedClass = createClass(enhancer, classLoaderMismatch);
if (logger.isTraceEnabled()) {
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
@@ -139,32 +132,6 @@ class ConfigurationClassEnhancer {
}
}
/**
* Checks whether the given config class relies on package visibility, either for
* the class and any of its constructors or for any of its {@code @Bean} methods.
*/
private boolean reliesOnPackageVisibility(Class<?> configSuperClass) {
int mod = configSuperClass.getModifiers();
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) {
return true;
}
for (Constructor<?> ctor : configSuperClass.getDeclaredConstructors()) {
mod = ctor.getModifiers();
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) {
return true;
}
}
for (Method method : ReflectionUtils.getDeclaredMethods(configSuperClass)) {
if (BeanAnnotationHelper.isBeanAnnotated(method)) {
mod = method.getModifiers();
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) {
return true;
}
}
}
return false;
}
/**
* Creates a new CGLIB {@link Enhancer} instance.
*/
@@ -172,22 +139,26 @@ class ConfigurationClassEnhancer {
Enhancer enhancer = new Enhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader smartClassLoader &&
smartClassLoader.isClassReloadable(configSuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setAttemptLoad(enhancer.getUseCache() && AotDetector.useGeneratedArtifacts());
enhancer.setAttemptLoad(!isClassReloadable(configSuperClass, classLoader));
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
/**
* Checks whether the given configuration class is reloadable.
*/
private boolean isClassReloadable(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
return (classLoader instanceof SmartClassLoader smartClassLoader &&
smartClassLoader.isClassReloadable(configSuperClass));
}
/**
* Uses enhancer to generate a subclass of superclass,
* ensuring that callbacks are registered for the new subclass.
@@ -197,9 +168,9 @@ class ConfigurationClassEnhancer {
try {
subclass = enhancer.createClass();
}
catch (Throwable ex) {
catch (CodeGenerationException ex) {
if (!fallback) {
throw (ex instanceof CodeGenerationException cgex ? cgex : new CodeGenerationException(ex));
throw ex;
}
// Possibly a package-visible @Bean method declaration not accessible
// in the given ClassLoader -> retry with original ClassLoader
@@ -217,7 +188,8 @@ class ConfigurationClassEnhancer {
/**
* Marker interface to be implemented by all @Configuration CGLIB subclasses.
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance}
* through checking to see if candidate classes are already assignable to it.
* through checking to see if candidate classes are already assignable to it, e.g.
* have already been enhanced.
* <p>Also extends {@link BeanFactoryAware}, as all enhanced {@code @Configuration}
* classes require access to the {@link BeanFactory} that created them.
* <p>Note that this interface is intended for framework-internal use only, however
@@ -578,7 +550,7 @@ class ConfigurationClassEnhancer {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(factoryBean.getClass());
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setAttemptLoad(AotDetector.useGeneratedArtifacts());
enhancer.setAttemptLoad(true);
enhancer.setCallbackType(MethodInterceptor.class);
// Ideally create enhanced FactoryBean proxy without constructor side effects,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -81,9 +81,10 @@ import org.springframework.util.MultiValueMap;
* any number of ConfigurationClass objects because one Configuration class may import
* another using the {@link Import} annotation).
*
* <p>This class helps separate the concern of parsing the structure of a Configuration class
* from the concern of registering BeanDefinition objects based on the content of that model
* (except {@code @ComponentScan} annotations which need to be registered immediately).
* <p>This class helps separate the concern of parsing the structure of a Configuration
* class from the concern of registering BeanDefinition objects based on the content of
* that model (with the exception of {@code @ComponentScan} annotations which need to be
* registered immediately).
*
* <p>This ASM-based implementation avoids reflection and eager class loading in order to
* interoperate effectively with lazy class loading in a Spring ApplicationContext.
@@ -160,23 +161,14 @@ class ConfigurationClassParser {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
ConfigurationClass configClass;
if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
configClass = parse(annotatedBeanDef.getMetadata(), holder.getBeanName());
parse(annotatedBeanDef.getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition abstractBeanDef && abstractBeanDef.hasBeanClass()) {
configClass = parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
}
else {
configClass = parse(bd.getBeanClassName(), holder.getBeanName());
}
// Downgrade to lite (no enhancement) in case of no instance-level @Bean methods.
if (!configClass.getMetadata().isAbstract() && !configClass.hasNonStaticBeanMethods() &&
ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(
bd.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE))) {
bd.setAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE,
ConfigurationClassUtils.CONFIGURATION_CLASS_LITE);
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
@@ -191,37 +183,31 @@ class ConfigurationClassParser {
this.deferredImportSelectorHandler.process();
}
final ConfigurationClass parse(AnnotationMetadata metadata, String beanName) {
ConfigurationClass configClass = new ConfigurationClass(metadata, beanName);
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
return configClass;
}
final ConfigurationClass parse(Class<?> clazz, String beanName) {
ConfigurationClass configClass = new ConfigurationClass(clazz, beanName);
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
return configClass;
}
final ConfigurationClass parse(@Nullable String className, String beanName) throws IOException {
protected final void parse(@Nullable String className, String beanName) throws IOException {
Assert.notNull(className, "No bean class name for configuration class bean definition");
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
ConfigurationClass configClass = new ConfigurationClass(reader, beanName);
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
return configClass;
processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
/**
* Validate each {@link ConfigurationClass} object.
* @see ConfigurationClass#validate
*/
void validate() {
public void validate() {
for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
configClass.validate(this.problemReporter);
}
}
Set<ConfigurationClass> getConfigurationClasses() {
public Set<ConfigurationClass> getConfigurationClasses() {
return this.configurationClasses.keySet();
}
@@ -230,7 +216,7 @@ class ConfigurationClassParser {
Collections.emptyList());
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) {
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
@@ -462,7 +448,7 @@ class ConfigurationClassParser {
/**
* Returns {@code @Import} classes, considering all meta-annotations.
* Returns {@code @Import} class, considering all meta-annotations.
*/
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
@@ -650,7 +636,7 @@ class ConfigurationClassParser {
private final MultiValueMap<String, AnnotationMetadata> imports = new LinkedMultiValueMap<>();
void registerImport(AnnotationMetadata importingClass, String importedClass) {
public void registerImport(AnnotationMetadata importingClass, String importedClass) {
this.imports.add(importedClass, importingClass);
}
@@ -705,7 +691,7 @@ class ConfigurationClassParser {
* @param configClass the source configuration class
* @param importSelector the selector to handle
*/
void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
@@ -717,7 +703,7 @@ class ConfigurationClassParser {
}
}
void process() {
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
@@ -741,7 +727,7 @@ class ConfigurationClassParser {
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
void register(DeferredImportSelectorHolder deferredImport) {
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
@@ -751,7 +737,7 @@ class ConfigurationClassParser {
deferredImport.getConfigurationClass());
}
void processGroupImports() {
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping.getImports().forEach(entry -> {
@@ -789,16 +775,16 @@ class ConfigurationClassParser {
private final DeferredImportSelector importSelector;
DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) {
public DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) {
this.configurationClass = configClass;
this.importSelector = selector;
}
ConfigurationClass getConfigurationClass() {
public ConfigurationClass getConfigurationClass() {
return this.configurationClass;
}
DeferredImportSelector getImportSelector() {
public DeferredImportSelector getImportSelector() {
return this.importSelector;
}
}
@@ -814,7 +800,7 @@ class ConfigurationClassParser {
this.group = group;
}
void add(DeferredImportSelectorHolder deferredImport) {
public void add(DeferredImportSelectorHolder deferredImport) {
this.deferredImports.add(deferredImport);
}
@@ -822,7 +808,7 @@ class ConfigurationClassParser {
* Return the imports defined by the group.
* @return each import with its associated configuration class
*/
Iterable<Group.Entry> getImports() {
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
@@ -830,7 +816,7 @@ class ConfigurationClassParser {
return this.group.selectImports();
}
Predicate<String> getCandidateFilter() {
public Predicate<String> getCandidateFilter() {
Predicate<String> mergedFilter = DEFAULT_EXCLUSION_FILTER;
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
Predicate<String> selectorFilter = deferredImport.getImportSelector().getExclusionFilter();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -61,8 +61,7 @@ import org.springframework.util.ClassUtils;
* interactions on a {@link org.springframework.context.ConfigurableApplicationContext}.
*
* <p>As of 6.1, this also includes support for JVM checkpoint/restore (Project CRaC)
* when the {@code org.crac:crac} dependency is on the classpath. All running beans
* will get stopped and restarted according to the CRaC checkpoint/restore callbacks.
* when the {@code org.crac:crac} dependency on the classpath.
*
* @author Mark Fisher
* @author Juergen Hoeller
@@ -380,7 +379,7 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
}
// Overridable hooks
// overridable hooks
/**
* Retrieve all applicable Lifecycle beans: all singletons that have already been created,
@@ -494,13 +493,11 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
}
}
try {
if (!latch.await(this.timeout, TimeUnit.MILLISECONDS)) {
// Count is still >0 after timeout
if (!countDownBeanNames.isEmpty() && logger.isInfoEnabled()) {
logger.info("Shutdown phase " + this.phase + " ends with " + countDownBeanNames.size() +
" bean" + (countDownBeanNames.size() > 1 ? "s" : "") +
" still running after timeout of " + this.timeout + "ms: " + countDownBeanNames);
}
latch.await(this.timeout, TimeUnit.MILLISECONDS);
if (latch.getCount() > 0 && !countDownBeanNames.isEmpty() && logger.isInfoEnabled()) {
logger.info("Shutdown phase " + this.phase + " ends with " + countDownBeanNames.size() +
" bean" + (countDownBeanNames.size() > 1 ? "s" : "") +
" still running after timeout of " + this.timeout + "ms: " + countDownBeanNames);
}
}
catch (InterruptedException ex) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -18,7 +18,6 @@ package org.springframework.scheduling.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
@@ -54,7 +53,6 @@ public class ExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionPa
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
private void configureRejectionPolicy(Element element, BeanDefinitionBuilder builder) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2012 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.
@@ -18,7 +18,6 @@ package org.springframework.scheduling.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
@@ -42,7 +41,6 @@ public class SchedulerBeanDefinitionParser extends AbstractSingleBeanDefinitionP
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
}
@@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -542,13 +543,15 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
* <p>Mark fields as disallowed, for example to avoid unwanted
* modifications by malicious users when binding HTTP request parameters.
* <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts),
* as well as direct equality.
* <p>The default implementation of this method stores disallowed field
* patterns in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String)
* canonical} form, and subsequently pattern matching in {@link #isAllowed}
* is case-insensitive. Subclasses that override this method must therefore
* take this transformation into account.
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
* well as direct equality.
* <p>The default implementation of this method stores disallowed field patterns
* in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical}
* form. As of Spring Framework 5.2.21, the default implementation also transforms
* disallowed field patterns to {@linkplain String#toLowerCase() lowercase} to
* support case-insensitive pattern matching in {@link #isAllowed}. Subclasses
* which override this method must therefore take both of these transformations
* into account.
* <p>More sophisticated matching can be implemented by overriding the
* {@link #isAllowed} method.
* <p>Alternatively, specify a list of <i>allowed</i> field patterns.
@@ -566,7 +569,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
else {
String[] fieldPatterns = new String[disallowedFields.length];
for (int i = 0; i < fieldPatterns.length; i++) {
fieldPatterns[i] = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]);
String field = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]);
fieldPatterns[i] = field.toLowerCase(Locale.ROOT);
}
this.disallowedFields = fieldPatterns;
}
@@ -1136,9 +1140,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
* Determine if the given field is allowed for binding.
* <p>Invoked for each passed-in property value.
* <p>Checks for {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts),
* as well as direct equality, in the configured lists of allowed field
* patterns and disallowed field patterns.
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
* well as direct equality, in the configured lists of allowed field patterns
* and disallowed field patterns.
* <p>Matching against allowed field patterns is case-sensitive; whereas,
* matching against disallowed field patterns is case-insensitive.
* <p>A field matching a disallowed pattern will not be accepted even if it
@@ -1154,13 +1158,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
protected boolean isAllowed(String field) {
String[] allowed = getAllowedFields();
String[] disallowed = getDisallowedFields();
if (!ObjectUtils.isEmpty(allowed) && !PatternMatchUtils.simpleMatch(allowed, field)) {
return false;
}
if (!ObjectUtils.isEmpty(disallowed)) {
return !PatternMatchUtils.simpleMatchIgnoreCase(disallowed, field);
}
return true;
return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) &&
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field.toLowerCase(Locale.ROOT))));
}
/**
@@ -76,7 +76,7 @@ class ConfigurationClassEnhancerTests {
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
}
@Test
@@ -104,31 +104,6 @@ class ConfigurationClassEnhancerTests {
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
}
@Test
void withNonPublicConstructor() {
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new OverridingClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
}
@Test
void withNonPublicMethod() {
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
@@ -136,7 +111,7 @@ class ConfigurationClassEnhancerTests {
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
classLoader = new OverridingClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
@@ -185,19 +160,6 @@ class ConfigurationClassEnhancerTests {
}
@Configuration
public static class MyConfigWithNonPublicConstructor {
MyConfigWithNonPublicConstructor() {
}
@Bean
public String myBean() {
return "bean";
}
}
@Configuration
public static class MyConfigWithNonPublicMethod {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -67,7 +67,6 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -105,7 +104,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).contains(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isSameAs(foo);
@@ -120,7 +118,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).contains(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isSameAs(foo);
@@ -129,29 +126,12 @@ class ConfigurationClassPostProcessorTests {
assertThat(beanFactory.getDependentBeans("config")).contains("bar");
}
@Test // gh-34663
void enhancementIsPresentForAbstractConfigClassWithoutBeanMethods() {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(AbstractConfigWithoutBeanMethods.class));
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanFactory.getBeanDefinition("config");
assertThat(beanDefinition.hasBeanClass()).isTrue();
assertThat(beanDefinition.getBeanClass().getName()).contains(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isSameAs(foo);
assertThat(beanFactory.getDependentBeans("foo")).contains("bar");
String[] dependentsOfSingletonBeanConfig = beanFactory.getDependentBeans(SingletonBeanConfig.class.getName());
assertThat(dependentsOfSingletonBeanConfig).containsOnly("foo", "bar");
}
@Test
void enhancementIsNotPresentForProxyBeanMethodsFlagSetToFalse() {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(NonEnhancedSingletonBeanConfig.class));
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isNotSameAs(foo);
@@ -163,7 +143,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isNotSameAs(foo);
@@ -175,7 +154,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("foo")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("bar")).hasBeanClass()).isTrue();
Foo foo = beanFactory.getBean("foo", Foo.class);
@@ -189,7 +167,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("foo")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("bar")).hasBeanClass()).isTrue();
Foo foo = beanFactory.getBean("foo", Foo.class);
@@ -197,15 +174,6 @@ class ConfigurationClassPostProcessorTests {
assertThat(bar.foo).isNotSameAs(foo);
}
@Test // gh-34486
void enhancementIsNotPresentWithEmptyConfig() {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(EmptyConfig.class));
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
}
@Test
void configurationIntrospectionOfInnerClassesWorksWithDotNameSyntax() {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(getClass().getName() + ".SingletonBeanConfig"));
@@ -1190,7 +1158,7 @@ class ConfigurationClassPostProcessorTests {
}
@Configuration
static final class StaticSingletonBeanConfig {
static class StaticSingletonBeanConfig {
@Bean
public static Foo foo() {
@@ -1203,16 +1171,6 @@ class ConfigurationClassPostProcessorTests {
}
}
@Configuration
@Import(SingletonBeanConfig.class)
abstract static class AbstractConfigWithoutBeanMethods {
// This class intentionally does NOT declare @Bean methods.
}
@Configuration
static final class EmptyConfig {
}
@Configuration
@Order(2)
static class OverridingSingletonBeanConfig {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -37,18 +37,16 @@ class InvalidConfigurationClassDefinitionTests {
@Test
void configurationClassesMayNotBeFinal() {
@Configuration
final class Config {
@Bean String dummy() { return "dummy"; }
}
final class Config { }
BeanDefinition configBeanDef = rootBeanDefinition(Config.class).getBeanDefinition();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("config", configBeanDef);
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
assertThatExceptionOfType(BeanDefinitionParsingException.class)
.isThrownBy(() -> pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("Remove the final modifier");
assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(() ->
pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("Remove the final modifier");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -38,8 +38,7 @@ import org.springframework.context.support.GenericApplicationContext;
* @author Juergen Hoeller
* @since 3.0
*/
// WARNING: This class MUST be public, since it is based on JUnit 3.
public class SpringAtInjectTckTests {
class SpringAtInjectTckTests {
@SuppressWarnings("unchecked")
public static Test suite() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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,11 +54,10 @@ class DefaultLifecycleProcessorTests {
@Test
void customLifecycleProcessorInstance() {
StaticApplicationContext context = new StaticApplicationContext();
BeanDefinition beanDefinition = new RootBeanDefinition(DefaultLifecycleProcessor.class);
beanDefinition.getPropertyValues().addPropertyValue("timeoutPerShutdownPhase", 1000);
context.registerBeanDefinition(StaticApplicationContext.LIFECYCLE_PROCESSOR_BEAN_NAME, beanDefinition);
StaticApplicationContext context = new StaticApplicationContext();
context.registerBeanDefinition("lifecycleProcessor", beanDefinition);
context.refresh();
LifecycleProcessor bean = context.getBean("lifecycleProcessor", LifecycleProcessor.class);
Object contextLifecycleProcessor = new DirectFieldAccessor(context).getPropertyValue("lifecycleProcessor");
@@ -71,12 +70,11 @@ class DefaultLifecycleProcessorTests {
@Test
void singleSmartLifecycleAutoStartup() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
bean.setAutoStartup(true);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean);
assertThat(bean.isRunning()).isFalse();
context.refresh();
assertThat(bean.isRunning()).isTrue();
@@ -116,13 +114,12 @@ class DefaultLifecycleProcessorTests {
@Test
void singleSmartLifecycleAutoStartupWithFailingLifecycleBean() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
bean.setAutoStartup(true);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean);
context.registerSingleton("failingBean", FailingLifecycleBean.class);
assertThat(bean.isRunning()).isFalse();
assertThatExceptionOfType(ApplicationContextException.class)
.isThrownBy(context::refresh).withCauseInstanceOf(IllegalStateException.class);
@@ -133,12 +130,11 @@ class DefaultLifecycleProcessorTests {
@Test
void singleSmartLifecycleWithoutAutoStartup() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
bean.setAutoStartup(false);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean);
assertThat(bean.isRunning()).isFalse();
context.refresh();
assertThat(bean.isRunning()).isFalse();
@@ -152,16 +148,15 @@ class DefaultLifecycleProcessorTests {
@Test
void singleSmartLifecycleAutoStartupWithNonAutoStartupDependency() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
bean.setAutoStartup(true);
TestSmartLifecycleBean dependency = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
dependency.setAutoStartup(false);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean);
context.getBeanFactory().registerSingleton("dependency", dependency);
context.getBeanFactory().registerDependentBean("dependency", "bean");
assertThat(bean.isRunning()).isFalse();
assertThat(dependency.isRunning()).isFalse();
context.refresh();
@@ -176,19 +171,18 @@ class DefaultLifecycleProcessorTests {
@Test
void smartLifecycleGroupStartup() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forStartupTests(Integer.MIN_VALUE, startedBeans);
TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forStartupTests(2, startedBeans);
TestSmartLifecycleBean bean3 = TestSmartLifecycleBean.forStartupTests(3, startedBeans);
TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.forStartupTests(Integer.MAX_VALUE, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean3", bean3);
context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("beanMax", beanMax);
context.getBeanFactory().registerSingleton("bean1", bean1);
assertThat(beanMin.isRunning()).isFalse();
assertThat(bean1.isRunning()).isFalse();
assertThat(bean2.isRunning()).isFalse();
@@ -208,17 +202,16 @@ class DefaultLifecycleProcessorTests {
@Test
void contextRefreshThenStartWithMixedBeans() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestLifecycleBean simpleBean1 = TestLifecycleBean.forStartupTests(startedBeans);
TestLifecycleBean simpleBean2 = TestLifecycleBean.forStartupTests(startedBeans);
TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forStartupTests(5, startedBeans);
TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forStartupTests(-3, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("simpleBean1", simpleBean1);
context.getBeanFactory().registerSingleton("smartBean1", smartBean1);
context.getBeanFactory().registerSingleton("simpleBean2", simpleBean2);
context.getBeanFactory().registerSingleton("smartBean2", smartBean2);
assertThat(simpleBean1.isRunning()).isFalse();
assertThat(simpleBean2.isRunning()).isFalse();
assertThat(smartBean1.isRunning()).isFalse();
@@ -240,17 +233,16 @@ class DefaultLifecycleProcessorTests {
@Test
void contextRefreshThenStopAndRestartWithMixedBeans() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestLifecycleBean simpleBean1 = TestLifecycleBean.forStartupTests(startedBeans);
TestLifecycleBean simpleBean2 = TestLifecycleBean.forStartupTests(startedBeans);
TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forStartupTests(5, startedBeans);
TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forStartupTests(-3, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("simpleBean1", simpleBean1);
context.getBeanFactory().registerSingleton("smartBean1", smartBean1);
context.getBeanFactory().registerSingleton("simpleBean2", simpleBean2);
context.getBeanFactory().registerSingleton("smartBean2", smartBean2);
assertThat(simpleBean1.isRunning()).isFalse();
assertThat(simpleBean2.isRunning()).isFalse();
assertThat(smartBean1.isRunning()).isFalse();
@@ -278,17 +270,16 @@ class DefaultLifecycleProcessorTests {
@Test
void contextRefreshThenStopForRestartWithMixedBeans() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestLifecycleBean simpleBean1 = TestLifecycleBean.forStartupTests(startedBeans);
TestLifecycleBean simpleBean2 = TestLifecycleBean.forStartupTests(startedBeans);
TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forStartupTests(5, startedBeans);
TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forStartupTests(-3, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("simpleBean1", simpleBean1);
context.getBeanFactory().registerSingleton("smartBean1", smartBean1);
context.getBeanFactory().registerSingleton("simpleBean2", simpleBean2);
context.getBeanFactory().registerSingleton("smartBean2", smartBean2);
assertThat(simpleBean1.isRunning()).isFalse();
assertThat(simpleBean2.isRunning()).isFalse();
assertThat(smartBean1.isRunning()).isFalse();
@@ -328,7 +319,6 @@ class DefaultLifecycleProcessorTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void smartLifecycleGroupShutdown() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forShutdownTests(1, 300, stoppedBeans);
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(3, 100, stoppedBeans);
@@ -337,6 +327,7 @@ class DefaultLifecycleProcessorTests {
TestSmartLifecycleBean bean5 = TestSmartLifecycleBean.forShutdownTests(2, 700, stoppedBeans);
TestSmartLifecycleBean bean6 = TestSmartLifecycleBean.forShutdownTests(Integer.MAX_VALUE, 200, stoppedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(3, 200, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean3", bean3);
@@ -344,7 +335,6 @@ class DefaultLifecycleProcessorTests {
context.getBeanFactory().registerSingleton("bean5", bean5);
context.getBeanFactory().registerSingleton("bean6", bean6);
context.getBeanFactory().registerSingleton("bean7", bean7);
context.refresh();
context.stop();
assertThat(stoppedBeans).satisfiesExactly(hasPhase(Integer.MAX_VALUE), hasPhase(3),
@@ -355,12 +345,11 @@ class DefaultLifecycleProcessorTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void singleSmartLifecycleShutdown() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forShutdownTests(99, 300, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean);
context.refresh();
assertThat(bean.isRunning()).isTrue();
context.stop();
assertThat(bean.isRunning()).isFalse();
@@ -370,11 +359,10 @@ class DefaultLifecycleProcessorTests {
@Test
void singleLifecycleShutdown() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
Lifecycle bean = new TestLifecycleBean(null, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean);
context.refresh();
assertThat(bean.isRunning()).isFalse();
bean.start();
@@ -387,7 +375,6 @@ class DefaultLifecycleProcessorTests {
@Test
void mixedShutdown() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
Lifecycle bean1 = TestLifecycleBean.forShutdownTests(stoppedBeans);
Lifecycle bean2 = TestSmartLifecycleBean.forShutdownTests(500, 200, stoppedBeans);
@@ -396,6 +383,7 @@ class DefaultLifecycleProcessorTests {
Lifecycle bean5 = TestSmartLifecycleBean.forShutdownTests(1, 200, stoppedBeans);
Lifecycle bean6 = TestSmartLifecycleBean.forShutdownTests(-1, 100, stoppedBeans);
Lifecycle bean7 = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 300, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean3", bean3);
@@ -403,7 +391,6 @@ class DefaultLifecycleProcessorTests {
context.getBeanFactory().registerSingleton("bean5", bean5);
context.getBeanFactory().registerSingleton("bean6", bean6);
context.getBeanFactory().registerSingleton("bean7", bean7);
context.refresh();
assertThat(bean2.isRunning()).isTrue();
assertThat(bean3.isRunning()).isTrue();
@@ -431,18 +418,17 @@ class DefaultLifecycleProcessorTests {
@Test
void dependencyStartedFirstEvenIfItsPhaseIsHigher() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forStartupTests(Integer.MIN_VALUE, startedBeans);
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forStartupTests(2, startedBeans);
TestSmartLifecycleBean bean99 = TestSmartLifecycleBean.forStartupTests(99, startedBeans);
TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.forStartupTests(Integer.MAX_VALUE, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean99", bean99);
context.getBeanFactory().registerSingleton("beanMax", beanMax);
context.getBeanFactory().registerDependentBean("bean99", "bean2");
context.refresh();
assertThat(beanMin.isRunning()).isTrue();
assertThat(bean2.isRunning()).isTrue();
@@ -460,7 +446,6 @@ class DefaultLifecycleProcessorTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void dependentShutdownFirstEvenIfItsPhaseIsLower() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 100, stoppedBeans);
TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forShutdownTests(1, 200, stoppedBeans);
@@ -468,6 +453,7 @@ class DefaultLifecycleProcessorTests {
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(2, 300, stoppedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(7, 400, stoppedBeans);
TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.forShutdownTests(Integer.MAX_VALUE, 400, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2);
@@ -475,7 +461,6 @@ class DefaultLifecycleProcessorTests {
context.getBeanFactory().registerSingleton("bean99", bean99);
context.getBeanFactory().registerSingleton("beanMax", beanMax);
context.getBeanFactory().registerDependentBean("bean99", "bean2");
context.refresh();
assertThat(beanMin.isRunning()).isTrue();
assertThat(bean1.isRunning()).isTrue();
@@ -501,18 +486,17 @@ class DefaultLifecycleProcessorTests {
@Test
void dependencyStartedFirstAndIsSmartLifecycle() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanNegative = TestSmartLifecycleBean.forStartupTests(-99, startedBeans);
TestSmartLifecycleBean bean99 = TestSmartLifecycleBean.forStartupTests(99, startedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forStartupTests(7, startedBeans);
TestLifecycleBean simpleBean = TestLifecycleBean.forStartupTests(startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanNegative", beanNegative);
context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("bean99", bean99);
context.getBeanFactory().registerSingleton("simpleBean", simpleBean);
context.getBeanFactory().registerDependentBean("bean7", "simpleBean");
context.refresh();
context.stop();
startedBeans.clear();
@@ -530,7 +514,6 @@ class DefaultLifecycleProcessorTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void dependentShutdownFirstAndIsSmartLifecycle() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 400, stoppedBeans);
TestSmartLifecycleBean beanNegative = TestSmartLifecycleBean.forShutdownTests(-99, 100, stoppedBeans);
@@ -538,6 +521,7 @@ class DefaultLifecycleProcessorTests {
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(2, 300, stoppedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(7, 400, stoppedBeans);
TestLifecycleBean simpleBean = TestLifecycleBean.forShutdownTests(stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("beanNegative", beanNegative);
context.getBeanFactory().registerSingleton("bean1", bean1);
@@ -545,7 +529,6 @@ class DefaultLifecycleProcessorTests {
context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("simpleBean", simpleBean);
context.getBeanFactory().registerDependentBean("simpleBean", "beanNegative");
context.refresh();
assertThat(beanMin.isRunning()).isTrue();
assertThat(beanNegative.isRunning()).isTrue();
@@ -568,16 +551,15 @@ class DefaultLifecycleProcessorTests {
@Test
void dependencyStartedFirstButNotSmartLifecycle() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forStartupTests(Integer.MIN_VALUE, startedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forStartupTests(7, startedBeans);
TestLifecycleBean simpleBean = TestLifecycleBean.forStartupTests(startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("simpleBean", simpleBean);
context.getBeanFactory().registerDependentBean("simpleBean", "beanMin");
context.refresh();
assertThat(beanMin.isRunning()).isTrue();
assertThat(bean7.isRunning()).isTrue();
@@ -590,20 +572,19 @@ class DefaultLifecycleProcessorTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void dependentShutdownFirstButNotSmartLifecycle() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forShutdownTests(1, 200, stoppedBeans);
TestLifecycleBean simpleBean = TestLifecycleBean.forShutdownTests(stoppedBeans);
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(2, 300, stoppedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(7, 400, stoppedBeans);
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 400, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("simpleBean", simpleBean);
context.getBeanFactory().registerDependentBean("bean2", "simpleBean");
context.refresh();
assertThat(beanMin.isRunning()).isTrue();
assertThat(bean1.isRunning()).isTrue();
@@ -630,7 +611,6 @@ class DefaultLifecycleProcessorTests {
};
}
private static class TestLifecycleBean implements Lifecycle {
private final CopyOnWriteArrayList<Lifecycle> startedBeans;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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,7 +29,6 @@ import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
@@ -73,33 +72,6 @@ class PropertySourcesPlaceholderConfigurerTests {
assertThat(ppc.getAppliedPropertySources()).isNotNull();
}
@Test // gh-34936
void replacementFromEnvironmentPropertiesWithConversion() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
record Point(int x, int y) {
}
Converter<Point, String> pointToStringConverter =
point -> "(%d,%d)".formatted(point.x, point.y);
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(Point.class, String.class, pointToStringConverter);
MockEnvironment env = new MockEnvironment();
env.setConversionService(conversionService);
env.setProperty("my.name", new Point(4,5));
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("(4,5)");
}
@Test
void localPropertiesViaResource() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -27,7 +27,7 @@ import org.springframework.core.testfixture.env.MockPropertySource;
* @author Chris Beams
* @author Sam Brannen
* @since 3.2
* @see MockPropertySource
* @see org.springframework.core.testfixture.env.MockPropertySource
*/
public class MockEnvironment extends AbstractEnvironment {
@@ -44,21 +44,19 @@ public class MockEnvironment extends AbstractEnvironment {
/**
* Set a property on the underlying {@link MockPropertySource} for this environment.
* @see MockPropertySource#setProperty(String, Object)
*/
public void setProperty(String name, Object value) {
this.propertySource.setProperty(name, value);
public void setProperty(String key, String value) {
this.propertySource.setProperty(key, value);
}
/**
* Convenient synonym for {@link #setProperty(String, Object)} that returns
* the current instance.
* <p>Useful for method chaining and fluent-style use.
* Convenient synonym for {@link #setProperty} that returns the current instance.
* Useful for method chaining and fluent-style use.
* @return this {@link MockEnvironment} instance
* @see MockPropertySource#withProperty(String, Object)
* @see MockPropertySource#withProperty
*/
public MockEnvironment withProperty(String name, Object value) {
setProperty(name, value);
public MockEnvironment withProperty(String key, String value) {
setProperty(key, value);
return this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -27,7 +27,6 @@ import java.util.stream.Stream;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* Gather the need for resources available at runtime.
@@ -51,14 +50,14 @@ public class ResourceHints {
this.resourceBundleHints = new LinkedHashSet<>();
}
/**
* Return the resources that should be made available at runtime.
* @return a stream of {@link ResourcePatternHints}
*/
public Stream<ResourcePatternHints> resourcePatternHints() {
Stream<ResourcePatternHints> patterns = this.resourcePatternHints.stream();
return (this.types.isEmpty() ? patterns : Stream.concat(Stream.of(typesPatternResourceHint()), patterns));
return (this.types.isEmpty() ? patterns
: Stream.concat(Stream.of(typesPatternResourceHint()), patterns));
}
/**
@@ -71,18 +70,18 @@ public class ResourceHints {
/**
* Register a pattern if the given {@code location} is available on the
* classpath. This delegates to {@link ClassLoader#getResource(String)} which
* validates directories as well. The location is not included in the hint.
* @param classLoader the ClassLoader to use, or {@code null} for the default
* classpath. This delegates to {@link ClassLoader#getResource(String)}
* which validates directories as well. The location is not included in
* the hint.
* @param classLoader the classloader to use
* @param location a '/'-separated path name that should exist
* @param resourceHint a builder to customize the resource pattern
* @return {@code this}, to facilitate method chaining
*/
public ResourceHints registerPatternIfPresent(@Nullable ClassLoader classLoader, String location,
Consumer<ResourcePatternHints.Builder> resourceHint) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
if (classLoaderToUse != null && classLoaderToUse.getResource(location) != null) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : getClass().getClassLoader());
if (classLoaderToUse.getResource(location) != null) {
registerPattern(resourceHint);
}
return this;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 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.
@@ -25,9 +25,8 @@ import org.springframework.lang.Nullable;
*
* <p>Implementations of this interface can be registered dynamically by using
* {@link org.springframework.context.annotation.ImportRuntimeHints @ImportRuntimeHints}
* or statically in {@code META-INF/spring/aot.factories} by using the fully-qualified
* class name of this interface as the key. A standard no-arg constructor is required
* for implementations.
* or statically in {@code META-INF/spring/aot.factories} by using the FQN of this
* interface as the key. A standard no-arg constructor is required for implementations.
*
* @author Brian Clozel
* @author Stephane Nicoll
@@ -39,7 +38,7 @@ public interface RuntimeHintsRegistrar {
/**
* Contribute hints to the given {@link RuntimeHints} instance.
* @param hints the hints contributed so far for the deployment unit
* @param classLoader the ClassLoader to use, or {@code null} for the default
* @param classLoader the classloader, or {@code null} if even the system ClassLoader isn't accessible
*/
void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -23,7 +23,6 @@ import java.util.List;
import org.springframework.aot.hint.ResourceHints;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;
/**
@@ -67,21 +66,19 @@ public class FilePatternResourceHintsRegistrar {
@Deprecated(since = "6.0.12", forRemoval = true)
public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
if (classLoaderToUse != null) {
List<String> includes = new ArrayList<>();
for (String location : this.classpathLocations) {
if (classLoaderToUse.getResource(location) != null) {
for (String filePrefix : this.filePrefixes) {
for (String fileExtension : this.fileExtensions) {
includes.add(location + filePrefix + "*" + fileExtension);
}
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : getClass().getClassLoader());
List<String> includes = new ArrayList<>();
for (String location : this.classpathLocations) {
if (classLoaderToUse.getResource(location) != null) {
for (String filePrefix : this.filePrefixes) {
for (String fileExtension : this.fileExtensions) {
includes.add(location + filePrefix + "*" + fileExtension);
}
}
}
if (!includes.isEmpty()) {
hints.registerPattern(hint -> hint.includes(includes.toArray(String[]::new)));
}
}
if (!includes.isEmpty()) {
hints.registerPattern(hint -> hint.includes(includes.toArray(String[]::new)));
}
}
@@ -249,7 +246,8 @@ public class FilePatternResourceHintsRegistrar {
* classpath location that resolves against the {@code ClassLoader}, files
* with the configured file prefixes and extensions are registered.
* @param hints the hints contributed so far for the deployment unit
* @param classLoader the ClassLoader to use, or {@code null} for the default
* @param classLoader the classloader, or {@code null} if even the system
* ClassLoader isn't accessible
*/
public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
build().registerHints(hints, classLoader);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -48,11 +48,10 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
if (classLoaderToUse != null) {
for (String resourceLocation : RESOURCE_LOCATIONS) {
registerHints(hints, classLoaderToUse, resourceLocation);
}
ClassLoader classLoaderToUse = (classLoader != null ? classLoader :
SpringFactoriesLoaderRuntimeHints.class.getClassLoader());
for (String resourceLocation : RESOURCE_LOCATIONS) {
registerHints(hints, classLoaderToUse, resourceLocation);
}
}
@@ -123,17 +123,13 @@ abstract public class AbstractClassGenerator<T> implements ClassGenerator {
}
public Object get(AbstractClassGenerator gen, boolean useCache) {
// SPRING PATCH BEGIN
Object value = null;
if (useCache) {
if (!useCache) {
return gen.generate(ClassLoaderData.this);
}
else {
Object cachedValue = generatedClasses.get(gen);
value = gen.unwrapCachedValue(cachedValue);
return gen.unwrapCachedValue(cachedValue);
}
if (value == null) { // fallback when cached WeakReference returns null
value = gen.generate(ClassLoaderData.this);
}
return value;
// SPRING PATCH END
}
}
@@ -463,21 +463,10 @@ public class ReflectUtils {
c = lookup.defineClass(b);
}
catch (LinkageError | IllegalArgumentException ex) {
if (ex instanceof LinkageError) {
// Could be a ClassLoader mismatch with the class pre-existing in a
// parent ClassLoader -> try loadClass before giving up completely.
try {
c = contextClass.getClassLoader().loadClass(className);
}
catch (ClassNotFoundException cnfe) {
}
}
if (c == null) {
// in case of plain LinkageError (class already defined)
// or IllegalArgumentException (class in different package):
// fall through to traditional ClassLoader.defineClass below
t = ex;
}
// in case of plain LinkageError (class already defined)
// or IllegalArgumentException (class in different package):
// fall through to traditional ClassLoader.defineClass below
t = ex;
}
catch (Throwable ex) {
throw new CodeGenerationException(ex);
@@ -538,26 +527,15 @@ public class ReflectUtils {
c = lookup.defineClass(b);
}
catch (LinkageError | IllegalAccessException ex) {
if (ex instanceof LinkageError) {
// Could be a ClassLoader mismatch with the class pre-existing in a
// parent ClassLoader -> try loadClass before giving up completely.
try {
c = contextClass.getClassLoader().loadClass(className);
throw new CodeGenerationException(ex) {
@Override
public String getMessage() {
return "ClassLoader mismatch for [" + contextClass.getName() +
"]: JVM should be started with --add-opens=java.base/java.lang=ALL-UNNAMED " +
"for ClassLoader.defineClass to be accessible on " + loader.getClass().getName() +
"; consider co-locating the affected class in that target ClassLoader instead.";
}
catch (ClassNotFoundException cnfe) {
}
}
if (c == null) {
throw new CodeGenerationException(ex) {
@Override
public String getMessage() {
return "ClassLoader mismatch for [" + contextClass.getName() +
"]: JVM should be started with --add-opens=java.base/java.lang=ALL-UNNAMED " +
"for ClassLoader.defineClass to be accessible on " + loader.getClass().getName() +
"; consider co-locating the affected class in that target ClassLoader instead.";
}
};
}
};
}
catch (Throwable ex) {
throw new CodeGenerationException(ex);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -117,7 +117,7 @@ public final class SpringProperties {
* @param key the property key
*/
public static void setFlag(String key) {
localProperties.setProperty(key, Boolean.TRUE.toString());
localProperties.put(key, Boolean.TRUE.toString());
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -33,10 +33,7 @@ import org.springframework.util.StringUtils;
*
* <p>As of Spring 4.1.2, this class extends {@link EnumerablePropertySource} instead
* of plain {@link PropertySource}, exposing {@link #getPropertyNames()} based on the
* accumulated property names from all contained sources - and failing with an
* {@code IllegalStateException} against any non-{@code EnumerablePropertySource}.
* <b>When used through the {@code EnumerablePropertySource} contract, all contained
* sources are expected to be of type {@code EnumerablePropertySource} as well.</b>
* accumulated property names from all contained sources (as far as possible).
*
* @author Chris Beams
* @author Juergen Hoeller
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -206,8 +206,6 @@ import org.springframework.util.StringUtils;
*/
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
private static final Resource[] EMPTY_RESOURCE_ARRAY = {};
private static final Log logger = LogFactory.getLog(PathMatchingResourcePatternResolver.class);
/**
@@ -250,8 +248,6 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
private PathMatcher pathMatcher = new AntPathMatcher();
private boolean useCaches = true;
/**
* Create a {@code PathMatchingResourcePatternResolver} with a
@@ -319,21 +315,6 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
return this.pathMatcher;
}
/**
* Specify whether this resolver should use jar caches. Default is {@code true}.
* <p>Switch this flag to {@code false} in order to avoid jar caching at the
* {@link JarURLConnection} level.
* <p>Note that {@link JarURLConnection#setDefaultUseCaches} can be turned off
* independently. This resolver-level setting is designed to only enforce
* {@code JarURLConnection#setUseCaches(false)} if necessary but otherwise
* leaves the JVM-level default in place.
* @since 6.1.19
* @see JarURLConnection#setUseCaches
*/
public void setUseCaches(boolean useCaches) {
this.useCaches = useCaches;
}
@Override
public Resource getResource(String location) {
@@ -357,7 +338,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
// all class path resources with the given name
Collections.addAll(resources, findAllClassPathResources(locationPatternWithoutPrefix));
}
return resources.toArray(EMPTY_RESOURCE_ARRAY);
return resources.toArray(new Resource[0]);
}
else {
// Generally only look for a pattern after a prefix here,
@@ -390,7 +371,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
if (logger.isTraceEnabled()) {
logger.trace("Resolved class path location [" + path + "] to resources " + result);
}
return result.toArray(EMPTY_RESOURCE_ARRAY);
return result.toArray(new Resource[0]);
}
/**
@@ -626,7 +607,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
if (logger.isTraceEnabled()) {
logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(EMPTY_RESOURCE_ARRAY);
return result.toArray(new Resource[0]);
}
/**
@@ -714,9 +695,6 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
if (con instanceof JarURLConnection jarCon) {
// Should usually be the case for traditional JAR files.
if (!this.useCaches) {
jarCon.setUseCaches(false);
}
jarFile = jarCon.getJarFile();
jarFileUrl = jarCon.getJarFileURL().toExternalForm();
JarEntry jarEntry = jarCon.getJarEntry();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -69,13 +69,7 @@ public class TaskRejectedException extends RejectedExecutionException {
private static String executorDescription(Executor executor) {
if (executor instanceof ExecutorService executorService) {
try {
return "ExecutorService in " + (executorService.isShutdown() ? "shutdown" : "active") + " state";
}
catch (Exception ex) {
// UnsupportedOperationException/IllegalStateException from ManagedExecutorService.isShutdown()
// Falling back to toString() below.
}
return "ExecutorService in " + (executorService.isShutdown() ? "shutdown" : "active") + " state";
}
return executor.toString();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2021 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.
@@ -69,7 +69,7 @@ public class SpringObjenesis implements Objenesis {
this.strategy = (strategy != null ? strategy : new StdInstantiatorStrategy());
// Evaluate the "spring.objenesis.ignore" property upfront...
if (SpringProperties.getFlag(IGNORE_OBJENESIS_PROPERTY_NAME)) {
if (SpringProperties.getFlag(SpringObjenesis.IGNORE_OBJENESIS_PROPERTY_NAME)) {
this.worthTrying = Boolean.FALSE;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -37,25 +37,13 @@ public abstract class PatternMatchUtils {
* @return whether the String matches the given pattern
*/
public static boolean simpleMatch(@Nullable String pattern, @Nullable String str) {
return simpleMatch(pattern, str, false);
}
/**
* Variant of {@link #simpleMatch(String, String)} that ignores upper/lower case.
* @since 6.1.20
*/
public static boolean simpleMatchIgnoreCase(@Nullable String pattern, @Nullable String str) {
return simpleMatch(pattern, str, true);
}
private static boolean simpleMatch(@Nullable String pattern, @Nullable String str, boolean ignoreCase) {
if (pattern == null || str == null) {
return false;
}
int firstIndex = pattern.indexOf('*');
if (firstIndex == -1) {
return (ignoreCase ? pattern.equalsIgnoreCase(str) : pattern.equals(str));
return pattern.equals(str);
}
if (firstIndex == 0) {
@@ -64,43 +52,25 @@ public abstract class PatternMatchUtils {
}
int nextIndex = pattern.indexOf('*', 1);
if (nextIndex == -1) {
String part = pattern.substring(1);
return (ignoreCase ? StringUtils.endsWithIgnoreCase(str, part) : str.endsWith(part));
return str.endsWith(pattern.substring(1));
}
String part = pattern.substring(1, nextIndex);
if (part.isEmpty()) {
return simpleMatch(pattern.substring(nextIndex), str, ignoreCase);
return simpleMatch(pattern.substring(nextIndex), str);
}
int partIndex = indexOf(str, part, 0, ignoreCase);
int partIndex = str.indexOf(part);
while (partIndex != -1) {
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()), ignoreCase)) {
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
return true;
}
partIndex = indexOf(str, part, partIndex + 1, ignoreCase);
partIndex = str.indexOf(part, partIndex + 1);
}
return false;
}
return (str.length() >= firstIndex &&
checkStartsWith(pattern, str, firstIndex, ignoreCase) &&
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex), ignoreCase));
}
private static boolean checkStartsWith(String pattern, String str, int index, boolean ignoreCase) {
String part = str.substring(0, index);
return (ignoreCase ? StringUtils.startsWithIgnoreCase(pattern, part) : pattern.startsWith(part));
}
private static int indexOf(String str, String otherStr, int startIndex, boolean ignoreCase) {
if (!ignoreCase) {
return str.indexOf(otherStr, startIndex);
}
for (int i = startIndex; i <= (str.length() - otherStr.length()); i++) {
if (str.regionMatches(true, i, otherStr, 0, otherStr.length())) {
return i;
}
}
return -1;
pattern.startsWith(str.substring(0, firstIndex)) &&
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex)));
}
/**
@@ -124,19 +94,4 @@ public abstract class PatternMatchUtils {
return false;
}
/**
* Variant of {@link #simpleMatch(String[], String)} that ignores upper/lower case.
* @since 6.1.20
*/
public static boolean simpleMatchIgnoreCase(@Nullable String[] patterns, @Nullable String str) {
if (patterns != null) {
for (String pattern : patterns) {
if (simpleMatch(pattern, str, true)) {
return true;
}
}
}
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -85,7 +85,6 @@ public class ExponentialBackOff implements BackOff {
*/
public static final int DEFAULT_MAX_ATTEMPTS = Integer.MAX_VALUE;
private long initialInterval = DEFAULT_INITIAL_INTERVAL;
private double multiplier = DEFAULT_MULTIPLIER;
@@ -205,7 +204,6 @@ public class ExponentialBackOff implements BackOff {
return this.maxAttempts;
}
@Override
public BackOffExecution start() {
return new ExponentialBackOffExecution();
@@ -227,7 +225,6 @@ public class ExponentialBackOff implements BackOff {
.toString();
}
private class ExponentialBackOffExecution implements BackOffExecution {
private long currentInterval = -1;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2018 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.
@@ -35,7 +35,6 @@ public class FixedBackOff implements BackOff {
*/
public static final long UNLIMITED_ATTEMPTS = Long.MAX_VALUE;
private long interval = DEFAULT_INTERVAL;
private long maxAttempts = UNLIMITED_ATTEMPTS;
@@ -87,7 +86,6 @@ public class FixedBackOff implements BackOff {
return this.maxAttempts;
}
@Override
public BackOffExecution start() {
return new FixedBackOffExecution();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -108,11 +108,9 @@ class PathMatchingResourcePatternResolverTests {
Path rootDir = Paths.get("src/test/resources/custom%23root").toAbsolutePath();
URL root = new URL("file:" + rootDir + "/");
resolver = new PathMatchingResourcePatternResolver(new DefaultResourceLoader(new URLClassLoader(new URL[] {root})));
resolver.setUseCaches(false);
assertExactFilenames("classpath*:scanned/*.txt", "resource#test1.txt", "resource#test2.txt");
}
@Nested
class WithHashtagsInTheirFilenames {
@@ -334,7 +332,7 @@ class PathMatchingResourcePatternResolverTests {
// Tests fail if we use resource.getURL().getPath(). They would also fail on macOS when
// using resource.getURI().getPath() if the resource paths are not Unicode normalized.
//
// On the JVM, all tests should pass when using resource.getFile().getPath(); however,
// On the JVM, all tests should pass when using resouce.getFile().getPath(); however,
// we use FileSystemResource#getPath since this test class is sometimes run within a
// GraalVM native image which cannot support Path#toFile.
//
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -53,22 +53,18 @@ class PatternMatchUtilsTests {
assertMatches(new String[] { null, "" }, "");
assertMatches(new String[] { null, "123" }, "123");
assertMatches(new String[] { null, "*" }, "123");
testMixedCaseMatch("abC", "Abc");
}
@Test
void startsWith() {
assertMatches("get*", "getMe");
assertDoesNotMatch("get*", "setMe");
testMixedCaseMatch("geT*", "GetMe");
}
@Test
void endsWith() {
assertMatches("*Test", "getMeTest");
assertDoesNotMatch("*Test", "setMe");
testMixedCaseMatch("*TeSt", "getMeTesT");
}
@Test
@@ -78,10 +74,6 @@ class PatternMatchUtilsTests {
assertMatches("*stuff*", "stuffTest");
assertMatches("*stuff*", "getstuff");
assertMatches("*stuff*", "stuff");
testMixedCaseMatch("*stuff*", "getStuffTest");
testMixedCaseMatch("*stuff*", "StuffTest");
testMixedCaseMatch("*stuff*", "getStuff");
testMixedCaseMatch("*stuff*", "Stuff");
}
@Test
@@ -90,8 +82,6 @@ class PatternMatchUtilsTests {
assertMatches("on*Event", "onEvent");
assertDoesNotMatch("3*3", "3");
assertMatches("3*3", "33");
testMixedCaseMatch("on*Event", "OnMyEvenT");
testMixedCaseMatch("on*Event", "OnEvenT");
}
@Test
@@ -132,27 +122,18 @@ class PatternMatchUtilsTests {
private void assertMatches(String pattern, String str) {
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isTrue();
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isTrue();
}
private void assertDoesNotMatch(String pattern, String str) {
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isFalse();
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isFalse();
}
private void testMixedCaseMatch(String pattern, String str) {
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isFalse();
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isTrue();
}
private void assertMatches(String[] patterns, String str) {
assertThat(PatternMatchUtils.simpleMatch(patterns, str)).isTrue();
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(patterns, str)).isTrue();
}
private void assertDoesNotMatch(String[] patterns, String str) {
assertThat(PatternMatchUtils.simpleMatch(patterns, str)).isFalse();
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(patterns, str)).isFalse();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -26,7 +26,7 @@ import org.springframework.core.env.PropertySource;
* a user-provided {@link Properties} object, or if omitted during construction,
* the implementation will initialize its own.
*
* <p>The {@link #setProperty} and {@link #withProperty} methods are exposed for
* The {@link #setProperty} and {@link #withProperty} methods are exposed for
* convenience, for example:
* <pre class="code">
* {@code
@@ -95,7 +95,7 @@ public class MockPropertySource extends PropertiesPropertySource {
/**
* Convenient synonym for {@link #setProperty} that returns the current instance.
* <p>Useful for method chaining and fluent-style use.
* Useful for method chaining and fluent-style use.
* @return this {@link MockPropertySource} instance
*/
public MockPropertySource withProperty(String name, Object value) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -85,13 +85,12 @@ public class SingleColumnRowMapper<T> implements RowMapper<T> {
* Set a {@link ConversionService} for converting a fetched value.
* <p>Default is the {@link DefaultConversionService}.
* @since 5.0.4
* @see DefaultConversionService#getSharedInstance()
* @see DefaultConversionService#getSharedInstance
*/
public void setConversionService(@Nullable ConversionService conversionService) {
this.conversionService = conversionService;
}
/**
* Extract a value for the single column in the current row.
* <p>Validates that there is only one column selected,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -439,11 +439,7 @@ public abstract class DataSourceUtils {
public static Connection getTargetConnection(Connection con) {
Connection conToUse = con;
while (conToUse instanceof ConnectionProxy connectionProxy) {
Connection targetCon = connectionProxy.getTargetConnection();
if (targetCon == conToUse) {
break;
}
conToUse = targetCon;
conToUse = connectionProxy.getTargetConnection();
}
return conToUse;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -210,23 +210,13 @@ public class TransactionAwareDataSourceProxy extends DelegatingDataSource {
sb.append('[').append(this.target).append(']');
}
else {
sb.append("from DataSource [").append(this.targetDataSource).append(']');
sb.append(" from DataSource [").append(this.targetDataSource).append(']');
}
return sb.toString();
}
case "close" -> {
// Handle close method: only close if not within a transaction.
if (this.target != null) {
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(this.targetDataSource);
if (conHolder != null && conHolder.hasConnection() && conHolder.getConnection() == this.target) {
// It's the transactional Connection: Don't close it.
conHolder.released();
}
else {
DataSourceUtils.doCloseConnection(this.target, this.targetDataSource);
}
}
DataSourceUtils.doReleaseConnection(this.target, this.targetDataSource);
this.closed = true;
return null;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -72,7 +72,7 @@ public class DataSourceTransactionManagerTests {
protected DataSource ds = mock();
protected ConnectionProxy con = mock();
protected Connection con = mock();
protected DataSourceTransactionManager tm;
@@ -81,7 +81,6 @@ public class DataSourceTransactionManagerTests {
void setup() throws Exception {
tm = createTransactionManager(ds);
given(ds.getConnection()).willReturn(con);
given(con.getTargetConnection()).willThrow(new UnsupportedOperationException());
}
protected DataSourceTransactionManager createTransactionManager(DataSource ds) {
@@ -1075,9 +1074,9 @@ public class DataSourceTransactionManagerTests {
Connection tCon = dsProxy.getConnection();
tCon.getWarnings();
tCon.clearWarnings();
assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con);
assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
// should be ignored
tCon.close();
dsProxy.getConnection().close();
}
catch (SQLException ex) {
throw new UncategorizedSQLException("", "", ex);
@@ -1111,9 +1110,9 @@ public class DataSourceTransactionManagerTests {
Connection tCon = dsProxy.getConnection();
assertThatExceptionOfType(SQLException.class).isThrownBy(tCon::getWarnings);
tCon.clearWarnings();
assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con);
assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
// should be ignored
tCon.close();
dsProxy.getConnection().close();
}
catch (SQLException ex) {
throw new UncategorizedSQLException("", "", ex);
@@ -1129,42 +1128,6 @@ public class DataSourceTransactionManagerTests {
verify(con).close();
}
@Test
void testTransactionAwareDataSourceProxyWithEarlyConnection() throws Exception {
given(ds.getConnection()).willReturn(mock(Connection.class), con);
given(con.getAutoCommit()).willReturn(true);
given(con.getWarnings()).willThrow(new SQLException());
TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
dsProxy.setLazyTransactionalConnections(false);
Connection tCon = dsProxy.getConnection();
TransactionTemplate tt = new TransactionTemplate(tm);
assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// something transactional
assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con);
try {
// should close the early Connection obtained before the transaction
tCon.close();
}
catch (SQLException ex) {
throw new UncategorizedSQLException("", "", ex);
}
}
});
assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
InOrder ordered = inOrder(con);
ordered.verify(con).setAutoCommit(false);
ordered.verify(con).commit();
ordered.verify(con).setAutoCommit(true);
verify(con).close();
}
@Test
void testTransactionAwareDataSourceProxyWithSuspension() throws Exception {
given(con.getAutoCommit()).willReturn(true);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -209,7 +209,6 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
this.observationRegistry = observationRegistry;
}
@Override
public C createListenerContainer(JmsListenerEndpoint endpoint) {
C instance = createContainerInstance();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -102,7 +102,7 @@ import org.springframework.util.ErrorHandler;
* (i.e. after your business logic executed but before the JMS part got committed),
* so duplicate message detection is just there to cover a corner case.
* <li>Or wrap your <i>entire processing with an XA transaction</i>, covering the
* receipt of the JMS message as well as the execution of the business logic in
* reception of the JMS message as well as the execution of the business logic in
* your message listener (including database operations etc). This is only
* supported by {@link DefaultMessageListenerContainer}, through specifying
* an external "transactionManager" (typically a
@@ -152,8 +152,7 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
implements MessageListenerContainer {
private static final boolean micrometerJakartaPresent = ClassUtils.isPresent(
"io.micrometer.jakarta9.instrument.jms.JmsInstrumentation",
AbstractMessageListenerContainer.class.getClassLoader());
"io.micrometer.jakarta9.instrument.jms.JmsInstrumentation", AbstractMessageListenerContainer.class.getClassLoader());
@Nullable
private volatile Object destination;
@@ -171,14 +170,14 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
@Nullable
private String subscriptionName;
private boolean pubSubNoLocal = false;
@Nullable
private Boolean replyPubSubDomain;
@Nullable
private QosSettings replyQosSettings;
private boolean pubSubNoLocal = false;
@Nullable
private MessageConverter messageConverter;
@@ -501,7 +500,12 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
*/
@Override
public boolean isReplyPubSubDomain() {
return (this.replyPubSubDomain != null ? this.replyPubSubDomain : isPubSubDomain());
if (this.replyPubSubDomain != null) {
return this.replyPubSubDomain;
}
else {
return isPubSubDomain();
}
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -48,8 +48,8 @@ public class DestinationVariableArgumentResolver implements RSocketServiceArgume
collection.forEach(requestValues::addRouteVariable);
return true;
}
else if (argument instanceof Object[] arguments) {
for (Object variable : arguments) {
else if (argument.getClass().isArray()) {
for (Object variable : (Object[]) argument) {
requestValues.addRouteVariable(variable);
}
return true;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -19,7 +19,6 @@ package org.springframework.orm.jpa;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.OptimisticLockException;
import jakarta.persistence.PersistenceException;
import org.junit.jupiter.api.Test;
import org.springframework.transaction.TransactionDefinition;
@@ -34,37 +33,33 @@ import static org.mockito.Mockito.mock;
/**
* @author Costin Leau
* @author Phillip Webb
* @author Juergen Hoeller
*/
class DefaultJpaDialectTests {
private final JpaDialect dialect = new DefaultJpaDialect();
private JpaDialect dialect = new DefaultJpaDialect();
@Test
void testDefaultTransactionDefinition() {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
assertThatExceptionOfType(TransactionException.class).isThrownBy(() ->
dialect.beginTransaction(null, definition));
}
@Test
void testDefaultBeginTransaction() throws Exception {
TransactionDefinition definition = new DefaultTransactionDefinition();
EntityManager entityManager = mock();
EntityTransaction entityTx = mock();
given(entityManager.getTransaction()).willReturn(entityTx);
dialect.beginTransaction(entityManager, definition);
}
@Test
void testCustomIsolationLevel() {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
assertThatExceptionOfType(TransactionException.class).isThrownBy(() ->
dialect.beginTransaction(null, definition));
}
@Test
void testTranslateException() {
PersistenceException ex = new OptimisticLockException();
assertThat(dialect.translateExceptionIfPossible(ex))
.isInstanceOf(JpaOptimisticLockingFailureException.class).hasCause(ex);
OptimisticLockException ex = new OptimisticLockException();
assertThat(dialect.translateExceptionIfPossible(ex).getCause()).isEqualTo(EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex).getCause());
}
}
@@ -23,7 +23,6 @@ import io.r2dbc.spi.Connection;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.IsolationLevel;
import io.r2dbc.spi.R2dbcBadGrammarException;
import io.r2dbc.spi.R2dbcTransientResourceException;
import io.r2dbc.spi.Statement;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -33,7 +32,6 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.r2dbc.BadSqlGrammarException;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.IllegalTransactionStateException;
@@ -317,31 +315,6 @@ class R2dbcTransactionManagerTests {
verify(connectionMock).close();
}
@Test
void testCommitAndRollbackFails() {
when(connectionMock.isAutoCommit()).thenReturn(false);
when(connectionMock.commitTransaction()).thenReturn(Mono.defer(() ->
Mono.error(new R2dbcBadGrammarException("Commit should fail"))));
when(connectionMock.rollbackTransaction()).thenReturn(Mono.defer(() ->
Mono.error(new R2dbcTransientResourceException("Rollback should also fail"))));
TransactionalOperator operator = TransactionalOperator.create(tm);
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.doOnNext(connection -> connection.createStatement("foo")).then()
.as(operator::transactional)
.as(StepVerifier::create)
.verifyError(TransientDataAccessResourceException.class);
verify(connectionMock).isAutoCommit();
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
verify(connectionMock).createStatement("foo");
verify(connectionMock).commitTransaction();
verify(connectionMock).rollbackTransaction();
verify(connectionMock).close();
verifyNoMoreInteractions(connectionMock);
}
@Test
void testTransactionSetRollbackOnly() {
when(connectionMock.isAutoCommit()).thenReturn(false);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -26,7 +26,7 @@ import org.springframework.core.env.PropertySource;
* a user-provided {@link Properties} object, or if omitted during construction,
* the implementation will initialize its own.
*
* <p>The {@link #setProperty} and {@link #withProperty} methods are exposed for
* The {@link #setProperty} and {@link #withProperty} methods are exposed for
* convenience, for example:
* <pre class="code">
* {@code
@@ -36,7 +36,7 @@ import org.springframework.core.env.PropertySource;
*
* @author Chris Beams
* @since 3.1
* @see MockEnvironment
* @see org.springframework.mock.env.MockEnvironment
*/
public class MockPropertySource extends PropertiesPropertySource {
@@ -95,7 +95,7 @@ public class MockPropertySource extends PropertiesPropertySource {
/**
* Convenient synonym for {@link #setProperty} that returns the current instance.
* <p>Useful for method chaining and fluent-style use.
* Useful for method chaining and fluent-style use.
* @return this {@link MockPropertySource} instance
*/
public MockPropertySource withProperty(String name, Object value) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -671,12 +671,7 @@ public class MockHttpServletResponse implements HttpServletResponse {
@Override
public void setHeader(String name, @Nullable String value) {
if (value == null) {
this.headers.remove(name);
}
else {
setHeaderValue(name, value);
}
setHeaderValue(name, value);
}
@Override
@@ -728,17 +723,14 @@ public class MockHttpServletResponse implements HttpServletResponse {
}
else if (HttpHeaders.CONTENT_LANGUAGE.equalsIgnoreCase(name)) {
String contentLanguages = value.toString();
// only set the locale if we replace the header or if there was none before
if (replaceHeader || !this.headers.containsKey(HttpHeaders.CONTENT_LANGUAGE)) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_LANGUAGE, contentLanguages);
Locale language = headers.getContentLanguage();
this.locale = language != null ? language : Locale.getDefault();
doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, replaceHeader);
}
else {
doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, false);
}
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_LANGUAGE, contentLanguages);
Locale language = headers.getContentLanguage();
setLocale(language != null ? language : Locale.getDefault());
// Since setLocale() sets the Content-Language header to the given
// single Locale, we have to explicitly set the Content-Language header
// to the user-provided value.
doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, true);
return true;
}
else if (HttpHeaders.SET_COOKIE.equalsIgnoreCase(name)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -87,18 +87,6 @@ class MockHttpServletResponseTests {
assertThat(response.containsHeader(headerName)).isFalse();
}
@ParameterizedTest
@ValueSource(strings = {
CONTENT_TYPE,
CONTENT_LANGUAGE,
"X-Test-Header"
})
void removeHeaderIfNullValue(String headerName) {
response.addHeader(headerName, "test");
response.setHeader(headerName, null);
assertThat(response.containsHeader(headerName)).isFalse();
}
@Test // gh-26493
void setLocaleWithNullValue() {
assertThat(response.getLocale()).isEqualTo(Locale.getDefault());
@@ -631,12 +619,4 @@ class MockHttpServletResponseTests {
assertThat(contentTypeHeader).isEqualTo("text/plain");
}
@Test // gh-34488
void shouldAddMultipleContentLanguage() {
response.addHeader("Content-Language", "en");
response.addHeader("Content-Language", "fr");
assertThat(response.getHeaders("Content-Language")).contains("en", "fr");
assertThat(response.getLocale()).isEqualTo(Locale.ENGLISH);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -494,17 +494,21 @@ public abstract class AbstractReactiveTransactionManager
}));
}
else if (ErrorPredicates.RUNTIME_OR_ERROR.test(ex)) {
Mono<Void> mono = Mono.empty();
Mono<Void> mono;
if (!beforeCompletionInvoked.get()) {
mono = triggerBeforeCompletion(synchronizationManager, status);
}
else {
mono = Mono.empty();
}
result = mono.then(doRollbackOnCommitException(synchronizationManager, status, ex))
.then(propagateException);
}
return result;
})
.then(Mono.defer(() -> triggerAfterCommit(synchronizationManager, status).onErrorResume(ex ->
triggerAfterCompletion(synchronizationManager, status, TransactionSynchronization.STATUS_COMMITTED).then(Mono.error(ex)))
triggerAfterCompletion(synchronizationManager, status, TransactionSynchronization.STATUS_COMMITTED).then(Mono.error(ex)))
.then(triggerAfterCompletion(synchronizationManager, status, TransactionSynchronization.STATUS_COMMITTED))
.then(Mono.defer(() -> {
if (status.isNewTransaction()) {
@@ -514,8 +518,8 @@ public abstract class AbstractReactiveTransactionManager
}))));
return commit
.onErrorResume(ex -> cleanupAfterCompletion(synchronizationManager, status).then(Mono.error(ex)))
.then(cleanupAfterCompletion(synchronizationManager, status));
.onErrorResume(ex -> cleanupAfterCompletion(synchronizationManager, status)
.then(Mono.error(ex))).then(cleanupAfterCompletion(synchronizationManager, status));
}
/**
@@ -567,8 +571,8 @@ public abstract class AbstractReactiveTransactionManager
}
return beforeCompletion;
}
})).onErrorResume(ErrorPredicates.RUNTIME_OR_ERROR, ex ->
triggerAfterCompletion(synchronizationManager, status, TransactionSynchronization.STATUS_UNKNOWN)
})).onErrorResume(ErrorPredicates.RUNTIME_OR_ERROR, ex -> triggerAfterCompletion(
synchronizationManager, status, TransactionSynchronization.STATUS_UNKNOWN)
.then(Mono.defer(() -> {
if (status.isNewTransaction()) {
this.transactionExecutionListeners.forEach(listener -> listener.afterRollback(status, ex));
@@ -619,7 +623,7 @@ public abstract class AbstractReactiveTransactionManager
return Mono.empty();
}))
.then(Mono.error(rbex));
}).then(Mono.defer(() -> triggerAfterCompletion(synchronizationManager, status, TransactionSynchronization.STATUS_ROLLED_BACK)))
}).then(triggerAfterCompletion(synchronizationManager, status, TransactionSynchronization.STATUS_ROLLED_BACK))
.then(Mono.defer(() -> {
this.transactionExecutionListeners.forEach(listener -> listener.afterRollback(status, null));
return Mono.empty();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -67,7 +67,6 @@ public final class ContentDisposition {
for (int i=33; i<= 126; i++) {
PRINTABLE.set(i);
}
PRINTABLE.set(34, false); // "
PRINTABLE.set(61, false); // =
PRINTABLE.set(63, false); // ?
PRINTABLE.set(95, false); // _
@@ -216,8 +216,9 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest
context = HttpClientContext.create();
}
// No custom request configuration was set
if (!hasCustomRequestConfig(context)) {
// Request configuration not set in the context
if (!(context instanceof HttpClientContext clientContext && clientContext.getRequestConfig() != null) &&
context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
RequestConfig config = null;
// Use request configuration given by the user, when available
if (httpRequest instanceof Configurable configurable) {
@@ -236,18 +237,6 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest
return new HttpComponentsClientHttpRequest(client, httpRequest, context);
}
@SuppressWarnings("deprecation") // HttpClientContext.REQUEST_CONFIG
private static boolean hasCustomRequestConfig(HttpContext context) {
if (context instanceof HttpClientContext clientContext) {
// Prior to 5.4, the default config was set to RequestConfig.DEFAULT
// As of 5.4, it is set to null
RequestConfig requestConfig = clientContext.getRequestConfig();
return requestConfig != null && !requestConfig.equals(RequestConfig.DEFAULT);
}
// Prior to 5.4, the config was stored as an attribute
return context.getAttribute(HttpClientContext.REQUEST_CONFIG) != null;
}
/**
* Create a default {@link RequestConfig} to use with the given client.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -139,7 +139,6 @@ final class DefaultRestClient implements RestClient {
this.builder = builder;
}
@Override
public RequestHeadersUriSpec<?> get() {
return methodInternal(HttpMethod.GET);
@@ -273,6 +272,8 @@ final class DefaultRestClient implements RestClient {
}
private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
private final HttpMethod httpMethod;
@@ -451,6 +452,7 @@ final class DefaultRestClient implements RestClient {
}
}
@Override
public ResponseSpec retrieve() {
ResponseSpec responseSpec = exchangeInternal(DefaultResponseSpec::new, false);
@@ -782,6 +784,8 @@ final class DefaultRestClient implements RestClient {
this.observationScope.close();
this.observation.stop();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -296,7 +296,14 @@ public class CorsConfiguration {
* allowCredentials} is set to {@code true}, that combination is handled
* by copying the method specified in the CORS preflight request.
* <p>If not set, only {@code "GET"} and {@code "HEAD"} are allowed.
* <p>By default, this is not set.
* <p>By default this is not set.
* <p><strong>Note:</strong> CORS checks use values from "Forwarded"
* (<a href="https://tools.ietf.org/html/rfc7239">RFC 7239</a>),
* "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers,
* if present, in order to reflect the client-originated address.
* Consider using the {@code ForwardedHeaderFilter} in order to choose from a
* central place whether to extract and use, or to discard such headers.
* See the Spring Framework reference for more on this filter.
*/
public void setAllowedMethods(@Nullable List<String> allowedMethods) {
this.allowedMethods = (allowedMethods != null ? new ArrayList<>(allowedMethods) : null);
@@ -448,7 +455,7 @@ public class CorsConfiguration {
* level of trust with the configured domains and also increases the surface
* attack of the web application by exposing sensitive user-specific
* information such as cookies and CSRF tokens.
* <p>By default, this is not set (i.e. user credentials are not supported).
* <p>By default this is not set (i.e. user credentials are not supported).
*/
public void setAllowCredentials(@Nullable Boolean allowCredentials) {
this.allowCredentials = allowCredentials;
@@ -472,7 +479,7 @@ public class CorsConfiguration {
* <p>Setting this property has an impact on how {@link #setAllowedOrigins(List)
* origins} and {@link #setAllowedOriginPatterns(List) originPatterns} are processed,
* see related API documentation for more details.
* <p>By default, this is not set (i.e. private network access is not supported).
* <p>By default this is not set (i.e. private network access is not supported).
* @since 5.3.32
* @see <a href="https://wicg.github.io/private-network-access/">Private network access specifications</a>
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -107,12 +107,12 @@ public interface ServerWebExchange {
}
/**
* Return the web session for the current request.
* <p>Always guaranteed to return either an instance matching the session id
* requested by the client, or a new session either because the client did not
* specify a session id or because the underlying session expired.
* <p>Use of this method does not automatically create a session. See
* {@link WebSession} for more details.
* Return the web session for the current request. Always guaranteed to
* return an instance either matching to the session id requested by the
* client, or with a new session id either because the client did not
* specify one or because the underlying session had expired. Use of this
* method does not automatically create a session. See {@link WebSession}
* for more details.
*/
Mono<WebSession> getSession();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -79,10 +79,10 @@ public class InMemoryWebSessionStore implements WebSessionStore {
}
/**
* Configure the {@link Clock} to use to set the {@code lastAccessTime} on
* every created session and to calculate if the session has expired.
* <p>This may be useful to align to different time zones or to set the clock
* back in a test, for example, {@code Clock.offset(clock, Duration.ofMinutes(-31))}
* Configure the {@link Clock} to use to set lastAccessTime on every created
* session and to calculate if it is expired.
* <p>This may be useful to align to different timezone or to set the clock
* back in a test, e.g. {@code Clock.offset(clock, Duration.ofMinutes(-31))}
* in order to simulate session expiration.
* <p>By default this is {@code Clock.system(ZoneId.of("GMT"))}.
* @param clock the clock to use
@@ -94,17 +94,16 @@ public class InMemoryWebSessionStore implements WebSessionStore {
}
/**
* Return the configured clock for session {@code lastAccessTime} calculations.
* Return the configured clock for session lastAccessTime calculations.
*/
public Clock getClock() {
return this.clock;
}
/**
* Return an {@linkplain Collections#unmodifiableMap unmodifiable} copy of the
* map of sessions.
* <p>This could be used for management purposes, to list active sessions,
* to invalidate expired sessions, etc.
* Return the map of sessions with an {@link Collections#unmodifiableMap
* unmodifiable} wrapper. This could be used for management purposes, to
* list active sessions, invalidate expired ones, etc.
* @since 5.0.8
*/
public Map<String, WebSession> getSessions() {
@@ -158,11 +157,10 @@ public class InMemoryWebSessionStore implements WebSessionStore {
}
/**
* Check for expired sessions and remove them.
* <p>Typically such checks are kicked off lazily during calls to
* {@link #createWebSession()} or {@link #retrieveSession}, no less than 60
* seconds apart.
* <p>This method can be called to force a check at a specific time.
* Check for expired sessions and remove them. Typically such checks are
* kicked off lazily during calls to {@link #createWebSession() create} or
* {@link #retrieveSession retrieve}, no less than 60 seconds apart.
* This method can be called to force a check at a specific time.
* @since 5.0.8
*/
public void removeExpiredSessions() {
@@ -280,7 +278,7 @@ public class InMemoryWebSessionStore implements WebSessionStore {
private void checkMaxSessionsLimit() {
if (sessions.size() >= maxSessions) {
expiredSessionChecker.removeExpiredSessions(clock.instant());
if (sessions.size() >= maxSessions && !sessions.containsKey(this.id.get())) {
if (sessions.size() >= maxSessions) {
throw new IllegalStateException("Max sessions limit reached: " + sessions.size());
}
}
@@ -32,10 +32,10 @@ import org.springframework.web.server.WebSession;
public interface WebSessionManager {
/**
* Return the {@link WebSession} for the given exchange.
* <p>Always guaranteed to return either an instance matching the session id
* requested by the client, or a new session either because the client did not
* specify a session id or because the underlying session expired.
* Return the {@link WebSession} for the given exchange. Always guaranteed
* to return an instance either matching to the session id requested by the
* client, or a new session either because the client did not specify one
* or because the underlying session expired.
* @param exchange the current exchange
* @return promise for the WebSession
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 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.
@@ -43,7 +43,7 @@ public interface WebSessionStore {
* Return the WebSession for the given id.
* <p><strong>Note:</strong> This method should perform an expiration check,
* and if it has expired remove the session and return empty. This method
* should also update the {@code lastAccessTime} of retrieved sessions.
* should also update the lastAccessTime of retrieved sessions.
* @param sessionId the session to load
* @return the session, or an empty {@code Mono}
*/
@@ -164,13 +164,7 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
@Override
public void setHeader(String name, String value) {
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
if (value != null) {
this.contentLength = toContentLengthInt(Long.parseLong(value));
}
else {
this.contentLength = null;
super.setHeader(name, null);
}
this.contentLength = toContentLengthInt(Long.parseLong(value));
}
else {
super.setHeader(name, value);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -305,13 +305,6 @@ class ContentDispositionTests {
tester.accept("foo.txt\\\\\\", "foo.txt\\\\\\\\\\\\");
}
@Test
void formatWithUtf8FilenameWithQuotes() {
String filename = "\"中文.txt";
assertThat(ContentDisposition.formData().filename(filename, StandardCharsets.UTF_8).build().toString())
.isEqualTo("form-data; filename=\"=?UTF-8?Q?=22=E4=B8=AD=E6=96=87.txt?=\"; filename*=UTF-8''%22%E4%B8%AD%E6%96%87.txt");
}
@Test
void formatWithEncodedFilenameUsingInvalidCharset() {
assertThatIllegalArgumentException().isThrownBy(() ->
@@ -270,17 +270,6 @@ class ContentCachingResponseWrapperTests {
.withMessageContaining(overflow);
}
@Test
void setContentLengthNull() {
MockHttpServletResponse response = new MockHttpServletResponse();
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
responseWrapper.setContentLength(1024);
responseWrapper.setHeader(CONTENT_LENGTH, null);
assertThat(response.getHeaderNames()).doesNotContain(CONTENT_LENGTH);
assertThat(responseWrapper.getHeader(CONTENT_LENGTH)).isNull();
}
private void assertHeader(HttpServletResponse response, String header, int value) {
assertHeader(response, header, Integer.toString(value));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -19,11 +19,11 @@ package org.springframework.web.server.session;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
import reactor.core.scheduler.Schedulers;
import reactor.test.StepVerifier;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.web.server.WebSession;
@@ -35,11 +35,10 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* Tests for {@link InMemoryWebSessionStore}.
*
* @author Rob Winch
* @author Sam Brannen
*/
class InMemoryWebSessionStoreTests {
private final InMemoryWebSessionStore store = new InMemoryWebSessionStore();
private InMemoryWebSessionStore store = new InMemoryWebSessionStore();
@Test
@@ -54,14 +53,13 @@ class InMemoryWebSessionStoreTests {
void startsSessionImplicitly() {
WebSession session = this.store.createWebSession().block();
assertThat(session).isNotNull();
// We intentionally do not invoke start().
// session.start();
session.start();
session.getAttributes().put("foo", "bar");
assertThat(session.isStarted()).isTrue();
}
@Test // gh-24027, gh-26958
void createSessionDoesNotBlock() {
public void createSessionDoesNotBlock() {
this.store.createWebSession()
.doOnNext(session -> assertThat(Schedulers.isInNonBlockingThread()).isTrue())
.block();
@@ -105,7 +103,7 @@ class InMemoryWebSessionStoreTests {
}
@Test // SPR-17051
void sessionInvalidatedBeforeSave() {
public void sessionInvalidatedBeforeSave() {
// Request 1 creates session
WebSession session1 = this.store.createWebSession().block();
assertThat(session1).isNotNull();
@@ -134,69 +132,33 @@ class InMemoryWebSessionStoreTests {
@Test
void expirationCheckPeriod() {
// Create 100 sessions
IntStream.rangeClosed(1, 100).forEach(i -> insertSession());
assertNumSessions(100);
// Force a new clock (31 min later). Don't use setter which would clean expired sessions.
DirectFieldAccessor accessor = new DirectFieldAccessor(this.store);
accessor.setPropertyValue("clock", Clock.offset(this.store.getClock(), Duration.ofMinutes(31)));
assertNumSessions(100);
Map<?,?> sessions = (Map<?, ?>) accessor.getPropertyValue("sessions");
assertThat(sessions).isNotNull();
// Create 1 more which forces a time-based check (clock moved forward).
// Create 100 sessions
IntStream.range(0, 100).forEach(i -> insertSession());
assertThat(sessions).hasSize(100);
// Force a new clock (31 min later), don't use setter which would clean expired sessions
accessor.setPropertyValue("clock", Clock.offset(this.store.getClock(), Duration.ofMinutes(31)));
assertThat(sessions).hasSize(100);
// Create 1 more which forces a time-based check (clock moved forward)
insertSession();
assertNumSessions(1);
assertThat(sessions).hasSize(1);
}
@Test
void maxSessions() {
this.store.setMaxSessions(10);
IntStream.rangeClosed(1, 10).forEach(i -> insertSession());
assertThatIllegalStateException()
.isThrownBy(this::insertSession)
.withMessage("Max sessions limit reached: 10");
IntStream.range(0, 10000).forEach(i -> insertSession());
assertThatIllegalStateException().isThrownBy(
this::insertSession)
.withMessage("Max sessions limit reached: 10000");
}
@Test
void updateSession() {
WebSession session = insertSession();
StepVerifier.create(session.save())
.expectComplete()
.verify();
}
@Test // gh-35013
void updateSessionAfterMaxSessionLimitIsExceeded() {
this.store.setMaxSessions(10);
WebSession session = insertSession();
assertNumSessions(1);
IntStream.rangeClosed(1, 9).forEach(i -> insertSession());
assertNumSessions(10);
// Updating an existing session should succeed.
StepVerifier.create(session.save())
.expectComplete()
.verify();
assertNumSessions(10);
// Saving an additional new session should fail.
assertThatIllegalStateException()
.isThrownBy(this::insertSession)
.withMessage("Max sessions limit reached: 10");
assertNumSessions(10);
// Updating an existing session again should still succeed.
StepVerifier.create(session.save())
.expectComplete()
.verify();
assertNumSessions(10);
}
private WebSession insertSession() {
WebSession session = this.store.createWebSession().block();
assertThat(session).isNotNull();
@@ -205,8 +167,4 @@ class InMemoryWebSessionStoreTests {
return session;
}
private void assertNumSessions(int numSessions) {
assertThat(store.getSessions()).hasSize(numSessions);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -671,12 +671,7 @@ public class MockHttpServletResponse implements HttpServletResponse {
@Override
public void setHeader(String name, @Nullable String value) {
if (value == null) {
this.headers.remove(name);
}
else {
setHeaderValue(name, value);
}
setHeaderValue(name, value);
}
@Override
@@ -728,17 +723,14 @@ public class MockHttpServletResponse implements HttpServletResponse {
}
else if (HttpHeaders.CONTENT_LANGUAGE.equalsIgnoreCase(name)) {
String contentLanguages = value.toString();
// only set the locale if we replace the header or if there was none before
if (replaceHeader || !this.headers.containsKey(HttpHeaders.CONTENT_LANGUAGE)) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_LANGUAGE, contentLanguages);
Locale language = headers.getContentLanguage();
this.locale = language != null ? language : Locale.getDefault();
doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, replaceHeader);
}
else {
doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, false);
}
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_LANGUAGE, contentLanguages);
Locale language = headers.getContentLanguage();
setLocale(language != null ? language : Locale.getDefault());
// Since setLocale() sets the Content-Language header to the given
// single Locale, we have to explicitly set the Content-Language header
// to the user-provided value.
doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, true);
return true;
}
else if (HttpHeaders.SET_COOKIE.equalsIgnoreCase(name)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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,7 +29,6 @@ import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aot.AotDetector;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.cglib.core.SpringNamingPolicy;
@@ -85,12 +84,13 @@ import org.springframework.web.util.pattern.PathPatternParser;
* {@link #relativeTo(org.springframework.web.util.UriComponentsBuilder)}.
* </ul>
*
* <p><strong>Note:</strong> As of 5.1, methods in this class do not extract
* {@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the
* client-originated address. Please, use
* {@link org.springframework.web.filter.ForwardedHeaderFilter
* ForwardedHeaderFilter}, or similar from the underlying server, to extract
* and use such headers, or to discard them.
* <p><strong>Note:</strong> This class uses values from "Forwarded"
* (<a href="https://tools.ietf.org/html/rfc7239">RFC 7239</a>),
* "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers,
* if present, in order to reflect the client-originated protocol and address.
* Consider using the {@code ForwardedHeaderFilter} in order to choose from a
* central place whether to extract and use, or to discard such headers.
* See the Spring Framework reference for more on this filter.
*
* @author Oliver Gierke
* @author Rossen Stoyanchev
@@ -140,7 +140,7 @@ public class MvcUriComponentsBuilder {
/**
* Create an instance of this class with a base URL. After that calls to one
* of the instance based {@code withXxx(...)} methods will create URLs relative
* of the instance based {@code withXxx(...}} methods will create URLs relative
* to the given base URL.
*/
public static MvcUriComponentsBuilder relativeTo(UriComponentsBuilder baseUrl) {
@@ -152,6 +152,8 @@ public class MvcUriComponentsBuilder {
* Create a {@link UriComponentsBuilder} from the mapping of a controller class
* and current request information including Servlet mapping. If the controller
* contains multiple mappings, only the first one is used.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param controllerType the controller to build a URI for
* @return a UriComponentsBuilder instance (never {@code null})
*/
@@ -164,6 +166,8 @@ public class MvcUriComponentsBuilder {
* {@code UriComponentsBuilder} representing the base URL. This is useful
* when using MvcUriComponentsBuilder outside the context of processing a
* request or to apply a custom baseUrl not matching the current request.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param builder the builder for the base URL; the builder will be cloned
* and therefore not modified and may be re-used for further calls.
* @param controllerType the controller to build a URI for
@@ -189,6 +193,8 @@ public class MvcUriComponentsBuilder {
* Create a {@link UriComponentsBuilder} from the mapping of a controller
* method and an array of method argument values. This method delegates
* to {@link #fromMethod(Class, Method, Object...)}.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param controllerType the controller
* @param methodName the method name
* @param args the argument values
@@ -208,6 +214,8 @@ public class MvcUriComponentsBuilder {
* accepts a {@code UriComponentsBuilder} representing the base URL. This is
* useful when using MvcUriComponentsBuilder outside the context of processing
* a request or to apply a custom baseUrl not matching the current request.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param builder the builder for the base URL; the builder will be cloned
* and therefore not modified and may be re-used for further calls.
* @param controllerType the controller
@@ -232,6 +240,8 @@ public class MvcUriComponentsBuilder {
* {@link org.springframework.web.method.support.UriComponentsContributor
* UriComponentsContributor}) while remaining argument values are ignored and
* can be {@code null}.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param controllerType the controller type
* @param method the controller method
* @param args argument values for the controller method
@@ -248,6 +258,8 @@ public class MvcUriComponentsBuilder {
* This is useful when using MvcUriComponentsBuilder outside the context of
* processing a request or to apply a custom baseUrl not matching the
* current request.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param baseUrl the builder for the base URL; the builder will be cloned
* and therefore not modified and may be re-used for further calls.
* @param controllerType the controller type
@@ -294,6 +306,8 @@ public class MvcUriComponentsBuilder {
* controller.getAddressesForCountry("US")
* builder = MvcUriComponentsBuilder.fromMethodCall(controller);
* </pre>
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param info either the value returned from a "mock" controller
* invocation or the "mock" controller itself after an invocation
* @return a UriComponents instance
@@ -314,6 +328,8 @@ public class MvcUriComponentsBuilder {
* {@code UriComponentsBuilder} representing the base URL. This is useful
* when using MvcUriComponentsBuilder outside the context of processing a
* request or to apply a custom baseUrl not matching the current request.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param builder the builder for the base URL; the builder will be cloned
* and therefore not modified and may be re-used for further calls.
* @param info either the value returned from a "mock" controller
@@ -339,6 +355,8 @@ public class MvcUriComponentsBuilder {
* <pre class="code">
* MvcUriComponentsBuilder.fromMethodCall(on(FooController.class).getFoo(1)).build();
* </pre>
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param controllerType the target controller
*/
public static <T> T on(Class<T> controllerType) {
@@ -361,6 +379,8 @@ public class MvcUriComponentsBuilder {
* fooController.saveFoo(2, null);
* builder = MvcUriComponentsBuilder.fromMethodCall(fooController);
* </pre>
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param controllerType the target controller
*/
public static <T> T controller(Class<T> controllerType) {
@@ -403,6 +423,9 @@ public class MvcUriComponentsBuilder {
* </pre>
* <p>Note that it's not necessary to specify all arguments. Only the ones
* required to prepare the URL, mainly {@code @RequestParam} and {@code @PathVariable}).
*
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param mappingName the mapping name
* @return a builder to prepare the URI String
* @throws IllegalArgumentException if the mapping name is not found or
@@ -418,6 +441,8 @@ public class MvcUriComponentsBuilder {
* {@code UriComponentsBuilder} representing the base URL. This is useful
* when using MvcUriComponentsBuilder outside the context of processing a
* request or to apply a custom baseUrl not matching the current request.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @param builder the builder for the base URL; the builder will be cloned
* and therefore not modified and may be re-used for further calls.
* @param name the mapping name
@@ -457,6 +482,8 @@ public class MvcUriComponentsBuilder {
/**
* An alternative to {@link #fromController(Class)} for use with an instance
* of this class created via a call to {@link #relativeTo}.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @since 4.2
*/
public UriComponentsBuilder withController(Class<?> controllerType) {
@@ -464,8 +491,10 @@ public class MvcUriComponentsBuilder {
}
/**
* An alternative to {@link #fromMethodName(Class, String, Object...)} for
* An alternative to {@link #fromMethodName(Class, String, Object...)}} for
* use with an instance of this class created via {@link #relativeTo}.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @since 4.2
*/
public UriComponentsBuilder withMethodName(Class<?> controllerType, String methodName, Object... args) {
@@ -475,6 +504,8 @@ public class MvcUriComponentsBuilder {
/**
* An alternative to {@link #fromMethodCall(Object)} for use with an instance
* of this class created via {@link #relativeTo}.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @since 4.2
*/
public UriComponentsBuilder withMethodCall(Object invocationInfo) {
@@ -484,6 +515,8 @@ public class MvcUriComponentsBuilder {
/**
* An alternative to {@link #fromMappingName(String)} for use with an instance
* of this class created via {@link #relativeTo}.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @since 4.2
*/
public MethodArgumentBuilder withMappingName(String mappingName) {
@@ -493,6 +526,8 @@ public class MvcUriComponentsBuilder {
/**
* An alternative to {@link #fromMethod(Class, Method, Object...)}
* for use with an instance of this class created via {@link #relativeTo}.
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
* @since 4.2
*/
public UriComponentsBuilder withMethod(Class<?> controllerType, Method method, Object... args) {
@@ -632,8 +667,8 @@ public class MvcUriComponentsBuilder {
private static String resolveEmbeddedValue(String value) {
if (value.contains(SystemPropertyUtils.PLACEHOLDER_PREFIX)) {
WebApplicationContext webApplicationContext = getWebApplicationContext();
if (webApplicationContext != null &&
webApplicationContext.getAutowireCapableBeanFactory() instanceof ConfigurableBeanFactory cbf) {
if (webApplicationContext != null
&& webApplicationContext.getAutowireCapableBeanFactory() instanceof ConfigurableBeanFactory cbf) {
String resolvedEmbeddedValue = cbf.resolveEmbeddedValue(value);
if (resolvedEmbeddedValue != null) {
return resolvedEmbeddedValue;
@@ -794,7 +829,7 @@ public class MvcUriComponentsBuilder {
enhancer.setSuperclass(controllerType);
enhancer.setInterfaces(new Class<?>[] {MethodInvocationInfo.class});
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setAttemptLoad(AotDetector.useGeneratedArtifacts());
enhancer.setAttemptLoad(true);
enhancer.setCallbackType(MethodInterceptor.class);
Class<?> proxyClass = enhancer.createClass();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -217,10 +217,11 @@ public abstract class AbstractMultiCheckedElementTag extends AbstractCheckedElem
throw new IllegalArgumentException("Attribute 'items' is required and must be a Collection, an Array or a Map");
}
if (itemsObject instanceof Object[] itemsArray) {
for (int itemIndex = 0; itemIndex < itemsArray.length; itemIndex++) {
Object item = itemsArray[itemIndex];
writeObjectEntry(tagWriter, valueProperty, labelProperty, item, itemIndex);
if (itemsObject.getClass().isArray()) {
Object[] itemsArray = (Object[]) itemsObject;
for (int i = 0; i < itemsArray.length; i++) {
Object item = itemsArray[i];
writeObjectEntry(tagWriter, valueProperty, labelProperty, item, i);
}
}
else if (itemsObject instanceof Collection<?> optionCollection) {