mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c85c3166c | |||
| b28a5f858c | |||
| e660859ec3 | |||
| a6ab308cf4 | |||
| c489234cc2 | |||
| af593588d3 | |||
| 75193b2e84 | |||
| f97e819c9e | |||
| f8e1ce3074 | |||
| d3ec9395e1 | |||
| de0cb53394 | |||
| 1bf5d8b9e5 | |||
| 15a306d8dd | |||
| 680939557c | |||
| 84f3ce1f91 | |||
| 2dc4fe0857 | |||
| cb28e2a701 | |||
| 0370aa6007 | |||
| 704650de6d | |||
| 8538a73f20 | |||
| c615a6667d | |||
| 45cfe70d30 | |||
| 288e255ce9 | |||
| 12159b98a2 | |||
| 7a8ec2a761 | |||
| ca4674df42 | |||
| 2ec32a4e2f | |||
| 3849ed3a0c | |||
| d62f17864c | |||
| 3c59c2a275 | |||
| 2b4c1e265c | |||
| cc0cbc15aa | |||
| f7bf2431fb | |||
| 40678bb981 | |||
| 75faf698af | |||
| 39c225c813 | |||
| 0c3d8d7a44 | |||
| ddcae04ad5 | |||
| 994bbec0c3 | |||
| afb378a59f | |||
| a4fc7d3c11 | |||
| 88c3a788f3 | |||
| d7ac89ecc9 | |||
| 9894174960 | |||
| 493f75e892 | |||
| df066d8190 | |||
| 5f7a6a0f38 | |||
| 4326c53222 | |||
| 3da7a35a91 | |||
| b9be40ccd2 | |||
| f7d4bd176e | |||
| 2b48254268 | |||
| bb46b31925 | |||
| 0c275107ea | |||
| 9931f442e4 | |||
| fc085e8663 | |||
| 9d7154901f | |||
| 24893d038f | |||
| b9482375b7 | |||
| ad61fb75da | |||
| c5aa7830bc | |||
| 14c8c9168c | |||
| 23eb0e3128 | |||
| 840bd574db | |||
| 6dea580145 | |||
| d1efc891db | |||
| c9849d6972 | |||
| 790abeda1c | |||
| 340b32a3cb | |||
| a7b7466274 | |||
| 3a8c0dbd8a | |||
| b387d9bf10 | |||
| 0f33f79c05 | |||
| ef65429823 | |||
| 0b4b313bae | |||
| 6879be7508 | |||
| 355fa258bd | |||
| 60337738d8 |
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=8.0.372-librca
|
||||
java=8.0.382-librca
|
||||
|
||||
+12
-12
@@ -28,18 +28,18 @@ configure(allprojects) { project ->
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
|
||||
mavenBom "io.netty:netty-bom:4.1.94.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.34"
|
||||
mavenBom "io.netty:netty-bom:4.1.101.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.38"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.3"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.51.v20230217"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.53.v20231009"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2"
|
||||
mavenBom "org.junit:junit-bom:5.8.2"
|
||||
}
|
||||
dependencies {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.20.0') {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.21.1') {
|
||||
entry 'log4j-api'
|
||||
entry 'log4j-core'
|
||||
entry 'log4j-jul'
|
||||
@@ -67,9 +67,9 @@ configure(allprojects) { project ->
|
||||
dependency "io.reactivex:rxjava:1.3.8"
|
||||
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
|
||||
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
|
||||
dependency "io.reactivex.rxjava3:rxjava:3.1.5"
|
||||
dependency "io.smallrye.reactive:mutiny:1.8.0"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
|
||||
dependency "io.reactivex.rxjava3:rxjava:3.1.8"
|
||||
dependency "io.smallrye.reactive:mutiny:1.9.0"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.8.RELEASE"
|
||||
|
||||
dependency "com.caucho:hessian:4.0.63"
|
||||
dependency "com.fasterxml:aalto-xml:1.3.1"
|
||||
@@ -96,7 +96,7 @@ configure(allprojects) { project ->
|
||||
|
||||
dependency "com.h2database:h2:2.1.214"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
|
||||
dependency "com.github.librepdf:openpdf:1.3.30"
|
||||
dependency "com.github.librepdf:openpdf:1.3.33"
|
||||
dependency "com.rometools:rome:1.18.0"
|
||||
dependency "commons-io:commons-io:2.5"
|
||||
dependency "io.vavr:vavr:0.10.4"
|
||||
@@ -128,18 +128,18 @@ configure(allprojects) { project ->
|
||||
dependency "org.webjars:webjars-locator-core:0.48"
|
||||
dependency "org.webjars:underscorejs:1.8.3"
|
||||
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.78') {
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.82') {
|
||||
entry 'tomcat-util'
|
||||
entry('tomcat-websocket') {
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.78') {
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.82') {
|
||||
entry 'tomcat-embed-core'
|
||||
entry 'tomcat-embed-websocket'
|
||||
}
|
||||
dependencySet(group: 'io.undertow', version: '2.2.25.Final') {
|
||||
dependencySet(group: 'io.undertow', version: '2.2.28.Final') {
|
||||
entry 'undertow-core'
|
||||
entry('undertow-servlet') {
|
||||
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
|
||||
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.12.1"
|
||||
toolVersion = "10.12.5"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.29-SNAPSHOT
|
||||
version=5.3.31
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -60,8 +60,8 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigA.class);
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(ctx::refresh)
|
||||
.withCauseInstanceOf(IllegalStateException.class);
|
||||
.isThrownBy(ctx::refresh)
|
||||
.withCauseInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,7 +70,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||
ctx.register(Config.class, SubclassProxyTxConfig.class, RepoConfigA.class);
|
||||
ctx.refresh();
|
||||
|
||||
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||
Thread.sleep(200); // allow @Scheduled method to be called several times
|
||||
|
||||
MyRepository repository = ctx.getBean(MyRepository.class);
|
||||
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
|
||||
@@ -85,7 +85,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigB.class);
|
||||
ctx.refresh();
|
||||
|
||||
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||
Thread.sleep(200); // allow @Scheduled method to be called several times
|
||||
|
||||
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
|
||||
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
|
||||
@@ -100,7 +100,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||
ctx.register(AspectConfig.class, MyRepositoryWithScheduledMethodImpl.class);
|
||||
ctx.refresh();
|
||||
|
||||
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||
Thread.sleep(200); // allow @Scheduled method to be called several times
|
||||
|
||||
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
|
||||
assertThat(AopUtils.isCglibProxy(repository)).isTrue();
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -248,8 +248,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
/**
|
||||
* If a pointcut expression has been specified in XML, the user cannot
|
||||
* write {@code and} as "&&" (though && will work).
|
||||
* We also allow {@code and} between two pointcut sub-expressions.
|
||||
* write "and" as "&&" (though {@code &&} will work).
|
||||
* <p>We also allow "and" between two pointcut sub-expressions.
|
||||
* <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
|
||||
*/
|
||||
private String replaceBooleanOperators(String pcExpr) {
|
||||
@@ -527,7 +527,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
return false;
|
||||
}
|
||||
AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
|
||||
return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
|
||||
return ObjectUtils.nullSafeEquals(getExpression(), otherPc.getExpression()) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
|
||||
@@ -535,7 +535,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
|
||||
int hashCode = ObjectUtils.nullSafeHashCode(getExpression());
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
|
||||
|
||||
@@ -424,10 +424,9 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* Method interceptor used for static targets with no advice chain. The call
|
||||
* is passed directly back to the target. Used when the proxy needs to be
|
||||
* exposed and it can't be determined that the method won't return
|
||||
* {@code this}.
|
||||
* Method interceptor used for static targets with no advice chain. The call is
|
||||
* passed directly back to the target. Used when the proxy needs to be exposed
|
||||
* and it can't be determined that the method won't return {@code this}.
|
||||
*/
|
||||
private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
|
||||
+4
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -77,9 +77,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode"));
|
||||
given(mi.getThis()).willReturn(new Object());
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
|
||||
ti.invoke(mi))
|
||||
.isSameAs(ex);
|
||||
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
|
||||
assertThat(th.getCalls()).isEqualTo(1);
|
||||
assertThat(th.getCalls("ioException")).isEqualTo(1);
|
||||
}
|
||||
@@ -92,9 +90,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
ConnectException ex = new ConnectException("");
|
||||
MethodInvocation mi = mock(MethodInvocation.class);
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
assertThatExceptionOfType(ConnectException.class).isThrownBy(() ->
|
||||
ti.invoke(mi))
|
||||
.isSameAs(ex);
|
||||
assertThatExceptionOfType(ConnectException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
|
||||
assertThat(th.getCalls()).isEqualTo(1);
|
||||
assertThat(th.getCalls("remoteException")).isEqualTo(1);
|
||||
}
|
||||
@@ -117,9 +113,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
ConnectException ex = new ConnectException("");
|
||||
MethodInvocation mi = mock(MethodInvocation.class);
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
assertThatExceptionOfType(Throwable.class).isThrownBy(() ->
|
||||
ti.invoke(mi))
|
||||
.isSameAs(t);
|
||||
assertThatExceptionOfType(Throwable.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(t);
|
||||
assertThat(th.getCalls()).isEqualTo(1);
|
||||
assertThat(th.getCalls("remoteException")).isEqualTo(1);
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1004,18 +1004,20 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
*/
|
||||
protected abstract static class PropertyHandler {
|
||||
|
||||
@Nullable
|
||||
private final Class<?> propertyType;
|
||||
|
||||
private final boolean readable;
|
||||
|
||||
private final boolean writable;
|
||||
|
||||
public PropertyHandler(Class<?> propertyType, boolean readable, boolean writable) {
|
||||
public PropertyHandler(@Nullable Class<?> propertyType, boolean readable, boolean writable) {
|
||||
this.propertyType = propertyType;
|
||||
this.readable = readable;
|
||||
this.writable = writable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Class<?> getPropertyType() {
|
||||
return this.propertyType;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.temporal.Temporal;
|
||||
@@ -30,6 +31,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -787,38 +789,28 @@ public abstract class BeanUtils {
|
||||
if (editable != null) {
|
||||
if (!editable.isInstance(target)) {
|
||||
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
|
||||
"] not assignable to Editable class [" + editable.getName() + "]");
|
||||
"] not assignable to editable class [" + editable.getName() + "]");
|
||||
}
|
||||
actualEditable = editable;
|
||||
}
|
||||
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
||||
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
|
||||
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
|
||||
CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ?
|
||||
CachedIntrospectionResults.forClass(source.getClass()) : null);
|
||||
|
||||
for (PropertyDescriptor targetPd : targetPds) {
|
||||
Method writeMethod = targetPd.getWriteMethod();
|
||||
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
|
||||
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
|
||||
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
|
||||
PropertyDescriptor sourcePd = (sourceResults != null ?
|
||||
sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd);
|
||||
if (sourcePd != null) {
|
||||
Method readMethod = sourcePd.getReadMethod();
|
||||
if (readMethod != null) {
|
||||
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
|
||||
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
|
||||
|
||||
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
|
||||
boolean isAssignable =
|
||||
(sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
|
||||
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
|
||||
targetResolvableType.isAssignableFrom(sourceResolvableType));
|
||||
|
||||
if (isAssignable) {
|
||||
if (isAssignable(writeMethod, readMethod)) {
|
||||
try {
|
||||
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
|
||||
readMethod.setAccessible(true);
|
||||
}
|
||||
ReflectionUtils.makeAccessible(readMethod);
|
||||
Object value = readMethod.invoke(source);
|
||||
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
|
||||
writeMethod.setAccessible(true);
|
||||
}
|
||||
ReflectionUtils.makeAccessible(writeMethod);
|
||||
writeMethod.invoke(target, value);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
@@ -832,6 +824,24 @@ public abstract class BeanUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAssignable(Method writeMethod, Method readMethod) {
|
||||
Type paramType = writeMethod.getGenericParameterTypes()[0];
|
||||
if (paramType instanceof Class) {
|
||||
return ClassUtils.isAssignable((Class<?>) paramType, readMethod.getReturnType());
|
||||
}
|
||||
else if (paramType.equals(readMethod.getGenericReturnType())) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
ResolvableType sourceType = ResolvableType.forMethodReturnType(readMethod);
|
||||
ResolvableType targetType = ResolvableType.forMethodParameter(writeMethod, 0);
|
||||
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
|
||||
return (sourceType.hasUnresolvableGenerics() || targetType.hasUnresolvableGenerics() ?
|
||||
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
|
||||
targetType.isAssignableFrom(sourceType));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
||||
@@ -892,7 +902,6 @@ public abstract class BeanUtils {
|
||||
}
|
||||
return kotlinConstructor.callBy(argParameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -248,9 +248,22 @@ public final class CachedIntrospectionResults {
|
||||
return beanInfo;
|
||||
}
|
||||
}
|
||||
return (shouldIntrospectorIgnoreBeaninfoClasses ?
|
||||
|
||||
BeanInfo beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ?
|
||||
Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) :
|
||||
Introspector.getBeanInfo(beanClass));
|
||||
|
||||
// Immediately remove class from Introspector cache to allow for proper garbage
|
||||
// collection on class loader shutdown; we cache it in CachedIntrospectionResults
|
||||
// in a GC-friendly manner. This is necessary (again) for the JDK ClassInfo cache.
|
||||
Class<?> classToFlush = beanClass;
|
||||
do {
|
||||
Introspector.flushFromCaches(classToFlush);
|
||||
classToFlush = classToFlush.getSuperclass();
|
||||
}
|
||||
while (classToFlush != null && classToFlush != Object.class);
|
||||
|
||||
return beanInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -129,7 +129,6 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
|
||||
ReflectionUtils.makeAccessible(this.field);
|
||||
return this.field.get(getWrappedInstance());
|
||||
}
|
||||
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new InvalidPropertyException(getWrappedClass(),
|
||||
this.field.getName(), "Field is not accessible", ex);
|
||||
|
||||
+67
-20
@@ -17,6 +17,7 @@
|
||||
package org.springframework.beans.factory.annotation;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
@@ -62,6 +63,10 @@ import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -144,6 +149,9 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
@Nullable
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Nullable
|
||||
private MetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
private final Set<String> lookupMethodsChecked = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
|
||||
|
||||
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256);
|
||||
@@ -238,6 +246,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
|
||||
}
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
this.metadataReaderFactory = new SimpleMetadataReaderFactory(this.beanFactory.getBeanClassLoader());
|
||||
}
|
||||
|
||||
|
||||
@@ -463,12 +472,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return InjectionMetadata.EMPTY;
|
||||
}
|
||||
|
||||
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
Class<?> targetClass = clazz;
|
||||
|
||||
do {
|
||||
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
|
||||
|
||||
final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();
|
||||
ReflectionUtils.doWithLocalFields(targetClass, field -> {
|
||||
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
|
||||
if (ann != null) {
|
||||
@@ -479,10 +487,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return;
|
||||
}
|
||||
boolean required = determineRequiredStatus(ann);
|
||||
currElements.add(new AutowiredFieldElement(field, required));
|
||||
fieldElements.add(new AutowiredFieldElement(field, required));
|
||||
}
|
||||
});
|
||||
|
||||
final List<InjectionMetadata.InjectedElement> methodElements = new ArrayList<>();
|
||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||
@@ -504,11 +513,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
boolean required = determineRequiredStatus(ann);
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
methodElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
}
|
||||
});
|
||||
|
||||
elements.addAll(0, currElements);
|
||||
elements.addAll(0, sortMethodElements(methodElements, targetClass));
|
||||
elements.addAll(0, fieldElements);
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
@@ -573,6 +583,47 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the method elements via ASM for deterministic declaration order if possible.
|
||||
*/
|
||||
private List<InjectionMetadata.InjectedElement> sortMethodElements(
|
||||
List<InjectionMetadata.InjectedElement> methodElements, Class<?> targetClass) {
|
||||
|
||||
if (this.metadataReaderFactory != null && methodElements.size() > 1) {
|
||||
// Try reading the class file via ASM for deterministic declaration order...
|
||||
// Unfortunately, the JVM's standard reflection returns methods in arbitrary
|
||||
// order, even between different runs of the same application on the same JVM.
|
||||
try {
|
||||
AnnotationMetadata asm =
|
||||
this.metadataReaderFactory.getMetadataReader(targetClass.getName()).getAnnotationMetadata();
|
||||
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Autowired.class.getName());
|
||||
if (asmMethods.size() >= methodElements.size()) {
|
||||
List<InjectionMetadata.InjectedElement> candidateMethods = new ArrayList<>(methodElements);
|
||||
List<InjectionMetadata.InjectedElement> selectedMethods = new ArrayList<>(asmMethods.size());
|
||||
for (MethodMetadata asmMethod : asmMethods) {
|
||||
for (Iterator<InjectionMetadata.InjectedElement> it = candidateMethods.iterator(); it.hasNext();) {
|
||||
InjectionMetadata.InjectedElement element = it.next();
|
||||
if (element.getMember().getName().equals(asmMethod.getMethodName())) {
|
||||
selectedMethods.add(element);
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectedMethods.size() == methodElements.size()) {
|
||||
// All reflection-detected methods found in ASM method set -> proceed
|
||||
return selectedMethods;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.debug("Failed to read class file via ASM for determining @Autowired method order", ex);
|
||||
// No worries, let's continue with the reflection metadata we started with...
|
||||
}
|
||||
}
|
||||
return methodElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the specified bean as dependent on the autowired beans.
|
||||
*/
|
||||
@@ -651,7 +702,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
|
||||
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
|
||||
desc.setContainingClass(bean.getClass());
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
Object value;
|
||||
@@ -670,8 +721,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
if (beanFactory.containsBean(autowiredBeanName) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
|
||||
cachedFieldValue = new ShortcutDependencyDescriptor(
|
||||
desc, autowiredBeanName, field.getType());
|
||||
cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);
|
||||
}
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
@@ -754,7 +804,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
int argumentCount = method.getParameterCount();
|
||||
Object[] arguments = new Object[argumentCount];
|
||||
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
|
||||
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(argumentCount * 2);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
@@ -763,7 +813,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
currDesc.setContainingClass(bean.getClass());
|
||||
descriptors[i] = currDesc;
|
||||
try {
|
||||
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
|
||||
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter);
|
||||
if (arg == null && !this.required) {
|
||||
arguments = null;
|
||||
break;
|
||||
@@ -778,16 +828,16 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
if (!this.cached) {
|
||||
if (arguments != null) {
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
|
||||
registerDependentBeans(beanName, autowiredBeans);
|
||||
if (autowiredBeans.size() == argumentCount) {
|
||||
Iterator<String> it = autowiredBeans.iterator();
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (autowiredBeanNames.size() == argumentCount) {
|
||||
Iterator<String> it = autowiredBeanNames.iterator();
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
String autowiredBeanName = it.next();
|
||||
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
|
||||
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
|
||||
descriptors[i], autowiredBeanName, paramTypes[i]);
|
||||
descriptors[i], autowiredBeanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -813,17 +863,14 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
private final String shortcut;
|
||||
|
||||
private final Class<?> requiredType;
|
||||
|
||||
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) {
|
||||
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut) {
|
||||
super(original);
|
||||
this.shortcut = shortcut;
|
||||
this.requiredType = requiredType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveShortcut(BeanFactory beanFactory) {
|
||||
return beanFactory.getBean(this.shortcut, this.requiredType);
|
||||
return beanFactory.getBean(this.shortcut, getDependencyType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1504,8 +1504,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
converter = bw;
|
||||
}
|
||||
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
||||
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(propertyNames.length * 2);
|
||||
for (String propertyName : propertyNames) {
|
||||
try {
|
||||
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -134,7 +134,7 @@ class BeanDefinitionValueResolver {
|
||||
return resolveInnerBean(argName, innerBeanName, bd);
|
||||
}
|
||||
else if (value instanceof DependencyDescriptor) {
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||
Object result = this.beanFactory.resolveDependency(
|
||||
(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
|
||||
+121
-42
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,6 +45,7 @@ import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.InjectionPoint;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
@@ -85,12 +86,6 @@ class ConstructorResolver {
|
||||
|
||||
private static final Object[] EMPTY_ARGS = new Object[0];
|
||||
|
||||
/**
|
||||
* Marker for autowired arguments in a cached argument array, to be replaced
|
||||
* by a {@linkplain #resolveAutowiredArgument resolved autowired argument}.
|
||||
*/
|
||||
private static final Object autowiredArgumentMarker = new Object();
|
||||
|
||||
private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint =
|
||||
new NamedThreadLocal<>("Current injection point");
|
||||
|
||||
@@ -729,7 +724,7 @@ class ConstructorResolver {
|
||||
|
||||
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
|
||||
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
||||
Set<String> allAutowiredBeanNames = new LinkedHashSet<>(paramTypes.length * 2);
|
||||
|
||||
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
|
||||
Class<?> paramType = paramTypes[paramIndex];
|
||||
@@ -764,8 +759,8 @@ class ConstructorResolver {
|
||||
throw new UnsatisfiedDependencyException(
|
||||
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
||||
"Could not convert argument value of type [" +
|
||||
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
|
||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
|
||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||
}
|
||||
Object sourceHolder = valueHolder.getSource();
|
||||
if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
|
||||
@@ -788,11 +783,17 @@ class ConstructorResolver {
|
||||
"] - did you specify the correct bean references as arguments?");
|
||||
}
|
||||
try {
|
||||
Object autowiredArgument = resolveAutowiredArgument(
|
||||
methodParam, beanName, autowiredBeanNames, converter, fallback);
|
||||
args.rawArguments[paramIndex] = autowiredArgument;
|
||||
args.arguments[paramIndex] = autowiredArgument;
|
||||
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
|
||||
ConstructorDependencyDescriptor desc = new ConstructorDependencyDescriptor(methodParam, true);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||
Object arg = resolveAutowiredArgument(
|
||||
desc, paramType, beanName, autowiredBeanNames, converter, fallback);
|
||||
if (arg != null) {
|
||||
setShortcutIfPossible(desc, paramType, autowiredBeanNames);
|
||||
}
|
||||
allAutowiredBeanNames.addAll(autowiredBeanNames);
|
||||
args.rawArguments[paramIndex] = arg;
|
||||
args.arguments[paramIndex] = arg;
|
||||
args.preparedArguments[paramIndex] = desc;
|
||||
args.resolveNecessary = true;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
@@ -802,14 +803,7 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Autowiring by type from bean name '" + beanName +
|
||||
"' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
|
||||
" to bean named '" + autowiredBeanName + "'");
|
||||
}
|
||||
}
|
||||
registerDependentBeans(executable, beanName, allAutowiredBeanNames);
|
||||
|
||||
return args;
|
||||
}
|
||||
@@ -829,31 +823,61 @@ class ConstructorResolver {
|
||||
Object[] resolvedArgs = new Object[argsToResolve.length];
|
||||
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
|
||||
Object argValue = argsToResolve[argIndex];
|
||||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||
if (argValue == autowiredArgumentMarker) {
|
||||
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
|
||||
Class<?> paramType = paramTypes[argIndex];
|
||||
boolean convertNecessary = false;
|
||||
if (argValue instanceof ConstructorDependencyDescriptor) {
|
||||
ConstructorDependencyDescriptor descriptor = (ConstructorDependencyDescriptor) argValue;
|
||||
try {
|
||||
argValue = resolveAutowiredArgument(descriptor, paramType, beanName,
|
||||
null, converter, true);
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
Set<String> autowiredBeanNames = null;
|
||||
if (descriptor.hasShortcut()) {
|
||||
// Reset shortcut and try to re-resolve it in this thread...
|
||||
descriptor.setShortcut(null);
|
||||
autowiredBeanNames = new LinkedHashSet<>(2);
|
||||
}
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
argValue = resolveAutowiredArgument(descriptor, paramType, beanName,
|
||||
autowiredBeanNames, converter, true);
|
||||
if (autowiredBeanNames != null && !descriptor.hasShortcut()) {
|
||||
// We encountered as stale shortcut before, and the shortcut has
|
||||
// not been re-resolved by another thread in the meantime...
|
||||
if (argValue != null) {
|
||||
setShortcutIfPossible(descriptor, paramType, autowiredBeanNames);
|
||||
}
|
||||
registerDependentBeans(executable, beanName, autowiredBeanNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (argValue instanceof BeanMetadataElement) {
|
||||
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
|
||||
convertNecessary = true;
|
||||
}
|
||||
else if (argValue instanceof String) {
|
||||
argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
|
||||
convertNecessary = true;
|
||||
}
|
||||
Class<?> paramType = paramTypes[argIndex];
|
||||
try {
|
||||
resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
|
||||
}
|
||||
catch (TypeMismatchException ex) {
|
||||
throw new UnsatisfiedDependencyException(
|
||||
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
||||
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
|
||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||
if (convertNecessary) {
|
||||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||
try {
|
||||
argValue = converter.convertIfNecessary(argValue, paramType, methodParam);
|
||||
}
|
||||
catch (TypeMismatchException ex) {
|
||||
throw new UnsatisfiedDependencyException(
|
||||
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
||||
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
|
||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
resolvedArgs[argIndex] = argValue;
|
||||
}
|
||||
return resolvedArgs;
|
||||
}
|
||||
|
||||
protected Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
|
||||
private Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
|
||||
Class<?> declaringClass = constructor.getDeclaringClass();
|
||||
Class<?> userClass = ClassUtils.getUserClass(declaringClass);
|
||||
if (userClass != declaringClass) {
|
||||
@@ -869,23 +893,22 @@ class ConstructorResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for resolving the specified argument which is supposed to be autowired.
|
||||
* Resolve the specified argument which is supposed to be autowired.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
|
||||
Object resolveAutowiredArgument(DependencyDescriptor descriptor, Class<?> paramType, String beanName,
|
||||
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
|
||||
|
||||
Class<?> paramType = param.getParameterType();
|
||||
if (InjectionPoint.class.isAssignableFrom(paramType)) {
|
||||
InjectionPoint injectionPoint = currentInjectionPoint.get();
|
||||
if (injectionPoint == null) {
|
||||
throw new IllegalStateException("No current InjectionPoint available for " + param);
|
||||
throw new IllegalStateException("No current InjectionPoint available for " + descriptor);
|
||||
}
|
||||
return injectionPoint;
|
||||
}
|
||||
|
||||
try {
|
||||
return this.beanFactory.resolveDependency(
|
||||
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
|
||||
return this.beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
throw ex;
|
||||
@@ -908,6 +931,31 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private void setShortcutIfPossible(
|
||||
ConstructorDependencyDescriptor descriptor, Class<?> paramType, Set<String> autowiredBeanNames) {
|
||||
|
||||
if (autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
if (this.beanFactory.containsBean(autowiredBeanName) &&
|
||||
this.beanFactory.isTypeMatch(autowiredBeanName, paramType)) {
|
||||
descriptor.setShortcut(autowiredBeanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerDependentBeans(
|
||||
Executable executable, String beanName, Set<String> autowiredBeanNames) {
|
||||
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Autowiring by type from bean name '" + beanName + "' via " +
|
||||
(executable instanceof Constructor ? "constructor" : "factory method") +
|
||||
" to bean named '" + autowiredBeanName + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static InjectionPoint setCurrentInjectionPoint(@Nullable InjectionPoint injectionPoint) {
|
||||
InjectionPoint old = currentInjectionPoint.get();
|
||||
if (injectionPoint != null) {
|
||||
@@ -1006,4 +1054,35 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* DependencyDescriptor marker for constructor arguments,
|
||||
* for differentiating between a provided DependencyDescriptor instance
|
||||
* and an internally built DependencyDescriptor for autowiring purposes.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class ConstructorDependencyDescriptor extends DependencyDescriptor {
|
||||
|
||||
@Nullable
|
||||
private volatile String shortcut;
|
||||
|
||||
public ConstructorDependencyDescriptor(MethodParameter methodParameter, boolean required) {
|
||||
super(methodParameter, required);
|
||||
}
|
||||
|
||||
public void setShortcut(@Nullable String shortcut) {
|
||||
this.shortcut = shortcut;
|
||||
}
|
||||
|
||||
public boolean hasShortcut() {
|
||||
return (this.shortcut != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveShortcut(BeanFactory beanFactory) {
|
||||
String shortcut = this.shortcut;
|
||||
return (shortcut != null ? beanFactory.getBean(shortcut, getDependencyType()) : null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -112,7 +112,7 @@ public class LookupOverride extends MethodOverride {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (29 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.beanName));
|
||||
return super.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+45
-37
@@ -1271,10 +1271,11 @@ class DefaultListableBeanFactoryTests {
|
||||
lbf.registerBeanDefinition("rod", bd);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
|
||||
lbf.registerBeanDefinition("rod2", bd2);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
|
||||
lbf.autowire(ConstructorDependency.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false))
|
||||
.withMessageContaining("rod")
|
||||
.withMessageContaining("rod2");
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(() -> lbf.autowire(ConstructorDependency.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false))
|
||||
.withMessageContaining("rod")
|
||||
.withMessageContaining("rod2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1336,11 +1337,12 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
|
||||
bd2.setDependsOn("tb1");
|
||||
lbf.registerBeanDefinition("tb2", bd2);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
|
||||
lbf.preInstantiateSingletons())
|
||||
.withMessageContaining("Circular")
|
||||
.withMessageContaining("'tb2'")
|
||||
.withMessageContaining("'tb1'");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> lbf.preInstantiateSingletons())
|
||||
.withMessageContaining("Circular")
|
||||
.withMessageContaining("'tb2'")
|
||||
.withMessageContaining("'tb1'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1354,11 +1356,12 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class);
|
||||
bd3.setDependsOn("tb1");
|
||||
lbf.registerBeanDefinition("tb3", bd3);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
lbf::preInstantiateSingletons)
|
||||
.withMessageContaining("Circular")
|
||||
.withMessageContaining("'tb3'")
|
||||
.withMessageContaining("'tb1'");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(lbf::preInstantiateSingletons)
|
||||
.withMessageContaining("Circular")
|
||||
.withMessageContaining("'tb3'")
|
||||
.withMessageContaining("'tb1'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1493,10 +1496,11 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(HighPriorityTestBean.class);
|
||||
lbf.registerBeanDefinition("bd1", bd1);
|
||||
lbf.registerBeanDefinition("bd2", bd2);
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(() ->
|
||||
lbf.getBean(TestBean.class))
|
||||
.withMessageContaining("Multiple beans found with the same priority")
|
||||
.withMessageContaining("5"); // conflicting priority
|
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
|
||||
.isThrownBy(() -> lbf.getBean(TestBean.class))
|
||||
.withMessageContaining("Multiple beans found with the same priority")
|
||||
.withMessageContaining("5"); // conflicting priority
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1698,9 +1702,9 @@ class DefaultListableBeanFactoryTests {
|
||||
lbf.registerBeanDefinition("bd1", bd1);
|
||||
lbf.registerBeanDefinition("bd2", bd2);
|
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(() ->
|
||||
lbf.getBean(ConstructorDependency.class, 42))
|
||||
.withMessageContaining("more than one 'primary'");
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
|
||||
.isThrownBy(() -> lbf.getBean(ConstructorDependency.class, 42))
|
||||
.withMessageContaining("more than one 'primary'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1881,10 +1885,11 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
|
||||
lbf.registerBeanDefinition("test", bd);
|
||||
lbf.registerBeanDefinition("spouse", bd2);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
|
||||
lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
|
||||
.withMessageContaining("test")
|
||||
.withMessageContaining("spouse");
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(() -> lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
|
||||
.withMessageContaining("test")
|
||||
.withMessageContaining("spouse");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1946,10 +1951,11 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(HighPriorityTestBean.class);
|
||||
lbf.registerBeanDefinition("test", bd);
|
||||
lbf.registerBeanDefinition("spouse", bd2);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
|
||||
lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
|
||||
.withCauseExactlyInstanceOf(NoUniqueBeanDefinitionException.class)
|
||||
.withMessageContaining("5");
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(() -> lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
|
||||
.withCauseExactlyInstanceOf(NoUniqueBeanDefinitionException.class)
|
||||
.withMessageContaining("5");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2185,19 +2191,21 @@ class DefaultListableBeanFactoryTests {
|
||||
@Test
|
||||
void beanDefinitionWithInterface() {
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(ITestBean.class));
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
|
||||
lbf.getBean("test"))
|
||||
.withMessageContaining("interface")
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> lbf.getBean("test"))
|
||||
.withMessageContaining("interface")
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithAbstractClass() {
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(AbstractBeanFactory.class));
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
|
||||
lbf.getBean("test"))
|
||||
.withMessageContaining("abstract")
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> lbf.getBean("test"))
|
||||
.withMessageContaining("abstract")
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+212
-19
@@ -72,6 +72,9 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -131,6 +134,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
bean = bf.getBean("annotatedBean", ResourceInjectionBean.class);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
|
||||
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -152,10 +157,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourceInjectionWithSometimesNullBean() {
|
||||
void resourceInjectionWithSometimesNullBeanEarly() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
@@ -170,6 +177,55 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
assertThat(bean.getTestBean2()).isNotNull();
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
assertThat(bean.getTestBean2()).isNotNull();
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourceInjectionWithSometimesNullBeanLate() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
RootBeanDefinition tb = new RootBeanDefinition(SometimesNullFactoryMethods.class);
|
||||
tb.setFactoryMethodName("createTestBean");
|
||||
tb.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("testBean", tb);
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
assertThat(bean.getTestBean2()).isNotNull();
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
@@ -194,17 +250,13 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getTestBean2()).isNotNull();
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
assertThat(bean.getTestBean2()).isNotNull();
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -233,10 +285,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
String[] depBeans = bf.getDependenciesForBean("annotatedBean");
|
||||
assertThat(depBeans.length).isEqualTo(2);
|
||||
assertThat(depBeans[0]).isEqualTo("testBean");
|
||||
assertThat(depBeans[1]).isEqualTo("nestedTestBean");
|
||||
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean", "nestedTestBean"});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -398,6 +447,22 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
|
||||
bf.destroySingleton("testBean");
|
||||
bf.registerSingleton("testBeanX", tb);
|
||||
|
||||
bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
|
||||
assertThat(bean.getNestedTestBeans()).hasSize(2);
|
||||
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
|
||||
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
|
||||
assertThat(bean.nestedTestBeansField).hasSize(2);
|
||||
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
|
||||
bf.destroySingleton("testBeanX");
|
||||
|
||||
bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
@@ -455,6 +520,22 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
|
||||
bf.removeBeanDefinition("testBean");
|
||||
bf.registerBeanDefinition("testBeanX", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
|
||||
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBeanX"));
|
||||
assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBeanX"));
|
||||
assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBeanX"));
|
||||
assertThat(bean.getTestBean4()).isSameAs(bf.getBean("testBeanX"));
|
||||
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
|
||||
assertThat(bean.getNestedTestBeans()).hasSize(2);
|
||||
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
|
||||
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
|
||||
assertThat(bean.nestedTestBeansField).hasSize(2);
|
||||
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
|
||||
bf.removeBeanDefinition("testBeanX");
|
||||
|
||||
bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
@@ -719,6 +800,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(
|
||||
new String[] {"testBean", "nestedTestBean", ObjectUtils.identityToString(bf)});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -740,6 +824,17 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
bf.destroySingleton("nestedTestBean");
|
||||
bf.registerSingleton("nestedTestBeanX", ntb);
|
||||
|
||||
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
bf.destroySingleton("nestedTestBeanX");
|
||||
|
||||
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
@@ -778,6 +873,17 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
bf.removeBeanDefinition("nestedTestBean");
|
||||
bf.registerBeanDefinition("nestedTestBeanX", new RootBeanDefinition(NestedTestBean.class));
|
||||
|
||||
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getNestedTestBean()).isSameAs(bf.getBean("nestedTestBeanX"));
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
bf.removeBeanDefinition("nestedTestBeanX");
|
||||
|
||||
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
@@ -881,6 +987,80 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
.satisfies(methodParameterDeclaredOn(ConstructorWithoutFallbackBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorResourceInjectionWithSometimesNullBeanEarly() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(ConstructorWithNullableArgument.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
RootBeanDefinition tb = new RootBeanDefinition(SometimesNullFactoryMethods.class);
|
||||
tb.setFactoryMethodName("createTestBean");
|
||||
tb.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("testBean", tb);
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
ConstructorWithNullableArgument bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorResourceInjectionWithSometimesNullBeanLate() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(ConstructorWithNullableArgument.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
RootBeanDefinition tb = new RootBeanDefinition(SometimesNullFactoryMethods.class);
|
||||
tb.setFactoryMethodName("createTestBean");
|
||||
tb.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("testBean", tb);
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
ConstructorWithNullableArgument bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = true;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNotNull();
|
||||
|
||||
SometimesNullFactoryMethods.active = false;
|
||||
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
|
||||
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithCollectionAndNullFromFactoryBean() {
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
|
||||
@@ -2441,13 +2621,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
@Autowired(required = false)
|
||||
private TestBean testBean;
|
||||
|
||||
private TestBean testBean2;
|
||||
TestBean testBean2;
|
||||
|
||||
@Autowired
|
||||
public void setTestBean2(TestBean testBean2) {
|
||||
if (this.testBean2 != null) {
|
||||
throw new IllegalStateException("Already called");
|
||||
}
|
||||
Assert.state(this.testBean != null, "Wrong initialization order");
|
||||
Assert.state(this.testBean2 == null, "Already called");
|
||||
this.testBean2 = testBean2;
|
||||
}
|
||||
|
||||
@@ -2482,7 +2661,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
@Required
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setTestBean2(TestBean testBean2) {
|
||||
super.setTestBean2(testBean2);
|
||||
this.testBean2 = testBean2;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@@ -2498,6 +2677,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
|
||||
@Autowired
|
||||
protected void initBeanFactory(BeanFactory beanFactory) {
|
||||
Assert.state(this.baseInjected, "Wrong initialization order");
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@@ -2804,6 +2984,21 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class ConstructorWithNullableArgument {
|
||||
|
||||
protected ITestBean testBean3;
|
||||
|
||||
@Autowired(required = false)
|
||||
public ConstructorWithNullableArgument(@Nullable ITestBean testBean3) {
|
||||
this.testBean3 = testBean3;
|
||||
}
|
||||
|
||||
public ITestBean getTestBean3() {
|
||||
return this.testBean3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ConstructorsCollectionResourceInjectionBean {
|
||||
|
||||
protected ITestBean testBean3;
|
||||
@@ -3906,9 +4101,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
private RT obj;
|
||||
|
||||
protected void setObj(RT obj) {
|
||||
if (this.obj != null) {
|
||||
throw new IllegalStateException("Already called");
|
||||
}
|
||||
Assert.state(this.obj == null, "Already called");
|
||||
this.obj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-12
@@ -4,25 +4,27 @@
|
||||
<beans default-lazy-init="true">
|
||||
|
||||
<bean name="beta" class="org.springframework.beans.factory.FactoryBeanTests$Beta" autowire="byType">
|
||||
<property name="name" value="${myName}"/>
|
||||
<property name="name" value="${myName}"/>
|
||||
</bean>
|
||||
|
||||
<bean id="alpha" class="org.springframework.beans.factory.FactoryBeanTests$Alpha" autowire="byType"/>
|
||||
|
||||
<bean id="gamma" class="org.springframework.beans.factory.FactoryBeanTests$Gamma"/>
|
||||
|
||||
<bean id="betaFactory" class="org.springframework.beans.factory.FactoryBeanTests$BetaFactoryBean" autowire="constructor">
|
||||
<property name="beta" ref="beta"/>
|
||||
</bean>
|
||||
<bean id="betaFactory" class="org.springframework.beans.factory.FactoryBeanTests$BetaFactoryBean" autowire="constructor">
|
||||
<property name="beta" ref="beta"/>
|
||||
</bean>
|
||||
|
||||
<bean id="gammaFactory" factory-bean="betaFactory" factory-method="getGamma"/>
|
||||
<bean id="gammaFactory" factory-bean="${gammaFactory}" factory-method="${gamma}"/>
|
||||
|
||||
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="properties">
|
||||
<props>
|
||||
<prop key="myName">yourName</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="properties">
|
||||
<props>
|
||||
<prop key="myName">yourName</prop>
|
||||
<prop key="gammaFactory">betaFactory</prop>
|
||||
<prop key="gamma">getGamma</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -477,7 +477,7 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.age;
|
||||
return TestBean.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -59,8 +59,8 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
* given internal {@link com.github.benmanes.caffeine.cache.Cache} to use.
|
||||
* @param name the name of the cache
|
||||
* @param cache the backing Caffeine Cache instance
|
||||
* @param allowNullValues whether to accept and convert {@code null}
|
||||
* values for this cache
|
||||
* @param allowNullValues whether to accept and convert {@code null} values
|
||||
* for this cache
|
||||
*/
|
||||
public CaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache<Object, Object> cache,
|
||||
boolean allowNullValues) {
|
||||
@@ -86,7 +86,7 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(Object key, final Callable<T> valueLoader) {
|
||||
public <T> T get(Object key, Callable<T> valueLoader) {
|
||||
return (T) fromStoreValue(this.cache.get(key, new LoadFunction(valueLoader)));
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ValueWrapper putIfAbsent(Object key, @Nullable final Object value) {
|
||||
public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
|
||||
PutIfAbsentFunction callable = new PutIfAbsentFunction(value);
|
||||
Object result = this.cache.get(key, callable);
|
||||
return (callable.called ? null : toValueWrapper(result));
|
||||
@@ -140,7 +140,7 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
@Nullable
|
||||
private final Object value;
|
||||
|
||||
private boolean called;
|
||||
boolean called;
|
||||
|
||||
public PutIfAbsentFunction(@Nullable Object value) {
|
||||
this.value = value;
|
||||
@@ -159,16 +159,17 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
private final Callable<?> valueLoader;
|
||||
|
||||
public LoadFunction(Callable<?> valueLoader) {
|
||||
Assert.notNull(valueLoader, "Callable must not be null");
|
||||
this.valueLoader = valueLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object apply(Object o) {
|
||||
public Object apply(Object key) {
|
||||
try {
|
||||
return toStoreValue(this.valueLoader.call());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new ValueRetrievalException(o, this.valueLoader, ex);
|
||||
throw new ValueRetrievalException(key, this.valueLoader, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+4
-4
@@ -110,7 +110,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* Set the Caffeine to use for building each individual
|
||||
* {@link CaffeineCache} instance.
|
||||
* @see #createNativeCaffeineCache
|
||||
* @see com.github.benmanes.caffeine.cache.Caffeine#build()
|
||||
* @see Caffeine#build()
|
||||
*/
|
||||
public void setCaffeine(Caffeine<Object, Object> caffeine) {
|
||||
Assert.notNull(caffeine, "Caffeine must not be null");
|
||||
@@ -121,7 +121,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* Set the {@link CaffeineSpec} to use for building each individual
|
||||
* {@link CaffeineCache} instance.
|
||||
* @see #createNativeCaffeineCache
|
||||
* @see com.github.benmanes.caffeine.cache.Caffeine#from(CaffeineSpec)
|
||||
* @see Caffeine#from(CaffeineSpec)
|
||||
*/
|
||||
public void setCaffeineSpec(CaffeineSpec caffeineSpec) {
|
||||
doSetCaffeine(Caffeine.from(caffeineSpec));
|
||||
@@ -132,7 +132,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* individual {@link CaffeineCache} instance. The given value needs to
|
||||
* comply with Caffeine's {@link CaffeineSpec} (see its javadoc).
|
||||
* @see #createNativeCaffeineCache
|
||||
* @see com.github.benmanes.caffeine.cache.Caffeine#from(String)
|
||||
* @see Caffeine#from(String)
|
||||
*/
|
||||
public void setCacheSpecification(String cacheSpecification) {
|
||||
doSetCaffeine(Caffeine.from(cacheSpecification));
|
||||
@@ -149,7 +149,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* Set the Caffeine CacheLoader to use for building each individual
|
||||
* {@link CaffeineCache} instance, turning it into a LoadingCache.
|
||||
* @see #createNativeCaffeineCache
|
||||
* @see com.github.benmanes.caffeine.cache.Caffeine#build(CacheLoader)
|
||||
* @see Caffeine#build(CacheLoader)
|
||||
* @see com.github.benmanes.caffeine.cache.LoadingCache
|
||||
*/
|
||||
public void setCacheLoader(CacheLoader<Object, Object> cacheLoader) {
|
||||
|
||||
+18
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,19 +38,17 @@ public class CaffeineCacheManagerTests {
|
||||
@Test
|
||||
public void testDynamicMode() {
|
||||
CacheManager cm = new CaffeineCacheManager();
|
||||
|
||||
Cache cache1 = cm.getCache("c1");
|
||||
boolean condition2 = cache1 instanceof CaffeineCache;
|
||||
assertThat(condition2).isTrue();
|
||||
assertThat(cache1).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache1again = cm.getCache("c1");
|
||||
assertThat(cache1).isSameAs(cache1again);
|
||||
Cache cache2 = cm.getCache("c2");
|
||||
boolean condition1 = cache2 instanceof CaffeineCache;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(cache2).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache2again = cm.getCache("c2");
|
||||
assertThat(cache2).isSameAs(cache2again);
|
||||
Cache cache3 = cm.getCache("c3");
|
||||
boolean condition = cache3 instanceof CaffeineCache;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(cache3).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache3again = cm.getCache("c3");
|
||||
assertThat(cache3).isSameAs(cache3again);
|
||||
|
||||
@@ -62,19 +60,23 @@ public class CaffeineCacheManagerTests {
|
||||
assertThat(cache1.get("key3").get()).isNull();
|
||||
cache1.evict("key3");
|
||||
assertThat(cache1.get("key3")).isNull();
|
||||
assertThat(cache1.get("key3", () -> "value3")).isEqualTo("value3");
|
||||
assertThat(cache1.get("key3", () -> "value3")).isEqualTo("value3");
|
||||
cache1.evict("key3");
|
||||
assertThat(cache1.get("key3", () -> (String) null)).isNull();
|
||||
assertThat(cache1.get("key3", () -> (String) null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticMode() {
|
||||
CaffeineCacheManager cm = new CaffeineCacheManager("c1", "c2");
|
||||
|
||||
Cache cache1 = cm.getCache("c1");
|
||||
boolean condition3 = cache1 instanceof CaffeineCache;
|
||||
assertThat(condition3).isTrue();
|
||||
assertThat(cache1).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache1again = cm.getCache("c1");
|
||||
assertThat(cache1).isSameAs(cache1again);
|
||||
Cache cache2 = cm.getCache("c2");
|
||||
boolean condition2 = cache2 instanceof CaffeineCache;
|
||||
assertThat(condition2).isTrue();
|
||||
assertThat(cache2).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache2again = cm.getCache("c2");
|
||||
assertThat(cache2).isSameAs(cache2again);
|
||||
Cache cache3 = cm.getCache("c3");
|
||||
@@ -91,13 +93,11 @@ public class CaffeineCacheManagerTests {
|
||||
|
||||
cm.setAllowNullValues(false);
|
||||
Cache cache1x = cm.getCache("c1");
|
||||
boolean condition1 = cache1x instanceof CaffeineCache;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(cache1x != cache1).isTrue();
|
||||
assertThat(cache1x).isInstanceOf(CaffeineCache.class);
|
||||
assertThat(cache1x).isNotSameAs(cache1);
|
||||
Cache cache2x = cm.getCache("c2");
|
||||
boolean condition = cache2x instanceof CaffeineCache;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(cache2x != cache2).isTrue();
|
||||
assertThat(cache2x).isInstanceOf(CaffeineCache.class);
|
||||
assertThat(cache2x).isNotSameAs(cache2);
|
||||
Cache cache3x = cm.getCache("c3");
|
||||
assertThat(cache3x).isNull();
|
||||
|
||||
@@ -190,7 +190,7 @@ public class CaffeineCacheManagerTests {
|
||||
assertThat(value.get()).isEqualTo("pong");
|
||||
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> assertThat(cache1.get("foo")).isNull())
|
||||
.withMessageContaining("I only know ping");
|
||||
.withMessageContaining("I only know ping");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+13
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -114,7 +114,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -126,14 +126,14 @@ class QuartzSupportTests {
|
||||
bean.start();
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
|
||||
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
|
||||
assertThat(taskExecutor.count).isEqualTo(DummyJob.count);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
void jobDetailWithRunnableInsteadOfJob() {
|
||||
JobDetailImpl jobDetail = new JobDetailImpl();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
@@ -156,7 +156,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -168,7 +168,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJobBean.param).isEqualTo(10);
|
||||
assertThat(DummyJobBean.count > 0).isTrue();
|
||||
assertThat(DummyJobBean.count).isGreaterThan(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
@@ -190,7 +190,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -203,7 +203,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJob.param).isEqualTo(10);
|
||||
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
|
||||
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
@@ -225,7 +225,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -239,7 +239,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJob.param).isEqualTo(0);
|
||||
assertThat(DummyJob.count == 0).isTrue();
|
||||
assertThat(DummyJob.count).isEqualTo(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
@@ -260,7 +260,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -273,7 +273,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJobBean.param).isEqualTo(10);
|
||||
assertThat(DummyJobBean.count > 0).isTrue();
|
||||
assertThat(DummyJobBean.count).isGreaterThan(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
@@ -292,7 +292,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJob.param).isEqualTo(10);
|
||||
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
|
||||
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,14 +23,20 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* Interface that defines common cache operations.
|
||||
*
|
||||
* <b>Note:</b> Due to the generic use of caching, it is recommended that
|
||||
* implementations allow storage of {@code null} values (for example to
|
||||
* cache methods that return {@code null}).
|
||||
* <p>Serves as an SPI for Spring's annotation-based caching model
|
||||
* ({@link org.springframework.cache.annotation.Cacheable} and co)
|
||||
* as well as an API for direct usage in applications.
|
||||
*
|
||||
* <p><b>Note:</b> Due to the generic use of caching, it is recommended
|
||||
* that implementations allow storage of {@code null} values
|
||||
* (for example to cache methods that return {@code null}).
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
* @see CacheManager
|
||||
* @see org.springframework.cache.annotation.Cacheable
|
||||
*/
|
||||
public interface Cache {
|
||||
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -175,9 +175,9 @@ public @interface Cacheable {
|
||||
* <li>Only one cache may be specified</li>
|
||||
* <li>No other cache-related operation can be combined</li>
|
||||
* </ol>
|
||||
* This is effectively a hint and the actual cache provider that you are
|
||||
* using may not support it in a synchronized fashion. Check your provider
|
||||
* documentation for more details on the actual semantics.
|
||||
* This is effectively a hint and the chosen cache provider might not actually
|
||||
* support it in a synchronized fashion. Check your provider documentation for
|
||||
* more details on the actual semantics.
|
||||
* @since 4.3
|
||||
* @see org.springframework.cache.Cache#get(Object, Callable)
|
||||
*/
|
||||
|
||||
Vendored
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -82,12 +82,12 @@ public abstract class AbstractCacheInvoker {
|
||||
* Execute {@link Cache#put(Object, Object)} on the specified {@link Cache}
|
||||
* and invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doPut(Cache cache, Object key, @Nullable Object result) {
|
||||
protected void doPut(Cache cache, Object key, @Nullable Object value) {
|
||||
try {
|
||||
cache.put(key, result);
|
||||
cache.put(key, value);
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
getErrorHandler().handleCachePutError(ex, cache, key, result);
|
||||
getErrorHandler().handleCachePutError(ex, cache, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-30
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -214,7 +214,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
if (getCacheResolver() == null) {
|
||||
// Lazily initialize cache resolver via default cache manager...
|
||||
// Lazily initialize cache resolver via default cache manager
|
||||
Assert.state(this.beanFactory != null, "CacheResolver or BeanFactory must be set on cache aspect");
|
||||
try {
|
||||
setCacheManager(this.beanFactory.getBean(CacheManager.class));
|
||||
@@ -307,22 +307,22 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a bean with the specified name and type. Used to resolve services that
|
||||
* are referenced by name in a {@link CacheOperation}.
|
||||
* @param beanName the name of the bean, as defined by the operation
|
||||
* @param expectedType type for the bean
|
||||
* @return the bean matching that name
|
||||
* Retrieve a bean with the specified name and type.
|
||||
* Used to resolve services that are referenced by name in a {@link CacheOperation}.
|
||||
* @param name the name of the bean, as defined by the cache operation
|
||||
* @param serviceType the type expected by the operation's service reference
|
||||
* @return the bean matching the expected type, qualified by the given name
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException if such bean does not exist
|
||||
* @see CacheOperation#getKeyGenerator()
|
||||
* @see CacheOperation#getCacheManager()
|
||||
* @see CacheOperation#getCacheResolver()
|
||||
*/
|
||||
protected <T> T getBean(String beanName, Class<T> expectedType) {
|
||||
protected <T> T getBean(String name, Class<T> serviceType) {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException(
|
||||
"BeanFactory must be set on cache aspect for " + expectedType.getSimpleName() + " retrieval");
|
||||
"BeanFactory must be set on cache aspect for " + serviceType.getSimpleName() + " retrieval");
|
||||
}
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, expectedType, beanName);
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, serviceType, name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,21 +388,20 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No caching required, only call the underlying method
|
||||
// No caching required, just call the underlying method
|
||||
return invokeOperation(invoker);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process any early evictions
|
||||
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
|
||||
CacheOperationExpressionEvaluator.NO_RESULT);
|
||||
|
||||
// Check if we have a cached item matching the conditions
|
||||
// Check if we have a cached value matching the conditions
|
||||
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
|
||||
|
||||
// Collect puts from any @Cacheable miss, if no cached item is found
|
||||
List<CachePutRequest> cachePutRequests = new ArrayList<>();
|
||||
// Collect puts from any @Cacheable miss, if no cached value is found
|
||||
List<CachePutRequest> cachePutRequests = new ArrayList<>(1);
|
||||
if (cacheHit == null) {
|
||||
collectPutRequests(contexts.get(CacheableOperation.class),
|
||||
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
|
||||
@@ -469,7 +468,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
private boolean hasCachePut(CacheOperationContexts contexts) {
|
||||
// Evaluate the conditions *without* the result object because we don't have it yet...
|
||||
Collection<CacheOperationContext> cachePutContexts = contexts.get(CachePutOperation.class);
|
||||
Collection<CacheOperationContext> excluded = new ArrayList<>();
|
||||
Collection<CacheOperationContext> excluded = new ArrayList<>(1);
|
||||
for (CacheOperationContext context : cachePutContexts) {
|
||||
try {
|
||||
if (!context.isConditionPassing(CacheOperationExpressionEvaluator.RESULT_UNAVAILABLE)) {
|
||||
@@ -522,9 +521,9 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a cached item only for {@link CacheableOperation} that passes the condition.
|
||||
* Find a cached value only for {@link CacheableOperation} that passes the condition.
|
||||
* @param contexts the cacheable operations
|
||||
* @return a {@link Cache.ValueWrapper} holding the cached item,
|
||||
* @return a {@link Cache.ValueWrapper} holding the cached value,
|
||||
* or {@code null} if none is found
|
||||
*/
|
||||
@Nullable
|
||||
@@ -549,9 +548,9 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
|
||||
/**
|
||||
* Collect the {@link CachePutRequest} for all {@link CacheOperation} using
|
||||
* the specified result item.
|
||||
* the specified result value.
|
||||
* @param contexts the contexts to handle
|
||||
* @param result the result item (never {@code null})
|
||||
* @param result the result value (never {@code null})
|
||||
* @param putRequests the collection to update
|
||||
*/
|
||||
private void collectPutRequests(Collection<CacheOperationContext> contexts,
|
||||
@@ -641,21 +640,22 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
if (syncEnabled) {
|
||||
if (this.contexts.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"@Cacheable(sync=true) cannot be combined with other cache operations on '" + method + "'");
|
||||
"A sync=true operation cannot be combined with other cache operations on '" + method + "'");
|
||||
}
|
||||
if (cacheOperationContexts.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"Only one @Cacheable(sync=true) entry is allowed on '" + method + "'");
|
||||
"Only one sync=true operation is allowed on '" + method + "'");
|
||||
}
|
||||
CacheOperationContext cacheOperationContext = cacheOperationContexts.iterator().next();
|
||||
CacheableOperation operation = (CacheableOperation) cacheOperationContext.getOperation();
|
||||
CacheOperation operation = cacheOperationContext.getOperation();
|
||||
if (cacheOperationContext.getCaches().size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"@Cacheable(sync=true) only allows a single cache on '" + operation + "'");
|
||||
"A sync=true operation is restricted to a single cache on '" + operation + "'");
|
||||
}
|
||||
if (StringUtils.hasText(operation.getUnless())) {
|
||||
if (operation instanceof CacheableOperation &&
|
||||
StringUtils.hasText(((CacheableOperation) operation).getUnless())) {
|
||||
throw new IllegalStateException(
|
||||
"@Cacheable(sync=true) does not support unless attribute on '" + operation + "'");
|
||||
"A sync=true operation does not support the unless attribute on '" + operation + "'");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -722,7 +722,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
this.args = extractArgs(metadata.method, args);
|
||||
this.target = target;
|
||||
this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
|
||||
this.cacheNames = createCacheNames(this.caches);
|
||||
this.cacheNames = prepareCacheNames(this.caches);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -810,8 +810,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
return this.cacheNames;
|
||||
}
|
||||
|
||||
private Collection<String> createCacheNames(Collection<? extends Cache> caches) {
|
||||
Collection<String> names = new ArrayList<>();
|
||||
private Collection<String> prepareCacheNames(Collection<? extends Cache> caches) {
|
||||
Collection<String> names = new ArrayList<>(caches.size());
|
||||
for (Cache cache : caches) {
|
||||
names.add(cache.getName());
|
||||
}
|
||||
@@ -885,13 +885,13 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal holder class for recording that a cache method was invoked.
|
||||
*/
|
||||
private static class InvocationAwareResult {
|
||||
|
||||
boolean invoked;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,14 +24,16 @@ import org.springframework.cache.Cache;
|
||||
/**
|
||||
* Simple cache manager working against a given collection of caches.
|
||||
* Useful for testing or simple caching declarations.
|
||||
* <p>
|
||||
* When using this implementation directly, i.e. not via a regular
|
||||
*
|
||||
* <p>When using this implementation directly, i.e. not via a regular
|
||||
* bean registration, {@link #initializeCaches()} should be invoked
|
||||
* to initialize its internal state once the
|
||||
* {@linkplain #setCaches(Collection) caches have been provided}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
* @see NoOpCache
|
||||
* @see org.springframework.cache.concurrent.ConcurrentMapCache
|
||||
*/
|
||||
public class SimpleCacheManager extends AbstractCacheManager {
|
||||
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -65,7 +65,7 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
|
||||
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
|
||||
*/
|
||||
public AnnotationConfigApplicationContext() {
|
||||
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
|
||||
StartupStep createAnnotatedBeanDefReader = getApplicationStartup().start("spring.context.annotated-bean-reader.create");
|
||||
this.reader = new AnnotatedBeanDefinitionReader(this);
|
||||
createAnnotatedBeanDefReader.end();
|
||||
this.scanner = new ClassPathBeanDefinitionScanner(this);
|
||||
@@ -163,7 +163,7 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
|
||||
@Override
|
||||
public void register(Class<?>... componentClasses) {
|
||||
Assert.notEmpty(componentClasses, "At least one component class must be specified");
|
||||
StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
|
||||
StartupStep registerComponentClass = getApplicationStartup().start("spring.context.component-classes.register")
|
||||
.tag("classes", () -> Arrays.toString(componentClasses));
|
||||
this.reader.register(componentClasses);
|
||||
registerComponentClass.end();
|
||||
@@ -180,7 +180,7 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
|
||||
@Override
|
||||
public void scan(String... basePackages) {
|
||||
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
||||
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
|
||||
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan")
|
||||
.tag("packages", () -> Arrays.toString(basePackages));
|
||||
this.scanner.scan(basePackages);
|
||||
scanPackages.end();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,6 +39,7 @@ final class BeanMethod extends ConfigurationMethod {
|
||||
super(metadata, configurationClass);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void validate(ProblemReporter problemReporter) {
|
||||
if (getMetadata().isStatic()) {
|
||||
@@ -55,9 +56,9 @@ final class BeanMethod extends ConfigurationMethod {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return ((this == obj) || ((obj instanceof BeanMethod) &&
|
||||
this.metadata.equals(((BeanMethod) obj).metadata)));
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof BeanMethod &&
|
||||
this.metadata.equals(((BeanMethod) other).metadata)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,6 +71,7 @@ final class BeanMethod extends ConfigurationMethod {
|
||||
return "BeanMethod: " + this.metadata;
|
||||
}
|
||||
|
||||
|
||||
private class NonOverridableMethodError extends Problem {
|
||||
|
||||
NonOverridableMethodError() {
|
||||
|
||||
+9
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -329,8 +329,8 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
||||
* @return {@code true} if the bean can be registered as-is;
|
||||
* {@code false} if it should be skipped because there is an
|
||||
* existing, compatible bean definition for the specified name
|
||||
* @throws ConflictingBeanDefinitionException if an existing, incompatible
|
||||
* bean definition has been found for the specified name
|
||||
* @throws IllegalStateException if an existing, incompatible bean definition
|
||||
* has been found for the specified name
|
||||
*/
|
||||
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
|
||||
if (!this.registry.containsBeanDefinition(beanName)) {
|
||||
@@ -354,16 +354,16 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
||||
* the given existing bean definition.
|
||||
* <p>The default implementation considers them as compatible when the existing
|
||||
* bean definition comes from the same source or from a non-scanning source.
|
||||
* @param newDefinition the new bean definition, originated from scanning
|
||||
* @param existingDefinition the existing bean definition, potentially an
|
||||
* @param newDef the new bean definition, originated from scanning
|
||||
* @param existingDef the existing bean definition, potentially an
|
||||
* explicitly defined one or a previously generated one from scanning
|
||||
* @return whether the definitions are considered as compatible, with the
|
||||
* new definition to be skipped in favor of the existing definition
|
||||
*/
|
||||
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
|
||||
return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
|
||||
(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) || // scanned same file twice
|
||||
newDefinition.equals(existingDefinition)); // scanned equivalent class twice
|
||||
protected boolean isCompatible(BeanDefinition newDef, BeanDefinition existingDef) {
|
||||
return (!(existingDef instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
|
||||
(newDef.getSource() != null && newDef.getSource().equals(existingDef.getSource())) || // scanned same file twice
|
||||
newDef.equals(existingDef)); // scanned equivalent class twice
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -104,8 +104,8 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>{@code @Configuration} classes may not only be bootstrapped using
|
||||
* component scanning, but may also themselves <em>configure</em> component scanning using
|
||||
* <p>{@code @Configuration} classes may not only be bootstrapped using component
|
||||
* scanning, but may also themselves <em>configure</em> component scanning using
|
||||
* the {@link ComponentScan @ComponentScan} annotation:
|
||||
*
|
||||
* <pre class="code">
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -424,6 +424,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
|
||||
public ConfigurationClassBeanDefinition(RootBeanDefinition original,
|
||||
ConfigurationClass configClass, MethodMetadata beanMethodMetadata, String derivedBeanName) {
|
||||
|
||||
super(original);
|
||||
this.annotationMetadata = configClass.getMetadata();
|
||||
this.factoryMethodMetadata = beanMethodMetadata;
|
||||
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -409,11 +409,14 @@ class ConfigurationClassParser {
|
||||
this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
|
||||
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
|
||||
if (asmMethods.size() >= beanMethods.size()) {
|
||||
Set<MethodMetadata> candidateMethods = new LinkedHashSet<>(beanMethods);
|
||||
Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
|
||||
for (MethodMetadata asmMethod : asmMethods) {
|
||||
for (MethodMetadata beanMethod : beanMethods) {
|
||||
for (Iterator<MethodMetadata> it = candidateMethods.iterator(); it.hasNext();) {
|
||||
MethodMetadata beanMethod = it.next();
|
||||
if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
|
||||
selectedMethods.add(beanMethod);
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -79,10 +79,11 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
||||
* to invoke each listener with.
|
||||
* <p>Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor},
|
||||
* executing all listeners synchronously in the calling thread.
|
||||
* <p>Consider specifying an asynchronous task executor here to not block the
|
||||
* caller until all listeners have been executed. However, note that asynchronous
|
||||
* execution will not participate in the caller's thread context (class loader,
|
||||
* transaction association) unless the TaskExecutor explicitly supports this.
|
||||
* <p>Consider specifying an asynchronous task executor here to not block the caller
|
||||
* until all listeners have been executed. However, note that asynchronous execution
|
||||
* will not participate in the caller's thread context (class loader, transaction context)
|
||||
* unless the TaskExecutor explicitly supports this.
|
||||
* @since 2.0
|
||||
* @see org.springframework.core.task.SyncTaskExecutor
|
||||
* @see org.springframework.core.task.SimpleAsyncTaskExecutor
|
||||
*/
|
||||
@@ -92,6 +93,7 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
||||
|
||||
/**
|
||||
* Return the current task executor for this multicaster.
|
||||
* @since 2.0
|
||||
*/
|
||||
@Nullable
|
||||
protected Executor getTaskExecutor() {
|
||||
|
||||
+25
-14
@@ -137,31 +137,39 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
implements ConfigurableApplicationContext {
|
||||
|
||||
/**
|
||||
* Name of the MessageSource bean in the factory.
|
||||
* If none is supplied, message resolution is delegated to the parent.
|
||||
* @see MessageSource
|
||||
*/
|
||||
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
|
||||
|
||||
/**
|
||||
* Name of the LifecycleProcessor bean in the factory.
|
||||
* If none is supplied, a DefaultLifecycleProcessor is used.
|
||||
* The name of the {@link LifecycleProcessor} bean in the context.
|
||||
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
|
||||
* @since 3.0
|
||||
* @see org.springframework.context.LifecycleProcessor
|
||||
* @see org.springframework.context.support.DefaultLifecycleProcessor
|
||||
* @see #start()
|
||||
* @see #stop()
|
||||
*/
|
||||
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
|
||||
|
||||
/**
|
||||
* Name of the ApplicationEventMulticaster bean in the factory.
|
||||
* If none is supplied, a default SimpleApplicationEventMulticaster is used.
|
||||
* The name of the {@link MessageSource} bean in the context.
|
||||
* If none is supplied, message resolution is delegated to the parent.
|
||||
* @see org.springframework.context.MessageSource
|
||||
* @see org.springframework.context.support.ResourceBundleMessageSource
|
||||
* @see org.springframework.context.support.ReloadableResourceBundleMessageSource
|
||||
* @see #getMessage(MessageSourceResolvable, Locale)
|
||||
*/
|
||||
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
|
||||
|
||||
/**
|
||||
* The name of the {@link ApplicationEventMulticaster} bean in the context.
|
||||
* If none is supplied, a {@link SimpleApplicationEventMulticaster} is used.
|
||||
* @see org.springframework.context.event.ApplicationEventMulticaster
|
||||
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
|
||||
* @see #publishEvent(ApplicationEvent)
|
||||
* @see #addApplicationListener(ApplicationListener)
|
||||
*/
|
||||
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
|
||||
|
||||
/**
|
||||
* Boolean flag controlled by a {@code spring.spel.ignore} system property that instructs Spring to
|
||||
* ignore SpEL, i.e. to not initialize the SpEL infrastructure.
|
||||
* Boolean flag controlled by a {@code spring.spel.ignore} system property that
|
||||
* instructs Spring to ignore SpEL, i.e. to not initialize the SpEL infrastructure.
|
||||
* <p>The default is "false".
|
||||
*/
|
||||
private static final boolean shouldIgnoreSpel = SpringProperties.getFlag("spring.spel.ignore");
|
||||
@@ -211,7 +219,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
private Thread shutdownHook;
|
||||
|
||||
/** ResourcePatternResolver used by this context. */
|
||||
private ResourcePatternResolver resourcePatternResolver;
|
||||
private final ResourcePatternResolver resourcePatternResolver;
|
||||
|
||||
/** LifecycleProcessor for managing the lifecycle of beans within this context. */
|
||||
@Nullable
|
||||
@@ -1083,6 +1091,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
// Let subclasses do some final clean-up if they wish...
|
||||
onClose();
|
||||
|
||||
// Reset common introspection caches to avoid class reference leaks.
|
||||
resetCommonCaches();
|
||||
|
||||
// Reset local application listeners to pre-refresh state.
|
||||
if (this.earlyApplicationListeners != null) {
|
||||
this.applicationListeners.clear();
|
||||
|
||||
+8
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionDocumentReader;
|
||||
import org.springframework.beans.factory.xml.ResourceEntityResolver;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -84,7 +85,7 @@ public abstract class AbstractXmlApplicationContext extends AbstractRefreshableC
|
||||
|
||||
// Configure the bean definition reader with this context's
|
||||
// resource loading environment.
|
||||
beanDefinitionReader.setEnvironment(this.getEnvironment());
|
||||
beanDefinitionReader.setEnvironment(getEnvironment());
|
||||
beanDefinitionReader.setResourceLoader(this);
|
||||
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
|
||||
|
||||
@@ -95,12 +96,13 @@ public abstract class AbstractXmlApplicationContext extends AbstractRefreshableC
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the bean definition reader used for loading the bean
|
||||
* definitions of this context. Default implementation is empty.
|
||||
* Initialize the bean definition reader used for loading the bean definitions
|
||||
* of this context. The default implementation sets the validating flag.
|
||||
* <p>Can be overridden in subclasses, e.g. for turning off XML validation
|
||||
* or using a different XmlBeanDefinitionParser implementation.
|
||||
* or using a different {@link BeanDefinitionDocumentReader} implementation.
|
||||
* @param reader the bean definition reader used by this context
|
||||
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
|
||||
* @see XmlBeanDefinitionReader#setValidating
|
||||
* @see XmlBeanDefinitionReader#setDocumentReaderClass
|
||||
*/
|
||||
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
|
||||
reader.setValidating(this.validating);
|
||||
|
||||
+2
-1
@@ -55,7 +55,7 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
// override classes that have not been loaded yet. If not accessible, we will
|
||||
// always override requested classes, even when the classes have been loaded
|
||||
// by the parent ClassLoader already and cannot be transformed anymore anyway.
|
||||
Method method = null;
|
||||
Method method;
|
||||
try {
|
||||
method = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
@@ -63,6 +63,7 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
catch (Throwable ex) {
|
||||
// Typically a JDK 9+ InaccessibleObjectException...
|
||||
// Avoid through JVM startup with --add-opens=java.base/java.lang=ALL-UNNAMED
|
||||
method = null;
|
||||
LogFactory.getLog(ContextTypeMatchClassLoader.class).debug(
|
||||
"ClassLoader.findLoadedClass not accessible -> will always override requested class", ex);
|
||||
}
|
||||
|
||||
+8
-2
@@ -28,8 +28,8 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
|
||||
/**
|
||||
* Annotation that marks a method to be scheduled. Exactly one of the
|
||||
* {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes must be
|
||||
* specified.
|
||||
* {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes
|
||||
* must be specified.
|
||||
*
|
||||
* <p>The annotated method must expect no arguments. It will typically have
|
||||
* a {@code void} return type; if not, the returned value will be ignored
|
||||
@@ -40,6 +40,12 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
* done manually or, more conveniently, through the {@code <task:annotation-driven/>}
|
||||
* XML element or {@link EnableScheduling @EnableScheduling} annotation.
|
||||
*
|
||||
* <p>This annotation can be used as a <em>{@linkplain Repeatable repeatable}</em>
|
||||
* annotation. If several scheduled declarations are found on the same method,
|
||||
* each of them will be processed independently, with a separate trigger firing
|
||||
* for each of them. As a consequence, such co-located schedules may overlap
|
||||
* and execute multiple times in parallel or in immediate succession.
|
||||
*
|
||||
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
|
||||
* <em>composed annotations</em> with attribute overrides.
|
||||
*
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -160,7 +160,7 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
* @since 5.1
|
||||
*/
|
||||
public ScheduledAnnotationBeanPostProcessor(ScheduledTaskRegistrar registrar) {
|
||||
Assert.notNull(registrar, "ScheduledTaskRegistrar is required");
|
||||
Assert.notNull(registrar, "ScheduledTaskRegistrar must not be null");
|
||||
this.registrar = registrar;
|
||||
}
|
||||
|
||||
@@ -580,7 +580,7 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
}
|
||||
if (tasks != null) {
|
||||
for (ScheduledTask task : tasks) {
|
||||
task.cancel();
|
||||
task.cancel(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -598,7 +598,7 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
Collection<Set<ScheduledTask>> allTasks = this.scheduledTasks.values();
|
||||
for (Set<ScheduledTask> tasks : allTasks) {
|
||||
for (ScheduledTask task : tasks) {
|
||||
task.cancel();
|
||||
task.cancel(false);
|
||||
}
|
||||
}
|
||||
this.scheduledTasks.clear();
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,10 +40,10 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
public interface SchedulingConfigurer {
|
||||
|
||||
/**
|
||||
* Callback allowing a {@link org.springframework.scheduling.TaskScheduler
|
||||
* TaskScheduler} and specific {@link org.springframework.scheduling.config.Task Task}
|
||||
* instances to be registered against the given the {@link ScheduledTaskRegistrar}.
|
||||
* @param taskRegistrar the registrar to be configured.
|
||||
* Callback allowing a {@link org.springframework.scheduling.TaskScheduler}
|
||||
* and specific {@link org.springframework.scheduling.config.Task} instances
|
||||
* to be registered against the given the {@link ScheduledTaskRegistrar}.
|
||||
* @param taskRegistrar the registrar to be configured
|
||||
*/
|
||||
void configureTasks(ScheduledTaskRegistrar taskRegistrar);
|
||||
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -41,8 +41,8 @@ import org.springframework.util.CollectionUtils;
|
||||
* Helper bean for registering tasks with a {@link TaskScheduler}, typically using cron
|
||||
* expressions.
|
||||
*
|
||||
* <p>As of Spring 3.1, {@code ScheduledTaskRegistrar} has a more prominent user-facing
|
||||
* role when used in conjunction with the {@link
|
||||
* <p>{@code ScheduledTaskRegistrar} has a more prominent user-facing role when used in
|
||||
* conjunction with the {@link
|
||||
* org.springframework.scheduling.annotation.EnableAsync @EnableAsync} annotation and its
|
||||
* {@link org.springframework.scheduling.annotation.SchedulingConfigurer
|
||||
* SchedulingConfigurer} callback interface.
|
||||
@@ -552,7 +552,7 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (ScheduledTask task : this.scheduledTasks) {
|
||||
task.cancel();
|
||||
task.cancel(false);
|
||||
}
|
||||
if (this.localExecutor != null) {
|
||||
this.localExecutor.shutdownNow();
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -102,8 +102,8 @@ public abstract class AbstractBindingResult extends AbstractErrors implements Bi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs,
|
||||
@Nullable String defaultMessage) {
|
||||
public void rejectValue(@Nullable String field, String errorCode,
|
||||
@Nullable Object[] errorArgs, @Nullable String defaultMessage) {
|
||||
|
||||
if (!StringUtils.hasLength(getNestedPath()) && !StringUtils.hasLength(field)) {
|
||||
// We're at the top of the nested object hierarchy,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,13 +28,14 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the {@link Errors} interface. Provides common
|
||||
* access to evaluated errors; however, does not define concrete management
|
||||
* Abstract implementation of the {@link Errors} interface.
|
||||
* Provides nested path handling but does not define concrete management
|
||||
* of {@link ObjectError ObjectErrors} and {@link FieldError FieldErrors}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 2.5.3
|
||||
* @see AbstractBindingResult
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class AbstractErrors implements Errors, Serializable {
|
||||
@@ -81,8 +82,8 @@ public abstract class AbstractErrors implements Errors, Serializable {
|
||||
nestedPath = "";
|
||||
}
|
||||
nestedPath = canonicalFieldName(nestedPath);
|
||||
if (nestedPath.length() > 0 && !nestedPath.endsWith(Errors.NESTED_PATH_SEPARATOR)) {
|
||||
nestedPath += Errors.NESTED_PATH_SEPARATOR;
|
||||
if (nestedPath.length() > 0 && !nestedPath.endsWith(NESTED_PATH_SEPARATOR)) {
|
||||
nestedPath += NESTED_PATH_SEPARATOR;
|
||||
}
|
||||
this.nestedPath = nestedPath;
|
||||
}
|
||||
@@ -97,7 +98,7 @@ public abstract class AbstractErrors implements Errors, Serializable {
|
||||
}
|
||||
else {
|
||||
String path = getNestedPath();
|
||||
return (path.endsWith(Errors.NESTED_PATH_SEPARATOR) ?
|
||||
return (path.endsWith(NESTED_PATH_SEPARATOR) ?
|
||||
path.substring(0, path.length() - NESTED_PATH_SEPARATOR.length()) : path);
|
||||
}
|
||||
}
|
||||
@@ -201,9 +202,9 @@ public abstract class AbstractErrors implements Errors, Serializable {
|
||||
List<FieldError> fieldErrors = getFieldErrors();
|
||||
List<FieldError> result = new ArrayList<>();
|
||||
String fixedField = fixedField(field);
|
||||
for (FieldError error : fieldErrors) {
|
||||
if (isMatchingFieldError(fixedField, error)) {
|
||||
result.add(error);
|
||||
for (FieldError fieldError : fieldErrors) {
|
||||
if (isMatchingFieldError(fixedField, fieldError)) {
|
||||
result.add(fieldError);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(result);
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -55,7 +55,7 @@ public class BeanPropertyBindingResult extends AbstractPropertyBindingResult imp
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of the {@link BeanPropertyBindingResult} class.
|
||||
* Create a new {@code BeanPropertyBindingResult} for the given target.
|
||||
* @param target the target bean to bind onto
|
||||
* @param objectName the name of the target object
|
||||
*/
|
||||
@@ -64,7 +64,7 @@ public class BeanPropertyBindingResult extends AbstractPropertyBindingResult imp
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the {@link BeanPropertyBindingResult} class.
|
||||
* Create a new {@code BeanPropertyBindingResult} for the given target.
|
||||
* @param target the target bean to bind onto
|
||||
* @param objectName the name of the target object
|
||||
* @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -128,7 +128,9 @@ public class BindException extends Exception implements BindingResult {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage) {
|
||||
public void rejectValue(@Nullable String field, String errorCode,
|
||||
@Nullable Object[] errorArgs, @Nullable String defaultMessage) {
|
||||
|
||||
this.bindingResult.rejectValue(field, errorCode, errorArgs, defaultMessage);
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,7 +46,7 @@ public class DirectFieldBindingResult extends AbstractPropertyBindingResult {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DirectFieldBindingResult instance.
|
||||
* Create a new {@code DirectFieldBindingResult} for the given target.
|
||||
* @param target the target object to bind onto
|
||||
* @param objectName the name of the target object
|
||||
*/
|
||||
@@ -55,7 +55,7 @@ public class DirectFieldBindingResult extends AbstractPropertyBindingResult {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DirectFieldBindingResult instance.
|
||||
* Create a new {@code DirectFieldBindingResult} for the given target.
|
||||
* @param target the target object to bind onto
|
||||
* @param objectName the name of the target object
|
||||
* @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,24 +22,24 @@ import org.springframework.beans.PropertyAccessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Stores and exposes information about data-binding and validation
|
||||
* errors for a specific object.
|
||||
* Stores and exposes information about data-binding and validation errors
|
||||
* for a specific object.
|
||||
*
|
||||
* <p>Field names can be properties of the target object (e.g. "name"
|
||||
* when binding to a customer object), or nested fields in case of
|
||||
* subobjects (e.g. "address.street"). Supports subtree navigation
|
||||
* via {@link #setNestedPath(String)}: for example, an
|
||||
* {@code AddressValidator} validates "address", not being aware
|
||||
* that this is a subobject of customer.
|
||||
* <p>Field names are typically properties of the target object (e.g. "name"
|
||||
* when binding to a customer object). Implementations may also support nested
|
||||
* fields in case of nested objects (e.g. "address.street"), in conjunction
|
||||
* with subtree navigation via {@link #setNestedPath}: for example, an
|
||||
* {@code AddressValidator} may validate "address", not being aware that this
|
||||
* is a nested object of a top-level customer object.
|
||||
*
|
||||
* <p>Note: {@code Errors} objects are single-threaded.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see #setNestedPath
|
||||
* @see BindException
|
||||
* @see DataBinder
|
||||
* @see Validator
|
||||
* @see ValidationUtils
|
||||
* @see BindException
|
||||
* @see BindingResult
|
||||
*/
|
||||
public interface Errors {
|
||||
|
||||
@@ -66,6 +66,7 @@ public interface Errors {
|
||||
* @param nestedPath nested path within this object,
|
||||
* e.g. "address" (defaults to "", {@code null} is also acceptable).
|
||||
* Can end with a dot: both "address" and "address." are valid.
|
||||
* @see #getNestedPath()
|
||||
*/
|
||||
void setNestedPath(String nestedPath);
|
||||
|
||||
@@ -73,6 +74,7 @@ public interface Errors {
|
||||
* Return the current nested path of this {@link Errors} object.
|
||||
* <p>Returns a nested path with a dot, i.e. "address.", for easy
|
||||
* building of concatenated paths. Default is an empty String.
|
||||
* @see #setNestedPath(String)
|
||||
*/
|
||||
String getNestedPath();
|
||||
|
||||
@@ -86,14 +88,14 @@ public interface Errors {
|
||||
* <p>For example: current path "spouse.", pushNestedPath("child") →
|
||||
* result path "spouse.child."; popNestedPath() → "spouse." again.
|
||||
* @param subPath the sub path to push onto the nested path stack
|
||||
* @see #popNestedPath
|
||||
* @see #popNestedPath()
|
||||
*/
|
||||
void pushNestedPath(String subPath);
|
||||
|
||||
/**
|
||||
* Pop the former nested path from the nested path stack.
|
||||
* @throws IllegalStateException if there is no former nested path on the stack
|
||||
* @see #pushNestedPath
|
||||
* @see #pushNestedPath(String)
|
||||
*/
|
||||
void popNestedPath() throws IllegalStateException;
|
||||
|
||||
@@ -101,6 +103,7 @@ public interface Errors {
|
||||
* Register a global error for the entire target object,
|
||||
* using the given error description.
|
||||
* @param errorCode error code, interpretable as a message key
|
||||
* @see #reject(String, Object[], String)
|
||||
*/
|
||||
void reject(String errorCode);
|
||||
|
||||
@@ -109,6 +112,7 @@ public interface Errors {
|
||||
* using the given error description.
|
||||
* @param errorCode error code, interpretable as a message key
|
||||
* @param defaultMessage fallback default message
|
||||
* @see #reject(String, Object[], String)
|
||||
*/
|
||||
void reject(String errorCode, String defaultMessage);
|
||||
|
||||
@@ -119,6 +123,7 @@ public interface Errors {
|
||||
* @param errorArgs error arguments, for argument binding via MessageFormat
|
||||
* (can be {@code null})
|
||||
* @param defaultMessage fallback default message
|
||||
* @see #rejectValue(String, String, Object[], String)
|
||||
*/
|
||||
void reject(String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage);
|
||||
|
||||
@@ -132,7 +137,7 @@ public interface Errors {
|
||||
* global error if the current object is the top object.
|
||||
* @param field the field name (may be {@code null} or empty String)
|
||||
* @param errorCode error code, interpretable as a message key
|
||||
* @see #getNestedPath()
|
||||
* @see #rejectValue(String, String, Object[], String)
|
||||
*/
|
||||
void rejectValue(@Nullable String field, String errorCode);
|
||||
|
||||
@@ -147,7 +152,7 @@ public interface Errors {
|
||||
* @param field the field name (may be {@code null} or empty String)
|
||||
* @param errorCode error code, interpretable as a message key
|
||||
* @param defaultMessage fallback default message
|
||||
* @see #getNestedPath()
|
||||
* @see #rejectValue(String, String, Object[], String)
|
||||
*/
|
||||
void rejectValue(@Nullable String field, String errorCode, String defaultMessage);
|
||||
|
||||
@@ -164,7 +169,7 @@ public interface Errors {
|
||||
* @param errorArgs error arguments, for argument binding via MessageFormat
|
||||
* (can be {@code null})
|
||||
* @param defaultMessage fallback default message
|
||||
* @see #getNestedPath()
|
||||
* @see #reject(String, Object[], String)
|
||||
*/
|
||||
void rejectValue(@Nullable String field, String errorCode,
|
||||
@Nullable Object[] errorArgs, @Nullable String defaultMessage);
|
||||
@@ -179,35 +184,40 @@ public interface Errors {
|
||||
* to refer to the same target object, or at least contain compatible errors
|
||||
* that apply to the target object of this {@code Errors} instance.
|
||||
* @param errors the {@code Errors} instance to merge in
|
||||
* @see #getAllErrors()
|
||||
*/
|
||||
void addAllErrors(Errors errors);
|
||||
|
||||
/**
|
||||
* Return if there were any errors.
|
||||
* Determine if there were any errors.
|
||||
* @see #hasGlobalErrors()
|
||||
* @see #hasFieldErrors()
|
||||
*/
|
||||
boolean hasErrors();
|
||||
|
||||
/**
|
||||
* Return the total number of errors.
|
||||
* Determine the total number of errors.
|
||||
* @see #getGlobalErrorCount()
|
||||
* @see #getFieldErrorCount()
|
||||
*/
|
||||
int getErrorCount();
|
||||
|
||||
/**
|
||||
* Get all errors, both global and field ones.
|
||||
* @return a list of {@link ObjectError} instances
|
||||
* @return a list of {@link ObjectError}/{@link FieldError} instances
|
||||
* @see #getGlobalErrors()
|
||||
* @see #getFieldErrors()
|
||||
*/
|
||||
List<ObjectError> getAllErrors();
|
||||
|
||||
/**
|
||||
* Are there any global errors?
|
||||
* @return {@code true} if there are any global errors
|
||||
* Determine if there were any global errors.
|
||||
* @see #hasFieldErrors()
|
||||
*/
|
||||
boolean hasGlobalErrors();
|
||||
|
||||
/**
|
||||
* Return the number of global errors.
|
||||
* @return the number of global errors
|
||||
* Determine the number of global errors.
|
||||
* @see #getFieldErrorCount()
|
||||
*/
|
||||
int getGlobalErrorCount();
|
||||
@@ -215,26 +225,26 @@ public interface Errors {
|
||||
/**
|
||||
* Get all global errors.
|
||||
* @return a list of {@link ObjectError} instances
|
||||
* @see #getFieldErrors()
|
||||
*/
|
||||
List<ObjectError> getGlobalErrors();
|
||||
|
||||
/**
|
||||
* Get the <i>first</i> global error, if any.
|
||||
* @return the global error, or {@code null}
|
||||
* @see #getFieldError()
|
||||
*/
|
||||
@Nullable
|
||||
ObjectError getGlobalError();
|
||||
|
||||
/**
|
||||
* Are there any field errors?
|
||||
* @return {@code true} if there are any errors associated with a field
|
||||
* Determine if there were any errors associated with a field.
|
||||
* @see #hasGlobalErrors()
|
||||
*/
|
||||
boolean hasFieldErrors();
|
||||
|
||||
/**
|
||||
* Return the number of errors associated with a field.
|
||||
* @return the number of errors associated with a field
|
||||
* Determine the number of errors associated with a field.
|
||||
* @see #getGlobalErrorCount()
|
||||
*/
|
||||
int getFieldErrorCount();
|
||||
@@ -242,36 +252,39 @@ public interface Errors {
|
||||
/**
|
||||
* Get all errors associated with a field.
|
||||
* @return a List of {@link FieldError} instances
|
||||
* @see #getGlobalErrors()
|
||||
*/
|
||||
List<FieldError> getFieldErrors();
|
||||
|
||||
/**
|
||||
* Get the <i>first</i> error associated with a field, if any.
|
||||
* @return the field-specific error, or {@code null}
|
||||
* @see #getGlobalError()
|
||||
*/
|
||||
@Nullable
|
||||
FieldError getFieldError();
|
||||
|
||||
/**
|
||||
* Are there any errors associated with the given field?
|
||||
* Determine if there were any errors associated with the given field.
|
||||
* @param field the field name
|
||||
* @return {@code true} if there were any errors associated with the given field
|
||||
* @see #hasFieldErrors()
|
||||
*/
|
||||
boolean hasFieldErrors(String field);
|
||||
|
||||
/**
|
||||
* Return the number of errors associated with the given field.
|
||||
* Determine the number of errors associated with the given field.
|
||||
* @param field the field name
|
||||
* @return the number of errors associated with the given field
|
||||
* @see #getFieldErrorCount()
|
||||
*/
|
||||
int getFieldErrorCount(String field);
|
||||
|
||||
/**
|
||||
* Get all errors associated with the given field.
|
||||
* <p>Implementations should support not only full field names like
|
||||
* "name" but also pattern matches like "na*" or "address.*".
|
||||
* <p>Implementations may support not only full field names like
|
||||
* "address.street" but also pattern matches like "address.*".
|
||||
* @param field the field name
|
||||
* @return a List of {@link FieldError} instances
|
||||
* @see #getFieldErrors()
|
||||
*/
|
||||
List<FieldError> getFieldErrors(String field);
|
||||
|
||||
@@ -279,6 +292,7 @@ public interface Errors {
|
||||
* Get the first error associated with the given field, if any.
|
||||
* @param field the field name
|
||||
* @return the field-specific error, or {@code null}
|
||||
* @see #getFieldError()
|
||||
*/
|
||||
@Nullable
|
||||
FieldError getFieldError(String field);
|
||||
@@ -290,17 +304,19 @@ public interface Errors {
|
||||
* even if there were type mismatches.
|
||||
* @param field the field name
|
||||
* @return the current value of the given field
|
||||
* @see #getFieldType(String)
|
||||
*/
|
||||
@Nullable
|
||||
Object getFieldValue(String field);
|
||||
|
||||
/**
|
||||
* Return the type of a given field.
|
||||
* Determine the type of the given field, as far as possible.
|
||||
* <p>Implementations should be able to determine the type even
|
||||
* when the field value is {@code null}, for example from some
|
||||
* associated descriptor.
|
||||
* @param field the field name
|
||||
* @return the type of the field, or {@code null} if not determinable
|
||||
* @see #getFieldValue(String)
|
||||
*/
|
||||
@Nullable
|
||||
Class<?> getFieldType(String field);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -54,14 +54,14 @@ package org.springframework.validation;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>See also the Spring reference manual for a fuller discussion of
|
||||
* the {@code Validator} interface and its role in an enterprise
|
||||
* application.
|
||||
* <p>See also the Spring reference manual for a fuller discussion of the
|
||||
* {@code Validator} interface and its role in an enterprise application.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @see SmartValidator
|
||||
* @see Errors
|
||||
* @see ValidationUtils
|
||||
* @see DataBinder#setValidator
|
||||
*/
|
||||
public interface Validator {
|
||||
|
||||
@@ -81,11 +81,14 @@ public interface Validator {
|
||||
boolean supports(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Validate the supplied {@code target} object, which must be
|
||||
* of a {@link Class} for which the {@link #supports(Class)} method
|
||||
* typically has (or would) return {@code true}.
|
||||
* Validate the given {@code target} object which must be of a
|
||||
* {@link Class} for which the {@link #supports(Class)} method
|
||||
* typically has returned (or would return) {@code true}.
|
||||
* <p>The supplied {@link Errors errors} instance can be used to report
|
||||
* any resulting validation errors.
|
||||
* any resulting validation errors, typically as part of a larger
|
||||
* binding process which this validator is meant to participate in.
|
||||
* Binding errors have typically been pre-registered with the
|
||||
* {@link Errors errors} instance before this invocation already.
|
||||
* @param target the object that is to be validated
|
||||
* @param errors contextual state about the validation process
|
||||
* @see ValidationUtils
|
||||
|
||||
+19
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,37 +26,46 @@ import org.springframework.lang.Nullable;
|
||||
* Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Christoph Dreis
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.3.7
|
||||
*/
|
||||
public abstract class ValidationAnnotationUtils {
|
||||
|
||||
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
|
||||
|
||||
/**
|
||||
* Determine any validation hints by the given annotation.
|
||||
* <p>This implementation checks for {@code @javax.validation.Valid},
|
||||
* Spring's {@link org.springframework.validation.annotation.Validated},
|
||||
* and custom annotations whose name starts with "Valid".
|
||||
* <p>This implementation checks for Spring's
|
||||
* {@link org.springframework.validation.annotation.Validated},
|
||||
* {@code @javax.validation.Valid}, and custom annotations whose
|
||||
* name starts with "Valid" which may optionally declare validation
|
||||
* hints through the "value" attribute.
|
||||
* @param ann the annotation (potentially a validation annotation)
|
||||
* @return the validation hints to apply (possibly an empty array),
|
||||
* or {@code null} if this annotation does not trigger any validation
|
||||
*/
|
||||
@Nullable
|
||||
public static Object[] determineValidationHints(Annotation ann) {
|
||||
// Direct presence of @Validated ?
|
||||
if (ann instanceof Validated) {
|
||||
return ((Validated) ann).value();
|
||||
}
|
||||
// Direct presence of @Valid ?
|
||||
Class<? extends Annotation> annotationType = ann.annotationType();
|
||||
String annotationName = annotationType.getName();
|
||||
if ("javax.validation.Valid".equals(annotationName)) {
|
||||
if ("javax.validation.Valid".equals(annotationType.getName())) {
|
||||
return EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
// Meta presence of @Validated ?
|
||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||
if (validatedAnn != null) {
|
||||
Object hints = validatedAnn.value();
|
||||
return convertValidationHints(hints);
|
||||
return validatedAnn.value();
|
||||
}
|
||||
// Custom validation annotation ?
|
||||
if (annotationType.getSimpleName().startsWith("Valid")) {
|
||||
Object hints = AnnotationUtils.getValue(ann);
|
||||
return convertValidationHints(hints);
|
||||
return convertValidationHints(AnnotationUtils.getValue(ann));
|
||||
}
|
||||
// No validation triggered
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+51
-44
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -92,7 +92,7 @@ public class ProxyFactoryBeanTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
public void setup() throws Exception {
|
||||
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
|
||||
parent.registerBeanDefinition("target2", new RootBeanDefinition(TestApplicationListener.class));
|
||||
this.factory = new DefaultListableBeanFactory(parent);
|
||||
@@ -139,22 +139,24 @@ public class ProxyFactoryBeanTests {
|
||||
private void testDoubleTargetSourceIsRejected(String name) {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(DBL_TARGETSOURCE_CONTEXT, CLASS));
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class).as("Should not allow TargetSource to be specified in interceptorNames as well as targetSource property")
|
||||
.isThrownBy(() -> bf.getBean(name))
|
||||
.havingCause()
|
||||
.isInstanceOf(AopConfigException.class)
|
||||
.withMessageContaining("TargetSource");
|
||||
.isThrownBy(() -> bf.getBean(name))
|
||||
.havingCause()
|
||||
.isInstanceOf(AopConfigException.class)
|
||||
.withMessageContaining("TargetSource");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTargetSourceNotAtEndOfInterceptorNamesIsRejected() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(NOTLAST_TARGETSOURCE_CONTEXT, CLASS));
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class).as("TargetSource or non-advised object must be last in interceptorNames")
|
||||
.isThrownBy(() -> bf.getBean("targetSourceNotLast"))
|
||||
.havingCause()
|
||||
.isInstanceOf(AopConfigException.class)
|
||||
.withMessageContaining("interceptorNames");
|
||||
.isThrownBy(() -> bf.getBean("targetSourceNotLast"))
|
||||
.havingCause()
|
||||
.isInstanceOf(AopConfigException.class)
|
||||
.withMessageContaining("interceptorNames");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -167,11 +169,11 @@ public class ProxyFactoryBeanTests {
|
||||
assertThat(cba.getCalls()).isEqualTo(0);
|
||||
|
||||
ITestBean tb = (ITestBean) bf.getBean("directTarget");
|
||||
assertThat(tb.getName().equals("Adam")).isTrue();
|
||||
assertThat(tb.getName()).isEqualTo("Adam");
|
||||
assertThat(cba.getCalls()).isEqualTo(1);
|
||||
|
||||
ProxyFactoryBean pfb = (ProxyFactoryBean) bf.getBean("&directTarget");
|
||||
assertThat(TestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(pfb.getObjectType()).isAssignableTo(TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -179,9 +181,9 @@ public class ProxyFactoryBeanTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(TARGETSOURCE_CONTEXT, CLASS));
|
||||
ITestBean tb = (ITestBean) bf.getBean("viaTargetSource");
|
||||
assertThat(tb.getName().equals("Adam")).isTrue();
|
||||
assertThat(tb.getName()).isEqualTo("Adam");
|
||||
ProxyFactoryBean pfb = (ProxyFactoryBean) bf.getBean("&viaTargetSource");
|
||||
assertThat(TestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(pfb.getObjectType()).isAssignableTo(TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -190,11 +192,15 @@ public class ProxyFactoryBeanTests {
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(TARGETSOURCE_CONTEXT, CLASS));
|
||||
|
||||
ITestBean tb = (ITestBean) bf.getBean("noTarget");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
tb.getName())
|
||||
.withMessage("getName");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(tb::getName).withMessage("getName");
|
||||
FactoryBean<?> pfb = (ProxyFactoryBean) bf.getBean("&noTarget");
|
||||
assertThat(ITestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(pfb.getObjectType()).isAssignableTo(ITestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetObjectTypeOnUninitializedFactoryBean() {
|
||||
ProxyFactoryBean pfb = new ProxyFactoryBean();
|
||||
assertThat(pfb.getObjectType()).isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,20 +225,20 @@ public class ProxyFactoryBeanTests {
|
||||
pc1.addAdvice(1, di);
|
||||
assertThat(pc2.getAdvisors()).isEqualTo(pc1.getAdvisors());
|
||||
assertThat(pc2.getAdvisors().length).as("Now have one more advisor").isEqualTo((oldLength + 1));
|
||||
assertThat(0).isEqualTo(di.getCount());
|
||||
assertThat(di.getCount()).isEqualTo(0);
|
||||
test1.setAge(5);
|
||||
assertThat(test1.getAge()).isEqualTo(test1_1.getAge());
|
||||
assertThat(3).isEqualTo(di.getCount());
|
||||
assertThat(di.getCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrototypeInstancesAreNotEqual() {
|
||||
assertThat(ITestBean.class.isAssignableFrom(factory.getType("prototype"))).as("Has correct object type").isTrue();
|
||||
assertThat(factory.getType("prototype")).isAssignableTo(ITestBean.class);
|
||||
ITestBean test2 = (ITestBean) factory.getBean("prototype");
|
||||
ITestBean test2_1 = (ITestBean) factory.getBean("prototype");
|
||||
assertThat(test2 != test2_1).as("Prototype instances !=").isTrue();
|
||||
assertThat(test2.equals(test2_1)).as("Prototype instances equal").isTrue();
|
||||
assertThat(ITestBean.class.isAssignableFrom(factory.getType("prototype"))).as("Has correct object type").isTrue();
|
||||
assertThat(test2).as("Prototype instances !=").isNotSameAs(test2_1);
|
||||
assertThat(test2).as("Prototype instances equal").isEqualTo(test2_1);
|
||||
assertThat(factory.getType("prototype")).isAssignableTo(ITestBean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,7 +268,7 @@ public class ProxyFactoryBeanTests {
|
||||
assertThat(prototype2FirstInstance.getCount()).isEqualTo(INITIAL_COUNT + 1);
|
||||
|
||||
SideEffectBean prototype2SecondInstance = (SideEffectBean) bf.getBean(beanName);
|
||||
assertThat(prototype2FirstInstance == prototype2SecondInstance).as("Prototypes are not ==").isFalse();
|
||||
assertThat(prototype2FirstInstance).as("Prototypes are not ==").isNotSameAs(prototype2SecondInstance);
|
||||
assertThat(prototype2SecondInstance.getCount()).isEqualTo(INITIAL_COUNT);
|
||||
assertThat(prototype2FirstInstance.getCount()).isEqualTo(INITIAL_COUNT + 1);
|
||||
|
||||
@@ -285,19 +291,19 @@ public class ProxyFactoryBeanTests {
|
||||
TestBean target = (TestBean) factory.getBean("test");
|
||||
target.setName(name);
|
||||
ITestBean autoInvoker = (ITestBean) factory.getBean("autoInvoker");
|
||||
assertThat(autoInvoker.getName().equals(name)).isTrue();
|
||||
assertThat(autoInvoker.getName()).isEqualTo(name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanGetFactoryReferenceAndManipulate() {
|
||||
ProxyFactoryBean config = (ProxyFactoryBean) factory.getBean("&test1");
|
||||
assertThat(ITestBean.class.isAssignableFrom(config.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(ITestBean.class.isAssignableFrom(factory.getType("test1"))).as("Has correct object type").isTrue();
|
||||
assertThat(config.getObjectType()).isAssignableTo(ITestBean.class);
|
||||
assertThat(factory.getType("test1")).isAssignableTo(ITestBean.class);
|
||||
// Trigger lazy initialization.
|
||||
config.getObject();
|
||||
assertThat(config.getAdvisors().length).as("Have one advisors").isEqualTo(1);
|
||||
assertThat(ITestBean.class.isAssignableFrom(config.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(ITestBean.class.isAssignableFrom(factory.getType("test1"))).as("Has correct object type").isTrue();
|
||||
assertThat(config.getObjectType()).isAssignableTo(ITestBean.class);
|
||||
assertThat(factory.getType("test1")).isAssignableTo(ITestBean.class);
|
||||
|
||||
ITestBean tb = (ITestBean) factory.getBean("test1");
|
||||
// no exception
|
||||
@@ -308,7 +314,7 @@ public class ProxyFactoryBeanTests {
|
||||
config.addAdvice(0, (MethodInterceptor) invocation -> {
|
||||
throw ex;
|
||||
});
|
||||
assertThat(config.getAdvisors().length).as("Have correct advisor count").isEqualTo(2);
|
||||
assertThat(config.getAdvisors()).as("Have correct advisor count").hasSize(2);
|
||||
|
||||
ITestBean tb1 = (ITestBean) factory.getBean("test1");
|
||||
assertThatException()
|
||||
@@ -348,31 +354,31 @@ public class ProxyFactoryBeanTests {
|
||||
// Add to head of interceptor chain
|
||||
int oldCount = config.getAdvisors().length;
|
||||
config.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class));
|
||||
assertThat(config.getAdvisors().length == oldCount + 1).isTrue();
|
||||
assertThat(config.getAdvisors()).hasSize(oldCount + 1);
|
||||
|
||||
TimeStamped ts = (TimeStamped) factory.getBean("test2");
|
||||
assertThat(ts.getTimeStamp()).isEqualTo(time);
|
||||
|
||||
// Can remove
|
||||
config.removeAdvice(ti);
|
||||
assertThat(config.getAdvisors().length == oldCount).isTrue();
|
||||
assertThat(config.getAdvisors()).hasSize(oldCount);
|
||||
|
||||
// Check no change on existing object reference
|
||||
assertThat(ts.getTimeStamp() == time).isTrue();
|
||||
assertThat(ts.getTimeStamp()).isEqualTo(time);
|
||||
|
||||
assertThat(factory.getBean("test2")).as("Should no longer implement TimeStamped")
|
||||
.isNotInstanceOf(TimeStamped.class);
|
||||
|
||||
// Now check non-effect of removing interceptor that isn't there
|
||||
config.removeAdvice(new DebugInterceptor());
|
||||
assertThat(config.getAdvisors().length == oldCount).isTrue();
|
||||
assertThat(config.getAdvisors()).hasSize(oldCount);
|
||||
|
||||
ITestBean it = (ITestBean) ts;
|
||||
DebugInterceptor debugInterceptor = new DebugInterceptor();
|
||||
config.addAdvice(0, debugInterceptor);
|
||||
it.getSpouse();
|
||||
// Won't affect existing reference
|
||||
assertThat(debugInterceptor.getCount() == 0).isTrue();
|
||||
assertThat(debugInterceptor.getCount()).isEqualTo(0);
|
||||
it = (ITestBean) factory.getBean("test2");
|
||||
it.getSpouse();
|
||||
assertThat(debugInterceptor.getCount()).isEqualTo(1);
|
||||
@@ -412,16 +418,16 @@ public class ProxyFactoryBeanTests {
|
||||
public void testMethodPointcuts() {
|
||||
ITestBean tb = (ITestBean) factory.getBean("pointcuts");
|
||||
PointcutForVoid.reset();
|
||||
assertThat(PointcutForVoid.methodNames.isEmpty()).as("No methods intercepted").isTrue();
|
||||
assertThat(PointcutForVoid.methodNames).as("No methods intercepted").isEmpty();
|
||||
tb.getAge();
|
||||
assertThat(PointcutForVoid.methodNames.isEmpty()).as("Not void: shouldn't have intercepted").isTrue();
|
||||
assertThat(PointcutForVoid.methodNames).as("Not void: shouldn't have intercepted").isEmpty();
|
||||
tb.setAge(1);
|
||||
tb.getAge();
|
||||
tb.setName("Tristan");
|
||||
tb.toString();
|
||||
assertThat(PointcutForVoid.methodNames.size()).as("Recorded wrong number of invocations").isEqualTo(2);
|
||||
assertThat(PointcutForVoid.methodNames.get(0).equals("setAge")).isTrue();
|
||||
assertThat(PointcutForVoid.methodNames.get(1).equals("setName")).isTrue();
|
||||
assertThat(PointcutForVoid.methodNames).as("Recorded wrong number of invocations").hasSize(2);
|
||||
assertThat(PointcutForVoid.methodNames.get(0)).isEqualTo("setAge");
|
||||
assertThat(PointcutForVoid.methodNames.get(1)).isEqualTo("setName");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -498,17 +504,17 @@ public class ProxyFactoryBeanTests {
|
||||
@Test
|
||||
public void testGlobalsCanAddAspectInterfaces() {
|
||||
AddedGlobalInterface agi = (AddedGlobalInterface) factory.getBean("autoInvoker");
|
||||
assertThat(agi.globalsAdded() == -1).isTrue();
|
||||
assertThat(agi.globalsAdded()).isEqualTo(-1);
|
||||
|
||||
ProxyFactoryBean pfb = (ProxyFactoryBean) factory.getBean("&validGlobals");
|
||||
// Trigger lazy initialization.
|
||||
pfb.getObject();
|
||||
// 2 globals + 2 explicit
|
||||
assertThat(pfb.getAdvisors().length).as("Have 2 globals and 2 explicit advisors").isEqualTo(3);
|
||||
assertThat(pfb.getAdvisors()).as("Have 2 globals and 2 explicit advisors").hasSize(3);
|
||||
|
||||
ApplicationListener<?> l = (ApplicationListener<?>) factory.getBean("validGlobals");
|
||||
agi = (AddedGlobalInterface) l;
|
||||
assertThat(agi.globalsAdded() == -1).isTrue();
|
||||
assertThat(agi.globalsAdded()).isEqualTo(-1);
|
||||
|
||||
assertThat(factory.getBean("test1")).as("Aspect interface shouldn't be implemented without globals")
|
||||
.isNotInstanceOf(AddedGlobalInterface.class);
|
||||
@@ -709,6 +715,7 @@ public class ProxyFactoryBeanTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Aspect interface
|
||||
*/
|
||||
|
||||
+6
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -118,6 +118,7 @@ class CacheReproTests {
|
||||
assertThat(cacheResolver.getCache("foo").get("foo")).isNull();
|
||||
Object result = bean.getSimple("foo"); // cache name = id
|
||||
assertThat(cacheResolver.getCache("foo").get("foo").get()).isEqualTo(result);
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
@@ -127,7 +128,7 @@ class CacheReproTests {
|
||||
Spr13081Service bean = context.getBean(Spr13081Service.class);
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() -> bean.getSimple(null))
|
||||
.withMessageContaining(MyCacheResolver.class.getName());
|
||||
.withMessageContaining(MyCacheResolver.class.getName());
|
||||
context.close();
|
||||
}
|
||||
|
||||
@@ -146,6 +147,7 @@ class CacheReproTests {
|
||||
TestBean tb2 = bean.findById("tb1").get();
|
||||
assertThat(tb2).isNotSameAs(tb);
|
||||
assertThat(cache.get("tb1").get()).isSameAs(tb2);
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
@@ -177,6 +179,7 @@ class CacheReproTests {
|
||||
bean.insertItem(tb);
|
||||
assertThat(bean.findById("tb1").get()).isSameAs(tb);
|
||||
assertThat(cache.get("tb1").get()).isSameAs(tb);
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
@@ -190,6 +193,7 @@ class CacheReproTests {
|
||||
bean.insertItem(tb);
|
||||
assertThat(bean.findById("tb1").get()).isSameAs(tb);
|
||||
assertThat(cache.get("tb1").get()).isSameAs(tb);
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
||||
Vendored
+20
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,8 +48,9 @@ public class CacheSyncFailureTests {
|
||||
|
||||
private SimpleService simpleService;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
public void setup() {
|
||||
this.context = new AnnotationConfigApplicationContext(Config.class);
|
||||
this.simpleService = this.context.getBean(SimpleService.class);
|
||||
}
|
||||
@@ -61,39 +62,40 @@ public class CacheSyncFailureTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void unlessSync() {
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
this.simpleService.unlessSync("key"))
|
||||
.withMessageContaining("@Cacheable(sync=true) does not support unless attribute");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.simpleService.unlessSync("key"))
|
||||
.withMessageContaining("A sync=true operation does not support the unless attribute");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void severalCachesSync() {
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
this.simpleService.severalCachesSync("key"))
|
||||
.withMessageContaining("@Cacheable(sync=true) only allows a single cache");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.simpleService.severalCachesSync("key"))
|
||||
.withMessageContaining("A sync=true operation is restricted to a single cache");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void severalCachesWithResolvedSync() {
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
this.simpleService.severalCachesWithResolvedSync("key"))
|
||||
.withMessageContaining("@Cacheable(sync=true) only allows a single cache");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.simpleService.severalCachesWithResolvedSync("key"))
|
||||
.withMessageContaining("A sync=true operation is restricted to a single cache");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void syncWithAnotherOperation() {
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
this.simpleService.syncWithAnotherOperation("key"))
|
||||
.withMessageContaining("@Cacheable(sync=true) cannot be combined with other cache operations");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.simpleService.syncWithAnotherOperation("key"))
|
||||
.withMessageContaining("A sync=true operation cannot be combined with other cache operations");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void syncWithTwoGetOperations() {
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
this.simpleService.syncWithTwoGetOperations("key"))
|
||||
.withMessageContaining("Only one @Cacheable(sync=true) entry is allowed");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.simpleService.syncWithTwoGetOperations("key"))
|
||||
.withMessageContaining("Only one sync=true operation is allowed");
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +133,7 @@ public class CacheSyncFailureTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class Config implements CachingConfigurer {
|
||||
|
||||
+2
@@ -197,6 +197,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
context.registerBeanDefinition("stubFooDao", new RootBeanDefinition(TestBean.class));
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
scanner.setIncludeAnnotationConfig(false);
|
||||
|
||||
// should not fail!
|
||||
scanner.scan(BASE_PACKAGE);
|
||||
}
|
||||
@@ -207,6 +208,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
scanner.setIncludeAnnotationConfig(false);
|
||||
scanner.scan("org.springframework.context.annotation3");
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
|
||||
.withMessageContaining("stubFooDao")
|
||||
.withMessageContaining(StubFooDao.class.getName());
|
||||
|
||||
+22
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -215,7 +215,7 @@ public class CommonAnnotationBeanPostProcessorTests {
|
||||
bf.registerBeanDefinition("testBean4", tbd);
|
||||
|
||||
bf.registerResolvableDependency(BeanFactory.class, bf);
|
||||
bf.registerResolvableDependency(INestedTestBean.class, (ObjectFactory<Object>) () -> new NestedTestBean());
|
||||
bf.registerResolvableDependency(INestedTestBean.class, (ObjectFactory<Object>) NestedTestBean::new);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer ppc = new org.springframework.beans.factory.config.PropertyPlaceholderConfigurer();
|
||||
@@ -233,7 +233,7 @@ public class CommonAnnotationBeanPostProcessorTests {
|
||||
assertThat(tb).isNotSameAs(anotherBean.getTestBean6());
|
||||
|
||||
String[] depBeans = bf.getDependenciesForBean("annotatedBean");
|
||||
assertThat(depBeans.length).isEqualTo(1);
|
||||
assertThat(depBeans).hasSize(1);
|
||||
assertThat(depBeans[0]).isEqualTo("testBean4");
|
||||
}
|
||||
|
||||
@@ -508,6 +508,25 @@ public class CommonAnnotationBeanPostProcessorTests {
|
||||
assertThat(tb.getName()).isEqualTo("notLazyAnymore");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyResolutionWithFallbackTypeMatch() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
|
||||
CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(LazyResourceCglibInjectionBean.class));
|
||||
bf.registerBeanDefinition("tb", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
LazyResourceCglibInjectionBean bean = (LazyResourceCglibInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bf.containsSingleton("tb")).isFalse();
|
||||
bean.testBean.setName("notLazyAnymore");
|
||||
assertThat(bf.containsSingleton("tb")).isTrue();
|
||||
TestBean tb = (TestBean) bf.getBean("tb");
|
||||
assertThat(tb.getName()).isEqualTo("notLazyAnymore");
|
||||
}
|
||||
|
||||
|
||||
public static class AnnotatedInitDestroyBean {
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+16
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,6 +36,10 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> GenericTestEvent<T> createGenericTestEvent(T payload) {
|
||||
return new GenericTestEvent<>(this, payload);
|
||||
}
|
||||
|
||||
|
||||
protected static class GenericTestEvent<T> extends ApplicationEvent {
|
||||
|
||||
@@ -51,6 +55,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static class SmartGenericTestEvent<T> extends GenericTestEvent<T> implements ResolvableTypeProvider {
|
||||
|
||||
private final ResolvableType resolvableType;
|
||||
@@ -67,6 +72,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static class StringEvent extends GenericTestEvent<String> {
|
||||
|
||||
public StringEvent(Object source, String payload) {
|
||||
@@ -74,6 +80,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static class LongEvent extends GenericTestEvent<Long> {
|
||||
|
||||
public LongEvent(Object source, Long payload) {
|
||||
@@ -81,31 +88,31 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> GenericTestEvent<T> createGenericTestEvent(T payload) {
|
||||
return new GenericTestEvent<>(this, payload);
|
||||
}
|
||||
|
||||
|
||||
static class GenericEventListener implements ApplicationListener<GenericTestEvent<?>> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(GenericTestEvent<?> event) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ObjectEventListener implements ApplicationListener<GenericTestEvent<Object>> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(GenericTestEvent<Object> event) {
|
||||
}
|
||||
}
|
||||
|
||||
static class UpperBoundEventListener
|
||||
implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
|
||||
|
||||
static class UpperBoundEventListener implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(GenericTestEvent<? extends RuntimeException> event) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class StringEventListener implements ApplicationListener<GenericTestEvent<String>> {
|
||||
|
||||
@Override
|
||||
@@ -113,6 +120,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static class RawApplicationListener implements ApplicationListener {
|
||||
|
||||
@@ -121,10 +129,10 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class TestEvents {
|
||||
|
||||
public GenericTestEvent<?> wildcardEvent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+23
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -164,12 +164,12 @@ class AnnotationDrivenEventListenerTests {
|
||||
ContextEventListener listener = this.context.getBean(ContextEventListener.class);
|
||||
|
||||
List<Object> events = this.eventCollector.getEvents(listener);
|
||||
assertThat(events.size()).as("Wrong number of initial context events").isEqualTo(1);
|
||||
assertThat(events).as("Wrong number of initial context events").hasSize(1);
|
||||
assertThat(events.get(0).getClass()).isEqualTo(ContextRefreshedEvent.class);
|
||||
|
||||
this.context.stop();
|
||||
List<Object> eventsAfterStop = this.eventCollector.getEvents(listener);
|
||||
assertThat(eventsAfterStop.size()).as("Wrong number of context events on shutdown").isEqualTo(2);
|
||||
assertThat(eventsAfterStop).as("Wrong number of context events on shutdown").hasSize(2);
|
||||
assertThat(eventsAfterStop.get(1).getClass()).isEqualTo(ContextStoppedEvent.class);
|
||||
this.eventCollector.assertTotalEventsCount(2);
|
||||
}
|
||||
@@ -334,7 +334,7 @@ class AnnotationDrivenEventListenerTests {
|
||||
load(ScopedProxyTestBean.class);
|
||||
|
||||
SimpleService proxy = this.context.getBean(SimpleService.class);
|
||||
assertThat(proxy instanceof Advised).as("bean should be a proxy").isTrue();
|
||||
assertThat(proxy).as("bean should be a proxy").isInstanceOf(Advised.class);
|
||||
this.eventCollector.assertNoEventReceived(proxy.getId());
|
||||
|
||||
this.context.publishEvent(new ContextRefreshedEvent(this.context));
|
||||
@@ -351,7 +351,7 @@ class AnnotationDrivenEventListenerTests {
|
||||
load(AnnotatedProxyTestBean.class);
|
||||
|
||||
AnnotatedSimpleService proxy = this.context.getBean(AnnotatedSimpleService.class);
|
||||
assertThat(proxy instanceof Advised).as("bean should be a proxy").isTrue();
|
||||
assertThat(proxy).as("bean should be a proxy").isInstanceOf(Advised.class);
|
||||
this.eventCollector.assertNoEventReceived(proxy.getId());
|
||||
|
||||
this.context.publishEvent(new ContextRefreshedEvent(this.context));
|
||||
@@ -517,7 +517,6 @@ class AnnotationDrivenEventListenerTests {
|
||||
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
|
||||
TestEventListener listener = this.context.getBean(TestEventListener.class);
|
||||
|
||||
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.eventCollector.assertNoEventReceived(replyEventListener);
|
||||
this.context.publishEvent(event);
|
||||
@@ -634,6 +633,17 @@ class AnnotationDrivenEventListenerTests {
|
||||
assertThat(listener.order).contains("first", "second", "third");
|
||||
}
|
||||
|
||||
@Test
|
||||
void publicSubclassWithInheritedEventListener() {
|
||||
load(PublicSubclassWithInheritedEventListener.class);
|
||||
TestEventListener listener = this.context.getBean(PublicSubclassWithInheritedEventListener.class);
|
||||
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.context.publishEvent("test");
|
||||
this.eventCollector.assertEvent(listener, "test");
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test @Disabled // SPR-15122
|
||||
void listenersReceiveEarlyEvents() {
|
||||
load(EventOnPostConstruct.class, OrderedTestListener.class);
|
||||
@@ -646,7 +656,7 @@ class AnnotationDrivenEventListenerTests {
|
||||
void missingListenerBeanIgnored() {
|
||||
load(MissingEventListener.class);
|
||||
context.getBean(UseMissingEventListener.class);
|
||||
context.getBean(ApplicationEventMulticaster.class).multicastEvent(new TestEvent(this));
|
||||
context.publishEvent(new TestEvent(this));
|
||||
}
|
||||
|
||||
|
||||
@@ -753,7 +763,6 @@ class AnnotationDrivenEventListenerTests {
|
||||
public void handleContextEvent(ApplicationContextEvent event) {
|
||||
collectEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -980,7 +989,6 @@ class AnnotationDrivenEventListenerTests {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@EventListener
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConditionalEvent {
|
||||
@@ -1032,7 +1040,7 @@ class AnnotationDrivenEventListenerTests {
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Component
|
||||
static class OrderedTestListener extends TestEventListener {
|
||||
|
||||
public final List<String> order = new ArrayList<>();
|
||||
@@ -1056,6 +1064,11 @@ class AnnotationDrivenEventListenerTests {
|
||||
}
|
||||
|
||||
|
||||
@Component
|
||||
public static class PublicSubclassWithInheritedEventListener extends TestEventListener {
|
||||
}
|
||||
|
||||
|
||||
static class EventOnPostConstruct {
|
||||
|
||||
@Autowired
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -104,7 +104,8 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE
|
||||
|
||||
@Test
|
||||
public void genericListenerStrictTypeSubClass() {
|
||||
supportsEventType(false, ObjectEventListener.class, ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class));
|
||||
supportsEventType(false, ObjectEventListener.class,
|
||||
ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+8
-8
@@ -63,7 +63,7 @@ public class EnableSchedulingTests {
|
||||
ctx = new AnnotationConfigApplicationContext(FixedRateTaskConfig.class);
|
||||
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(2);
|
||||
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class EnableSchedulingTests {
|
||||
ctx = new AnnotationConfigApplicationContext(FixedRateTaskConfigSubclass.class);
|
||||
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(2);
|
||||
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public class EnableSchedulingTests {
|
||||
ctx = new AnnotationConfigApplicationContext(ExplicitSchedulerConfig.class);
|
||||
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(1);
|
||||
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10);
|
||||
assertThat(ctx.getBean(ExplicitSchedulerConfig.class).threadName).startsWith("explicitScheduler-");
|
||||
assertThat(Arrays.asList(ctx.getDefaultListableBeanFactory().getDependentBeans("myTaskScheduler")).contains(
|
||||
@@ -102,7 +102,7 @@ public class EnableSchedulingTests {
|
||||
ctx = new AnnotationConfigApplicationContext(ExplicitScheduledTaskRegistrarConfig.class);
|
||||
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(1);
|
||||
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10);
|
||||
assertThat(ctx.getBean(ExplicitScheduledTaskRegistrarConfig.class).threadName).startsWith("explicitScheduler1");
|
||||
}
|
||||
@@ -124,7 +124,7 @@ public class EnableSchedulingTests {
|
||||
ctx = new AnnotationConfigApplicationContext(
|
||||
SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask_disambiguatedByScheduledTaskRegistrar.class);
|
||||
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(ThreadAwareWorker.class).executedByThread).startsWith("explicitScheduler2-");
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public class EnableSchedulingTests {
|
||||
ctx = new AnnotationConfigApplicationContext(
|
||||
SchedulingEnabled_withAmbiguousTaskSchedulers_andSingleTask_disambiguatedBySchedulerNameAttribute.class);
|
||||
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(ThreadAwareWorker.class).executedByThread).startsWith("explicitScheduler2-");
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ public class EnableSchedulingTests {
|
||||
public void withTaskAddedVia_configureTasks() throws InterruptedException {
|
||||
ctx = new AnnotationConfigApplicationContext(SchedulingEnabled_withTaskAddedVia_configureTasks.class);
|
||||
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(ThreadAwareWorker.class).executedByThread).startsWith("taskScheduler-");
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public class EnableSchedulingTests {
|
||||
public void withTriggerTask() throws InterruptedException {
|
||||
ctx = new AnnotationConfigApplicationContext(TriggerTaskConfig.class);
|
||||
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThan(1);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+131
-120
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -95,11 +95,11 @@ class DataBinderTests {
|
||||
binder.bind(pvs);
|
||||
binder.close();
|
||||
|
||||
assertThat(rod.getName().equals("Rod")).as("changed name correctly").isTrue();
|
||||
assertThat(rod.getAge() == 32).as("changed age correctly").isTrue();
|
||||
assertThat(rod.getName()).as("changed name correctly").isEqualTo("Rod");
|
||||
assertThat(rod.getAge()).as("changed age correctly").isEqualTo(32);
|
||||
|
||||
Map<?, ?> map = binder.getBindingResult().getModel();
|
||||
assertThat(map.size() == 2).as("There is one element in map").isTrue();
|
||||
assertThat(map).as("There is one element in map").hasSize(2);
|
||||
TestBean tb = (TestBean) map.get("person");
|
||||
assertThat(tb.equals(rod)).as("Same object").isTrue();
|
||||
|
||||
@@ -157,8 +157,9 @@ class DataBinderTests {
|
||||
pvs.add("name", "Rod");
|
||||
pvs.add("age", 32);
|
||||
pvs.add("nonExisting", "someValue");
|
||||
assertThatExceptionOfType(NotWritablePropertyException.class).isThrownBy(() ->
|
||||
binder.bind(pvs));
|
||||
|
||||
assertThatExceptionOfType(NotWritablePropertyException.class)
|
||||
.isThrownBy(() -> binder.bind(pvs));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -168,8 +169,9 @@ class DataBinderTests {
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.add("name", "Rod");
|
||||
pvs.add("spouse.age", 32);
|
||||
assertThatExceptionOfType(NullValueInNestedPathException.class).isThrownBy(() ->
|
||||
binder.bind(pvs));
|
||||
|
||||
assertThatExceptionOfType(NullValueInNestedPathException.class)
|
||||
.isThrownBy(() -> binder.bind(pvs));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -197,57 +199,56 @@ class DataBinderTests {
|
||||
pvs.add("age", "32x");
|
||||
pvs.add("touchy", "m.y");
|
||||
binder.bind(pvs);
|
||||
assertThatExceptionOfType(BindException.class).isThrownBy(
|
||||
binder::close)
|
||||
.satisfies(ex -> {
|
||||
assertThat(rod.getName()).isEqualTo("Rod");
|
||||
Map<?, ?> map = binder.getBindingResult().getModel();
|
||||
TestBean tb = (TestBean) map.get("person");
|
||||
assertThat(tb).isSameAs(rod);
|
||||
|
||||
BindingResult br = (BindingResult) map.get(BindingResult.MODEL_KEY_PREFIX + "person");
|
||||
assertThat(BindingResultUtils.getBindingResult(map, "person")).isEqualTo(br);
|
||||
assertThat(BindingResultUtils.getRequiredBindingResult(map, "person")).isEqualTo(br);
|
||||
assertThatExceptionOfType(BindException.class).isThrownBy(binder::close).satisfies(ex -> {
|
||||
assertThat(rod.getName()).isEqualTo("Rod");
|
||||
Map<?, ?> map = binder.getBindingResult().getModel();
|
||||
TestBean tb = (TestBean) map.get("person");
|
||||
assertThat(tb).isSameAs(rod);
|
||||
|
||||
assertThat(BindingResultUtils.getBindingResult(map, "someOtherName")).isNull();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
BindingResultUtils.getRequiredBindingResult(map, "someOtherName"));
|
||||
BindingResult br = (BindingResult) map.get(BindingResult.MODEL_KEY_PREFIX + "person");
|
||||
assertThat(BindingResultUtils.getBindingResult(map, "person")).isEqualTo(br);
|
||||
assertThat(BindingResultUtils.getRequiredBindingResult(map, "person")).isEqualTo(br);
|
||||
|
||||
assertThat(binder.getBindingResult()).as("Added itself to map").isSameAs(br);
|
||||
assertThat(br.hasErrors()).isTrue();
|
||||
assertThat(br.getErrorCount()).isEqualTo(2);
|
||||
assertThat(BindingResultUtils.getBindingResult(map, "someOtherName")).isNull();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
BindingResultUtils.getRequiredBindingResult(map, "someOtherName"));
|
||||
|
||||
assertThat(br.hasFieldErrors("age")).isTrue();
|
||||
assertThat(br.getFieldErrorCount("age")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("age")).isEqualTo("32x");
|
||||
FieldError ageError = binder.getBindingResult().getFieldError("age");
|
||||
assertThat(ageError).isNotNull();
|
||||
assertThat(ageError.getCode()).isEqualTo("typeMismatch");
|
||||
assertThat(ageError.getRejectedValue()).isEqualTo("32x");
|
||||
assertThat(ageError.contains(TypeMismatchException.class)).isTrue();
|
||||
assertThat(ageError.contains(NumberFormatException.class)).isTrue();
|
||||
assertThat(ageError.unwrap(NumberFormatException.class).getMessage()).contains("32x");
|
||||
assertThat(tb.getAge()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult()).as("Added itself to map").isSameAs(br);
|
||||
assertThat(br.hasErrors()).isTrue();
|
||||
assertThat(br.getErrorCount()).isEqualTo(2);
|
||||
|
||||
assertThat(br.hasFieldErrors("touchy")).isTrue();
|
||||
assertThat(br.getFieldErrorCount("touchy")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("touchy")).isEqualTo("m.y");
|
||||
FieldError touchyError = binder.getBindingResult().getFieldError("touchy");
|
||||
assertThat(touchyError).isNotNull();
|
||||
assertThat(touchyError.getCode()).isEqualTo("methodInvocation");
|
||||
assertThat(touchyError.getRejectedValue()).isEqualTo("m.y");
|
||||
assertThat(touchyError.contains(MethodInvocationException.class)).isTrue();
|
||||
assertThat(touchyError.unwrap(MethodInvocationException.class).getCause().getMessage()).contains("a .");
|
||||
assertThat(tb.getTouchy()).isNull();
|
||||
assertThat(br.hasFieldErrors("age")).isTrue();
|
||||
assertThat(br.getFieldErrorCount("age")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("age")).isEqualTo("32x");
|
||||
FieldError ageError = binder.getBindingResult().getFieldError("age");
|
||||
assertThat(ageError).isNotNull();
|
||||
assertThat(ageError.getCode()).isEqualTo("typeMismatch");
|
||||
assertThat(ageError.getRejectedValue()).isEqualTo("32x");
|
||||
assertThat(ageError.contains(TypeMismatchException.class)).isTrue();
|
||||
assertThat(ageError.contains(NumberFormatException.class)).isTrue();
|
||||
assertThat(ageError.unwrap(NumberFormatException.class).getMessage()).contains("32x");
|
||||
assertThat(tb.getAge()).isEqualTo(0);
|
||||
|
||||
DataBinder binder2 = new DataBinder(new TestBean(), "person");
|
||||
MutablePropertyValues pvs2 = new MutablePropertyValues();
|
||||
pvs2.add("name", "Rod");
|
||||
pvs2.add("age", "32x");
|
||||
pvs2.add("touchy", "m.y");
|
||||
binder2.bind(pvs2);
|
||||
assertThat(ex.getBindingResult()).isEqualTo(binder2.getBindingResult());
|
||||
});
|
||||
assertThat(br.hasFieldErrors("touchy")).isTrue();
|
||||
assertThat(br.getFieldErrorCount("touchy")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("touchy")).isEqualTo("m.y");
|
||||
FieldError touchyError = binder.getBindingResult().getFieldError("touchy");
|
||||
assertThat(touchyError).isNotNull();
|
||||
assertThat(touchyError.getCode()).isEqualTo("methodInvocation");
|
||||
assertThat(touchyError.getRejectedValue()).isEqualTo("m.y");
|
||||
assertThat(touchyError.contains(MethodInvocationException.class)).isTrue();
|
||||
assertThat(touchyError.unwrap(MethodInvocationException.class).getCause().getMessage()).contains("a .");
|
||||
assertThat(tb.getTouchy()).isNull();
|
||||
|
||||
DataBinder binder2 = new DataBinder(new TestBean(), "person");
|
||||
MutablePropertyValues pvs2 = new MutablePropertyValues();
|
||||
pvs2.add("name", "Rod");
|
||||
pvs2.add("age", "32x");
|
||||
pvs2.add("touchy", "m.y");
|
||||
binder2.bind(pvs2);
|
||||
assertThat(ex.getBindingResult()).isEqualTo(binder2.getBindingResult());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -257,15 +258,17 @@ class DataBinderTests {
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.add("class.classLoader.URLs[0]", "https://myserver");
|
||||
binder.setIgnoreUnknownFields(false);
|
||||
assertThatExceptionOfType(NotWritablePropertyException.class).isThrownBy(() ->
|
||||
binder.bind(pvs))
|
||||
.withMessageContaining("classLoader");
|
||||
|
||||
assertThatExceptionOfType(NotWritablePropertyException.class)
|
||||
.isThrownBy(() -> binder.bind(pvs))
|
||||
.withMessageContaining("classLoader");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingWithErrorsAndCustomEditors() {
|
||||
TestBean rod = new TestBean();
|
||||
DataBinder binder = new DataBinder(rod, "person");
|
||||
|
||||
binder.registerCustomEditor(String.class, "touchy", new PropertyEditorSupport() {
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
@@ -286,6 +289,7 @@ class DataBinderTests {
|
||||
return ((TestBean) getValue()).getName();
|
||||
}
|
||||
});
|
||||
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.add("name", "Rod");
|
||||
pvs.add("age", "32x");
|
||||
@@ -293,41 +297,39 @@ class DataBinderTests {
|
||||
pvs.add("spouse", "Kerry");
|
||||
binder.bind(pvs);
|
||||
|
||||
assertThatExceptionOfType(BindException.class).isThrownBy(
|
||||
binder::close)
|
||||
.satisfies(ex -> {
|
||||
assertThat(rod.getName()).isEqualTo("Rod");
|
||||
Map<?, ?> model = binder.getBindingResult().getModel();
|
||||
TestBean tb = (TestBean) model.get("person");
|
||||
assertThat(tb).isEqualTo(rod);
|
||||
assertThatExceptionOfType(BindException.class).isThrownBy(binder::close).satisfies(ex -> {
|
||||
assertThat(rod.getName()).isEqualTo("Rod");
|
||||
Map<?, ?> model = binder.getBindingResult().getModel();
|
||||
TestBean tb = (TestBean) model.get("person");
|
||||
assertThat(tb).isEqualTo(rod);
|
||||
|
||||
BindingResult br = (BindingResult) model.get(BindingResult.MODEL_KEY_PREFIX + "person");
|
||||
assertThat(binder.getBindingResult()).isSameAs(br);
|
||||
assertThat(br.hasErrors()).isTrue();
|
||||
assertThat(br.getErrorCount()).isEqualTo(2);
|
||||
BindingResult br = (BindingResult) model.get(BindingResult.MODEL_KEY_PREFIX + "person");
|
||||
assertThat(binder.getBindingResult()).isSameAs(br);
|
||||
assertThat(br.hasErrors()).isTrue();
|
||||
assertThat(br.getErrorCount()).isEqualTo(2);
|
||||
|
||||
assertThat(br.hasFieldErrors("age")).isTrue();
|
||||
assertThat(br.getFieldErrorCount("age")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("age")).isEqualTo("32x");
|
||||
FieldError ageError = binder.getBindingResult().getFieldError("age");
|
||||
assertThat(ageError).isNotNull();
|
||||
assertThat(ageError.getCode()).isEqualTo("typeMismatch");
|
||||
assertThat(ageError.getRejectedValue()).isEqualTo("32x");
|
||||
assertThat(tb.getAge()).isEqualTo(0);
|
||||
assertThat(br.hasFieldErrors("age")).isTrue();
|
||||
assertThat(br.getFieldErrorCount("age")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("age")).isEqualTo("32x");
|
||||
FieldError ageError = binder.getBindingResult().getFieldError("age");
|
||||
assertThat(ageError).isNotNull();
|
||||
assertThat(ageError.getCode()).isEqualTo("typeMismatch");
|
||||
assertThat(ageError.getRejectedValue()).isEqualTo("32x");
|
||||
assertThat(tb.getAge()).isEqualTo(0);
|
||||
|
||||
assertThat(br.hasFieldErrors("touchy")).isTrue();
|
||||
assertThat(br.getFieldErrorCount("touchy")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("touchy")).isEqualTo("m.y");
|
||||
FieldError touchyError = binder.getBindingResult().getFieldError("touchy");
|
||||
assertThat(touchyError).isNotNull();
|
||||
assertThat(touchyError.getCode()).isEqualTo("methodInvocation");
|
||||
assertThat(touchyError.getRejectedValue()).isEqualTo("m.y");
|
||||
assertThat(tb.getTouchy()).isNull();
|
||||
assertThat(br.hasFieldErrors("touchy")).isTrue();
|
||||
assertThat(br.getFieldErrorCount("touchy")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("touchy")).isEqualTo("m.y");
|
||||
FieldError touchyError = binder.getBindingResult().getFieldError("touchy");
|
||||
assertThat(touchyError).isNotNull();
|
||||
assertThat(touchyError.getCode()).isEqualTo("methodInvocation");
|
||||
assertThat(touchyError.getRejectedValue()).isEqualTo("m.y");
|
||||
assertThat(tb.getTouchy()).isNull();
|
||||
|
||||
assertThat(br.hasFieldErrors("spouse")).isFalse();
|
||||
assertThat(binder.getBindingResult().getFieldValue("spouse")).isEqualTo("Kerry");
|
||||
assertThat(tb.getSpouse()).isNotNull();
|
||||
});
|
||||
assertThat(br.hasFieldErrors("spouse")).isFalse();
|
||||
assertThat(binder.getBindingResult().getFieldValue("spouse")).isEqualTo("Kerry");
|
||||
assertThat(tb.getSpouse()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -576,7 +578,7 @@ class DataBinderTests {
|
||||
editor = binder.getBindingResult().findEditor("myFloat", null);
|
||||
assertThat(editor).isNotNull();
|
||||
editor.setAsText("1,6");
|
||||
assertThat(((Number) editor.getValue()).floatValue() == 1.6f).isTrue();
|
||||
assertThat(((Number) editor.getValue()).floatValue()).isEqualTo(1.6f);
|
||||
}
|
||||
finally {
|
||||
LocaleContextHolder.resetLocaleContext();
|
||||
@@ -752,15 +754,15 @@ class DataBinderTests {
|
||||
binder.bind(pvs);
|
||||
binder.close();
|
||||
|
||||
assertThat("Rod".equals(rod.getName())).as("changed name correctly").isTrue();
|
||||
assertThat("Rod".equals(rod.getTouchy())).as("changed touchy correctly").isTrue();
|
||||
assertThat(rod.getAge() == 0).as("did not change age").isTrue();
|
||||
assertThat(rod.getName()).as("changed name correctly").isEqualTo("Rod");
|
||||
assertThat(rod.getTouchy()).as("changed touchy correctly").isEqualTo("Rod");
|
||||
assertThat(rod.getAge()).as("did not change age").isEqualTo(0);
|
||||
String[] disallowedFields = binder.getBindingResult().getSuppressedFields();
|
||||
assertThat(disallowedFields).hasSize(1);
|
||||
assertThat(disallowedFields[0]).isEqualTo("age");
|
||||
|
||||
Map<?,?> m = binder.getBindingResult().getModel();
|
||||
assertThat(m.size() == 2).as("There is one element in map").isTrue();
|
||||
assertThat(m).as("There is one element in map").hasSize(2);
|
||||
TestBean tb = (TestBean) m.get("person");
|
||||
assertThat(tb.equals(rod)).as("Same object").isTrue();
|
||||
}
|
||||
@@ -914,7 +916,7 @@ class DataBinderTests {
|
||||
binder.getBindingResult().rejectValue("touchy", "someCode", "someMessage");
|
||||
binder.getBindingResult().rejectValue("spouse.name", "someCode", "someMessage");
|
||||
|
||||
assertThat(binder.getBindingResult().getNestedPath()).isEqualTo("");
|
||||
assertThat(binder.getBindingResult().getNestedPath()).isEmpty();
|
||||
assertThat(binder.getBindingResult().getFieldValue("name")).isEqualTo("value");
|
||||
assertThat(binder.getBindingResult().getFieldError("name").getRejectedValue()).isEqualTo("prefixvalue");
|
||||
assertThat(tb.getName()).isEqualTo("prefixvalue");
|
||||
@@ -1010,7 +1012,7 @@ class DataBinderTests {
|
||||
binder.getBindingResult().rejectValue("touchy", "someCode", "someMessage");
|
||||
binder.getBindingResult().rejectValue("spouse.name", "someCode", "someMessage");
|
||||
|
||||
assertThat(binder.getBindingResult().getNestedPath()).isEqualTo("");
|
||||
assertThat(binder.getBindingResult().getNestedPath()).isEmpty();
|
||||
assertThat(binder.getBindingResult().getFieldValue("name")).isEqualTo("value");
|
||||
assertThat(binder.getBindingResult().getFieldError("name").getRejectedValue()).isEqualTo("prefixvalue");
|
||||
assertThat(tb.getName()).isEqualTo("prefixvalue");
|
||||
@@ -1134,12 +1136,11 @@ class DataBinderTests {
|
||||
tb2.setAge(34);
|
||||
tb.setSpouse(tb2);
|
||||
DataBinder db = new DataBinder(tb, "tb");
|
||||
db.setValidator(new TestBeanValidator());
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.add("spouse.age", "argh");
|
||||
db.bind(pvs);
|
||||
Errors errors = db.getBindingResult();
|
||||
Validator testValidator = new TestBeanValidator();
|
||||
testValidator.validate(tb, errors);
|
||||
|
||||
errors.setNestedPath("spouse");
|
||||
assertThat(errors.getNestedPath()).isEqualTo("spouse.");
|
||||
@@ -1148,7 +1149,7 @@ class DataBinderTests {
|
||||
spouseValidator.validate(tb.getSpouse(), errors);
|
||||
|
||||
errors.setNestedPath("");
|
||||
assertThat(errors.getNestedPath()).isEqualTo("");
|
||||
assertThat(errors.getNestedPath()).isEmpty();
|
||||
errors.pushNestedPath("spouse");
|
||||
assertThat(errors.getNestedPath()).isEqualTo("spouse.");
|
||||
errors.pushNestedPath("spouse");
|
||||
@@ -1156,7 +1157,7 @@ class DataBinderTests {
|
||||
errors.popNestedPath();
|
||||
assertThat(errors.getNestedPath()).isEqualTo("spouse.");
|
||||
errors.popNestedPath();
|
||||
assertThat(errors.getNestedPath()).isEqualTo("");
|
||||
assertThat(errors.getNestedPath()).isEmpty();
|
||||
try {
|
||||
errors.popNestedPath();
|
||||
}
|
||||
@@ -1166,7 +1167,7 @@ class DataBinderTests {
|
||||
errors.pushNestedPath("spouse");
|
||||
assertThat(errors.getNestedPath()).isEqualTo("spouse.");
|
||||
errors.setNestedPath("");
|
||||
assertThat(errors.getNestedPath()).isEqualTo("");
|
||||
assertThat(errors.getNestedPath()).isEmpty();
|
||||
try {
|
||||
errors.popNestedPath();
|
||||
}
|
||||
@@ -1187,8 +1188,7 @@ class DataBinderTests {
|
||||
void validatorWithErrors() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setSpouse(new TestBean());
|
||||
|
||||
Errors errors = new BeanPropertyBindingResult(tb, "tb");
|
||||
Errors errors = new DataBinder(tb, "tb").getBindingResult();
|
||||
|
||||
Validator testValidator = new TestBeanValidator();
|
||||
testValidator.validate(tb, errors);
|
||||
@@ -1201,7 +1201,11 @@ class DataBinderTests {
|
||||
errors.setNestedPath("");
|
||||
assertThat(errors.hasErrors()).isTrue();
|
||||
assertThat(errors.getErrorCount()).isEqualTo(6);
|
||||
assertThat(errors.getAllErrors())
|
||||
.containsAll(errors.getGlobalErrors())
|
||||
.containsAll(errors.getFieldErrors());
|
||||
|
||||
assertThat(errors.hasGlobalErrors()).isTrue();
|
||||
assertThat(errors.getGlobalErrorCount()).isEqualTo(2);
|
||||
assertThat(errors.getGlobalError().getCode()).isEqualTo("NAME_TOUCHY_MISMATCH");
|
||||
assertThat((errors.getGlobalErrors().get(0)).getCode()).isEqualTo("NAME_TOUCHY_MISMATCH");
|
||||
@@ -1257,10 +1261,11 @@ class DataBinderTests {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setSpouse(new TestBean());
|
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(tb, "tb");
|
||||
DataBinder dataBinder = new DataBinder(tb, "tb");
|
||||
DefaultMessageCodesResolver codesResolver = new DefaultMessageCodesResolver();
|
||||
codesResolver.setPrefix("validation.");
|
||||
errors.setMessageCodesResolver(codesResolver);
|
||||
dataBinder.setMessageCodesResolver(codesResolver);
|
||||
Errors errors = dataBinder.getBindingResult();
|
||||
|
||||
Validator testValidator = new TestBeanValidator();
|
||||
testValidator.validate(tb, errors);
|
||||
@@ -1273,7 +1278,11 @@ class DataBinderTests {
|
||||
errors.setNestedPath("");
|
||||
assertThat(errors.hasErrors()).isTrue();
|
||||
assertThat(errors.getErrorCount()).isEqualTo(6);
|
||||
assertThat(errors.getAllErrors())
|
||||
.containsAll(errors.getGlobalErrors())
|
||||
.containsAll(errors.getFieldErrors());
|
||||
|
||||
assertThat(errors.hasGlobalErrors()).isTrue();
|
||||
assertThat(errors.getGlobalErrorCount()).isEqualTo(2);
|
||||
assertThat(errors.getGlobalError().getCode()).isEqualTo("validation.NAME_TOUCHY_MISMATCH");
|
||||
assertThat((errors.getGlobalErrors().get(0)).getCode()).isEqualTo("validation.NAME_TOUCHY_MISMATCH");
|
||||
@@ -1327,9 +1336,11 @@ class DataBinderTests {
|
||||
@Test
|
||||
void validatorWithNestedObjectNull() {
|
||||
TestBean tb = new TestBean();
|
||||
Errors errors = new BeanPropertyBindingResult(tb, "tb");
|
||||
Errors errors = new DataBinder(tb, "tb").getBindingResult();
|
||||
|
||||
Validator testValidator = new TestBeanValidator();
|
||||
testValidator.validate(tb, errors);
|
||||
|
||||
errors.setNestedPath("spouse.");
|
||||
assertThat(errors.getNestedPath()).isEqualTo("spouse.");
|
||||
Validator spouseValidator = new SpouseValidator();
|
||||
@@ -1724,7 +1735,7 @@ class DataBinderTests {
|
||||
pvs.add("stringArray", new String[] {"a1", "b2"});
|
||||
binder.bind(pvs);
|
||||
assertThat(binder.getBindingResult().hasErrors()).isFalse();
|
||||
assertThat(tb.getStringArray().length).isEqualTo(2);
|
||||
assertThat(tb.getStringArray()).hasSize(2);
|
||||
assertThat(tb.getStringArray()[0]).isEqualTo("Xa1");
|
||||
assertThat(tb.getStringArray()[1]).isEqualTo("Xb2");
|
||||
}
|
||||
@@ -1800,16 +1811,16 @@ class DataBinderTests {
|
||||
tb.setName("myName");
|
||||
tb.setAge(99);
|
||||
|
||||
BeanPropertyBindingResult ex = new BeanPropertyBindingResult(tb, "tb");
|
||||
ex.reject("invalid");
|
||||
ex.rejectValue("age", "invalidField");
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(tb, "tb");
|
||||
errors.reject("invalid");
|
||||
errors.rejectValue("age", "invalidField");
|
||||
|
||||
StaticMessageSource ms = new StaticMessageSource();
|
||||
ms.addMessage("invalid", Locale.US, "general error");
|
||||
ms.addMessage("invalidField", Locale.US, "invalid field");
|
||||
|
||||
assertThat(ms.getMessage(ex.getGlobalError(), Locale.US)).isEqualTo("general error");
|
||||
assertThat(ms.getMessage(ex.getFieldError("age"), Locale.US)).isEqualTo("invalid field");
|
||||
assertThat(ms.getMessage(errors.getGlobalError(), Locale.US)).isEqualTo("general error");
|
||||
assertThat(ms.getMessage(errors.getFieldError("age"), Locale.US)).isEqualTo("invalid field");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1877,13 +1888,13 @@ class DataBinderTests {
|
||||
void autoGrowBeyondDefaultLimit() {
|
||||
TestBean testBean = new TestBean();
|
||||
DataBinder binder = new DataBinder(testBean, "testBean");
|
||||
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues();
|
||||
mpvs.add("friends[256]", "");
|
||||
|
||||
assertThatExceptionOfType(InvalidPropertyException.class)
|
||||
.isThrownBy(() -> binder.bind(mpvs))
|
||||
.havingRootCause()
|
||||
.isInstanceOf(IndexOutOfBoundsException.class);
|
||||
.isThrownBy(() -> binder.bind(mpvs))
|
||||
.havingRootCause()
|
||||
.isInstanceOf(IndexOutOfBoundsException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1904,13 +1915,13 @@ class DataBinderTests {
|
||||
TestBean testBean = new TestBean();
|
||||
DataBinder binder = new DataBinder(testBean, "testBean");
|
||||
binder.setAutoGrowCollectionLimit(10);
|
||||
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues();
|
||||
mpvs.add("friends[16]", "");
|
||||
|
||||
assertThatExceptionOfType(InvalidPropertyException.class)
|
||||
.isThrownBy(() -> binder.bind(mpvs))
|
||||
.havingRootCause()
|
||||
.isInstanceOf(IndexOutOfBoundsException.class);
|
||||
.isThrownBy(() -> binder.bind(mpvs))
|
||||
.havingRootCause()
|
||||
.isInstanceOf(IndexOutOfBoundsException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1926,7 +1937,7 @@ class DataBinderTests {
|
||||
List<Object> list = (List<Object>) form.getF().get("list");
|
||||
assertThat(list.get(0)).isEqualTo("firstValue");
|
||||
assertThat(list.get(1)).isEqualTo("secondValue");
|
||||
assertThat(list.size()).isEqualTo(2);
|
||||
assertThat(list).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1959,7 +1970,7 @@ class DataBinderTests {
|
||||
pvs.add("integerList[256]", "1");
|
||||
|
||||
binder.bind(pvs);
|
||||
assertThat(tb.getIntegerList().size()).isEqualTo(257);
|
||||
assertThat(tb.getIntegerList()).hasSize(257);
|
||||
assertThat(tb.getIntegerList().get(256)).isEqualTo(Integer.valueOf(1));
|
||||
assertThat(binder.getBindingResult().getFieldValue("integerList[256]")).isEqualTo(1);
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
public class ValidationUtilsTests {
|
||||
|
||||
@Test
|
||||
public void testInvokeValidatorWithNullValidator() throws Exception {
|
||||
public void testInvokeValidatorWithNullValidator() {
|
||||
TestBean tb = new TestBean();
|
||||
Errors errors = new BeanPropertyBindingResult(tb, "tb");
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
@@ -43,14 +43,14 @@ public class ValidationUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeValidatorWithNullErrors() throws Exception {
|
||||
public void testInvokeValidatorWithNullErrors() {
|
||||
TestBean tb = new TestBean();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
ValidationUtils.invokeValidator(new EmptyValidator(), tb, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeValidatorSunnyDay() throws Exception {
|
||||
public void testInvokeValidatorSunnyDay() {
|
||||
TestBean tb = new TestBean();
|
||||
Errors errors = new BeanPropertyBindingResult(tb, "tb");
|
||||
ValidationUtils.invokeValidator(new EmptyValidator(), tb, errors);
|
||||
@@ -59,7 +59,7 @@ public class ValidationUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationUtilsSunnyDay() throws Exception {
|
||||
public void testValidationUtilsSunnyDay() {
|
||||
TestBean tb = new TestBean("");
|
||||
|
||||
Validator testValidator = new EmptyValidator();
|
||||
@@ -75,7 +75,7 @@ public class ValidationUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationUtilsNull() throws Exception {
|
||||
public void testValidationUtilsNull() {
|
||||
TestBean tb = new TestBean();
|
||||
Errors errors = new BeanPropertyBindingResult(tb, "tb");
|
||||
Validator testValidator = new EmptyValidator();
|
||||
@@ -85,7 +85,7 @@ public class ValidationUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationUtilsEmpty() throws Exception {
|
||||
public void testValidationUtilsEmpty() {
|
||||
TestBean tb = new TestBean("");
|
||||
Errors errors = new BeanPropertyBindingResult(tb, "tb");
|
||||
Validator testValidator = new EmptyValidator();
|
||||
@@ -113,7 +113,7 @@ public class ValidationUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationUtilsEmptyOrWhitespace() throws Exception {
|
||||
public void testValidationUtilsEmptyOrWhitespace() {
|
||||
TestBean tb = new TestBean();
|
||||
Validator testValidator = new EmptyOrWhitespaceValidator();
|
||||
|
||||
|
||||
@@ -16,12 +16,24 @@
|
||||
package org.springframework.cglib.beans;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.*;
|
||||
import java.security.ProtectionDomain;
|
||||
import org.springframework.cglib.core.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.asm.Type;
|
||||
import java.util.*;
|
||||
import org.springframework.cglib.core.AbstractClassGenerator;
|
||||
import org.springframework.cglib.core.ClassEmitter;
|
||||
import org.springframework.cglib.core.CodeEmitter;
|
||||
import org.springframework.cglib.core.Constants;
|
||||
import org.springframework.cglib.core.Converter;
|
||||
import org.springframework.cglib.core.EmitUtils;
|
||||
import org.springframework.cglib.core.KeyFactory;
|
||||
import org.springframework.cglib.core.Local;
|
||||
import org.springframework.cglib.core.MethodInfo;
|
||||
import org.springframework.cglib.core.ReflectUtils;
|
||||
import org.springframework.cglib.core.Signature;
|
||||
import org.springframework.cglib.core.TypeUtils;
|
||||
|
||||
/**
|
||||
* @author Chris Nokleberg
|
||||
@@ -29,151 +41,154 @@ import java.util.*;
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
abstract public class BeanCopier
|
||||
{
|
||||
private static final BeanCopierKey KEY_FACTORY =
|
||||
(BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
|
||||
private static final Type CONVERTER =
|
||||
TypeUtils.parseType("org.springframework.cglib.core.Converter");
|
||||
private static final Type BEAN_COPIER =
|
||||
TypeUtils.parseType("org.springframework.cglib.beans.BeanCopier");
|
||||
private static final Signature COPY =
|
||||
new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER });
|
||||
private static final Signature CONVERT =
|
||||
TypeUtils.parseSignature("Object convert(Object, Class, Object)");
|
||||
|
||||
interface BeanCopierKey {
|
||||
public Object newInstance(String source, String target, boolean useConverter);
|
||||
}
|
||||
private static final BeanCopierKey KEY_FACTORY =
|
||||
(BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
|
||||
private static final Type CONVERTER =
|
||||
TypeUtils.parseType("org.springframework.cglib.core.Converter");
|
||||
private static final Type BEAN_COPIER =
|
||||
TypeUtils.parseType("org.springframework.cglib.beans.BeanCopier");
|
||||
private static final Signature COPY =
|
||||
new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER });
|
||||
private static final Signature CONVERT =
|
||||
TypeUtils.parseSignature("Object convert(Object, Class, Object)");
|
||||
|
||||
public static BeanCopier create(Class source, Class target, boolean useConverter) {
|
||||
Generator gen = new Generator();
|
||||
gen.setSource(source);
|
||||
gen.setTarget(target);
|
||||
gen.setUseConverter(useConverter);
|
||||
return gen.create();
|
||||
}
|
||||
interface BeanCopierKey {
|
||||
public Object newInstance(String source, String target, boolean useConverter);
|
||||
}
|
||||
|
||||
abstract public void copy(Object from, Object to, Converter converter);
|
||||
public static BeanCopier create(Class source, Class target, boolean useConverter) {
|
||||
Generator gen = new Generator();
|
||||
gen.setSource(source);
|
||||
gen.setTarget(target);
|
||||
gen.setUseConverter(useConverter);
|
||||
return gen.create();
|
||||
}
|
||||
|
||||
public static class Generator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(BeanCopier.class.getName());
|
||||
private Class source;
|
||||
private Class target;
|
||||
private boolean useConverter;
|
||||
abstract public void copy(Object from, Object to, Converter converter);
|
||||
|
||||
public Generator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
public static class Generator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(BeanCopier.class.getName());
|
||||
private Class source;
|
||||
private Class target;
|
||||
private boolean useConverter;
|
||||
|
||||
public void setSource(Class source) {
|
||||
if(!Modifier.isPublic(source.getModifiers())){
|
||||
setNamePrefix(source.getName());
|
||||
}
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public void setTarget(Class target) {
|
||||
if(!Modifier.isPublic(target.getModifiers())){
|
||||
setNamePrefix(target.getName());
|
||||
}
|
||||
this.target = target;
|
||||
public Generator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
|
||||
public void setSource(Class source) {
|
||||
this.source = source;
|
||||
// SPRING PATCH BEGIN
|
||||
setContextClass(source);
|
||||
setNamePrefix(source.getName());
|
||||
// SPRING PATCH END
|
||||
}
|
||||
|
||||
public void setTarget(Class target) {
|
||||
this.target = target;
|
||||
// SPRING PATCH BEGIN
|
||||
setContextClass(target);
|
||||
setNamePrefix(target.getName());
|
||||
// SPRING PATCH END
|
||||
}
|
||||
}
|
||||
|
||||
public void setUseConverter(boolean useConverter) {
|
||||
this.useConverter = useConverter;
|
||||
}
|
||||
public void setUseConverter(boolean useConverter) {
|
||||
this.useConverter = useConverter;
|
||||
}
|
||||
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
return source.getClassLoader();
|
||||
}
|
||||
@Override
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
return source.getClassLoader();
|
||||
}
|
||||
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(source);
|
||||
}
|
||||
@Override
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(source);
|
||||
}
|
||||
|
||||
public BeanCopier create() {
|
||||
Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
|
||||
return (BeanCopier)super.create(key);
|
||||
}
|
||||
public BeanCopier create() {
|
||||
Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
|
||||
return (BeanCopier)super.create(key);
|
||||
}
|
||||
|
||||
public void generateClass(ClassVisitor v) {
|
||||
Type sourceType = Type.getType(source);
|
||||
Type targetType = Type.getType(target);
|
||||
ClassEmitter ce = new ClassEmitter(v);
|
||||
ce.begin_class(Constants.V1_8,
|
||||
Constants.ACC_PUBLIC,
|
||||
getClassName(),
|
||||
BEAN_COPIER,
|
||||
null,
|
||||
Constants.SOURCE_FILE);
|
||||
@Override
|
||||
public void generateClass(ClassVisitor v) {
|
||||
Type sourceType = Type.getType(source);
|
||||
Type targetType = Type.getType(target);
|
||||
ClassEmitter ce = new ClassEmitter(v);
|
||||
ce.begin_class(Constants.V1_8,
|
||||
Constants.ACC_PUBLIC,
|
||||
getClassName(),
|
||||
BEAN_COPIER,
|
||||
null,
|
||||
Constants.SOURCE_FILE);
|
||||
|
||||
EmitUtils.null_constructor(ce);
|
||||
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
|
||||
PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
|
||||
PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);
|
||||
EmitUtils.null_constructor(ce);
|
||||
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
|
||||
PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
|
||||
PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);
|
||||
|
||||
Map names = new HashMap();
|
||||
for (int i = 0; i < getters.length; i++) {
|
||||
names.put(getters[i].getName(), getters[i]);
|
||||
}
|
||||
Local targetLocal = e.make_local();
|
||||
Local sourceLocal = e.make_local();
|
||||
if (useConverter) {
|
||||
e.load_arg(1);
|
||||
e.checkcast(targetType);
|
||||
e.store_local(targetLocal);
|
||||
e.load_arg(0);
|
||||
e.checkcast(sourceType);
|
||||
e.store_local(sourceLocal);
|
||||
} else {
|
||||
e.load_arg(1);
|
||||
e.checkcast(targetType);
|
||||
e.load_arg(0);
|
||||
e.checkcast(sourceType);
|
||||
}
|
||||
for (int i = 0; i < setters.length; i++) {
|
||||
PropertyDescriptor setter = setters[i];
|
||||
PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
|
||||
if (getter != null) {
|
||||
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
|
||||
MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
|
||||
if (useConverter) {
|
||||
Type setterType = write.getSignature().getArgumentTypes()[0];
|
||||
e.load_local(targetLocal);
|
||||
e.load_arg(2);
|
||||
e.load_local(sourceLocal);
|
||||
e.invoke(read);
|
||||
e.box(read.getSignature().getReturnType());
|
||||
EmitUtils.load_class(e, setterType);
|
||||
e.push(write.getSignature().getName());
|
||||
e.invoke_interface(CONVERTER, CONVERT);
|
||||
e.unbox_or_zero(setterType);
|
||||
e.invoke(write);
|
||||
} else if (compatible(getter, setter)) {
|
||||
e.dup2();
|
||||
e.invoke(read);
|
||||
e.invoke(write);
|
||||
}
|
||||
}
|
||||
}
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
ce.end_class();
|
||||
}
|
||||
Map names = new HashMap();
|
||||
for (PropertyDescriptor getter : getters) {
|
||||
names.put(getter.getName(), getter);
|
||||
}
|
||||
Local targetLocal = e.make_local();
|
||||
Local sourceLocal = e.make_local();
|
||||
if (useConverter) {
|
||||
e.load_arg(1);
|
||||
e.checkcast(targetType);
|
||||
e.store_local(targetLocal);
|
||||
e.load_arg(0);
|
||||
e.checkcast(sourceType);
|
||||
e.store_local(sourceLocal);
|
||||
} else {
|
||||
e.load_arg(1);
|
||||
e.checkcast(targetType);
|
||||
e.load_arg(0);
|
||||
e.checkcast(sourceType);
|
||||
}
|
||||
for (PropertyDescriptor setter : setters) {
|
||||
PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
|
||||
if (getter != null) {
|
||||
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
|
||||
MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
|
||||
if (useConverter) {
|
||||
Type setterType = write.getSignature().getArgumentTypes()[0];
|
||||
e.load_local(targetLocal);
|
||||
e.load_arg(2);
|
||||
e.load_local(sourceLocal);
|
||||
e.invoke(read);
|
||||
e.box(read.getSignature().getReturnType());
|
||||
EmitUtils.load_class(e, setterType);
|
||||
e.push(write.getSignature().getName());
|
||||
e.invoke_interface(CONVERTER, CONVERT);
|
||||
e.unbox_or_zero(setterType);
|
||||
e.invoke(write);
|
||||
} else if (compatible(getter, setter)) {
|
||||
e.dup2();
|
||||
e.invoke(read);
|
||||
e.invoke(write);
|
||||
}
|
||||
}
|
||||
}
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
ce.end_class();
|
||||
}
|
||||
|
||||
private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
|
||||
// TODO: allow automatic widening conversions?
|
||||
return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
|
||||
}
|
||||
private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
|
||||
// TODO: allow automatic widening conversions?
|
||||
return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
|
||||
}
|
||||
|
||||
protected Object firstInstance(Class type) {
|
||||
return ReflectUtils.newInstance(type);
|
||||
}
|
||||
@Override
|
||||
protected Object firstInstance(Class type) {
|
||||
return ReflectUtils.newInstance(type);
|
||||
}
|
||||
|
||||
protected Object nextInstance(Object instance) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected Object nextInstance(Object instance) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,18 @@ package org.springframework.cglib.beans;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.*;
|
||||
import org.springframework.cglib.core.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.cglib.core.AbstractClassGenerator;
|
||||
import org.springframework.cglib.core.ClassEmitter;
|
||||
import org.springframework.cglib.core.Constants;
|
||||
import org.springframework.cglib.core.EmitUtils;
|
||||
import org.springframework.cglib.core.KeyFactory;
|
||||
import org.springframework.cglib.core.ReflectUtils;
|
||||
|
||||
/**
|
||||
* @author Juozas Baliuka, Chris Nokleberg
|
||||
@@ -28,126 +36,131 @@ import org.springframework.asm.Type;
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public class BeanGenerator extends AbstractClassGenerator
|
||||
{
|
||||
private static final Source SOURCE = new Source(BeanGenerator.class.getName());
|
||||
private static final BeanGeneratorKey KEY_FACTORY =
|
||||
(BeanGeneratorKey)KeyFactory.create(BeanGeneratorKey.class);
|
||||
|
||||
interface BeanGeneratorKey {
|
||||
public Object newInstance(String superclass, Map props);
|
||||
}
|
||||
private static final Source SOURCE = new Source(BeanGenerator.class.getName());
|
||||
private static final BeanGeneratorKey KEY_FACTORY =
|
||||
(BeanGeneratorKey)KeyFactory.create(BeanGeneratorKey.class);
|
||||
|
||||
private Class superclass;
|
||||
private Map props = new HashMap();
|
||||
private boolean classOnly;
|
||||
interface BeanGeneratorKey {
|
||||
public Object newInstance(String superclass, Map props);
|
||||
}
|
||||
|
||||
public BeanGenerator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
private Class superclass;
|
||||
private Map props = new HashMap();
|
||||
private boolean classOnly;
|
||||
|
||||
/**
|
||||
* Set the class which the generated class will extend. The class
|
||||
* must not be declared as final, and must have a non-private
|
||||
* no-argument constructor.
|
||||
* @param superclass class to extend, or null to extend Object
|
||||
*/
|
||||
public void setSuperclass(Class superclass) {
|
||||
if (superclass != null && superclass.equals(Object.class)) {
|
||||
superclass = null;
|
||||
}
|
||||
this.superclass = superclass;
|
||||
public BeanGenerator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class which the generated class will extend. The class
|
||||
* must not be declared as final, and must have a non-private
|
||||
* no-argument constructor.
|
||||
* @param superclass class to extend, or null to extend Object
|
||||
*/
|
||||
public void setSuperclass(Class superclass) {
|
||||
if (superclass != null && superclass.equals(Object.class)) {
|
||||
superclass = null;
|
||||
}
|
||||
this.superclass = superclass;
|
||||
// SPRING PATCH BEGIN
|
||||
setContextClass(superclass);
|
||||
// SPRING PATCH END
|
||||
}
|
||||
}
|
||||
|
||||
public void addProperty(String name, Class type) {
|
||||
if (props.containsKey(name)) {
|
||||
throw new IllegalArgumentException("Duplicate property name \"" + name + "\"");
|
||||
}
|
||||
props.put(name, Type.getType(type));
|
||||
}
|
||||
public void addProperty(String name, Class type) {
|
||||
if (props.containsKey(name)) {
|
||||
throw new IllegalArgumentException("Duplicate property name \"" + name + "\"");
|
||||
}
|
||||
props.put(name, Type.getType(type));
|
||||
}
|
||||
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
if (superclass != null) {
|
||||
return superclass.getClassLoader();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
if (superclass != null) {
|
||||
return superclass.getClassLoader();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(superclass);
|
||||
}
|
||||
@Override
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(superclass);
|
||||
}
|
||||
|
||||
public Object create() {
|
||||
classOnly = false;
|
||||
return createHelper();
|
||||
}
|
||||
public Object create() {
|
||||
classOnly = false;
|
||||
return createHelper();
|
||||
}
|
||||
|
||||
public Object createClass() {
|
||||
classOnly = true;
|
||||
return createHelper();
|
||||
}
|
||||
public Object createClass() {
|
||||
classOnly = true;
|
||||
return createHelper();
|
||||
}
|
||||
|
||||
private Object createHelper() {
|
||||
if (superclass != null) {
|
||||
setNamePrefix(superclass.getName());
|
||||
}
|
||||
String superName = (superclass != null) ? superclass.getName() : "java.lang.Object";
|
||||
Object key = KEY_FACTORY.newInstance(superName, props);
|
||||
return super.create(key);
|
||||
}
|
||||
private Object createHelper() {
|
||||
if (superclass != null) {
|
||||
setNamePrefix(superclass.getName());
|
||||
}
|
||||
String superName = (superclass != null) ? superclass.getName() : "java.lang.Object";
|
||||
Object key = KEY_FACTORY.newInstance(superName, props);
|
||||
return super.create(key);
|
||||
}
|
||||
|
||||
public void generateClass(ClassVisitor v) throws Exception {
|
||||
int size = props.size();
|
||||
String[] names = (String[])props.keySet().toArray(new String[size]);
|
||||
Type[] types = new Type[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
types[i] = (Type)props.get(names[i]);
|
||||
}
|
||||
ClassEmitter ce = new ClassEmitter(v);
|
||||
ce.begin_class(Constants.V1_8,
|
||||
Constants.ACC_PUBLIC,
|
||||
getClassName(),
|
||||
superclass != null ? Type.getType(superclass) : Constants.TYPE_OBJECT,
|
||||
null,
|
||||
null);
|
||||
EmitUtils.null_constructor(ce);
|
||||
EmitUtils.add_properties(ce, names, types);
|
||||
ce.end_class();
|
||||
}
|
||||
@Override
|
||||
public void generateClass(ClassVisitor v) throws Exception {
|
||||
int size = props.size();
|
||||
String[] names = (String[])props.keySet().toArray(new String[size]);
|
||||
Type[] types = new Type[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
types[i] = (Type)props.get(names[i]);
|
||||
}
|
||||
ClassEmitter ce = new ClassEmitter(v);
|
||||
ce.begin_class(Constants.V1_8,
|
||||
Constants.ACC_PUBLIC,
|
||||
getClassName(),
|
||||
superclass != null ? Type.getType(superclass) : Constants.TYPE_OBJECT,
|
||||
null,
|
||||
null);
|
||||
EmitUtils.null_constructor(ce);
|
||||
EmitUtils.add_properties(ce, names, types);
|
||||
ce.end_class();
|
||||
}
|
||||
|
||||
protected Object firstInstance(Class type) {
|
||||
if (classOnly) {
|
||||
return type;
|
||||
} else {
|
||||
return ReflectUtils.newInstance(type);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected Object firstInstance(Class type) {
|
||||
if (classOnly) {
|
||||
return type;
|
||||
} else {
|
||||
return ReflectUtils.newInstance(type);
|
||||
}
|
||||
}
|
||||
|
||||
protected Object nextInstance(Object instance) {
|
||||
Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
|
||||
if (classOnly) {
|
||||
return protoclass;
|
||||
} else {
|
||||
return ReflectUtils.newInstance(protoclass);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected Object nextInstance(Object instance) {
|
||||
Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
|
||||
if (classOnly) {
|
||||
return protoclass;
|
||||
} else {
|
||||
return ReflectUtils.newInstance(protoclass);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addProperties(BeanGenerator gen, Map props) {
|
||||
for (Iterator it = props.keySet().iterator(); it.hasNext();) {
|
||||
String name = (String)it.next();
|
||||
gen.addProperty(name, (Class)props.get(name));
|
||||
}
|
||||
}
|
||||
public static void addProperties(BeanGenerator gen, Map props) {
|
||||
for (Iterator it = props.keySet().iterator(); it.hasNext();) {
|
||||
String name = (String)it.next();
|
||||
gen.addProperty(name, (Class)props.get(name));
|
||||
}
|
||||
}
|
||||
|
||||
public static void addProperties(BeanGenerator gen, Class type) {
|
||||
addProperties(gen, ReflectUtils.getBeanProperties(type));
|
||||
}
|
||||
public static void addProperties(BeanGenerator gen, Class type) {
|
||||
addProperties(gen, ReflectUtils.getBeanProperties(type));
|
||||
}
|
||||
|
||||
public static void addProperties(BeanGenerator gen, PropertyDescriptor[] descriptors) {
|
||||
for (int i = 0; i < descriptors.length; i++) {
|
||||
gen.addProperty(descriptors[i].getName(), descriptors[i].getPropertyType());
|
||||
}
|
||||
}
|
||||
public static void addProperties(BeanGenerator gen, PropertyDescriptor[] descriptors) {
|
||||
for (PropertyDescriptor descriptor : descriptors) {
|
||||
gen.addProperty(descriptor.getName(), descriptor.getPropertyType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,293 +41,310 @@ import org.springframework.cglib.core.ReflectUtils;
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
abstract public class BeanMap implements Map {
|
||||
/**
|
||||
* Limit the properties reflected in the key set of the map
|
||||
* to readable properties.
|
||||
* @see BeanMap.Generator#setRequire
|
||||
*/
|
||||
public static final int REQUIRE_GETTER = 1;
|
||||
/**
|
||||
* Limit the properties reflected in the key set of the map
|
||||
* to readable properties.
|
||||
* @see BeanMap.Generator#setRequire
|
||||
*/
|
||||
public static final int REQUIRE_GETTER = 1;
|
||||
|
||||
/**
|
||||
* Limit the properties reflected in the key set of the map
|
||||
* to writable properties.
|
||||
* @see BeanMap.Generator#setRequire
|
||||
*/
|
||||
public static final int REQUIRE_SETTER = 2;
|
||||
|
||||
/**
|
||||
* Helper method to create a new <code>BeanMap</code>. For finer
|
||||
* control over the generated instance, use a new instance of
|
||||
* <code>BeanMap.Generator</code> instead of this static method.
|
||||
* @param bean the JavaBean underlying the map
|
||||
* @return a new <code>BeanMap</code> instance
|
||||
*/
|
||||
public static BeanMap create(Object bean) {
|
||||
Generator gen = new Generator();
|
||||
gen.setBean(bean);
|
||||
return gen.create();
|
||||
}
|
||||
/**
|
||||
* Limit the properties reflected in the key set of the map
|
||||
* to writable properties.
|
||||
* @see BeanMap.Generator#setRequire
|
||||
*/
|
||||
public static final int REQUIRE_SETTER = 2;
|
||||
|
||||
public static class Generator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(BeanMap.class.getName());
|
||||
/**
|
||||
* Helper method to create a new <code>BeanMap</code>. For finer
|
||||
* control over the generated instance, use a new instance of
|
||||
* <code>BeanMap.Generator</code> instead of this static method.
|
||||
* @param bean the JavaBean underlying the map
|
||||
* @return a new <code>BeanMap</code> instance
|
||||
*/
|
||||
public static BeanMap create(Object bean) {
|
||||
Generator gen = new Generator();
|
||||
gen.setBean(bean);
|
||||
return gen.create();
|
||||
}
|
||||
|
||||
private static final BeanMapKey KEY_FACTORY =
|
||||
(BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME);
|
||||
public static class Generator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(BeanMap.class.getName());
|
||||
|
||||
interface BeanMapKey {
|
||||
public Object newInstance(Class type, int require);
|
||||
}
|
||||
|
||||
private Object bean;
|
||||
private Class beanClass;
|
||||
private int require;
|
||||
|
||||
public Generator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
private static final BeanMapKey KEY_FACTORY =
|
||||
(BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME);
|
||||
|
||||
/**
|
||||
* Set the bean that the generated map should reflect. The bean may be swapped
|
||||
* out for another bean of the same type using {@link #setBean}.
|
||||
* Calling this method overrides any value previously set using {@link #setBeanClass}.
|
||||
* You must call either this method or {@link #setBeanClass} before {@link #create}.
|
||||
* @param bean the initial bean
|
||||
*/
|
||||
public void setBean(Object bean) {
|
||||
this.bean = bean;
|
||||
if (bean != null) {
|
||||
interface BeanMapKey {
|
||||
public Object newInstance(Class type, int require);
|
||||
}
|
||||
|
||||
private Object bean;
|
||||
private Class beanClass;
|
||||
private int require;
|
||||
|
||||
public Generator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean that the generated map should reflect. The bean may be swapped
|
||||
* out for another bean of the same type using {@link #setBean}.
|
||||
* Calling this method overrides any value previously set using {@link #setBeanClass}.
|
||||
* You must call either this method or {@link #setBeanClass} before {@link #create}.
|
||||
* @param bean the initial bean
|
||||
*/
|
||||
public void setBean(Object bean) {
|
||||
this.bean = bean;
|
||||
if (bean != null) {
|
||||
beanClass = bean.getClass();
|
||||
// SPRING PATCH BEGIN
|
||||
setContextClass(beanClass);
|
||||
// SPRING PATCH END
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class of the bean that the generated map should support.
|
||||
* You must call either this method or {@link #setBeanClass} before {@link #create}.
|
||||
* @param beanClass the class of the bean
|
||||
*/
|
||||
public void setBeanClass(Class beanClass) {
|
||||
this.beanClass = beanClass;
|
||||
}
|
||||
/**
|
||||
* Set the class of the bean that the generated map should support.
|
||||
* You must call either this method or {@link #setBeanClass} before {@link #create}.
|
||||
* @param beanClass the class of the bean
|
||||
*/
|
||||
public void setBeanClass(Class beanClass) {
|
||||
this.beanClass = beanClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the properties reflected by the generated map.
|
||||
* @param require any combination of {@link #REQUIRE_GETTER} and
|
||||
* {@link #REQUIRE_SETTER}; default is zero (any property allowed)
|
||||
*/
|
||||
public void setRequire(int require) {
|
||||
this.require = require;
|
||||
}
|
||||
/**
|
||||
* Limit the properties reflected by the generated map.
|
||||
* @param require any combination of {@link #REQUIRE_GETTER} and
|
||||
* {@link #REQUIRE_SETTER}; default is zero (any property allowed)
|
||||
*/
|
||||
public void setRequire(int require) {
|
||||
this.require = require;
|
||||
}
|
||||
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
return beanClass.getClassLoader();
|
||||
}
|
||||
@Override
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
return beanClass.getClassLoader();
|
||||
}
|
||||
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(beanClass);
|
||||
}
|
||||
@Override
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(beanClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>BeanMap</code>. An existing
|
||||
* generated class will be reused if possible.
|
||||
*/
|
||||
public BeanMap create() {
|
||||
if (beanClass == null)
|
||||
throw new IllegalArgumentException("Class of bean unknown");
|
||||
setNamePrefix(beanClass.getName());
|
||||
return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require));
|
||||
}
|
||||
/**
|
||||
* Create a new instance of the <code>BeanMap</code>. An existing
|
||||
* generated class will be reused if possible.
|
||||
*/
|
||||
public BeanMap create() {
|
||||
if (beanClass == null) {
|
||||
throw new IllegalArgumentException("Class of bean unknown");
|
||||
}
|
||||
setNamePrefix(beanClass.getName());
|
||||
return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require));
|
||||
}
|
||||
|
||||
public void generateClass(ClassVisitor v) throws Exception {
|
||||
new BeanMapEmitter(v, getClassName(), beanClass, require);
|
||||
}
|
||||
@Override
|
||||
public void generateClass(ClassVisitor v) throws Exception {
|
||||
new BeanMapEmitter(v, getClassName(), beanClass, require);
|
||||
}
|
||||
|
||||
protected Object firstInstance(Class type) {
|
||||
return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean);
|
||||
}
|
||||
@Override
|
||||
protected Object firstInstance(Class type) {
|
||||
return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean);
|
||||
}
|
||||
|
||||
protected Object nextInstance(Object instance) {
|
||||
return ((BeanMap)instance).newInstance(bean);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected Object nextInstance(Object instance) {
|
||||
return ((BeanMap)instance).newInstance(bean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <code>BeanMap</code> instance using the specified bean.
|
||||
* This is faster than using the {@link #create} static method.
|
||||
* @param bean the JavaBean underlying the map
|
||||
* @return a new <code>BeanMap</code> instance
|
||||
*/
|
||||
abstract public BeanMap newInstance(Object bean);
|
||||
/**
|
||||
* Create a new <code>BeanMap</code> instance using the specified bean.
|
||||
* This is faster than using the {@link #create} static method.
|
||||
* @param bean the JavaBean underlying the map
|
||||
* @return a new <code>BeanMap</code> instance
|
||||
*/
|
||||
abstract public BeanMap newInstance(Object bean);
|
||||
|
||||
/**
|
||||
* Get the type of a property.
|
||||
* @param name the name of the JavaBean property
|
||||
* @return the type of the property, or null if the property does not exist
|
||||
*/
|
||||
abstract public Class getPropertyType(String name);
|
||||
/**
|
||||
* Get the type of a property.
|
||||
* @param name the name of the JavaBean property
|
||||
* @return the type of the property, or null if the property does not exist
|
||||
*/
|
||||
abstract public Class getPropertyType(String name);
|
||||
|
||||
protected Object bean;
|
||||
protected Object bean;
|
||||
|
||||
protected BeanMap() {
|
||||
}
|
||||
protected BeanMap() {
|
||||
}
|
||||
|
||||
protected BeanMap(Object bean) {
|
||||
setBean(bean);
|
||||
}
|
||||
protected BeanMap(Object bean) {
|
||||
setBean(bean);
|
||||
}
|
||||
|
||||
public Object get(Object key) {
|
||||
return get(bean, key);
|
||||
}
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
return get(bean, key);
|
||||
}
|
||||
|
||||
public Object put(Object key, Object value) {
|
||||
return put(bean, key, value);
|
||||
}
|
||||
@Override
|
||||
public Object put(Object key, Object value) {
|
||||
return put(bean, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property of a bean. This allows a <code>BeanMap</code>
|
||||
* to be used statically for multiple beans--the bean instance tied to the
|
||||
* map is ignored and the bean passed to this method is used instead.
|
||||
* @param bean the bean to query; must be compatible with the type of
|
||||
* this <code>BeanMap</code>
|
||||
* @param key must be a String
|
||||
* @return the current value, or null if there is no matching property
|
||||
*/
|
||||
abstract public Object get(Object bean, Object key);
|
||||
/**
|
||||
* Get the property of a bean. This allows a <code>BeanMap</code>
|
||||
* to be used statically for multiple beans--the bean instance tied to the
|
||||
* map is ignored and the bean passed to this method is used instead.
|
||||
* @param bean the bean to query; must be compatible with the type of
|
||||
* this <code>BeanMap</code>
|
||||
* @param key must be a String
|
||||
* @return the current value, or null if there is no matching property
|
||||
*/
|
||||
abstract public Object get(Object bean, Object key);
|
||||
|
||||
/**
|
||||
* Set the property of a bean. This allows a <code>BeanMap</code>
|
||||
* to be used statically for multiple beans--the bean instance tied to the
|
||||
* map is ignored and the bean passed to this method is used instead.
|
||||
* @param key must be a String
|
||||
* @return the old value, if there was one, or null
|
||||
*/
|
||||
abstract public Object put(Object bean, Object key, Object value);
|
||||
/**
|
||||
* Set the property of a bean. This allows a <code>BeanMap</code>
|
||||
* to be used statically for multiple beans--the bean instance tied to the
|
||||
* map is ignored and the bean passed to this method is used instead.
|
||||
* @param key must be a String
|
||||
* @return the old value, if there was one, or null
|
||||
*/
|
||||
abstract public Object put(Object bean, Object key, Object value);
|
||||
|
||||
/**
|
||||
* Change the underlying bean this map should use.
|
||||
* @param bean the new JavaBean
|
||||
* @see #getBean
|
||||
*/
|
||||
public void setBean(Object bean) {
|
||||
this.bean = bean;
|
||||
}
|
||||
/**
|
||||
* Change the underlying bean this map should use.
|
||||
* @param bean the new JavaBean
|
||||
* @see #getBean
|
||||
*/
|
||||
public void setBean(Object bean) {
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean currently in use by this map.
|
||||
* @return the current JavaBean
|
||||
* @see #setBean
|
||||
*/
|
||||
public Object getBean() {
|
||||
return bean;
|
||||
}
|
||||
/**
|
||||
* Return the bean currently in use by this map.
|
||||
* @return the current JavaBean
|
||||
* @see #setBean
|
||||
*/
|
||||
public Object getBean() {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return keySet().contains(key);
|
||||
}
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return keySet().contains(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
||||
Object v = get(it.next());
|
||||
if (((value == null) && (v == null)) || (value != null && value.equals(v)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
||||
Object v = get(it.next());
|
||||
if (((value == null) && (v == null)) || (value != null && value.equals(v))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return keySet().size();
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return keySet().size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
public Object remove(Object key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@Override
|
||||
public Object remove(Object key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void putAll(Map t) {
|
||||
for (Iterator it = t.keySet().iterator(); it.hasNext();) {
|
||||
Object key = it.next();
|
||||
put(key, t.get(key));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void putAll(Map t) {
|
||||
for (Object key : t.keySet()) {
|
||||
put(key, t.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof Map)) {
|
||||
return false;
|
||||
}
|
||||
Map other = (Map)o;
|
||||
if (size() != other.size()) {
|
||||
return false;
|
||||
}
|
||||
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
||||
Object key = it.next();
|
||||
if (!other.containsKey(key)) {
|
||||
return false;
|
||||
}
|
||||
Object v1 = get(key);
|
||||
Object v2 = other.get(key);
|
||||
if (!((v1 == null) ? v2 == null : v1.equals(v2))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof Map)) {
|
||||
return false;
|
||||
}
|
||||
Map other = (Map)o;
|
||||
if (size() != other.size()) {
|
||||
return false;
|
||||
}
|
||||
for (Object key : keySet()) {
|
||||
if (!other.containsKey(key)) {
|
||||
return false;
|
||||
}
|
||||
Object v1 = get(key);
|
||||
Object v2 = other.get(key);
|
||||
if (!((v1 == null) ? v2 == null : v1.equals(v2))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int code = 0;
|
||||
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
||||
Object key = it.next();
|
||||
Object value = get(key);
|
||||
code += ((key == null) ? 0 : key.hashCode()) ^
|
||||
((value == null) ? 0 : value.hashCode());
|
||||
}
|
||||
return code;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int code = 0;
|
||||
for (Object key : keySet()) {
|
||||
Object value = get(key);
|
||||
code += ((key == null) ? 0 : key.hashCode()) ^
|
||||
((value == null) ? 0 : value.hashCode());
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
public Set entrySet() {
|
||||
HashMap copy = new HashMap();
|
||||
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
||||
Object key = it.next();
|
||||
copy.put(key, get(key));
|
||||
}
|
||||
return Collections.unmodifiableMap(copy).entrySet();
|
||||
}
|
||||
// TODO: optimize
|
||||
@Override
|
||||
public Set entrySet() {
|
||||
HashMap copy = new HashMap();
|
||||
for (Object key : keySet()) {
|
||||
copy.put(key, get(key));
|
||||
}
|
||||
return Collections.unmodifiableMap(copy).entrySet();
|
||||
}
|
||||
|
||||
public Collection values() {
|
||||
Set keys = keySet();
|
||||
List values = new ArrayList(keys.size());
|
||||
for (Iterator it = keys.iterator(); it.hasNext();) {
|
||||
values.add(get(it.next()));
|
||||
}
|
||||
return Collections.unmodifiableCollection(values);
|
||||
}
|
||||
@Override
|
||||
public Collection values() {
|
||||
Set keys = keySet();
|
||||
List values = new ArrayList(keys.size());
|
||||
for (Iterator it = keys.iterator(); it.hasNext();) {
|
||||
values.add(get(it.next()));
|
||||
}
|
||||
return Collections.unmodifiableCollection(values);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see java.util.AbstractMap#toString
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append('{');
|
||||
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
||||
Object key = it.next();
|
||||
sb.append(key);
|
||||
sb.append('=');
|
||||
sb.append(get(key));
|
||||
if (it.hasNext()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
/*
|
||||
* @see java.util.AbstractMap#toString
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('{');
|
||||
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
||||
Object key = it.next();
|
||||
sb.append(key);
|
||||
sb.append('=');
|
||||
sb.append(get(key));
|
||||
if (it.hasNext()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,23 @@
|
||||
*/
|
||||
package org.springframework.cglib.beans;
|
||||
|
||||
import java.beans.*;
|
||||
import java.util.*;
|
||||
import org.springframework.cglib.core.*;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.cglib.core.ClassEmitter;
|
||||
import org.springframework.cglib.core.CodeEmitter;
|
||||
import org.springframework.cglib.core.Constants;
|
||||
import org.springframework.cglib.core.EmitUtils;
|
||||
import org.springframework.cglib.core.MethodInfo;
|
||||
import org.springframework.cglib.core.ObjectSwitchCallback;
|
||||
import org.springframework.cglib.core.ReflectUtils;
|
||||
import org.springframework.cglib.core.Signature;
|
||||
import org.springframework.cglib.core.TypeUtils;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
class BeanMapEmitter extends ClassEmitter {
|
||||
@@ -50,7 +61,7 @@ class BeanMapEmitter extends ClassEmitter {
|
||||
EmitUtils.null_constructor(this);
|
||||
EmitUtils.factory_method(this, NEW_INSTANCE);
|
||||
generateConstructor();
|
||||
|
||||
|
||||
Map getters = makePropertyMap(ReflectUtils.getBeanGetters(type));
|
||||
Map setters = makePropertyMap(ReflectUtils.getBeanSetters(type));
|
||||
Map allProps = new HashMap();
|
||||
@@ -79,8 +90,8 @@ class BeanMapEmitter extends ClassEmitter {
|
||||
|
||||
private Map makePropertyMap(PropertyDescriptor[] props) {
|
||||
Map names = new HashMap();
|
||||
for (int i = 0; i < props.length; i++) {
|
||||
names.put(props[i].getName(), props[i]);
|
||||
for (PropertyDescriptor prop : props) {
|
||||
names.put(prop.getName(), prop);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
@@ -97,7 +108,7 @@ class BeanMapEmitter extends ClassEmitter {
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
}
|
||||
|
||||
|
||||
private void generateGet(Class type, final Map getters) {
|
||||
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null);
|
||||
e.load_arg(0);
|
||||
@@ -105,14 +116,16 @@ class BeanMapEmitter extends ClassEmitter {
|
||||
e.load_arg(1);
|
||||
e.checkcast(Constants.TYPE_STRING);
|
||||
EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
|
||||
public void processCase(Object key, Label end) {
|
||||
@Override
|
||||
public void processCase(Object key, Label end) {
|
||||
PropertyDescriptor pd = (PropertyDescriptor)getters.get(key);
|
||||
MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod());
|
||||
e.invoke(method);
|
||||
e.box(method.getSignature().getReturnType());
|
||||
e.return_value();
|
||||
}
|
||||
public void processDefault() {
|
||||
@Override
|
||||
public void processDefault() {
|
||||
e.aconst_null();
|
||||
e.return_value();
|
||||
}
|
||||
@@ -127,7 +140,8 @@ class BeanMapEmitter extends ClassEmitter {
|
||||
e.load_arg(1);
|
||||
e.checkcast(Constants.TYPE_STRING);
|
||||
EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
|
||||
public void processCase(Object key, Label end) {
|
||||
@Override
|
||||
public void processCase(Object key, Label end) {
|
||||
PropertyDescriptor pd = (PropertyDescriptor)setters.get(key);
|
||||
if (pd.getReadMethod() == null) {
|
||||
e.aconst_null();
|
||||
@@ -144,7 +158,8 @@ class BeanMapEmitter extends ClassEmitter {
|
||||
e.invoke(write);
|
||||
e.return_value();
|
||||
}
|
||||
public void processDefault() {
|
||||
@Override
|
||||
public void processDefault() {
|
||||
// fall-through
|
||||
}
|
||||
});
|
||||
@@ -152,7 +167,7 @@ class BeanMapEmitter extends ClassEmitter {
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
}
|
||||
|
||||
|
||||
private void generateKeySet(String[] allNames) {
|
||||
// static initializer
|
||||
declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null);
|
||||
@@ -178,12 +193,14 @@ class BeanMapEmitter extends ClassEmitter {
|
||||
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null);
|
||||
e.load_arg(0);
|
||||
EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
|
||||
public void processCase(Object key, Label end) {
|
||||
@Override
|
||||
public void processCase(Object key, Label end) {
|
||||
PropertyDescriptor pd = (PropertyDescriptor)allProps.get(key);
|
||||
EmitUtils.load_class(e, Type.getType(pd.getPropertyType()));
|
||||
e.return_value();
|
||||
}
|
||||
public void processDefault() {
|
||||
@Override
|
||||
public void processDefault() {
|
||||
e.aconst_null();
|
||||
e.return_value();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
package org.springframework.cglib.beans;
|
||||
|
||||
import java.security.ProtectionDomain;
|
||||
import org.springframework.cglib.core.*;
|
||||
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.cglib.core.AbstractClassGenerator;
|
||||
import org.springframework.cglib.core.KeyFactory;
|
||||
import org.springframework.cglib.core.ReflectUtils;
|
||||
|
||||
/**
|
||||
* @author Juozas Baliuka
|
||||
@@ -25,118 +28,123 @@ import org.springframework.asm.ClassVisitor;
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
abstract public class BulkBean
|
||||
{
|
||||
private static final BulkBeanKey KEY_FACTORY =
|
||||
(BulkBeanKey)KeyFactory.create(BulkBeanKey.class);
|
||||
|
||||
interface BulkBeanKey {
|
||||
public Object newInstance(String target, String[] getters, String[] setters, String[] types);
|
||||
}
|
||||
|
||||
protected Class target;
|
||||
protected String[] getters, setters;
|
||||
protected Class[] types;
|
||||
|
||||
protected BulkBean() { }
|
||||
|
||||
abstract public void getPropertyValues(Object bean, Object[] values);
|
||||
abstract public void setPropertyValues(Object bean, Object[] values);
|
||||
private static final BulkBeanKey KEY_FACTORY =
|
||||
(BulkBeanKey)KeyFactory.create(BulkBeanKey.class);
|
||||
|
||||
public Object[] getPropertyValues(Object bean) {
|
||||
Object[] values = new Object[getters.length];
|
||||
getPropertyValues(bean, values);
|
||||
return values;
|
||||
}
|
||||
|
||||
public Class[] getPropertyTypes() {
|
||||
return types.clone();
|
||||
}
|
||||
|
||||
public String[] getGetters() {
|
||||
return getters.clone();
|
||||
}
|
||||
|
||||
public String[] getSetters() {
|
||||
return setters.clone();
|
||||
}
|
||||
interface BulkBeanKey {
|
||||
public Object newInstance(String target, String[] getters, String[] setters, String[] types);
|
||||
}
|
||||
|
||||
public static BulkBean create(Class target, String[] getters, String[] setters, Class[] types) {
|
||||
Generator gen = new Generator();
|
||||
gen.setTarget(target);
|
||||
gen.setGetters(getters);
|
||||
gen.setSetters(setters);
|
||||
gen.setTypes(types);
|
||||
return gen.create();
|
||||
}
|
||||
protected Class target;
|
||||
protected String[] getters, setters;
|
||||
protected Class[] types;
|
||||
|
||||
public static class Generator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(BulkBean.class.getName());
|
||||
private Class target;
|
||||
private String[] getters;
|
||||
private String[] setters;
|
||||
private Class[] types;
|
||||
protected BulkBean() { }
|
||||
|
||||
public Generator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
abstract public void getPropertyValues(Object bean, Object[] values);
|
||||
abstract public void setPropertyValues(Object bean, Object[] values);
|
||||
|
||||
public void setTarget(Class target) {
|
||||
this.target = target;
|
||||
public Object[] getPropertyValues(Object bean) {
|
||||
Object[] values = new Object[getters.length];
|
||||
getPropertyValues(bean, values);
|
||||
return values;
|
||||
}
|
||||
|
||||
public Class[] getPropertyTypes() {
|
||||
return types.clone();
|
||||
}
|
||||
|
||||
public String[] getGetters() {
|
||||
return getters.clone();
|
||||
}
|
||||
|
||||
public String[] getSetters() {
|
||||
return setters.clone();
|
||||
}
|
||||
|
||||
public static BulkBean create(Class target, String[] getters, String[] setters, Class[] types) {
|
||||
Generator gen = new Generator();
|
||||
gen.setTarget(target);
|
||||
gen.setGetters(getters);
|
||||
gen.setSetters(setters);
|
||||
gen.setTypes(types);
|
||||
return gen.create();
|
||||
}
|
||||
|
||||
public static class Generator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(BulkBean.class.getName());
|
||||
private Class target;
|
||||
private String[] getters;
|
||||
private String[] setters;
|
||||
private Class[] types;
|
||||
|
||||
public Generator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
|
||||
public void setTarget(Class target) {
|
||||
this.target = target;
|
||||
// SPRING PATCH BEGIN
|
||||
setContextClass(target);
|
||||
// SPRING PATCH END
|
||||
}
|
||||
}
|
||||
|
||||
public void setGetters(String[] getters) {
|
||||
this.getters = getters;
|
||||
}
|
||||
public void setGetters(String[] getters) {
|
||||
this.getters = getters;
|
||||
}
|
||||
|
||||
public void setSetters(String[] setters) {
|
||||
this.setters = setters;
|
||||
}
|
||||
public void setSetters(String[] setters) {
|
||||
this.setters = setters;
|
||||
}
|
||||
|
||||
public void setTypes(Class[] types) {
|
||||
this.types = types;
|
||||
}
|
||||
public void setTypes(Class[] types) {
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
return target.getClassLoader();
|
||||
}
|
||||
@Override
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
return target.getClassLoader();
|
||||
}
|
||||
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(target);
|
||||
}
|
||||
@Override
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(target);
|
||||
}
|
||||
|
||||
public BulkBean create() {
|
||||
setNamePrefix(target.getName());
|
||||
String targetClassName = target.getName();
|
||||
String[] typeClassNames = ReflectUtils.getNames(types);
|
||||
Object key = KEY_FACTORY.newInstance(targetClassName, getters, setters, typeClassNames);
|
||||
return (BulkBean)super.create(key);
|
||||
}
|
||||
public BulkBean create() {
|
||||
setNamePrefix(target.getName());
|
||||
String targetClassName = target.getName();
|
||||
String[] typeClassNames = ReflectUtils.getNames(types);
|
||||
Object key = KEY_FACTORY.newInstance(targetClassName, getters, setters, typeClassNames);
|
||||
return (BulkBean)super.create(key);
|
||||
}
|
||||
|
||||
public void generateClass(ClassVisitor v) throws Exception {
|
||||
new BulkBeanEmitter(v, getClassName(), target, getters, setters, types);
|
||||
}
|
||||
@Override
|
||||
public void generateClass(ClassVisitor v) throws Exception {
|
||||
new BulkBeanEmitter(v, getClassName(), target, getters, setters, types);
|
||||
}
|
||||
|
||||
protected Object firstInstance(Class type) {
|
||||
BulkBean instance = (BulkBean)ReflectUtils.newInstance(type);
|
||||
instance.target = target;
|
||||
|
||||
int length = getters.length;
|
||||
instance.getters = new String[length];
|
||||
System.arraycopy(getters, 0, instance.getters, 0, length);
|
||||
|
||||
instance.setters = new String[length];
|
||||
System.arraycopy(setters, 0, instance.setters, 0, length);
|
||||
|
||||
instance.types = new Class[types.length];
|
||||
System.arraycopy(types, 0, instance.types, 0, types.length);
|
||||
@Override
|
||||
protected Object firstInstance(Class type) {
|
||||
BulkBean instance = (BulkBean)ReflectUtils.newInstance(type);
|
||||
instance.target = target;
|
||||
|
||||
return instance;
|
||||
}
|
||||
int length = getters.length;
|
||||
instance.getters = new String[length];
|
||||
System.arraycopy(getters, 0, instance.getters, 0, length);
|
||||
|
||||
protected Object nextInstance(Object instance) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
instance.setters = new String[length];
|
||||
System.arraycopy(setters, 0, instance.setters, 0, length);
|
||||
|
||||
instance.types = new Class[types.length];
|
||||
System.arraycopy(types, 0, instance.types, 0, types.length);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object nextInstance(Object instance) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,19 @@ package org.springframework.cglib.beans;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import org.springframework.cglib.core.*;
|
||||
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.cglib.core.Block;
|
||||
import org.springframework.cglib.core.ClassEmitter;
|
||||
import org.springframework.cglib.core.CodeEmitter;
|
||||
import org.springframework.cglib.core.Constants;
|
||||
import org.springframework.cglib.core.EmitUtils;
|
||||
import org.springframework.cglib.core.Local;
|
||||
import org.springframework.cglib.core.MethodInfo;
|
||||
import org.springframework.cglib.core.ReflectUtils;
|
||||
import org.springframework.cglib.core.Signature;
|
||||
import org.springframework.cglib.core.TypeUtils;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
class BulkBeanEmitter extends ClassEmitter {
|
||||
@@ -33,7 +43,7 @@ class BulkBeanEmitter extends ClassEmitter {
|
||||
TypeUtils.parseType("org.springframework.cglib.beans.BulkBean");
|
||||
private static final Type BULK_BEAN_EXCEPTION =
|
||||
TypeUtils.parseType("org.springframework.cglib.beans.BulkBeanException");
|
||||
|
||||
|
||||
public BulkBeanEmitter(ClassVisitor v,
|
||||
String className,
|
||||
Class target,
|
||||
@@ -116,7 +126,7 @@ class BulkBeanEmitter extends ClassEmitter {
|
||||
}
|
||||
e.end_method();
|
||||
}
|
||||
|
||||
|
||||
private static void validate(Class target,
|
||||
String[] getters,
|
||||
String[] setters,
|
||||
|
||||
@@ -20,7 +20,7 @@ public class BulkBeanException extends RuntimeException
|
||||
{
|
||||
private int index;
|
||||
private Throwable cause;
|
||||
|
||||
|
||||
public BulkBeanException(String message, int index) {
|
||||
super(message);
|
||||
this.index = index;
|
||||
@@ -35,8 +35,9 @@ public class BulkBeanException extends RuntimeException
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public Throwable getCause() {
|
||||
|
||||
@Override
|
||||
public Throwable getCause() {
|
||||
return cause;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
*/
|
||||
package org.springframework.cglib.beans;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public /* need it for class loading */ class FixedKeySet extends AbstractSet {
|
||||
@@ -27,11 +32,13 @@ public /* need it for class loading */ class FixedKeySet extends AbstractSet {
|
||||
set = Collections.unmodifiableSet(new HashSet(Arrays.asList(keys)));
|
||||
}
|
||||
|
||||
public Iterator iterator() {
|
||||
@Override
|
||||
public Iterator iterator() {
|
||||
return set.iterator();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,115 +18,129 @@ package org.springframework.cglib.beans;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.ProtectionDomain;
|
||||
import org.springframework.cglib.core.*;
|
||||
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.cglib.core.AbstractClassGenerator;
|
||||
import org.springframework.cglib.core.ClassEmitter;
|
||||
import org.springframework.cglib.core.CodeEmitter;
|
||||
import org.springframework.cglib.core.Constants;
|
||||
import org.springframework.cglib.core.EmitUtils;
|
||||
import org.springframework.cglib.core.MethodInfo;
|
||||
import org.springframework.cglib.core.ReflectUtils;
|
||||
import org.springframework.cglib.core.Signature;
|
||||
import org.springframework.cglib.core.TypeUtils;
|
||||
/**
|
||||
* @author Chris Nokleberg
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public class ImmutableBean
|
||||
{
|
||||
private static final Type ILLEGAL_STATE_EXCEPTION =
|
||||
TypeUtils.parseType("IllegalStateException");
|
||||
private static final Signature CSTRUCT_OBJECT =
|
||||
TypeUtils.parseConstructor("Object");
|
||||
private static final Class[] OBJECT_CLASSES = { Object.class };
|
||||
private static final String FIELD_NAME = "CGLIB$RWBean";
|
||||
private static final Type ILLEGAL_STATE_EXCEPTION =
|
||||
TypeUtils.parseType("IllegalStateException");
|
||||
private static final Signature CSTRUCT_OBJECT =
|
||||
TypeUtils.parseConstructor("Object");
|
||||
private static final Class[] OBJECT_CLASSES = { Object.class };
|
||||
private static final String FIELD_NAME = "CGLIB$RWBean";
|
||||
|
||||
private ImmutableBean() {
|
||||
}
|
||||
private ImmutableBean() {
|
||||
}
|
||||
|
||||
public static Object create(Object bean) {
|
||||
Generator gen = new Generator();
|
||||
gen.setBean(bean);
|
||||
return gen.create();
|
||||
}
|
||||
public static Object create(Object bean) {
|
||||
Generator gen = new Generator();
|
||||
gen.setBean(bean);
|
||||
return gen.create();
|
||||
}
|
||||
|
||||
public static class Generator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(ImmutableBean.class.getName());
|
||||
private Object bean;
|
||||
private Class target;
|
||||
public static class Generator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(ImmutableBean.class.getName());
|
||||
private Object bean;
|
||||
private Class target;
|
||||
|
||||
public Generator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
public Generator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
|
||||
public void setBean(Object bean) {
|
||||
this.bean = bean;
|
||||
target = bean.getClass();
|
||||
public void setBean(Object bean) {
|
||||
this.bean = bean;
|
||||
target = bean.getClass();
|
||||
// SPRING PATCH BEGIN
|
||||
setContextClass(target);
|
||||
// SPRING PATCH END
|
||||
}
|
||||
}
|
||||
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
return target.getClassLoader();
|
||||
}
|
||||
@Override
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
return target.getClassLoader();
|
||||
}
|
||||
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(target);
|
||||
}
|
||||
@Override
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(target);
|
||||
}
|
||||
|
||||
public Object create() {
|
||||
String name = target.getName();
|
||||
setNamePrefix(name);
|
||||
return super.create(name);
|
||||
}
|
||||
public Object create() {
|
||||
String name = target.getName();
|
||||
setNamePrefix(name);
|
||||
return super.create(name);
|
||||
}
|
||||
|
||||
public void generateClass(ClassVisitor v) {
|
||||
Type targetType = Type.getType(target);
|
||||
ClassEmitter ce = new ClassEmitter(v);
|
||||
ce.begin_class(Constants.V1_8,
|
||||
Constants.ACC_PUBLIC,
|
||||
getClassName(),
|
||||
targetType,
|
||||
null,
|
||||
Constants.SOURCE_FILE);
|
||||
@Override
|
||||
public void generateClass(ClassVisitor v) {
|
||||
Type targetType = Type.getType(target);
|
||||
ClassEmitter ce = new ClassEmitter(v);
|
||||
ce.begin_class(Constants.V1_8,
|
||||
Constants.ACC_PUBLIC,
|
||||
getClassName(),
|
||||
targetType,
|
||||
null,
|
||||
Constants.SOURCE_FILE);
|
||||
|
||||
ce.declare_field(Constants.ACC_FINAL | Constants.ACC_PRIVATE, FIELD_NAME, targetType, null);
|
||||
ce.declare_field(Constants.ACC_FINAL | Constants.ACC_PRIVATE, FIELD_NAME, targetType, null);
|
||||
|
||||
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
|
||||
e.load_this();
|
||||
e.super_invoke_constructor();
|
||||
e.load_this();
|
||||
e.load_arg(0);
|
||||
e.checkcast(targetType);
|
||||
e.putfield(FIELD_NAME);
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
|
||||
e.load_this();
|
||||
e.super_invoke_constructor();
|
||||
e.load_this();
|
||||
e.load_arg(0);
|
||||
e.checkcast(targetType);
|
||||
e.putfield(FIELD_NAME);
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
|
||||
PropertyDescriptor[] descriptors = ReflectUtils.getBeanProperties(target);
|
||||
Method[] getters = ReflectUtils.getPropertyMethods(descriptors, true, false);
|
||||
Method[] setters = ReflectUtils.getPropertyMethods(descriptors, false, true);
|
||||
PropertyDescriptor[] descriptors = ReflectUtils.getBeanProperties(target);
|
||||
Method[] getters = ReflectUtils.getPropertyMethods(descriptors, true, false);
|
||||
Method[] setters = ReflectUtils.getPropertyMethods(descriptors, false, true);
|
||||
|
||||
for (int i = 0; i < getters.length; i++) {
|
||||
MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]);
|
||||
e = EmitUtils.begin_method(ce, getter, Constants.ACC_PUBLIC);
|
||||
e.load_this();
|
||||
e.getfield(FIELD_NAME);
|
||||
e.invoke(getter);
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
}
|
||||
for (Method getter2 : getters) {
|
||||
MethodInfo getter = ReflectUtils.getMethodInfo(getter2);
|
||||
e = EmitUtils.begin_method(ce, getter, Constants.ACC_PUBLIC);
|
||||
e.load_this();
|
||||
e.getfield(FIELD_NAME);
|
||||
e.invoke(getter);
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
}
|
||||
|
||||
for (int i = 0; i < setters.length; i++) {
|
||||
MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]);
|
||||
e = EmitUtils.begin_method(ce, setter, Constants.ACC_PUBLIC);
|
||||
e.throw_exception(ILLEGAL_STATE_EXCEPTION, "Bean is immutable");
|
||||
e.end_method();
|
||||
}
|
||||
for (Method setter2 : setters) {
|
||||
MethodInfo setter = ReflectUtils.getMethodInfo(setter2);
|
||||
e = EmitUtils.begin_method(ce, setter, Constants.ACC_PUBLIC);
|
||||
e.throw_exception(ILLEGAL_STATE_EXCEPTION, "Bean is immutable");
|
||||
e.end_method();
|
||||
}
|
||||
|
||||
ce.end_class();
|
||||
}
|
||||
ce.end_class();
|
||||
}
|
||||
|
||||
protected Object firstInstance(Class type) {
|
||||
return ReflectUtils.newInstance(type, OBJECT_CLASSES, new Object[]{ bean });
|
||||
}
|
||||
@Override
|
||||
protected Object firstInstance(Class type) {
|
||||
return ReflectUtils.newInstance(type, OBJECT_CLASSES, new Object[]{ bean });
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
protected Object nextInstance(Object instance) {
|
||||
return firstInstance(instance.getClass());
|
||||
}
|
||||
}
|
||||
// TODO: optimize
|
||||
@Override
|
||||
protected Object nextInstance(Object instance) {
|
||||
return firstInstance(instance.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -74,7 +74,8 @@ public final class MethodIntrospector {
|
||||
T result = metadataLookup.inspect(specificMethod);
|
||||
if (result != null) {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
|
||||
if (bridgedMethod == specificMethod || bridgedMethod == method ||
|
||||
metadataLookup.inspect(bridgedMethod) == null) {
|
||||
methodMap.put(specificMethod, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -92,8 +91,7 @@ public abstract class ParameterizedTypeReference<T> {
|
||||
* @since 4.3.12
|
||||
*/
|
||||
public static <T> ParameterizedTypeReference<T> forType(Type type) {
|
||||
return new ParameterizedTypeReference<T>(type) {
|
||||
};
|
||||
return new ParameterizedTypeReference<T>(type) {};
|
||||
}
|
||||
|
||||
private static Class<?> findParameterizedTypeReferenceSubclass(Class<?> child) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,8 +24,6 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.function.Function;
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferredKt;
|
||||
import kotlinx.coroutines.Deferred;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.blockhound.BlockHound;
|
||||
import reactor.blockhound.integration.BlockHoundIntegration;
|
||||
@@ -39,13 +37,14 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A registry of adapters to adapt Reactive Streams {@link Publisher} to/from
|
||||
* various async/reactive types such as {@code CompletableFuture}, RxJava
|
||||
* {@code Flowable}, and others.
|
||||
* A registry of adapters to adapt Reactive Streams {@link Publisher} to/from various
|
||||
* async/reactive types such as {@code CompletableFuture}, RxJava {@code Flowable}, etc.
|
||||
* This is designed to complement Spring's Reactor {@code Mono}/{@code Flux} support while
|
||||
* also being usable without Reactor, e.g. just for {@code org.reactivestreams} bridging.
|
||||
*
|
||||
* <p>By default, depending on classpath availability, adapters are registered
|
||||
* for Reactor, RxJava 3, {@link CompletableFuture}, {@code Flow.Publisher},
|
||||
* and Kotlin Coroutines' {@code Deferred} and {@code Flow}.
|
||||
* <p>By default, depending on classpath availability, adapters are registered for Reactor
|
||||
* (including {@code CompletableFuture} and {@code Flow.Publisher} adapters), RxJava 3,
|
||||
* Kotlin Coroutines' {@code Deferred} (bridged via Reactor) and SmallRye Mutiny 1.x.
|
||||
*
|
||||
* <p><strong>Note:</strong> As of Spring Framework 5.3.11, support for
|
||||
* RxJava 1.x and 2.x is deprecated in favor of RxJava 3.
|
||||
@@ -136,16 +135,45 @@ public class ReactiveAdapterRegistry {
|
||||
* Register a reactive type along with functions to adapt to and from a
|
||||
* Reactive Streams {@link Publisher}. The function arguments assume that
|
||||
* their input is neither {@code null} nor {@link Optional}.
|
||||
* <p>This variant registers the new adapter after existing adapters.
|
||||
* It will be matched for the exact reactive type if no earlier adapter was
|
||||
* registered for the specific type, and it will be matched for assignability
|
||||
* in a second pass if no earlier adapter had an assignable type before.
|
||||
* @see #registerReactiveTypeOverride
|
||||
* @see #getAdapter
|
||||
*/
|
||||
public void registerReactiveType(ReactiveTypeDescriptor descriptor,
|
||||
Function<Object, Publisher<?>> toAdapter, Function<Publisher<?>, Object> fromAdapter) {
|
||||
|
||||
if (reactorPresent) {
|
||||
this.adapters.add(new ReactorAdapter(descriptor, toAdapter, fromAdapter));
|
||||
}
|
||||
else {
|
||||
this.adapters.add(new ReactiveAdapter(descriptor, toAdapter, fromAdapter));
|
||||
}
|
||||
this.adapters.add(buildAdapter(descriptor, toAdapter, fromAdapter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a reactive type along with functions to adapt to and from a
|
||||
* Reactive Streams {@link Publisher}. The function arguments assume that
|
||||
* their input is neither {@code null} nor {@link Optional}.
|
||||
* <p>This variant registers the new adapter first, effectively overriding
|
||||
* any previously registered adapters for the same reactive type. This allows
|
||||
* for overriding existing adapters, in particular default adapters.
|
||||
* <p>Note that existing adapters for specific types will still match before
|
||||
* an assignability match with the new adapter. In order to override all
|
||||
* existing matches, a new reactive type adapter needs to be registered
|
||||
* for every specific type, not relying on subtype assignability matches.
|
||||
* @since 5.3.30
|
||||
* @see #registerReactiveType
|
||||
* @see #getAdapter
|
||||
*/
|
||||
public void registerReactiveTypeOverride(ReactiveTypeDescriptor descriptor,
|
||||
Function<Object, Publisher<?>> toAdapter, Function<Publisher<?>, Object> fromAdapter) {
|
||||
|
||||
this.adapters.add(0, buildAdapter(descriptor, toAdapter, fromAdapter));
|
||||
}
|
||||
|
||||
private ReactiveAdapter buildAdapter(ReactiveTypeDescriptor descriptor,
|
||||
Function<Object, Publisher<?>> toAdapter, Function<Publisher<?>, Object> fromAdapter) {
|
||||
|
||||
return (reactorPresent ? new ReactorAdapter(descriptor, toAdapter, fromAdapter) :
|
||||
new ReactiveAdapter(descriptor, toAdapter, fromAdapter));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,9 +429,9 @@ public class ReactiveAdapterRegistry {
|
||||
@SuppressWarnings("KotlinInternalInJava")
|
||||
void registerAdapters(ReactiveAdapterRegistry registry) {
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.singleOptionalValue(Deferred.class,
|
||||
() -> CompletableDeferredKt.CompletableDeferred(null)),
|
||||
source -> CoroutinesUtils.deferredToMono((Deferred<?>) source),
|
||||
ReactiveTypeDescriptor.singleOptionalValue(kotlinx.coroutines.Deferred.class,
|
||||
() -> kotlinx.coroutines.CompletableDeferredKt.CompletableDeferred(null)),
|
||||
source -> CoroutinesUtils.deferredToMono((kotlinx.coroutines.Deferred<?>) source),
|
||||
source -> CoroutinesUtils.monoToDeferred(Mono.from(source)));
|
||||
|
||||
registry.registerReactiveType(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,7 +37,7 @@ public final class ReactiveTypeDescriptor {
|
||||
private final boolean noValue;
|
||||
|
||||
@Nullable
|
||||
private final Supplier<?> emptyValueSupplier;
|
||||
private final Supplier<?> emptySupplier;
|
||||
|
||||
private final boolean deferred;
|
||||
|
||||
@@ -55,7 +55,7 @@ public final class ReactiveTypeDescriptor {
|
||||
this.reactiveType = reactiveType;
|
||||
this.multiValue = multiValue;
|
||||
this.noValue = noValue;
|
||||
this.emptyValueSupplier = emptySupplier;
|
||||
this.emptySupplier = emptySupplier;
|
||||
this.deferred = deferred;
|
||||
}
|
||||
|
||||
@@ -89,16 +89,16 @@ public final class ReactiveTypeDescriptor {
|
||||
* Return {@code true} if the reactive type can complete with no values.
|
||||
*/
|
||||
public boolean supportsEmpty() {
|
||||
return (this.emptyValueSupplier != null);
|
||||
return (this.emptySupplier != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an empty-value instance for the underlying reactive or async type.
|
||||
* Use of this type implies {@link #supportsEmpty()} is {@code true}.
|
||||
* <p>Use of this type implies {@link #supportsEmpty()} is {@code true}.
|
||||
*/
|
||||
public Object getEmptyValue() {
|
||||
Assert.state(this.emptyValueSupplier != null, "Empty values not supported");
|
||||
return this.emptyValueSupplier.get();
|
||||
Assert.state(this.emptySupplier != null, "Empty values not supported");
|
||||
return this.emptySupplier.get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -138,7 +138,7 @@ public class ResolvableType implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor used to create a new {@link ResolvableType} for cache key purposes,
|
||||
* Private constructor used to create a new {@code ResolvableType} for cache key purposes,
|
||||
* with no upfront resolution.
|
||||
*/
|
||||
private ResolvableType(
|
||||
@@ -153,7 +153,7 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor used to create a new {@link ResolvableType} for cache value purposes,
|
||||
* Private constructor used to create a new {@code ResolvableType} for cache value purposes,
|
||||
* with upfront resolution and a pre-calculated hash.
|
||||
* @since 4.2
|
||||
*/
|
||||
@@ -169,7 +169,7 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor used to create a new {@link ResolvableType} for uncached purposes,
|
||||
* Private constructor used to create a new {@code ResolvableType} for uncached purposes,
|
||||
* with upfront resolution but lazily calculated hash.
|
||||
*/
|
||||
private ResolvableType(Type type, @Nullable TypeProvider typeProvider,
|
||||
@@ -184,7 +184,7 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor used to create a new {@link ResolvableType} on a {@link Class} basis.
|
||||
* Private constructor used to create a new {@code ResolvableType} on a {@link Class} basis.
|
||||
* <p>Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper.
|
||||
* @since 4.2
|
||||
*/
|
||||
@@ -223,7 +223,7 @@ public class ResolvableType implements Serializable {
|
||||
|
||||
/**
|
||||
* Return the underlying source of the resolvable type. Will return a {@link Field},
|
||||
* {@link MethodParameter} or {@link Type} depending on how the {@link ResolvableType}
|
||||
* {@link MethodParameter} or {@link Type} depending on how the {@code ResolvableType}
|
||||
* was constructed. This method is primarily to provide access to additional type
|
||||
* information or meta-data that alternative JVM languages may provide.
|
||||
*/
|
||||
@@ -341,13 +341,14 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
}
|
||||
if (ourResolved == null) {
|
||||
ourResolved = resolve(Object.class);
|
||||
ourResolved = toClass();
|
||||
}
|
||||
Class<?> otherResolved = other.toClass();
|
||||
|
||||
// We need an exact type match for generics
|
||||
// List<CharSequence> is not assignable from List<String>
|
||||
if (exactMatch ? !ourResolved.equals(otherResolved) : !ClassUtils.isAssignable(ourResolved, otherResolved)) {
|
||||
if (exactMatch ? !ourResolved.equals(otherResolved) :
|
||||
!ClassUtils.isAssignable(ourResolved, otherResolved)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -358,13 +359,15 @@ public class ResolvableType implements Serializable {
|
||||
if (ourGenerics.length != typeGenerics.length) {
|
||||
return false;
|
||||
}
|
||||
if (matchedBefore == null) {
|
||||
matchedBefore = new IdentityHashMap<>(1);
|
||||
}
|
||||
matchedBefore.put(this.type, other.type);
|
||||
for (int i = 0; i < ourGenerics.length; i++) {
|
||||
if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) {
|
||||
return false;
|
||||
if (ourGenerics.length > 0) {
|
||||
if (matchedBefore == null) {
|
||||
matchedBefore = new IdentityHashMap<>(1);
|
||||
}
|
||||
matchedBefore.put(this.type, other.type);
|
||||
for (int i = 0; i < ourGenerics.length; i++) {
|
||||
if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,12 +432,12 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this type as a {@link ResolvableType} of the specified class. Searches
|
||||
* Return this type as a {@code ResolvableType} of the specified class. Searches
|
||||
* {@link #getSuperType() supertype} and {@link #getInterfaces() interface}
|
||||
* hierarchies to find a match, returning {@link #NONE} if this type does not
|
||||
* implement or extend the specified class.
|
||||
* @param type the required type (typically narrowed)
|
||||
* @return a {@link ResolvableType} representing this object as the specified
|
||||
* @return a {@code ResolvableType} representing this object as the specified
|
||||
* type, or {@link #NONE} if not resolvable as that type
|
||||
* @see #asCollection()
|
||||
* @see #asMap()
|
||||
@@ -459,9 +462,9 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} representing the direct supertype of this type.
|
||||
* Return a {@code ResolvableType} representing the direct supertype of this type.
|
||||
* <p>If no supertype is available this method returns {@link #NONE}.
|
||||
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
|
||||
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
|
||||
* @see #getInterfaces()
|
||||
*/
|
||||
public ResolvableType getSuperType() {
|
||||
@@ -488,10 +491,10 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} array representing the direct interfaces
|
||||
* Return a {@code ResolvableType} array representing the direct interfaces
|
||||
* implemented by this type. If this type does not implement any interfaces an
|
||||
* empty array is returned.
|
||||
* <p>Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}.
|
||||
* <p>Note: The resulting {@code ResolvableType} instances may not be {@link Serializable}.
|
||||
* @see #getSuperType()
|
||||
*/
|
||||
public ResolvableType[] getInterfaces() {
|
||||
@@ -621,17 +624,17 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified nesting level.
|
||||
* Return a {@code ResolvableType} for the specified nesting level.
|
||||
* <p>See {@link #getNested(int, Map)} for details.
|
||||
* @param nestingLevel the nesting level
|
||||
* @return the {@link ResolvableType} type, or {@code #NONE}
|
||||
* @return the {@code ResolvableType} type, or {@code #NONE}
|
||||
*/
|
||||
public ResolvableType getNested(int nestingLevel) {
|
||||
return getNested(nestingLevel, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified nesting level.
|
||||
* Return a {@code ResolvableType} for the specified nesting level.
|
||||
* <p>The nesting level refers to the specific generic parameter that should be returned.
|
||||
* A nesting level of 1 indicates this type; 2 indicates the first nested generic;
|
||||
* 3 the second; and so on. For example, given {@code List<Set<Integer>>} level 1 refers
|
||||
@@ -648,7 +651,7 @@ public class ResolvableType implements Serializable {
|
||||
* current type, 2 for the first nested generic, 3 for the second and so on
|
||||
* @param typeIndexesPerLevel a map containing the generic index for a given
|
||||
* nesting level (may be {@code null})
|
||||
* @return a {@link ResolvableType} for the nested level, or {@link #NONE}
|
||||
* @return a {@code ResolvableType} for the nested level, or {@link #NONE}
|
||||
*/
|
||||
public ResolvableType getNested(int nestingLevel, @Nullable Map<Integer, Integer> typeIndexesPerLevel) {
|
||||
ResolvableType result = this;
|
||||
@@ -670,7 +673,7 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} representing the generic parameter for the
|
||||
* Return a {@code ResolvableType} representing the generic parameter for the
|
||||
* given indexes. Indexes are zero based; for example given the type
|
||||
* {@code Map<Integer, List<String>>}, {@code getGeneric(0)} will access the
|
||||
* {@code Integer}. Nested generics can be accessed by specifying multiple indexes;
|
||||
@@ -680,7 +683,7 @@ public class ResolvableType implements Serializable {
|
||||
* <p>If no generic is available at the specified indexes {@link #NONE} is returned.
|
||||
* @param indexes the indexes that refer to the generic parameter
|
||||
* (may be omitted to return the first generic)
|
||||
* @return a {@link ResolvableType} for the specified generic, or {@link #NONE}
|
||||
* @return a {@code ResolvableType} for the specified generic, or {@link #NONE}
|
||||
* @see #hasGenerics()
|
||||
* @see #getGenerics()
|
||||
* @see #resolveGeneric(int...)
|
||||
@@ -703,12 +706,12 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters of
|
||||
* Return an array of {@code ResolvableType ResolvableTypes} representing the generic parameters of
|
||||
* this type. If no generics are available an empty array is returned. If you need to
|
||||
* access a specific generic consider using the {@link #getGeneric(int...)} method as
|
||||
* it allows access to nested generics and protects against
|
||||
* {@code IndexOutOfBoundsExceptions}.
|
||||
* @return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters
|
||||
* @return an array of {@code ResolvableType ResolvableTypes} representing the generic parameters
|
||||
* (never {@code null})
|
||||
* @see #hasGenerics()
|
||||
* @see #getGeneric(int...)
|
||||
@@ -780,7 +783,7 @@ public class ResolvableType implements Serializable {
|
||||
|
||||
/**
|
||||
* Convenience method that will {@link #getGeneric(int...) get} and
|
||||
* {@link #resolve() resolve} a specific generic parameters.
|
||||
* {@link #resolve() resolve} a specific generic parameter.
|
||||
* @param indexes the indexes that refer to the generic parameter
|
||||
* (may be omitted to return the first generic)
|
||||
* @return a resolved {@link Class} or {@code null}
|
||||
@@ -842,7 +845,7 @@ public class ResolvableType implements Serializable {
|
||||
|
||||
/**
|
||||
* Resolve this type by a single level, returning the resolved value or {@link #NONE}.
|
||||
* <p>Note: The returned {@link ResolvableType} should only be used as an intermediary
|
||||
* <p>Note: The returned {@code ResolvableType} should only be used as an intermediary
|
||||
* as it cannot be serialized.
|
||||
*/
|
||||
ResolvableType resolveType() {
|
||||
@@ -964,7 +967,7 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts this {@link ResolvableType} to a {@link VariableResolver}.
|
||||
* Adapts this {@code ResolvableType} to a {@link VariableResolver}.
|
||||
*/
|
||||
@Nullable
|
||||
VariableResolver asVariableResolver() {
|
||||
@@ -1011,12 +1014,12 @@ public class ResolvableType implements Serializable {
|
||||
// Factory methods
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Class},
|
||||
* Return a {@code ResolvableType} for the specified {@link Class},
|
||||
* using the full generic type information for assignability checks.
|
||||
* <p>For example: {@code ResolvableType.forClass(MyArrayList.class)}.
|
||||
* @param clazz the class to introspect ({@code null} is semantically
|
||||
* equivalent to {@code Object.class} for typical use cases here)
|
||||
* @return a {@link ResolvableType} for the specified class
|
||||
* @return a {@code ResolvableType} for the specified class
|
||||
* @see #forClass(Class, Class)
|
||||
* @see #forClassWithGenerics(Class, Class...)
|
||||
*/
|
||||
@@ -1025,13 +1028,13 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Class},
|
||||
* Return a {@code ResolvableType} for the specified {@link Class},
|
||||
* doing assignability checks against the raw class only (analogous to
|
||||
* {@link Class#isAssignableFrom}, which this serves as a wrapper for).
|
||||
* <p>For example: {@code ResolvableType.forRawClass(List.class)}.
|
||||
* @param clazz the class to introspect ({@code null} is semantically
|
||||
* equivalent to {@code Object.class} for typical use cases here)
|
||||
* @return a {@link ResolvableType} for the specified class
|
||||
* @return a {@code ResolvableType} for the specified class
|
||||
* @since 4.2
|
||||
* @see #forClass(Class)
|
||||
* @see #getRawClass()
|
||||
@@ -1055,12 +1058,12 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified base type
|
||||
* Return a {@code ResolvableType} for the specified base type
|
||||
* (interface or base class) with a given implementation class.
|
||||
* <p>For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}.
|
||||
* @param baseType the base type (must not be {@code null})
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified base type backed by the
|
||||
* @return a {@code ResolvableType} for the specified base type backed by the
|
||||
* given implementation class
|
||||
* @see #forClass(Class)
|
||||
* @see #forClassWithGenerics(Class, Class...)
|
||||
@@ -1072,10 +1075,10 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
|
||||
* Return a {@code ResolvableType} for the specified {@link Class} with pre-declared generics.
|
||||
* @param clazz the class (or interface) to introspect
|
||||
* @param generics the generics of the class
|
||||
* @return a {@link ResolvableType} for the specific class and generics
|
||||
* @return a {@code ResolvableType} for the specific class and generics
|
||||
* @see #forClassWithGenerics(Class, ResolvableType...)
|
||||
*/
|
||||
public static ResolvableType forClassWithGenerics(Class<?> clazz, Class<?>... generics) {
|
||||
@@ -1089,10 +1092,10 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
|
||||
* Return a {@code ResolvableType} for the specified {@link Class} with pre-declared generics.
|
||||
* @param clazz the class (or interface) to introspect
|
||||
* @param generics the generics of the class
|
||||
* @return a {@link ResolvableType} for the specific class and generics
|
||||
* @return a {@code ResolvableType} for the specific class and generics
|
||||
* @see #forClassWithGenerics(Class, Class...)
|
||||
*/
|
||||
public static ResolvableType forClassWithGenerics(Class<?> clazz, ResolvableType... generics) {
|
||||
@@ -1113,12 +1116,12 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified instance. The instance does not
|
||||
* Return a {@code ResolvableType} for the specified instance. The instance does not
|
||||
* convey generic information but if it implements {@link ResolvableTypeProvider} a
|
||||
* more precise {@link ResolvableType} can be used than the simple one based on
|
||||
* more precise {@code ResolvableType} can be used than the simple one based on
|
||||
* the {@link #forClass(Class) Class instance}.
|
||||
* @param instance the instance (possibly {@code null})
|
||||
* @return a {@link ResolvableType} for the specified instance,
|
||||
* @return a {@code ResolvableType} for the specified instance,
|
||||
* or {@code NONE} for {@code null}
|
||||
* @since 4.2
|
||||
* @see ResolvableTypeProvider
|
||||
@@ -1134,9 +1137,9 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field}.
|
||||
* Return a {@code ResolvableType} for the specified {@link Field}.
|
||||
* @param field the source field
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
* @return a {@code ResolvableType} for the specified field
|
||||
* @see #forField(Field, Class)
|
||||
*/
|
||||
public static ResolvableType forField(Field field) {
|
||||
@@ -1145,13 +1148,13 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with a given
|
||||
* Return a {@code ResolvableType} for the specified {@link Field} with a given
|
||||
* implementation.
|
||||
* <p>Use this variant when the class that declares the field includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param field the source field
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
* @return a {@code ResolvableType} for the specified field
|
||||
* @see #forField(Field)
|
||||
*/
|
||||
public static ResolvableType forField(Field field, Class<?> implementationClass) {
|
||||
@@ -1161,13 +1164,13 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with a given
|
||||
* Return a {@code ResolvableType} for the specified {@link Field} with a given
|
||||
* implementation.
|
||||
* <p>Use this variant when the class that declares the field includes generic
|
||||
* parameter variables that are satisfied by the implementation type.
|
||||
* @param field the source field
|
||||
* @param implementationType the implementation type
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
* @return a {@code ResolvableType} for the specified field
|
||||
* @see #forField(Field)
|
||||
*/
|
||||
public static ResolvableType forField(Field field, @Nullable ResolvableType implementationType) {
|
||||
@@ -1178,7 +1181,7 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with the
|
||||
* Return a {@code ResolvableType} for the specified {@link Field} with the
|
||||
* given nesting level.
|
||||
* @param field the source field
|
||||
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
|
||||
@@ -1191,7 +1194,7 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with a given
|
||||
* Return a {@code ResolvableType} for the specified {@link Field} with a given
|
||||
* implementation and the given nesting level.
|
||||
* <p>Use this variant when the class that declares the field includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
@@ -1199,7 +1202,7 @@ public class ResolvableType implements Serializable {
|
||||
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
|
||||
* generic type; etc)
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
* @return a {@code ResolvableType} for the specified field
|
||||
* @see #forField(Field)
|
||||
*/
|
||||
public static ResolvableType forField(Field field, int nestingLevel, @Nullable Class<?> implementationClass) {
|
||||
@@ -1209,10 +1212,10 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Constructor} parameter.
|
||||
* Return a {@code ResolvableType} for the specified {@link Constructor} parameter.
|
||||
* @param constructor the source constructor (must not be {@code null})
|
||||
* @param parameterIndex the parameter index
|
||||
* @return a {@link ResolvableType} for the specified constructor parameter
|
||||
* @return a {@code ResolvableType} for the specified constructor parameter
|
||||
* @see #forConstructorParameter(Constructor, int, Class)
|
||||
*/
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) {
|
||||
@@ -1221,14 +1224,14 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Constructor} parameter
|
||||
* Return a {@code ResolvableType} for the specified {@link Constructor} parameter
|
||||
* with a given implementation. Use this variant when the class that declares the
|
||||
* constructor includes generic parameter variables that are satisfied by the
|
||||
* implementation class.
|
||||
* @param constructor the source constructor (must not be {@code null})
|
||||
* @param parameterIndex the parameter index
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified constructor parameter
|
||||
* @return a {@code ResolvableType} for the specified constructor parameter
|
||||
* @see #forConstructorParameter(Constructor, int)
|
||||
*/
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
|
||||
@@ -1240,9 +1243,9 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* Return a {@code ResolvableType} for the specified {@link Method} return type.
|
||||
* @param method the source for the method return type
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @return a {@code ResolvableType} for the specified method return
|
||||
* @see #forMethodReturnType(Method, Class)
|
||||
*/
|
||||
public static ResolvableType forMethodReturnType(Method method) {
|
||||
@@ -1251,12 +1254,12 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* Return a {@code ResolvableType} for the specified {@link Method} return type.
|
||||
* <p>Use this variant when the class that declares the method includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param method the source for the method return type
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @return a {@code ResolvableType} for the specified method return
|
||||
* @see #forMethodReturnType(Method)
|
||||
*/
|
||||
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) {
|
||||
@@ -1266,10 +1269,10 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} parameter.
|
||||
* Return a {@code ResolvableType} for the specified {@link Method} parameter.
|
||||
* @param method the source method (must not be {@code null})
|
||||
* @param parameterIndex the parameter index
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @return a {@code ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(Method, int, Class)
|
||||
* @see #forMethodParameter(MethodParameter)
|
||||
*/
|
||||
@@ -1279,13 +1282,13 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} parameter with a
|
||||
* Return a {@code ResolvableType} for the specified {@link Method} parameter with a
|
||||
* given implementation. Use this variant when the class that declares the method
|
||||
* includes generic parameter variables that are satisfied by the implementation class.
|
||||
* @param method the source method (must not be {@code null})
|
||||
* @param parameterIndex the parameter index
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @return a {@code ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(Method, int, Class)
|
||||
* @see #forMethodParameter(MethodParameter)
|
||||
*/
|
||||
@@ -1296,9 +1299,9 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link MethodParameter}.
|
||||
* Return a {@code ResolvableType} for the specified {@link MethodParameter}.
|
||||
* @param methodParameter the source method parameter (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @return a {@code ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(Method, int)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(MethodParameter methodParameter) {
|
||||
@@ -1306,12 +1309,12 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link MethodParameter} with a
|
||||
* Return a {@code ResolvableType} for the specified {@link MethodParameter} with a
|
||||
* given implementation type. Use this variant when the class that declares the method
|
||||
* includes generic parameter variables that are satisfied by the implementation type.
|
||||
* @param methodParameter the source method parameter (must not be {@code null})
|
||||
* @param implementationType the implementation type
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @return a {@code ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(MethodParameter)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(MethodParameter methodParameter,
|
||||
@@ -1326,11 +1329,11 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link MethodParameter},
|
||||
* Return a {@code ResolvableType} for the specified {@link MethodParameter},
|
||||
* overriding the target type to resolve with a specific given type.
|
||||
* @param methodParameter the source method parameter (must not be {@code null})
|
||||
* @param targetType the type to resolve (a part of the method parameter's type)
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @return a {@code ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(Method, int)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable Type targetType) {
|
||||
@@ -1339,13 +1342,13 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link MethodParameter} at
|
||||
* Return a {@code ResolvableType} for the specified {@link MethodParameter} at
|
||||
* a specific nesting level, overriding the target type to resolve with a specific
|
||||
* given type.
|
||||
* @param methodParameter the source method parameter (must not be {@code null})
|
||||
* @param targetType the type to resolve (a part of the method parameter's type)
|
||||
* @param nestingLevel the nesting level to use
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @return a {@code ResolvableType} for the specified method parameter
|
||||
* @since 5.2
|
||||
* @see #forMethodParameter(Method, int)
|
||||
*/
|
||||
@@ -1358,9 +1361,9 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} as an array of the specified {@code componentType}.
|
||||
* Return a {@code ResolvableType} as an array of the specified {@code componentType}.
|
||||
* @param componentType the component type
|
||||
* @return a {@link ResolvableType} as an array of the specified component type
|
||||
* @return a {@code ResolvableType} as an array of the specified component type
|
||||
*/
|
||||
public static ResolvableType forArrayComponent(ResolvableType componentType) {
|
||||
Assert.notNull(componentType, "Component type must not be null");
|
||||
@@ -1369,10 +1372,10 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Type}.
|
||||
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
|
||||
* Return a {@code ResolvableType} for the specified {@link Type}.
|
||||
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
|
||||
* @param type the source type (potentially {@code null})
|
||||
* @return a {@link ResolvableType} for the specified {@link Type}
|
||||
* @return a {@code ResolvableType} for the specified {@link Type}
|
||||
* @see #forType(Type, ResolvableType)
|
||||
*/
|
||||
public static ResolvableType forType(@Nullable Type type) {
|
||||
@@ -1380,12 +1383,12 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Type} backed by the given
|
||||
* Return a {@code ResolvableType} for the specified {@link Type} backed by the given
|
||||
* owner type.
|
||||
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
|
||||
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
|
||||
* @param type the source type or {@code null}
|
||||
* @param owner the owner type used to resolve variables
|
||||
* @return a {@link ResolvableType} for the specified {@link Type} and owner
|
||||
* @return a {@code ResolvableType} for the specified {@link Type} and owner
|
||||
* @see #forType(Type)
|
||||
*/
|
||||
public static ResolvableType forType(@Nullable Type type, @Nullable ResolvableType owner) {
|
||||
@@ -1398,10 +1401,10 @@ public class ResolvableType implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}.
|
||||
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
|
||||
* Return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}.
|
||||
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
|
||||
* @param typeReference the reference to obtain the source type from
|
||||
* @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}
|
||||
* @return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}
|
||||
* @since 4.3.12
|
||||
* @see #forType(Type)
|
||||
*/
|
||||
@@ -1410,23 +1413,23 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Type} backed by a given
|
||||
* Return a {@code ResolvableType} for the specified {@link Type} backed by a given
|
||||
* {@link VariableResolver}.
|
||||
* @param type the source type or {@code null}
|
||||
* @param variableResolver the variable resolver or {@code null}
|
||||
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver}
|
||||
* @return a {@code ResolvableType} for the specified {@link Type} and {@link VariableResolver}
|
||||
*/
|
||||
static ResolvableType forType(@Nullable Type type, @Nullable VariableResolver variableResolver) {
|
||||
return forType(type, null, variableResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Type} backed by a given
|
||||
* Return a {@code ResolvableType} for the specified {@link Type} backed by a given
|
||||
* {@link VariableResolver}.
|
||||
* @param type the source type or {@code null}
|
||||
* @param typeProvider the type provider or {@code null}
|
||||
* @param variableResolver the variable resolver or {@code null}
|
||||
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver}
|
||||
* @return a {@code ResolvableType} for the specified {@link Type} and {@link VariableResolver}
|
||||
*/
|
||||
static ResolvableType forType(
|
||||
@Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1321,6 +1321,9 @@ public abstract class AnnotationUtils {
|
||||
public static void clearCache() {
|
||||
AnnotationTypeMappings.clearCache();
|
||||
AnnotationsScanner.clearCache();
|
||||
AttributeMethods.cache.clear();
|
||||
RepeatableContainers.cache.clear();
|
||||
OrderUtils.orderCache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,9 +39,7 @@ final class AttributeMethods {
|
||||
|
||||
static final AttributeMethods NONE = new AttributeMethods(null, new Method[0]);
|
||||
|
||||
|
||||
private static final Map<Class<? extends Annotation>, AttributeMethods> cache =
|
||||
new ConcurrentReferenceHashMap<>();
|
||||
static final Map<Class<? extends Annotation>, AttributeMethods> cache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
private static final Comparator<Method> methodComparator = (m1, m2) -> {
|
||||
if (m1 != null && m2 != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -41,7 +41,7 @@ public abstract class OrderUtils {
|
||||
private static final String JAVAX_PRIORITY_ANNOTATION = "javax.annotation.Priority";
|
||||
|
||||
/** Cache for @Order value (or NOT_ANNOTATED marker) per Class. */
|
||||
private static final Map<AnnotatedElement, Object> orderCache = new ConcurrentReferenceHashMap<>(64);
|
||||
static final Map<AnnotatedElement, Object> orderCache = new ConcurrentReferenceHashMap<>(64);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -43,6 +43,8 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
public abstract class RepeatableContainers {
|
||||
|
||||
static final Map<Class<? extends Annotation>, Object> cache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
@Nullable
|
||||
private final RepeatableContainers parent;
|
||||
|
||||
@@ -137,8 +139,6 @@ public abstract class RepeatableContainers {
|
||||
*/
|
||||
private static class StandardRepeatableContainers extends RepeatableContainers {
|
||||
|
||||
private static final Map<Class<? extends Annotation>, Object> cache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
private static final Object NONE = new Object();
|
||||
|
||||
private static StandardRepeatableContainers INSTANCE = new StandardRepeatableContainers();
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -205,8 +205,7 @@ public class GenericConversionService implements ConfigurableConversionService {
|
||||
* @param targetType the target type
|
||||
* @return the converted value
|
||||
* @throws ConversionException if a conversion exception occurred
|
||||
* @throws IllegalArgumentException if targetType is {@code null},
|
||||
* or sourceType is {@code null} but source is not {@code null}
|
||||
* @throws IllegalArgumentException if targetType is {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public Object convert(@Nullable Object source, TypeDescriptor targetType) {
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -252,7 +252,7 @@ public abstract class CommandLinePropertySource<T> extends EnumerablePropertySou
|
||||
@Override
|
||||
public final boolean containsProperty(String name) {
|
||||
if (this.nonOptionArgsPropertyName.equals(name)) {
|
||||
return !this.getNonOptionArgs().isEmpty();
|
||||
return !getNonOptionArgs().isEmpty();
|
||||
}
|
||||
return this.containsOption(name);
|
||||
}
|
||||
@@ -270,7 +270,7 @@ public abstract class CommandLinePropertySource<T> extends EnumerablePropertySou
|
||||
@Nullable
|
||||
public final String getProperty(String name) {
|
||||
if (this.nonOptionArgsPropertyName.equals(name)) {
|
||||
Collection<String> nonOptionArguments = this.getNonOptionArgs();
|
||||
Collection<String> nonOptionArguments = getNonOptionArgs();
|
||||
if (nonOptionArguments.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -278,7 +278,7 @@ public abstract class CommandLinePropertySource<T> extends EnumerablePropertySou
|
||||
return StringUtils.collectionToCommaDelimitedString(nonOptionArguments);
|
||||
}
|
||||
}
|
||||
Collection<String> optionValues = this.getOptionValues(name);
|
||||
Collection<String> optionValues = getOptionValues(name);
|
||||
if (optionValues == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+6
-10
@@ -168,31 +168,27 @@ final class ProfilesParser {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.expressions.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ParsedProfiles that = (ParsedProfiles) obj;
|
||||
return this.expressions.equals(that.expressions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.expressions.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringUtils.collectionToDelimitedString(this.expressions, " or ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -429,8 +429,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
String filePath = new File(path).getAbsolutePath();
|
||||
int prefixIndex = filePath.indexOf(':');
|
||||
if (prefixIndex == 1) {
|
||||
// Possibly "c:" drive prefix on Windows, to be upper-cased for proper duplicate detection
|
||||
filePath = StringUtils.capitalize(filePath);
|
||||
// Possibly a drive prefix on Windows (for example, "c:"), so we prepend a slash
|
||||
// and convert the drive letter to uppercase for consistent duplicate detection.
|
||||
filePath = "/" + StringUtils.capitalize(filePath);
|
||||
}
|
||||
// # can appear in directories/filenames, java.net.URL should not treat it as a fragment
|
||||
filePath = StringUtils.replace(filePath, "#", "%23");
|
||||
|
||||
+11
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,8 +33,8 @@ import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
* {@link TaskExecutor} implementation that fires up a new Thread for each task,
|
||||
* executing it asynchronously.
|
||||
*
|
||||
* <p>Supports limiting concurrent threads through the "concurrencyLimit"
|
||||
* bean property. By default, the number of concurrent threads is unlimited.
|
||||
* <p>Supports limiting concurrent threads through {@link #setConcurrencyLimit}.
|
||||
* By default, the number of concurrent task executions is unlimited.
|
||||
*
|
||||
* <p><b>NOTE: This implementation does not reuse threads!</b> Consider a
|
||||
* thread-pooling TaskExecutor implementation instead, in particular for
|
||||
@@ -133,33 +133,31 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
|
||||
* have to cast it and call {@code Future#get} to evaluate exceptions.
|
||||
* @since 4.3
|
||||
*/
|
||||
public final void setTaskDecorator(TaskDecorator taskDecorator) {
|
||||
public void setTaskDecorator(TaskDecorator taskDecorator) {
|
||||
this.taskDecorator = taskDecorator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of parallel accesses allowed.
|
||||
* -1 indicates no concurrency limit at all.
|
||||
* <p>In principle, this limit can be changed at runtime,
|
||||
* although it is generally designed as a config time setting.
|
||||
* NOTE: Do not switch between -1 and any concrete limit at runtime,
|
||||
* as this will lead to inconsistent concurrency counts: A limit
|
||||
* of -1 effectively turns off concurrency counting completely.
|
||||
* Set the maximum number of parallel task executions allowed.
|
||||
* The default of -1 indicates no concurrency limit at all.
|
||||
* <p>This is the equivalent of a maximum pool size in a thread pool,
|
||||
* preventing temporary overload of the thread management system.
|
||||
* @see #UNBOUNDED_CONCURRENCY
|
||||
* @see org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor#setMaxPoolSize
|
||||
*/
|
||||
public void setConcurrencyLimit(int concurrencyLimit) {
|
||||
this.concurrencyThrottle.setConcurrencyLimit(concurrencyLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of parallel accesses allowed.
|
||||
* Return the maximum number of parallel task executions allowed.
|
||||
*/
|
||||
public final int getConcurrencyLimit() {
|
||||
return this.concurrencyThrottle.getConcurrencyLimit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this throttle is currently active.
|
||||
* Return whether the concurrency throttle is currently active.
|
||||
* @return {@code true} if the concurrency limit for this instance is active
|
||||
* @see #getConcurrencyLimit()
|
||||
* @see #setConcurrencyLimit
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -171,6 +171,7 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
||||
return (access & Opcodes.ACC_INTERFACE) != 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link MergedAnnotation} source.
|
||||
*/
|
||||
@@ -182,11 +183,6 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.className.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
@@ -198,11 +194,15 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
||||
return this.className.equals(((Source) obj).className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.className.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.className;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.springframework.lang.Nullable;
|
||||
* @author Keith Donald
|
||||
* @author Rob Harrop
|
||||
* @author Sam Brannen
|
||||
* @author Sebastien Deleuze
|
||||
* @since 1.1
|
||||
* @see TypeUtils
|
||||
* @see ReflectionUtils
|
||||
@@ -83,6 +84,12 @@ public abstract class ClassUtils {
|
||||
/** The ".class" file suffix. */
|
||||
public static final String CLASS_FILE_SUFFIX = ".class";
|
||||
|
||||
/** Precomputed value for the combination of private, static and final modifiers. */
|
||||
private static final int NON_OVERRIDABLE_MODIFIER = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL;
|
||||
|
||||
/** Precomputed value for the combination of public and protected modifiers. */
|
||||
private static final int OVERRIDABLE_MODIFIER = Modifier.PUBLIC | Modifier.PROTECTED;
|
||||
|
||||
|
||||
/**
|
||||
* Map with primitive wrapper type as key and corresponding primitive
|
||||
@@ -1379,10 +1386,10 @@ public abstract class ClassUtils {
|
||||
* @param targetClass the target class to check against
|
||||
*/
|
||||
private static boolean isOverridable(Method method, @Nullable Class<?> targetClass) {
|
||||
if (Modifier.isPrivate(method.getModifiers())) {
|
||||
if ((method.getModifiers() & NON_OVERRIDABLE_MODIFIER) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())) {
|
||||
if ((method.getModifiers() & OVERRIDABLE_MODIFIER) != 0) {
|
||||
return true;
|
||||
}
|
||||
return (targetClass == null ||
|
||||
@@ -1403,7 +1410,7 @@ public abstract class ClassUtils {
|
||||
Assert.notNull(methodName, "Method name must not be null");
|
||||
try {
|
||||
Method method = clazz.getMethod(methodName, args);
|
||||
return Modifier.isStatic(method.getModifiers()) ? method : null;
|
||||
return (Modifier.isStatic(method.getModifiers()) ? method : null);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -69,7 +69,7 @@ public abstract class ConcurrencyThrottleSupport implements Serializable {
|
||||
|
||||
/**
|
||||
* Set the maximum number of concurrent access attempts allowed.
|
||||
* -1 indicates unbounded concurrency.
|
||||
* The default of -1 indicates no concurrency limit at all.
|
||||
* <p>In principle, this limit can be changed at runtime,
|
||||
* although it is generally designed as a config time setting.
|
||||
* <p>NOTE: Do not switch between -1 and any concrete limit at runtime,
|
||||
@@ -143,9 +143,10 @@ public abstract class ConcurrencyThrottleSupport implements Serializable {
|
||||
*/
|
||||
protected void afterAccess() {
|
||||
if (this.concurrencyLimit >= 0) {
|
||||
boolean debug = logger.isDebugEnabled();
|
||||
synchronized (this.monitor) {
|
||||
this.concurrencyCount--;
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (debug) {
|
||||
logger.debug("Returning from throttle at concurrency count " + this.concurrencyCount);
|
||||
}
|
||||
this.monitor.notify();
|
||||
|
||||
+10
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -752,28 +752,27 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (this.key + "=" + this.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public final boolean equals(@Nullable Object other) {
|
||||
public boolean equals(@Nullable Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof Map.Entry)) {
|
||||
if (!(other instanceof Map.Entry<?, ?>)) {
|
||||
return false;
|
||||
}
|
||||
Map.Entry otherEntry = (Map.Entry) other;
|
||||
Map.Entry<?, ?> otherEntry = (Map.Entry<?, ?>) other;
|
||||
return (ObjectUtils.nullSafeEquals(getKey(), otherEntry.getKey()) &&
|
||||
ObjectUtils.nullSafeEquals(getValue(), otherEntry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
public int hashCode() {
|
||||
return (ObjectUtils.nullSafeHashCode(this.key) ^ ObjectUtils.nullSafeHashCode(this.value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (this.key + "=" + this.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -123,8 +123,8 @@ public class MethodInvoker {
|
||||
|
||||
/**
|
||||
* Set a fully qualified static method name to invoke,
|
||||
* e.g. "example.MyExampleClass.myExampleMethod".
|
||||
* Convenient alternative to specifying targetClass and targetMethod.
|
||||
* e.g. "example.MyExampleClass.myExampleMethod". This is a
|
||||
* convenient alternative to specifying targetClass and targetMethod.
|
||||
* @see #setTargetClass
|
||||
* @see #setTargetMethod
|
||||
*/
|
||||
@@ -157,14 +157,16 @@ public class MethodInvoker {
|
||||
public void prepare() throws ClassNotFoundException, NoSuchMethodException {
|
||||
if (this.staticMethod != null) {
|
||||
int lastDotIndex = this.staticMethod.lastIndexOf('.');
|
||||
if (lastDotIndex == -1 || lastDotIndex == this.staticMethod.length()) {
|
||||
if (lastDotIndex == -1 || lastDotIndex == this.staticMethod.length() - 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"staticMethod must be a fully qualified class plus method name: " +
|
||||
"e.g. 'example.MyExampleClass.myExampleMethod'");
|
||||
}
|
||||
String className = this.staticMethod.substring(0, lastDotIndex);
|
||||
String methodName = this.staticMethod.substring(lastDotIndex + 1);
|
||||
this.targetClass = resolveClassName(className);
|
||||
if (this.targetClass == null || !this.targetClass.getName().equals(className)) {
|
||||
this.targetClass = resolveClassName(className);
|
||||
}
|
||||
this.targetMethod = methodName;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,8 +70,8 @@ public abstract class ObjectUtils {
|
||||
private static final String ARRAY_ELEMENT_SEPARATOR = ", ";
|
||||
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
private static final String NON_EMPTY_ARRAY = ARRAY_START + "..." + ARRAY_END;
|
||||
private static final String EMPTY_COLLECTION = "[]";
|
||||
private static final String NON_EMPTY_COLLECTION = "[...]";
|
||||
private static final String COLLECTION = "[...]";
|
||||
private static final String MAP = NON_EMPTY_ARRAY;
|
||||
|
||||
|
||||
/**
|
||||
@@ -938,10 +938,9 @@ public abstract class ObjectUtils {
|
||||
* <li>{@code"Optional[<concise-string>]"} if {@code obj} is a non-empty {@code Optional},
|
||||
* where {@code <concise-string>} is the result of invoking {@link #nullSafeConciseToString}
|
||||
* on the object contained in the {@code Optional}</li>
|
||||
* <li>{@code "{}"} if {@code obj} is an empty array or {@link Map}</li>
|
||||
* <li>{@code "{...}"} if {@code obj} is a non-empty array or {@link Map}</li>
|
||||
* <li>{@code "[]"} if {@code obj} is an empty {@link Collection}</li>
|
||||
* <li>{@code "[...]"} if {@code obj} is a non-empty {@link Collection}</li>
|
||||
* <li>{@code "{}"} if {@code obj} is an empty array</li>
|
||||
* <li>{@code "{...}"} if {@code obj} is a {@link Map} or a non-empty array</li>
|
||||
* <li>{@code "[...]"} if {@code obj} is a {@link Collection}</li>
|
||||
* <li>{@linkplain Class#getName() Class name} if {@code obj} is a {@link Class}</li>
|
||||
* <li>{@linkplain Charset#name() Charset name} if {@code obj} is a {@link Charset}</li>
|
||||
* <li>{@linkplain TimeZone#getID() TimeZone ID} if {@code obj} is a {@link TimeZone}</li>
|
||||
@@ -977,12 +976,11 @@ public abstract class ObjectUtils {
|
||||
if (obj.getClass().isArray()) {
|
||||
return (Array.getLength(obj) == 0 ? EMPTY_ARRAY : NON_EMPTY_ARRAY);
|
||||
}
|
||||
if (obj instanceof Collection<?>) {
|
||||
return (((Collection<?>) obj).isEmpty() ? EMPTY_COLLECTION : NON_EMPTY_COLLECTION);
|
||||
if (obj instanceof Collection) {
|
||||
return COLLECTION;
|
||||
}
|
||||
if (obj instanceof Map<?, ?>) {
|
||||
// EMPTY_ARRAY and NON_EMPTY_ARRAY are also used for maps.
|
||||
return (((Map<?, ?>) obj).isEmpty() ? EMPTY_ARRAY : NON_EMPTY_ARRAY);
|
||||
if (obj instanceof Map) {
|
||||
return MAP;
|
||||
}
|
||||
if (obj instanceof Class<?>) {
|
||||
return ((Class<?>) obj).getName();
|
||||
|
||||
@@ -247,26 +247,26 @@ public abstract class StringUtils {
|
||||
/**
|
||||
* Trim <em>all</em> whitespace from the given {@code CharSequence}:
|
||||
* leading, trailing, and in between characters.
|
||||
* @param text the {@code CharSequence} to check
|
||||
* @param str the {@code CharSequence} to check
|
||||
* @return the trimmed {@code CharSequence}
|
||||
* @since 5.3.22
|
||||
* @see #trimAllWhitespace(String)
|
||||
* @see java.lang.Character#isWhitespace
|
||||
*/
|
||||
public static CharSequence trimAllWhitespace(CharSequence text) {
|
||||
if (!hasLength(text)) {
|
||||
return text;
|
||||
public static CharSequence trimAllWhitespace(CharSequence str) {
|
||||
if (!hasLength(str)) {
|
||||
return str;
|
||||
}
|
||||
|
||||
int len = text.length();
|
||||
StringBuilder sb = new StringBuilder(text.length());
|
||||
int len = str.length();
|
||||
StringBuilder sb = new StringBuilder(str.length());
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = text.charAt(i);
|
||||
char c = str.charAt(i);
|
||||
if (!Character.isWhitespace(c)) {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
return sb;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,9 +278,10 @@ public abstract class StringUtils {
|
||||
* @see java.lang.Character#isWhitespace
|
||||
*/
|
||||
public static String trimAllWhitespace(String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
if (!hasLength(str)) {
|
||||
return str;
|
||||
}
|
||||
|
||||
return trimAllWhitespace((CharSequence) str).toString();
|
||||
}
|
||||
|
||||
|
||||
+14
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,12 +20,12 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link BackOff} that increases the back off period for each
|
||||
* retry attempt. When the interval has reached the {@link #setMaxInterval(long)
|
||||
* retry attempt. When the interval has reached the {@linkplain #setMaxInterval
|
||||
* max interval}, it is no longer increased. Stops retrying once the
|
||||
* {@link #setMaxElapsedTime(long) max elapsed time} has been reached.
|
||||
* {@linkplain #setMaxElapsedTime max elapsed time} has been reached.
|
||||
*
|
||||
* <p>Example: The default interval is {@value #DEFAULT_INITIAL_INTERVAL} ms,
|
||||
* the default multiplier is {@value #DEFAULT_MULTIPLIER}, and the default max
|
||||
* <p>Example: The default interval is {@value #DEFAULT_INITIAL_INTERVAL} ms;
|
||||
* the default multiplier is {@value #DEFAULT_MULTIPLIER}; and the default max
|
||||
* interval is {@value #DEFAULT_MAX_INTERVAL}. For 10 attempts the sequence will be
|
||||
* as follows:
|
||||
*
|
||||
@@ -44,10 +44,9 @@ import org.springframework.util.Assert;
|
||||
* 10 30000
|
||||
* </pre>
|
||||
*
|
||||
* <p>Note that the default max elapsed time is {@link Long#MAX_VALUE}. Use
|
||||
* {@link #setMaxElapsedTime(long)} to limit the maximum length of time
|
||||
* that an instance should accumulate before returning
|
||||
* {@link BackOffExecution#STOP}.
|
||||
* <p>Note that the default max elapsed time is {@link Long#MAX_VALUE}.
|
||||
* Use {@link #setMaxElapsedTime} to limit the maximum length of time that an
|
||||
* instance should accumulate before returning {@link BackOffExecution#STOP}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
@@ -107,7 +106,7 @@ public class ExponentialBackOff implements BackOff {
|
||||
|
||||
|
||||
/**
|
||||
* The initial interval in milliseconds.
|
||||
* Set the initial interval in milliseconds.
|
||||
*/
|
||||
public void setInitialInterval(long initialInterval) {
|
||||
this.initialInterval = initialInterval;
|
||||
@@ -121,7 +120,7 @@ public class ExponentialBackOff implements BackOff {
|
||||
}
|
||||
|
||||
/**
|
||||
* The value to multiply the current interval by for each retry attempt.
|
||||
* Set the value to multiply the current interval by for each retry attempt.
|
||||
*/
|
||||
public void setMultiplier(double multiplier) {
|
||||
checkMultiplier(multiplier);
|
||||
@@ -136,21 +135,21 @@ public class ExponentialBackOff implements BackOff {
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum back off time.
|
||||
* Set the maximum back off time in milliseconds.
|
||||
*/
|
||||
public void setMaxInterval(long maxInterval) {
|
||||
this.maxInterval = maxInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum back off time.
|
||||
* Return the maximum back off time in milliseconds.
|
||||
*/
|
||||
public long getMaxInterval() {
|
||||
return this.maxInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum elapsed time in milliseconds after which a call to
|
||||
* Set the maximum elapsed time in milliseconds after which a call to
|
||||
* {@link BackOffExecution#nextBackOff()} returns {@link BackOffExecution#STOP}.
|
||||
*/
|
||||
public void setMaxElapsedTime(long maxElapsedTime) {
|
||||
@@ -184,10 +183,9 @@ public class ExponentialBackOff implements BackOff {
|
||||
|
||||
@Override
|
||||
public long nextBackOff() {
|
||||
if (this.currentElapsedTime >= maxElapsedTime) {
|
||||
if (this.currentElapsedTime >= getMaxElapsedTime()) {
|
||||
return STOP;
|
||||
}
|
||||
|
||||
long nextInterval = computeNextInterval();
|
||||
this.currentElapsedTime += nextInterval;
|
||||
return nextInterval;
|
||||
@@ -214,7 +212,6 @@ public class ExponentialBackOff implements BackOff {
|
||||
return Math.min(i, maxInterval);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("ExponentialBackOff{");
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -85,7 +85,7 @@ public class NullSafeComparator<T> implements Comparator<T> {
|
||||
* @param nullsLow whether to treat nulls lower or higher than non-null objects
|
||||
*/
|
||||
public NullSafeComparator(Comparator<T> comparator, boolean nullsLow) {
|
||||
Assert.notNull(comparator, "Non-null Comparator is required");
|
||||
Assert.notNull(comparator, "Comparator must not be null");
|
||||
this.nonNullComparator = comparator;
|
||||
this.nullsLow = nullsLow;
|
||||
}
|
||||
@@ -107,16 +107,16 @@ public class NullSafeComparator<T> implements Comparator<T> {
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean equals(@Nullable Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof NullSafeComparator)) {
|
||||
if (!(other instanceof NullSafeComparator<?>)) {
|
||||
return false;
|
||||
}
|
||||
NullSafeComparator<T> otherComp = (NullSafeComparator<T>) other;
|
||||
return (this.nonNullComparator.equals(otherComp.nonNullComparator) && this.nullsLow == otherComp.nullsLow);
|
||||
NullSafeComparator<?> otherComp = (NullSafeComparator<?>) other;
|
||||
return (this.nonNullComparator.equals(otherComp.nonNullComparator) &&
|
||||
this.nullsLow == otherComp.nullsLow);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+44
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Unit tests for {@link ReactiveAdapterRegistry}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
class ReactiveAdapterRegistryTests {
|
||||
@@ -52,14 +53,40 @@ class ReactiveAdapterRegistryTests {
|
||||
ReactiveAdapter adapter2 = getAdapter(ExtendedFlux.class);
|
||||
assertThat(adapter2).isSameAs(adapter1);
|
||||
|
||||
// Register regular reactive type (after existing adapters)
|
||||
this.registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.multiValue(ExtendedFlux.class, ExtendedFlux::empty),
|
||||
o -> (ExtendedFlux<?>) o,
|
||||
ExtendedFlux::from);
|
||||
|
||||
// Matches for ExtendedFlux itself
|
||||
ReactiveAdapter adapter3 = getAdapter(ExtendedFlux.class);
|
||||
assertThat(adapter3).isNotNull();
|
||||
assertThat(adapter3).isNotSameAs(adapter1);
|
||||
|
||||
// Does not match for ExtendedFlux subclass since the default Flux adapter
|
||||
// is being assignability-checked first when no specific match was found
|
||||
ReactiveAdapter adapter4 = getAdapter(ExtendedExtendedFlux.class);
|
||||
assertThat(adapter4).isSameAs(adapter1);
|
||||
|
||||
// Register reactive type override (before existing adapters)
|
||||
this.registry.registerReactiveTypeOverride(
|
||||
ReactiveTypeDescriptor.multiValue(Flux.class, ExtendedFlux::empty),
|
||||
o -> (ExtendedFlux<?>) o,
|
||||
ExtendedFlux::from);
|
||||
|
||||
// Override match for Flux
|
||||
ReactiveAdapter adapter5 = getAdapter(Flux.class);
|
||||
assertThat(adapter5).isNotNull();
|
||||
assertThat(adapter5).isNotSameAs(adapter1);
|
||||
|
||||
// Initially registered adapter specifically matches for ExtendedFlux
|
||||
ReactiveAdapter adapter6 = getAdapter(ExtendedFlux.class);
|
||||
assertThat(adapter6).isSameAs(adapter3);
|
||||
|
||||
// Override match for ExtendedFlux subclass
|
||||
ReactiveAdapter adapter7 = getAdapter(ExtendedExtendedFlux.class);
|
||||
assertThat(adapter7).isSameAs(adapter5);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +106,10 @@ class ReactiveAdapterRegistryTests {
|
||||
}
|
||||
|
||||
|
||||
private static class ExtendedExtendedFlux<T> extends ExtendedFlux<T> {
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class Reactor {
|
||||
|
||||
@@ -100,7 +131,7 @@ class ReactiveAdapterRegistryTests {
|
||||
List<Integer> sequence = Arrays.asList(1, 2, 3);
|
||||
Publisher<Integer> source = io.reactivex.rxjava3.core.Flowable.fromIterable(sequence);
|
||||
Object target = getAdapter(Flux.class).fromPublisher(source);
|
||||
assertThat(target instanceof Flux).isTrue();
|
||||
assertThat(target).isInstanceOf(Flux.class);
|
||||
assertThat(((Flux<Integer>) target).collectList().block(ONE_SECOND)).isEqualTo(sequence);
|
||||
}
|
||||
|
||||
@@ -108,7 +139,7 @@ class ReactiveAdapterRegistryTests {
|
||||
void toMono() {
|
||||
Publisher<Integer> source = io.reactivex.rxjava3.core.Flowable.fromArray(1, 2, 3);
|
||||
Object target = getAdapter(Mono.class).fromPublisher(source);
|
||||
assertThat(target instanceof Mono).isTrue();
|
||||
assertThat(target).isInstanceOf(Mono.class);
|
||||
assertThat(((Mono<Integer>) target).block(ONE_SECOND)).isEqualTo(Integer.valueOf(1));
|
||||
}
|
||||
|
||||
@@ -116,7 +147,7 @@ class ReactiveAdapterRegistryTests {
|
||||
void toCompletableFuture() throws Exception {
|
||||
Publisher<Integer> source = Flux.fromArray(new Integer[] {1, 2, 3});
|
||||
Object target = getAdapter(CompletableFuture.class).fromPublisher(source);
|
||||
assertThat(target instanceof CompletableFuture).isTrue();
|
||||
assertThat(target).isInstanceOf(CompletableFuture.class);
|
||||
assertThat(((CompletableFuture<Integer>) target).get()).isEqualTo(Integer.valueOf(1));
|
||||
}
|
||||
|
||||
@@ -125,7 +156,7 @@ class ReactiveAdapterRegistryTests {
|
||||
CompletableFuture<Integer> future = new CompletableFuture<>();
|
||||
future.complete(1);
|
||||
Object target = getAdapter(CompletableFuture.class).toPublisher(future);
|
||||
assertThat(target instanceof Mono).as("Expected Mono Publisher: " + target.getClass().getName()).isTrue();
|
||||
assertThat(target).as("Expected Mono Publisher: " + target.getClass().getName()).isInstanceOf(Mono.class);
|
||||
assertThat(((Mono<Integer>) target).block(ONE_SECOND)).isEqualTo(Integer.valueOf(1));
|
||||
}
|
||||
}
|
||||
@@ -294,7 +325,7 @@ class ReactiveAdapterRegistryTests {
|
||||
List<Integer> sequence = Arrays.asList(1, 2, 3);
|
||||
Publisher<Integer> source = Flux.fromIterable(sequence);
|
||||
Object target = getAdapter(io.reactivex.rxjava3.core.Flowable.class).fromPublisher(source);
|
||||
assertThat(target instanceof io.reactivex.rxjava3.core.Flowable).isTrue();
|
||||
assertThat(target).isInstanceOf(io.reactivex.rxjava3.core.Flowable.class);
|
||||
assertThat(((io.reactivex.rxjava3.core.Flowable<?>) target).toList().blockingGet()).isEqualTo(sequence);
|
||||
}
|
||||
|
||||
@@ -303,7 +334,7 @@ class ReactiveAdapterRegistryTests {
|
||||
List<Integer> sequence = Arrays.asList(1, 2, 3);
|
||||
Publisher<Integer> source = Flux.fromIterable(sequence);
|
||||
Object target = getAdapter(io.reactivex.rxjava3.core.Observable.class).fromPublisher(source);
|
||||
assertThat(target instanceof io.reactivex.rxjava3.core.Observable).isTrue();
|
||||
assertThat(target).isInstanceOf(io.reactivex.rxjava3.core.Observable.class);
|
||||
assertThat(((io.reactivex.rxjava3.core.Observable<?>) target).toList().blockingGet()).isEqualTo(sequence);
|
||||
}
|
||||
|
||||
@@ -311,7 +342,7 @@ class ReactiveAdapterRegistryTests {
|
||||
void toSingle() {
|
||||
Publisher<Integer> source = Flux.fromArray(new Integer[] {1});
|
||||
Object target = getAdapter(io.reactivex.rxjava3.core.Single.class).fromPublisher(source);
|
||||
assertThat(target instanceof io.reactivex.rxjava3.core.Single).isTrue();
|
||||
assertThat(target).isInstanceOf(io.reactivex.rxjava3.core.Single.class);
|
||||
assertThat(((io.reactivex.rxjava3.core.Single<Integer>) target).blockingGet()).isEqualTo(Integer.valueOf(1));
|
||||
}
|
||||
|
||||
@@ -319,7 +350,7 @@ class ReactiveAdapterRegistryTests {
|
||||
void toCompletable() {
|
||||
Publisher<Integer> source = Flux.fromArray(new Integer[] {1, 2, 3});
|
||||
Object target = getAdapter(io.reactivex.rxjava3.core.Completable.class).fromPublisher(source);
|
||||
assertThat(target instanceof io.reactivex.rxjava3.core.Completable).isTrue();
|
||||
assertThat(target).isInstanceOf(io.reactivex.rxjava3.core.Completable.class);
|
||||
((io.reactivex.rxjava3.core.Completable) target).blockingAwait();
|
||||
}
|
||||
|
||||
@@ -328,7 +359,7 @@ class ReactiveAdapterRegistryTests {
|
||||
List<Integer> sequence = Arrays.asList(1, 2, 3);
|
||||
Object source = io.reactivex.rxjava3.core.Flowable.fromIterable(sequence);
|
||||
Object target = getAdapter(io.reactivex.rxjava3.core.Flowable.class).toPublisher(source);
|
||||
assertThat(target instanceof Flux).as("Expected Flux Publisher: " + target.getClass().getName()).isTrue();
|
||||
assertThat(target).as("Expected Flux Publisher: " + target.getClass().getName()).isInstanceOf(Flux.class);
|
||||
assertThat(((Flux<Integer>) target).collectList().block(ONE_SECOND)).isEqualTo(sequence);
|
||||
}
|
||||
|
||||
@@ -337,7 +368,7 @@ class ReactiveAdapterRegistryTests {
|
||||
List<Integer> sequence = Arrays.asList(1, 2, 3);
|
||||
Object source = io.reactivex.rxjava3.core.Observable.fromIterable(sequence);
|
||||
Object target = getAdapter(io.reactivex.rxjava3.core.Observable.class).toPublisher(source);
|
||||
assertThat(target instanceof Flux).as("Expected Flux Publisher: " + target.getClass().getName()).isTrue();
|
||||
assertThat(target).as("Expected Flux Publisher: " + target.getClass().getName()).isInstanceOf(Flux.class);
|
||||
assertThat(((Flux<Integer>) target).collectList().block(ONE_SECOND)).isEqualTo(sequence);
|
||||
}
|
||||
|
||||
@@ -345,7 +376,7 @@ class ReactiveAdapterRegistryTests {
|
||||
void fromSingle() {
|
||||
Object source = io.reactivex.rxjava3.core.Single.just(1);
|
||||
Object target = getAdapter(io.reactivex.rxjava3.core.Single.class).toPublisher(source);
|
||||
assertThat(target instanceof Mono).as("Expected Mono Publisher: " + target.getClass().getName()).isTrue();
|
||||
assertThat(target).as("Expected Mono Publisher: " + target.getClass().getName()).isInstanceOf(Mono.class);
|
||||
assertThat(((Mono<Integer>) target).block(ONE_SECOND)).isEqualTo(Integer.valueOf(1));
|
||||
}
|
||||
|
||||
@@ -353,7 +384,7 @@ class ReactiveAdapterRegistryTests {
|
||||
void fromCompletable() {
|
||||
Object source = io.reactivex.rxjava3.core.Completable.complete();
|
||||
Object target = getAdapter(io.reactivex.rxjava3.core.Completable.class).toPublisher(source);
|
||||
assertThat(target instanceof Mono).as("Expected Mono Publisher: " + target.getClass().getName()).isTrue();
|
||||
assertThat(target).as("Expected Mono Publisher: " + target.getClass().getName()).isInstanceOf(Mono.class);
|
||||
((Mono<Void>) target).block(ONE_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -200,8 +200,8 @@ class ResolvableTypeTests {
|
||||
@Test
|
||||
void forFieldMustNotBeNull() throws Exception {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ResolvableType.forField(null))
|
||||
.withMessage("Field must not be null");
|
||||
.isThrownBy(() -> ResolvableType.forField(null))
|
||||
.withMessage("Field must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -214,8 +214,8 @@ class ResolvableTypeTests {
|
||||
@Test
|
||||
void forConstructorParameterMustNotBeNull() throws Exception {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0))
|
||||
.withMessage("Constructor must not be null");
|
||||
.isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0))
|
||||
.withMessage("Constructor must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -228,8 +228,8 @@ class ResolvableTypeTests {
|
||||
@Test
|
||||
void forMethodParameterByIndexMustNotBeNull() throws Exception {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ResolvableType.forMethodParameter(null, 0))
|
||||
.withMessage("Method must not be null");
|
||||
.isThrownBy(() -> ResolvableType.forMethodParameter(null, 0))
|
||||
.withMessage("Method must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -268,8 +268,8 @@ class ResolvableTypeTests {
|
||||
@Test
|
||||
void forMethodParameterMustNotBeNull() throws Exception {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ResolvableType.forMethodParameter(null))
|
||||
.withMessage("MethodParameter must not be null");
|
||||
.isThrownBy(() -> ResolvableType.forMethodParameter(null))
|
||||
.withMessage("MethodParameter must not be null");
|
||||
}
|
||||
|
||||
@Test // SPR-16210
|
||||
@@ -295,8 +295,8 @@ class ResolvableTypeTests {
|
||||
@Test
|
||||
void forMethodReturnMustNotBeNull() throws Exception {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ResolvableType.forMethodReturnType(null))
|
||||
.withMessage("Method must not be null");
|
||||
.isThrownBy(() -> ResolvableType.forMethodReturnType(null))
|
||||
.withMessage("Method must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -343,7 +343,7 @@ class ResolvableTypeTests {
|
||||
ResolvableType type = ResolvableType.forField(field);
|
||||
assertThat(type.isArray()).isTrue();
|
||||
assertThat(type.getComponentType().getType())
|
||||
.isEqualTo(((Class) field.getGenericType()).getComponentType());
|
||||
.isEqualTo(((Class) field.getGenericType()).getComponentType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -535,7 +535,7 @@ class ResolvableTypeTests {
|
||||
void getGenericsFromParameterizedType() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClass(List.class, ExtendsList.class);
|
||||
ResolvableType[] generics = type.getGenerics();
|
||||
assertThat(generics.length).isEqualTo(1);
|
||||
assertThat(generics).hasSize(1);
|
||||
assertThat(generics[0].resolve()).isEqualTo(CharSequence.class);
|
||||
}
|
||||
|
||||
@@ -543,7 +543,7 @@ class ResolvableTypeTests {
|
||||
void getGenericsFromClass() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClass(List.class);
|
||||
ResolvableType[] generics = type.getGenerics();
|
||||
assertThat(generics.length).isEqualTo(1);
|
||||
assertThat(generics).hasSize(1);
|
||||
assertThat(generics[0].getType().toString()).isEqualTo("E");
|
||||
}
|
||||
|
||||
@@ -558,7 +558,7 @@ class ResolvableTypeTests {
|
||||
void getResolvedGenerics() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClass(List.class, ExtendsList.class);
|
||||
Class<?>[] generics = type.resolveGenerics();
|
||||
assertThat(generics.length).isEqualTo(1);
|
||||
assertThat(generics).hasSize(1);
|
||||
assertThat(generics[0]).isEqualTo(CharSequence.class);
|
||||
}
|
||||
|
||||
@@ -686,7 +686,6 @@ class ResolvableTypeTests {
|
||||
assertThat(type.resolve()).isEqualTo(CharSequence.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void resolveBoundedTypeVariableWildcardResult() throws Exception {
|
||||
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVariableWildcardResult"));
|
||||
@@ -701,30 +700,26 @@ class ResolvableTypeTests {
|
||||
|
||||
@Test
|
||||
void resolveTypeVariableFromSimpleInterfaceType() {
|
||||
ResolvableType type = ResolvableType.forClass(
|
||||
MySimpleInterfaceType.class).as(MyInterfaceType.class);
|
||||
ResolvableType type = ResolvableType.forClass(MySimpleInterfaceType.class).as(MyInterfaceType.class);
|
||||
assertThat(type.resolveGeneric()).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveTypeVariableFromSimpleCollectionInterfaceType() {
|
||||
ResolvableType type = ResolvableType.forClass(
|
||||
MyCollectionInterfaceType.class).as(MyInterfaceType.class);
|
||||
ResolvableType type = ResolvableType.forClass(MyCollectionInterfaceType.class).as(MyInterfaceType.class);
|
||||
assertThat(type.resolveGeneric()).isEqualTo(Collection.class);
|
||||
assertThat(type.resolveGeneric(0, 0)).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveTypeVariableFromSimpleSuperclassType() {
|
||||
ResolvableType type = ResolvableType.forClass(
|
||||
MySimpleSuperclassType.class).as(MySuperclassType.class);
|
||||
ResolvableType type = ResolvableType.forClass(MySimpleSuperclassType.class).as(MySuperclassType.class);
|
||||
assertThat(type.resolveGeneric()).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveTypeVariableFromSimpleCollectionSuperclassType() {
|
||||
ResolvableType type = ResolvableType.forClass(
|
||||
MyCollectionSuperclassType.class).as(MySuperclassType.class);
|
||||
ResolvableType type = ResolvableType.forClass(MyCollectionSuperclassType.class).as(MySuperclassType.class);
|
||||
assertThat(type.resolveGeneric()).isEqualTo(Collection.class);
|
||||
assertThat(type.resolveGeneric(0, 0)).isEqualTo(String.class);
|
||||
}
|
||||
@@ -751,8 +746,7 @@ class ResolvableTypeTests {
|
||||
void resolveTypeVariableFromSuperType() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
|
||||
assertThat(type.resolve()).isEqualTo(ExtendsList.class);
|
||||
assertThat(type.asCollection().resolveGeneric())
|
||||
.isEqualTo(CharSequence.class);
|
||||
assertThat(type.asCollection().resolveGeneric()).isEqualTo(CharSequence.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -964,8 +958,8 @@ class ResolvableTypeTests {
|
||||
@Test
|
||||
void isAssignableFromMustNotBeNull() throws Exception {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null))
|
||||
.withMessage("ResolvableType must not be null");
|
||||
.isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null))
|
||||
.withMessage("ResolvableType must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1004,6 +998,7 @@ class ResolvableTypeTests {
|
||||
void isAssignableFromCannotBeResolved() throws Exception {
|
||||
ResolvableType objectType = ResolvableType.forClass(Object.class);
|
||||
ResolvableType unresolvableVariable = ResolvableType.forField(AssignmentBase.class.getField("o"));
|
||||
|
||||
assertThat(unresolvableVariable.resolve()).isNull();
|
||||
assertThatResolvableType(objectType).isAssignableFrom(unresolvableVariable);
|
||||
assertThatResolvableType(unresolvableVariable).isAssignableFrom(objectType);
|
||||
@@ -1220,9 +1215,9 @@ class ResolvableTypeTests {
|
||||
@Test
|
||||
void forClassWithMismatchedGenerics() throws Exception {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class))
|
||||
.withMessageContaining("Mismatched number of generics specified for")
|
||||
.withMessageContaining("java.util.Map<K,V>");
|
||||
.isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class))
|
||||
.withMessageContaining("Mismatched number of generics specified for")
|
||||
.withMessageContaining("java.util.Map<K,V>");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1277,7 +1272,7 @@ class ResolvableTypeTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasUnresolvableGenericsWhenImplementesRawInterface() throws Exception {
|
||||
void hasUnresolvableGenericsWhenImplementingRawInterface() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClass(MySimpleInterfaceTypeWithImplementsRaw.class);
|
||||
for (ResolvableType generic : type.getGenerics()) {
|
||||
assertThat(generic.resolve()).isNotNull();
|
||||
@@ -1307,7 +1302,7 @@ class ResolvableTypeTests {
|
||||
Type type = resolvableType.getType();
|
||||
assertThat(type).isInstanceOf(ParameterizedType.class);
|
||||
assertThat(((ParameterizedType) type).getRawType()).isEqualTo(Callable.class);
|
||||
assertThat(((ParameterizedType) type).getActualTypeArguments().length).isEqualTo(1);
|
||||
assertThat(((ParameterizedType) type).getActualTypeArguments()).hasSize(1);
|
||||
assertThat(((ParameterizedType) type).getActualTypeArguments()[0]).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user