Merge branch '6.1.x'

# Conflicts:
#	spring-beans/src/main/java/org/springframework/beans/factory/aot/DefaultBeanRegistrationCodeFragments.java
This commit is contained in:
Juergen Hoeller 2024-07-10 15:16:40 +02:00
commit a9efe10428
6 changed files with 72 additions and 76 deletions

View File

@ -211,19 +211,28 @@ the JDBC driver. If the count is not available, the JDBC driver returns a value
==== ====
In such a scenario, with automatic setting of values on an underlying `PreparedStatement`, In such a scenario, with automatic setting of values on an underlying `PreparedStatement`,
the corresponding JDBC type for each value needs to be derived from the given Java type. the corresponding JDBC type for each value needs to be derived from the given Java type.
While this usually works well, there is a potential for issues (for example, with Map-contained While this usually works well, there is a potential for issues (for example, with
`null` values). Spring, by default, calls `ParameterMetaData.getParameterType` in such a Map-contained `null` values). Spring, by default, calls `ParameterMetaData.getParameterType`
case, which can be expensive with your JDBC driver. You should use a recent driver in such a case, which can be expensive with your JDBC driver. You should use a recent driver
version and consider setting the `spring.jdbc.getParameterType.ignore` property to `true` version and consider setting the `spring.jdbc.getParameterType.ignore` property to `true`
(as a JVM system property or via the (as a JVM system property or via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism) if you encounter xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism)
a performance issue (as reported on Oracle 12c, JBoss, and PostgreSQL). if you encounter a specific performance issue for your application.
Alternatively, you might consider specifying the corresponding JDBC types explicitly, As of 6.1.2, Spring bypasses the default `getParameterType` resolution on PostgreSQL and
either through a `BatchPreparedStatementSetter` (as shown earlier), through an explicit type MS SQL Server. This is a common optimization to avoid further roundtrips to the DBMS just
array given to a `List<Object[]>` based call, through `registerSqlType` calls on a for parameter type resolution which is known to make a very significant difference on
custom `MapSqlParameterSource` instance, or through a `BeanPropertySqlParameterSource` PostgreSQL and MS SQL Server specifically, in particular for batch operations. If you
that derives the SQL type from the Java-declared property type even for a null value. happen to see a side effect e.g. when setting a byte array to null without specific type
indication, you may explicitly set the `spring.jdbc.getParameterType.ignore=false` flag
as a system property (see above) to restore full `getParameterType` resolution.
Alternatively, you could consider specifying the corresponding JDBC types explicitly,
either through a `BatchPreparedStatementSetter` (as shown earlier), through an explicit
type array given to a `List<Object[]>` based call, through `registerSqlType` calls on a
custom `MapSqlParameterSource` instance, through a `BeanPropertySqlParameterSource`
that derives the SQL type from the Java-declared property type even for a null value, or
through providing individual `SqlParameterValue` instances instead of plain null values.
==== ====

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -80,8 +80,8 @@ class ScopedProxyBeanRegistrationAotProcessor implements BeanRegistrationAotProc
} }
@Nullable @Nullable
private BeanDefinition getTargetBeanDefinition(ConfigurableBeanFactory beanFactory, private BeanDefinition getTargetBeanDefinition(
@Nullable String targetBeanName) { ConfigurableBeanFactory beanFactory, @Nullable String targetBeanName) {
if (targetBeanName != null && beanFactory.containsBean(targetBeanName)) { if (targetBeanName != null && beanFactory.containsBean(targetBeanName)) {
return beanFactory.getMergedBeanDefinition(targetBeanName); return beanFactory.getMergedBeanDefinition(targetBeanName);
@ -124,16 +124,12 @@ class ScopedProxyBeanRegistrationAotProcessor implements BeanRegistrationAotProc
@Override @Override
public CodeBlock generateSetBeanDefinitionPropertiesCode( public CodeBlock generateSetBeanDefinitionPropertiesCode(
GenerationContext generationContext, GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode,
BeanRegistrationCode beanRegistrationCode,
RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) { RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
RootBeanDefinition processedBeanDefinition = new RootBeanDefinition( RootBeanDefinition processedBeanDefinition = new RootBeanDefinition(beanDefinition);
beanDefinition); processedBeanDefinition.setTargetType(this.targetBeanDefinition.getResolvableType());
processedBeanDefinition processedBeanDefinition.getPropertyValues().removePropertyValue("targetBeanName");
.setTargetType(this.targetBeanDefinition.getResolvableType());
processedBeanDefinition.getPropertyValues()
.removePropertyValue("targetBeanName");
return super.generateSetBeanDefinitionPropertiesCode(generationContext, return super.generateSetBeanDefinitionPropertiesCode(generationContext,
beanRegistrationCode, processedBeanDefinition, attributeFilter); beanRegistrationCode, processedBeanDefinition, attributeFilter);
} }
@ -144,20 +140,15 @@ class ScopedProxyBeanRegistrationAotProcessor implements BeanRegistrationAotProc
GeneratedMethod generatedMethod = beanRegistrationCode.getMethods() GeneratedMethod generatedMethod = beanRegistrationCode.getMethods()
.add("getScopedProxyInstance", method -> { .add("getScopedProxyInstance", method -> {
method.addJavadoc( method.addJavadoc("Create the scoped proxy bean instance for '$L'.",
"Create the scoped proxy bean instance for '$L'.",
this.registeredBean.getBeanName()); this.registeredBean.getBeanName());
method.addModifiers(Modifier.PRIVATE, Modifier.STATIC); method.addModifiers(Modifier.PRIVATE, Modifier.STATIC);
method.returns(ScopedProxyFactoryBean.class); method.returns(ScopedProxyFactoryBean.class);
method.addParameter(RegisteredBean.class, method.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER_NAME);
REGISTERED_BEAN_PARAMETER_NAME);
method.addStatement("$T factory = new $T()", method.addStatement("$T factory = new $T()",
ScopedProxyFactoryBean.class, ScopedProxyFactoryBean.class, ScopedProxyFactoryBean.class);
ScopedProxyFactoryBean.class); method.addStatement("factory.setTargetBeanName($S)", this.targetBeanName);
method.addStatement("factory.setTargetBeanName($S)", method.addStatement("factory.setBeanFactory($L.getBeanFactory())",
this.targetBeanName);
method.addStatement(
"factory.setBeanFactory($L.getBeanFactory())",
REGISTERED_BEAN_PARAMETER_NAME); REGISTERED_BEAN_PARAMETER_NAME);
method.addStatement("return factory"); method.addStatement("return factory");
}); });

View File

@ -1098,7 +1098,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
} }
} }
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -58,39 +58,39 @@ public class BeanRegistrationCodeFragmentsDecorator implements BeanRegistrationC
public CodeBlock generateNewBeanDefinitionCode(GenerationContext generationContext, public CodeBlock generateNewBeanDefinitionCode(GenerationContext generationContext,
ResolvableType beanType, BeanRegistrationCode beanRegistrationCode) { ResolvableType beanType, BeanRegistrationCode beanRegistrationCode) {
return this.delegate.generateNewBeanDefinitionCode(generationContext, return this.delegate.generateNewBeanDefinitionCode(generationContext, beanType, beanRegistrationCode);
beanType, beanRegistrationCode);
} }
@Override @Override
public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext, public CodeBlock generateSetBeanDefinitionPropertiesCode(
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode,
Predicate<String> attributeFilter) { RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
return this.delegate.generateSetBeanDefinitionPropertiesCode( return this.delegate.generateSetBeanDefinitionPropertiesCode(
generationContext, beanRegistrationCode, beanDefinition, attributeFilter); generationContext, beanRegistrationCode, beanDefinition, attributeFilter);
} }
@Override @Override
public CodeBlock generateSetBeanInstanceSupplierCode(GenerationContext generationContext, public CodeBlock generateSetBeanInstanceSupplierCode(
BeanRegistrationCode beanRegistrationCode, CodeBlock instanceSupplierCode, GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode,
List<MethodReference> postProcessors) { CodeBlock instanceSupplierCode, List<MethodReference> postProcessors) {
return this.delegate.generateSetBeanInstanceSupplierCode(generationContext, return this.delegate.generateSetBeanInstanceSupplierCode(generationContext,
beanRegistrationCode, instanceSupplierCode, postProcessors); beanRegistrationCode, instanceSupplierCode, postProcessors);
} }
@Override @Override
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext, public CodeBlock generateInstanceSupplierCode(
BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) { GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode,
boolean allowDirectSupplierShortcut) {
return this.delegate.generateInstanceSupplierCode(generationContext, return this.delegate.generateInstanceSupplierCode(generationContext,
beanRegistrationCode, allowDirectSupplierShortcut); beanRegistrationCode, allowDirectSupplierShortcut);
} }
@Override @Override
public CodeBlock generateReturnCode(GenerationContext generationContext, public CodeBlock generateReturnCode(
BeanRegistrationCode beanRegistrationCode) { GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
return this.delegate.generateReturnCode(generationContext, beanRegistrationCode); return this.delegate.generateReturnCode(generationContext, beanRegistrationCode);
} }

