mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f9c59b17b | |||
| edfcc6ffb1 | |||
| f93132b11e | |||
| 6ab4c84bd5 | |||
| d5fca0d2c5 | |||
| cbb94193fe | |||
| 5b5e2b6876 | |||
| a5b0399a1d | |||
| 71f2725638 | |||
| daee9f1242 | |||
| 89ae20b9ff | |||
| a5a7d63dab | |||
| d879112885 | |||
| 0a71df7aee | |||
| 82b7fcb2cc | |||
| 6717fca4ec | |||
| 86ea7003f7 | |||
| 0a157faec1 | |||
| 2548d44f3b | |||
| 6cc6ea1b2b | |||
| b73ca60811 | |||
| d689629036 | |||
| 077721bdb4 | |||
| b7654dd984 | |||
| 4645ce60c8 | |||
| 564d3f6f5e | |||
| 7a1dfe79a0 | |||
| 19257f4fdb | |||
| f06167ea01 | |||
| 618e003b91 | |||
| 8c494e5165 | |||
| 62d7396769 | |||
| 252058a65a |
@@ -11,7 +11,7 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Generate Changelog
|
||||
uses: spring-io/github-changelog-generator@185319ad7eaa75b0e8e72e4b6db19c8b2cb8c4c1 #v0.0.11
|
||||
uses: spring-io/github-changelog-generator@86958813a62af8fb223b3fd3b5152035504bcb83 #v0.0.12
|
||||
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.21.2");
|
||||
checkstyle.setToolVersion("10.23.1");
|
||||
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/**");
|
||||
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**", "buildSrc/build/**");
|
||||
List<String> buildFolders = List.of("bin", "build", "out");
|
||||
project.allprojects(subproject -> {
|
||||
Path rootPath = project.getRootDir().toPath();
|
||||
|
||||
@@ -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.119.Final"))
|
||||
api(platform("io.netty:netty-bom:4.1.121.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2023.0.16"))
|
||||
api(platform("io.projectreactor:reactor-bom:2023.0.18"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.5"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.24"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.26"))
|
||||
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.17"))
|
||||
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.17"))
|
||||
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.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.2")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.3")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.4.4")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.4")
|
||||
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")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.1.18-SNAPSHOT
|
||||
version=6.1.20
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
+11
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,7 +373,8 @@ 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.");
|
||||
"' is ambiguous: there are " + this.numberOfRemainingUnboundArguments + " candidates. " +
|
||||
"Consider compiling with -parameters in order to make declared parameter names available.");
|
||||
}
|
||||
|
||||
// We're all set... find the unbound parameter, and bind it.
|
||||
@@ -485,8 +486,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<>();
|
||||
@@ -535,8 +536,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<>();
|
||||
@@ -741,7 +742,9 @@ 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-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +35,7 @@ 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;
|
||||
@@ -201,7 +202,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
enhancer.setSuperclass(proxySuperClass);
|
||||
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setAttemptLoad(enhancer.getUseCache() && AotDetector.useGeneratedArtifacts());
|
||||
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
|
||||
|
||||
Callback[] callbacks = getCallbacks(rootClass);
|
||||
|
||||
+31
-34
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,7 +203,6 @@ 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);
|
||||
@@ -301,7 +300,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;
|
||||
@@ -320,7 +319,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();
|
||||
@@ -329,7 +328,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();
|
||||
@@ -343,20 +342,19 @@ 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();
|
||||
@@ -370,8 +368,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);
|
||||
}
|
||||
|
||||
@@ -379,7 +377,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;
|
||||
@@ -393,9 +391,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);
|
||||
@@ -426,7 +424,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);
|
||||
@@ -439,12 +437,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
|
||||
@@ -452,7 +450,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("");
|
||||
@@ -466,7 +464,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();
|
||||
@@ -475,7 +473,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");
|
||||
}
|
||||
|
||||
@@ -487,7 +485,6 @@ 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
|
||||
}
|
||||
@@ -812,20 +809,20 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
invocations.add("before");
|
||||
}
|
||||
|
||||
@AfterReturning("echo()")
|
||||
void afterReturning() {
|
||||
invocations.add("after returning");
|
||||
}
|
||||
|
||||
@AfterThrowing("echo()")
|
||||
void afterThrowing() {
|
||||
invocations.add("after throwing");
|
||||
}
|
||||
|
||||
@After("echo()")
|
||||
void after() {
|
||||
invocations.add("after");
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "this(target) && execution(* echo(*))", returning = "returnValue")
|
||||
void afterReturning(JoinPoint joinPoint, Echo target, Object returnValue) {
|
||||
invocations.add("after returning");
|
||||
}
|
||||
|
||||
@AfterThrowing(pointcut = "this(target) && execution(* echo(*))", throwing = "exception")
|
||||
void afterThrowing(JoinPoint joinPoint, Echo target, Throwable exception) {
|
||||
invocations.add("after throwing");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -967,7 +964,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;
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -291,7 +291,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
String lastKey = tokens.keys[tokens.keys.length - 1];
|
||||
|
||||
if (propValue.getClass().isArray()) {
|
||||
Class<?> requiredType = propValue.getClass().componentType();
|
||||
Class<?> componentType = propValue.getClass().componentType();
|
||||
int arrayIndex = Integer.parseInt(lastKey);
|
||||
Object oldValue = null;
|
||||
try {
|
||||
@@ -299,10 +299,9 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
oldValue = Array.get(propValue, arrayIndex);
|
||||
}
|
||||
Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
|
||||
requiredType, ph.nested(tokens.keys.length));
|
||||
componentType, 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-2018 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +25,7 @@ import org.springframework.lang.Nullable;
|
||||
* analogous to an InvocationTargetException.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MethodInvocationException extends PropertyAccessException {
|
||||
@@ -41,7 +42,9 @@ 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);
|
||||
super(propertyChangeEvent,
|
||||
"Property '" + propertyChangeEvent.getPropertyName() + "' threw exception: " + cause,
|
||||
cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+13
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,8 +145,6 @@ 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)}.
|
||||
@@ -176,8 +174,6 @@ 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
|
||||
@@ -210,8 +206,6 @@ 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)}.
|
||||
@@ -239,8 +233,6 @@ 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
|
||||
@@ -265,21 +257,24 @@ 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.
|
||||
* 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.
|
||||
* <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
|
||||
@@ -295,7 +290,9 @@ 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.
|
||||
* 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.
|
||||
* <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
|
||||
@@ -304,11 +301,12 @@ 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)
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +156,8 @@ 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());
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +22,7 @@ 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;
|
||||
@@ -153,7 +154,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(beanDefinition.getBeanClass());
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setAttemptLoad(AotDetector.useGeneratedArtifacts());
|
||||
if (this.owner instanceof ConfigurableBeanFactory cbf) {
|
||||
ClassLoader cl = cbf.getBeanClassLoader();
|
||||
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
|
||||
@@ -265,7 +266,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||
|
||||
/**
|
||||
* CGLIB MethodInterceptor to override methods, replacing them with a call
|
||||
* to a generic MethodReplacer.
|
||||
* to a generic {@link MethodReplacer}.
|
||||
*/
|
||||
private static class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
|
||||
|
||||
@@ -277,10 +278,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);
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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);
|
||||
|
||||
+16
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +18,7 @@ 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;
|
||||
@@ -28,7 +29,6 @@ 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.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -83,14 +83,23 @@ class BeanDefinitionPropertyValueCodeGeneratorDelegatesTests {
|
||||
CodeBlock generatedCode = createValueCodeGenerator(generatedClass).generateCode(value);
|
||||
typeBuilder.set(type -> {
|
||||
type.addModifiers(Modifier.PUBLIC);
|
||||
type.addSuperinterface(
|
||||
ParameterizedTypeName.get(Supplier.class, Object.class));
|
||||
type.addMethod(MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC)
|
||||
type.addMethod(MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(Object.class).addStatement("return $L", generatedCode).build());
|
||||
});
|
||||
generationContext.writeGeneratedContent();
|
||||
TestCompiler.forSystem().with(generationContext).compile(compiled ->
|
||||
result.accept(compiled.getInstance(Supplier.class).get(), 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+67
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,12 +21,13 @@ 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;
|
||||
@@ -43,14 +44,17 @@ 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();
|
||||
@@ -60,12 +64,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();
|
||||
@@ -73,44 +77,46 @@ class QualifierAnnotationAutowireBeanFactoryTests {
|
||||
new DependencyDescriptor(Person.class.getDeclaredField("name"), true))).isFalse();
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test
|
||||
void testAutowireCandidateWithFieldDescriptor() throws Exception {
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
lbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
|
||||
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();
|
||||
@@ -118,56 +124,61 @@ 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 {
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
lbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
|
||||
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 {
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
lbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
|
||||
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 =
|
||||
@@ -175,37 +186,70 @@ class QualifierAnnotationAutowireBeanFactoryTests {
|
||||
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(qualifiedParam, false);
|
||||
DependencyDescriptor nonqualifiedDescriptor = new DependencyDescriptor(nonqualifiedParam, false);
|
||||
qualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
assertThat(qualifiedParam.getParameterName()).isEqualTo("tpb");
|
||||
nonqualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
|
||||
assertThat(qualifiedParam.getParameterName()).isEqualTo("tpb");
|
||||
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 {
|
||||
@@ -247,4 +291,8 @@ class QualifierAnnotationAutowireBeanFactoryTests {
|
||||
private @interface TestQualifier {
|
||||
}
|
||||
|
||||
|
||||
@Qualifier("spouse")
|
||||
private TestBean testBean;
|
||||
|
||||
}
|
||||
|
||||
@@ -58,3 +58,10 @@ 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"
|
||||
}
|
||||
}
|
||||
|
||||
+33
-10
@@ -26,6 +26,7 @@ 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;
|
||||
@@ -115,6 +116,12 @@ class ConfigurationClassEnhancer {
|
||||
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
|
||||
if (classLoaderMismatch && classLoader instanceof SmartClassLoader smartClassLoader) {
|
||||
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);
|
||||
Class<?> enhancedClass = createClass(enhancer, classLoaderMismatch);
|
||||
@@ -131,6 +138,26 @@ class ConfigurationClassEnhancer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given config class relies on package visibility,
|
||||
* either for the class itself 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 (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.
|
||||
*/
|
||||
@@ -138,26 +165,22 @@ 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(!isClassReloadable(configSuperClass, classLoader));
|
||||
enhancer.setAttemptLoad(enhancer.getUseCache() && AotDetector.useGeneratedArtifacts());
|
||||
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.
|
||||
@@ -548,7 +571,7 @@ class ConfigurationClassEnhancer {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(factoryBean.getClass());
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setAttemptLoad(AotDetector.useGeneratedArtifacts());
|
||||
enhancer.setCallbackType(MethodInterceptor.class);
|
||||
|
||||
// Ideally create enhanced FactoryBean proxy without constructor side effects,
|
||||
|
||||
+3
-2
@@ -172,8 +172,9 @@ class ConfigurationClassParser {
|
||||
}
|
||||
|
||||
// Downgrade to lite (no enhancement) in case of no instance-level @Bean methods.
|
||||
if (!configClass.hasNonStaticBeanMethods() && ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(
|
||||
bd.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE))) {
|
||||
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);
|
||||
}
|
||||
|
||||
+11
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,7 +61,8 @@ 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 on the classpath.
|
||||
* 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.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
@@ -379,7 +380,7 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
}
|
||||
|
||||
|
||||
// overridable hooks
|
||||
// Overridable hooks
|
||||
|
||||
/**
|
||||
* Retrieve all applicable Lifecycle beans: all singletons that have already been created,
|
||||
@@ -493,11 +494,13 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
}
|
||||
}
|
||||
try {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
|
||||
@@ -27,7 +27,6 @@ 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;
|
||||
@@ -543,15 +542,13 @@ 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. 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.
|
||||
* {@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.
|
||||
* <p>More sophisticated matching can be implemented by overriding the
|
||||
* {@link #isAllowed} method.
|
||||
* <p>Alternatively, specify a list of <i>allowed</i> field patterns.
|
||||
@@ -569,8 +566,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
else {
|
||||
String[] fieldPatterns = new String[disallowedFields.length];
|
||||
for (int i = 0; i < fieldPatterns.length; i++) {
|
||||
String field = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]);
|
||||
fieldPatterns[i] = field.toLowerCase(Locale.ROOT);
|
||||
fieldPatterns[i] = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]);
|
||||
}
|
||||
this.disallowedFields = fieldPatterns;
|
||||
}
|
||||
@@ -1140,9 +1136,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
|
||||
@@ -1158,8 +1154,13 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
protected boolean isAllowed(String field) {
|
||||
String[] allowed = getAllowedFields();
|
||||
String[] disallowed = getDisallowedFields();
|
||||
return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) &&
|
||||
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field.toLowerCase(Locale.ROOT))));
|
||||
if (!ObjectUtils.isEmpty(allowed) && !PatternMatchUtils.simpleMatch(allowed, field)) {
|
||||
return false;
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(disallowed)) {
|
||||
return !PatternMatchUtils.simpleMatchIgnoreCase(disallowed, field);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -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.getParent());
|
||||
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -111,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);
|
||||
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||
|
||||
classLoader = new OverridingClassLoader(getClass().getClassLoader());
|
||||
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
|
||||
|
||||
+23
-1
@@ -129,6 +129,22 @@ 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));
|
||||
@@ -181,7 +197,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
assertThat(bar.foo).isNotSameAs(foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // gh-34486
|
||||
void enhancementIsNotPresentWithEmptyConfig() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(EmptyConfig.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
@@ -1187,6 +1203,12 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(SingletonBeanConfig.class)
|
||||
abstract static class AbstractConfigWithoutBeanMethods {
|
||||
// This class intentionally does NOT declare @Bean methods.
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static final class EmptyConfig {
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,7 +38,8 @@ import org.springframework.context.support.GenericApplicationContext;
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
class SpringAtInjectTckTests {
|
||||
// WARNING: This class MUST be public, since it is based on JUnit 3.
|
||||
public class SpringAtInjectTckTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Test suite() {
|
||||
|
||||
+41
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,10 +54,11 @@ class DefaultLifecycleProcessorTests {
|
||||
|
||||
@Test
|
||||
void customLifecycleProcessorInstance() {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(DefaultLifecycleProcessor.class);
|
||||
beanDefinition.getPropertyValues().addPropertyValue("timeoutPerShutdownPhase", 1000);
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
context.registerBeanDefinition("lifecycleProcessor", beanDefinition);
|
||||
context.registerBeanDefinition(StaticApplicationContext.LIFECYCLE_PROCESSOR_BEAN_NAME, beanDefinition);
|
||||
|
||||
context.refresh();
|
||||
LifecycleProcessor bean = context.getBean("lifecycleProcessor", LifecycleProcessor.class);
|
||||
Object contextLifecycleProcessor = new DirectFieldAccessor(context).getPropertyValue("lifecycleProcessor");
|
||||
@@ -70,11 +71,12 @@ 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();
|
||||
@@ -114,12 +116,13 @@ 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);
|
||||
@@ -130,11 +133,12 @@ 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();
|
||||
@@ -148,15 +152,16 @@ 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();
|
||||
@@ -171,18 +176,19 @@ 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();
|
||||
@@ -202,16 +208,17 @@ 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();
|
||||
@@ -233,16 +240,17 @@ 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();
|
||||
@@ -270,16 +278,17 @@ 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();
|
||||
@@ -319,6 +328,7 @@ 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);
|
||||
@@ -327,7 +337,6 @@ 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);
|
||||
@@ -335,6 +344,7 @@ 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),
|
||||
@@ -345,11 +355,12 @@ 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();
|
||||
@@ -359,10 +370,11 @@ 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();
|
||||
@@ -375,6 +387,7 @@ 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);
|
||||
@@ -383,7 +396,6 @@ 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);
|
||||
@@ -391,6 +403,7 @@ 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();
|
||||
@@ -418,17 +431,18 @@ 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();
|
||||
@@ -446,6 +460,7 @@ 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);
|
||||
@@ -453,7 +468,6 @@ 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);
|
||||
@@ -461,6 +475,7 @@ 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();
|
||||
@@ -486,17 +501,18 @@ 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();
|
||||
@@ -514,6 +530,7 @@ 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);
|
||||
@@ -521,7 +538,6 @@ 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);
|
||||
@@ -529,6 +545,7 @@ 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();
|
||||
@@ -551,15 +568,16 @@ 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();
|
||||
@@ -572,19 +590,20 @@ 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();
|
||||
@@ -611,6 +630,7 @@ class DefaultLifecycleProcessorTests {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static class TestLifecycleBean implements Lifecycle {
|
||||
|
||||
private final CopyOnWriteArrayList<Lifecycle> startedBeans;
|
||||
|
||||
@@ -463,10 +463,21 @@ public class ReflectUtils {
|
||||
c = lookup.defineClass(b);
|
||||
}
|
||||
catch (LinkageError | IllegalArgumentException ex) {
|
||||
// in case of plain LinkageError (class already defined)
|
||||
// or IllegalArgumentException (class in different package):
|
||||
// fall through to traditional ClassLoader.defineClass below
|
||||
t = 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;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new CodeGenerationException(ex);
|
||||
@@ -527,15 +538,26 @@ public class ReflectUtils {
|
||||
c = lookup.defineClass(b);
|
||||
}
|
||||
catch (LinkageError | IllegalAccessException ex) {
|
||||
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.";
|
||||
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) {
|
||||
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-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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.put(key, Boolean.TRUE.toString());
|
||||
localProperties.setProperty(key, Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,7 +33,10 @@ 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 (as far as possible).
|
||||
* 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>
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
|
||||
+26
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +206,8 @@ 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);
|
||||
|
||||
/**
|
||||
@@ -248,6 +250,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
private boolean useCaches = true;
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@code PathMatchingResourcePatternResolver} with a
|
||||
@@ -315,6 +319,21 @@ 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) {
|
||||
@@ -338,7 +357,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
// all class path resources with the given name
|
||||
Collections.addAll(resources, findAllClassPathResources(locationPatternWithoutPrefix));
|
||||
}
|
||||
return resources.toArray(new Resource[0]);
|
||||
return resources.toArray(EMPTY_RESOURCE_ARRAY);
|
||||
}
|
||||
else {
|
||||
// Generally only look for a pattern after a prefix here,
|
||||
@@ -371,7 +390,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resolved class path location [" + path + "] to resources " + result);
|
||||
}
|
||||
return result.toArray(new Resource[0]);
|
||||
return result.toArray(EMPTY_RESOURCE_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -607,7 +626,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
|
||||
}
|
||||
return result.toArray(new Resource[0]);
|
||||
return result.toArray(EMPTY_RESOURCE_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -695,6 +714,9 @@ 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-2021 the original author or authors.
|
||||
* Copyright 2002-2025 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(SpringObjenesis.IGNORE_OBJENESIS_PROPERTY_NAME)) {
|
||||
if (SpringProperties.getFlag(IGNORE_OBJENESIS_PROPERTY_NAME)) {
|
||||
this.worthTrying = Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,13 +37,25 @@ 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 pattern.equals(str);
|
||||
return (ignoreCase ? pattern.equalsIgnoreCase(str) : pattern.equals(str));
|
||||
}
|
||||
|
||||
if (firstIndex == 0) {
|
||||
@@ -52,25 +64,43 @@ public abstract class PatternMatchUtils {
|
||||
}
|
||||
int nextIndex = pattern.indexOf('*', 1);
|
||||
if (nextIndex == -1) {
|
||||
return str.endsWith(pattern.substring(1));
|
||||
String part = pattern.substring(1);
|
||||
return (ignoreCase ? StringUtils.endsWithIgnoreCase(str, part) : str.endsWith(part));
|
||||
}
|
||||
String part = pattern.substring(1, nextIndex);
|
||||
if (part.isEmpty()) {
|
||||
return simpleMatch(pattern.substring(nextIndex), str);
|
||||
return simpleMatch(pattern.substring(nextIndex), str, ignoreCase);
|
||||
}
|
||||
int partIndex = str.indexOf(part);
|
||||
int partIndex = indexOf(str, part, 0, ignoreCase);
|
||||
while (partIndex != -1) {
|
||||
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
|
||||
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()), ignoreCase)) {
|
||||
return true;
|
||||
}
|
||||
partIndex = str.indexOf(part, partIndex + 1);
|
||||
partIndex = indexOf(str, part, partIndex + 1, ignoreCase);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return (str.length() >= firstIndex &&
|
||||
pattern.startsWith(str.substring(0, firstIndex)) &&
|
||||
simpleMatch(pattern.substring(firstIndex), str.substring(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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,4 +124,19 @@ 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-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +85,7 @@ 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;
|
||||
@@ -204,6 +205,7 @@ public class ExponentialBackOff implements BackOff {
|
||||
return this.maxAttempts;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BackOffExecution start() {
|
||||
return new ExponentialBackOffExecution();
|
||||
@@ -225,6 +227,7 @@ public class ExponentialBackOff implements BackOff {
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
||||
private class ExponentialBackOffExecution implements BackOffExecution {
|
||||
|
||||
private long currentInterval = -1;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +35,7 @@ public class FixedBackOff implements BackOff {
|
||||
*/
|
||||
public static final long UNLIMITED_ATTEMPTS = Long.MAX_VALUE;
|
||||
|
||||
|
||||
private long interval = DEFAULT_INTERVAL;
|
||||
|
||||
private long maxAttempts = UNLIMITED_ATTEMPTS;
|
||||
@@ -86,6 +87,7 @@ public class FixedBackOff implements BackOff {
|
||||
return this.maxAttempts;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BackOffExecution start() {
|
||||
return new FixedBackOffExecution();
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,9 +108,11 @@ 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 {
|
||||
|
||||
@@ -332,7 +334,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 resouce.getFile().getPath(); however,
|
||||
// On the JVM, all tests should pass when using resource.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-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,18 +53,22 @@ 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
|
||||
@@ -74,6 +78,10 @@ class PatternMatchUtilsTests {
|
||||
assertMatches("*stuff*", "stuffTest");
|
||||
assertMatches("*stuff*", "getstuff");
|
||||
assertMatches("*stuff*", "stuff");
|
||||
testMixedCaseMatch("*stuff*", "getStuffTest");
|
||||
testMixedCaseMatch("*stuff*", "StuffTest");
|
||||
testMixedCaseMatch("*stuff*", "getStuff");
|
||||
testMixedCaseMatch("*stuff*", "Stuff");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,6 +90,8 @@ class PatternMatchUtilsTests {
|
||||
assertMatches("on*Event", "onEvent");
|
||||
assertDoesNotMatch("3*3", "3");
|
||||
assertMatches("3*3", "33");
|
||||
testMixedCaseMatch("on*Event", "OnMyEvenT");
|
||||
testMixedCaseMatch("on*Event", "OnEvenT");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -122,18 +132,27 @@ 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-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,12 +85,13 @@ 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,
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +209,7 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public C createListenerContainer(JmsListenerEndpoint endpoint) {
|
||||
C instance = createContainerInstance();
|
||||
|
||||
+7
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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
|
||||
* reception of the JMS message as well as the execution of the business logic in
|
||||
* receipt 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,7 +152,8 @@ 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;
|
||||
@@ -170,14 +171,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;
|
||||
|
||||
@@ -500,12 +501,7 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
|
||||
*/
|
||||
@Override
|
||||
public boolean isReplyPubSubDomain() {
|
||||
if (this.replyPubSubDomain != null) {
|
||||
return this.replyPubSubDomain;
|
||||
}
|
||||
else {
|
||||
return isPubSubDomain();
|
||||
}
|
||||
return (this.replyPubSubDomain != null ? this.replyPubSubDomain : isPubSubDomain());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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.getClass().isArray()) {
|
||||
for (Object variable : (Object[]) argument) {
|
||||
else if (argument instanceof Object[] arguments) {
|
||||
for (Object variable : arguments) {
|
||||
requestValues.addRouteVariable(variable);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +19,7 @@ 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;
|
||||
@@ -33,33 +34,37 @@ import static org.mockito.Mockito.mock;
|
||||
/**
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class DefaultJpaDialectTests {
|
||||
|
||||
private JpaDialect dialect = new DefaultJpaDialect();
|
||||
private final 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 testTranslateException() {
|
||||
OptimisticLockException ex = new OptimisticLockException();
|
||||
assertThat(dialect.translateExceptionIfPossible(ex).getCause()).isEqualTo(EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex).getCause());
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+27
@@ -23,6 +23,7 @@ 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;
|
||||
@@ -32,6 +33,7 @@ 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;
|
||||
@@ -315,6 +317,31 @@ 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);
|
||||
|
||||
+8
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,21 +494,17 @@ public abstract class AbstractReactiveTransactionManager
|
||||
}));
|
||||
}
|
||||
else if (ErrorPredicates.RUNTIME_OR_ERROR.test(ex)) {
|
||||
Mono<Void> mono;
|
||||
Mono<Void> mono = Mono.empty();
|
||||
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()) {
|
||||
@@ -518,8 +514,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));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,8 +567,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));
|
||||
@@ -623,7 +619,7 @@ public abstract class AbstractReactiveTransactionManager
|
||||
return Mono.empty();
|
||||
}))
|
||||
.then(Mono.error(rbex));
|
||||
}).then(triggerAfterCompletion(synchronizationManager, status, TransactionSynchronization.STATUS_ROLLED_BACK))
|
||||
}).then(Mono.defer(() -> triggerAfterCompletion(synchronizationManager, status, TransactionSynchronization.STATUS_ROLLED_BACK)))
|
||||
.then(Mono.defer(() -> {
|
||||
this.transactionExecutionListeners.forEach(listener -> listener.afterRollback(status, null));
|
||||
return Mono.empty();
|
||||
|
||||
+14
-3
@@ -216,9 +216,8 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest
|
||||
context = HttpClientContext.create();
|
||||
}
|
||||
|
||||
// Request configuration not set in the context
|
||||
if (!(context instanceof HttpClientContext clientContext && clientContext.getRequestConfig() != null) &&
|
||||
context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
|
||||
// No custom request configuration was set
|
||||
if (!hasCustomRequestConfig(context)) {
|
||||
RequestConfig config = null;
|
||||
// Use request configuration given by the user, when available
|
||||
if (httpRequest instanceof Configurable configurable) {
|
||||
@@ -237,6 +236,18 @@ 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-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +139,7 @@ final class DefaultRestClient implements RestClient {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public RequestHeadersUriSpec<?> get() {
|
||||
return methodInternal(HttpMethod.GET);
|
||||
@@ -272,8 +273,6 @@ final class DefaultRestClient implements RestClient {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
|
||||
|
||||
private final HttpMethod httpMethod;
|
||||
@@ -452,7 +451,6 @@ final class DefaultRestClient implements RestClient {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResponseSpec retrieve() {
|
||||
ResponseSpec responseSpec = exchangeInternal(DefaultResponseSpec::new, false);
|
||||
@@ -784,8 +782,6 @@ final class DefaultRestClient implements RestClient {
|
||||
this.observationScope.close();
|
||||
this.observation.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,14 +296,7 @@ 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><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.
|
||||
* <p>By default, this is not set.
|
||||
*/
|
||||
public void setAllowedMethods(@Nullable List<String> allowedMethods) {
|
||||
this.allowedMethods = (allowedMethods != null ? new ArrayList<>(allowedMethods) : null);
|
||||
@@ -455,7 +448,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;
|
||||
@@ -479,7 +472,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>
|
||||
*/
|
||||
|
||||
+13
-48
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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,6 +29,7 @@ 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;
|
||||
@@ -84,13 +85,12 @@ import org.springframework.web.util.pattern.PathPatternParser;
|
||||
* {@link #relativeTo(org.springframework.web.util.UriComponentsBuilder)}.
|
||||
* </ul>
|
||||
*
|
||||
* <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.
|
||||
* <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.
|
||||
*
|
||||
* @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,8 +152,6 @@ 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})
|
||||
*/
|
||||
@@ -166,8 +164,6 @@ 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
|
||||
@@ -193,8 +189,6 @@ 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
|
||||
@@ -214,8 +208,6 @@ 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
|
||||
@@ -240,8 +232,6 @@ 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
|
||||
@@ -258,8 +248,6 @@ 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
|
||||
@@ -306,8 +294,6 @@ 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
|
||||
@@ -328,8 +314,6 @@ 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
|
||||
@@ -355,8 +339,6 @@ 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) {
|
||||
@@ -379,8 +361,6 @@ 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) {
|
||||
@@ -423,9 +403,6 @@ 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
|
||||
@@ -441,8 +418,6 @@ 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
|
||||
@@ -482,8 +457,6 @@ 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) {
|
||||
@@ -491,10 +464,8 @@ 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) {
|
||||
@@ -504,8 +475,6 @@ 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) {
|
||||
@@ -515,8 +484,6 @@ 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) {
|
||||
@@ -526,8 +493,6 @@ 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) {
|
||||
@@ -667,8 +632,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;
|
||||
@@ -829,7 +794,7 @@ public class MvcUriComponentsBuilder {
|
||||
enhancer.setSuperclass(controllerType);
|
||||
enhancer.setInterfaces(new Class<?>[] {MethodInvocationInfo.class});
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setAttemptLoad(AotDetector.useGeneratedArtifacts());
|
||||
enhancer.setCallbackType(MethodInterceptor.class);
|
||||
|
||||
Class<?> proxyClass = enhancer.createClass();
|
||||
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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,11 +217,10 @@ 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.getClass().isArray()) {
|
||||
Object[] itemsArray = (Object[]) itemsObject;
|
||||
for (int i = 0; i < itemsArray.length; i++) {
|
||||
Object item = itemsArray[i];
|
||||
writeObjectEntry(tagWriter, valueProperty, labelProperty, item, i);
|
||||
if (itemsObject instanceof Object[] itemsArray) {
|
||||
for (int itemIndex = 0; itemIndex < itemsArray.length; itemIndex++) {
|
||||
Object item = itemsArray[itemIndex];
|
||||
writeObjectEntry(tagWriter, valueProperty, labelProperty, item, itemIndex);
|
||||
}
|
||||
}
|
||||
else if (itemsObject instanceof Collection<?> optionCollection) {
|
||||
|
||||
Reference in New Issue
Block a user