View File

@ -46,8 +46,7 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.function.SingletonSupplier; import org.springframework.util.function.SingletonSupplier;
/** /**
* Internal {@link BeanRegistrationCodeFragments} implementation used by * Internal {@link BeanRegistrationCodeFragments} implementation used by default.
* default.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Stephane Nicoll * @author Stephane Nicoll
@ -92,9 +91,8 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
private Class<?> extractDeclaringClass(RegisteredBean registeredBean, InstantiationDescriptor instantiationDescriptor) { private Class<?> extractDeclaringClass(RegisteredBean registeredBean, InstantiationDescriptor instantiationDescriptor) {
Class<?> declaringClass = ClassUtils.getUserClass(instantiationDescriptor.targetClass()); Class<?> declaringClass = ClassUtils.getUserClass(instantiationDescriptor.targetClass());
if (instantiationDescriptor.executable() instanceof Constructor<?> if (instantiationDescriptor.executable() instanceof Constructor<?> ctor &&
&& AccessControl.forMember(instantiationDescriptor.executable()).isPublic() AccessControl.forMember(ctor).isPublic() && FactoryBean.class.isAssignableFrom(declaringClass)) {
&& FactoryBean.class.isAssignableFrom(declaringClass)) {
return extractTargetClassFromFactoryBean(declaringClass, registeredBean.getBeanType()); return extractTargetClassFromFactoryBean(declaringClass, registeredBean.getBeanType());
} }
return declaringClass; return declaringClass;
@ -125,17 +123,15 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
ResolvableType beanType, BeanRegistrationCode beanRegistrationCode) { ResolvableType beanType, BeanRegistrationCode beanRegistrationCode) {
CodeBlock.Builder code = CodeBlock.builder(); CodeBlock.Builder code = CodeBlock.builder();
RootBeanDefinition mergedBeanDefinition = this.registeredBean.getMergedBeanDefinition(); RootBeanDefinition mbd = this.registeredBean.getMergedBeanDefinition();
Class<?> beanClass = (mergedBeanDefinition.hasBeanClass() Class<?> beanClass = (mbd.hasBeanClass() ? ClassUtils.getUserClass(mbd.getBeanClass()) : null);
? ClassUtils.getUserClass(mergedBeanDefinition.getBeanClass()) : null);
CodeBlock beanClassCode = generateBeanClassCode( CodeBlock beanClassCode = generateBeanClassCode(
beanRegistrationCode.getClassName().packageName(), beanRegistrationCode.getClassName().packageName(),
(beanClass != null ? beanClass : beanType.toClass())); (beanClass != null ? beanClass : beanType.toClass()));
code.addStatement("$T $L = new $T($L)", RootBeanDefinition.class, code.addStatement("$T $L = new $T($L)", RootBeanDefinition.class,
BEAN_DEFINITION_VARIABLE, RootBeanDefinition.class, beanClassCode); BEAN_DEFINITION_VARIABLE, RootBeanDefinition.class, beanClassCode);
if (targetTypeNecessary(beanType, beanClass)) { if (targetTypeNecessary(beanType, beanClass)) {
code.addStatement("$L.setTargetType($L)", BEAN_DEFINITION_VARIABLE, code.addStatement("$L.setTargetType($L)", BEAN_DEFINITION_VARIABLE, generateBeanTypeCode(beanType));
generateBeanTypeCode(beanType));
} }
return code.build(); return code.build();
} }
@ -160,8 +156,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
if (beanType.hasGenerics()) { if (beanType.hasGenerics()) {
return true; return true;
} }
if (beanClass != null if (beanClass != null && this.registeredBean.getMergedBeanDefinition().getFactoryMethodName() != null) {
&& this.registeredBean.getMergedBeanDefinition().getFactoryMethodName() != null) {
return true; return true;
} }
return (beanClass != null && !beanType.toClass().equals(beanClass)); return (beanClass != null && !beanType.toClass().equals(beanClass));
@ -169,21 +164,19 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
@Override @Override
public CodeBlock generateSetBeanDefinitionPropertiesCode( public CodeBlock generateSetBeanDefinitionPropertiesCode(
GenerationContext generationContext, GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode,
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
Predicate<String> attributeFilter) {
Loader loader = AotServices.factories(this.registeredBean.getBeanFactory().getBeanClassLoader()); Loader loader = AotServices.factories(this.registeredBean.getBeanFactory().getBeanClassLoader());
List<Delegate> additionalDelegates = loader.load(Delegate.class).asList(); List<Delegate> additionalDelegates = loader.load(Delegate.class).asList();
return new BeanDefinitionPropertiesCodeGenerator(generationContext.getRuntimeHints(), return new BeanDefinitionPropertiesCodeGenerator(generationContext.getRuntimeHints(),
attributeFilter, beanRegistrationCode.getMethods(), attributeFilter, beanRegistrationCode.getMethods(),
additionalDelegates, (name, value) -> generateValueCode(generationContext, name, value) additionalDelegates, (name, value) -> generateValueCode(generationContext, name, value))
).generateCode(beanDefinition); .generateCode(beanDefinition);
} }
@Nullable @Nullable
protected CodeBlock generateValueCode(GenerationContext generationContext, protected CodeBlock generateValueCode(GenerationContext generationContext, String name, Object value) {
String name, Object value) {
RegisteredBean innerRegisteredBean = getInnerRegisteredBean(value); RegisteredBean innerRegisteredBean = getInnerRegisteredBean(value);
if (innerRegisteredBean != null) { if (innerRegisteredBean != null) {
BeanDefinitionMethodGenerator methodGenerator = this.beanDefinitionMethodGeneratorFactory BeanDefinitionMethodGenerator methodGenerator = this.beanDefinitionMethodGeneratorFactory
@ -209,9 +202,8 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
@Override @Override
public CodeBlock generateSetBeanInstanceSupplierCode( public CodeBlock generateSetBeanInstanceSupplierCode(
GenerationContext generationContext, GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode,
BeanRegistrationCode beanRegistrationCode, CodeBlock instanceSupplierCode, CodeBlock instanceSupplierCode, List<MethodReference> postProcessors) {
List<MethodReference> postProcessors) {
CodeBlock.Builder code = CodeBlock.builder(); CodeBlock.Builder code = CodeBlock.builder();
if (postProcessors.isEmpty()) { if (postProcessors.isEmpty()) {
@ -231,19 +223,21 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
} }
@Override @Override
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext, public CodeBlock generateInstanceSupplierCode(
BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) { GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode,
boolean allowDirectSupplierShortcut) {
if (hasInstanceSupplier()) { if (hasInstanceSupplier()) {
throw new AotBeanProcessingException(this.registeredBean, "instance supplier is not supported"); throw new AotBeanProcessingException(this.registeredBean, "instance supplier is not supported");
} }
return new InstanceSupplierCodeGenerator(generationContext, beanRegistrationCode.getClassName(), return new InstanceSupplierCodeGenerator(generationContext, beanRegistrationCode.getClassName(),
beanRegistrationCode.getMethods(), allowDirectSupplierShortcut).generateCode( beanRegistrationCode.getMethods(), allowDirectSupplierShortcut)
this.registeredBean, this.instantiationDescriptor.get()); .generateCode(this.registeredBean, this.instantiationDescriptor.get());
} }
@Override @Override
public CodeBlock generateReturnCode(GenerationContext generationContext, public CodeBlock generateReturnCode(
BeanRegistrationCode beanRegistrationCode) { GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
CodeBlock.Builder code = CodeBlock.builder(); CodeBlock.Builder code = CodeBlock.builder();
code.addStatement("return $L", BEAN_DEFINITION_VARIABLE); code.addStatement("return $L", BEAN_DEFINITION_VARIABLE);

View File

@ -787,7 +787,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
@Override @Override
public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext, public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) { BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition,
Predicate<String> attributeFilter) {
CodeBlock.Builder code = CodeBlock.builder(); CodeBlock.Builder code = CodeBlock.builder();
code.add(super.generateSetBeanDefinitionPropertiesCode(generationContext, code.add(super.generateSetBeanDefinitionPropertiesCode(generationContext,
@ -808,7 +809,9 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
.generateCode(this.registeredBean, instantiationDescriptor); .generateCode(this.registeredBean, instantiationDescriptor);
} }
private InstantiationDescriptor proxyInstantiationDescriptor(RuntimeHints runtimeHints, InstantiationDescriptor instantiationDescriptor) { private InstantiationDescriptor proxyInstantiationDescriptor(
RuntimeHints runtimeHints, InstantiationDescriptor instantiationDescriptor) {
Executable userExecutable = instantiationDescriptor.executable(); Executable userExecutable = instantiationDescriptor.executable();
if (userExecutable instanceof Constructor<?> userConstructor) { if (userExecutable instanceof Constructor<?> userConstructor) {
try { try {