All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.springframework.boot.context.properties.bind.DefaultBindConstructorProvider Maven / Gradle / Ivy
/*
* Copyright 2012-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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.context.properties.bind;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.stream.Stream;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.KotlinDetector;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Default {@link BindConstructorProvider} implementation.
*
* @author Madhura Bhave
* @author Phillip Webb
*/
class DefaultBindConstructorProvider implements BindConstructorProvider {
@Override
public Constructor getBindConstructor(Bindable bindable, boolean isNestedConstructorBinding) {
Constructors constructors = Constructors.getConstructors(bindable.getType().resolve(),
isNestedConstructorBinding);
if (constructors.getBind() != null && constructors.isDeducedBindConstructor()
&& !constructors.isImmutableType()) {
if (bindable.getValue() != null && bindable.getValue().get() != null) {
return null;
}
}
return constructors.getBind();
}
@Override
public Constructor getBindConstructor(Class type, boolean isNestedConstructorBinding) {
Constructors constructors = Constructors.getConstructors(type, isNestedConstructorBinding);
return constructors.getBind();
}
/**
* Data holder for autowired and bind constructors.
*/
static final class Constructors {
private static final Constructors NONE = new Constructors(false, null, false, false);
private final boolean hasAutowired;
private final Constructor bind;
private final boolean deducedBindConstructor;
private final boolean immutableType;
private Constructors(boolean hasAutowired, Constructor bind, boolean deducedBindConstructor,
boolean immutableType) {
this.hasAutowired = hasAutowired;
this.bind = bind;
this.deducedBindConstructor = deducedBindConstructor;
this.immutableType = immutableType;
}
boolean hasAutowired() {
return this.hasAutowired;
}
Constructor getBind() {
return this.bind;
}
boolean isDeducedBindConstructor() {
return this.deducedBindConstructor;
}
boolean isImmutableType() {
return this.immutableType;
}
static Constructors getConstructors(Class type, boolean isNestedConstructorBinding) {
if (type == null) {
return NONE;
}
boolean hasAutowiredConstructor = isAutowiredPresent(type);
Constructor[] candidates = getCandidateConstructors(type);
MergedAnnotations[] candidateAnnotations = getAnnotations(candidates);
boolean deducedBindConstructor = false;
boolean immutableType = type.isRecord();
Constructor bind = getConstructorBindingAnnotated(type, candidates, candidateAnnotations);
if (bind == null && !hasAutowiredConstructor) {
bind = deduceBindConstructor(type, candidates);
deducedBindConstructor = bind != null;
}
if (bind == null && !hasAutowiredConstructor && isKotlinType(type)) {
bind = deduceKotlinBindConstructor(type);
deducedBindConstructor = bind != null;
}
if (bind != null || isNestedConstructorBinding) {
Assert.state(!hasAutowiredConstructor,
() -> type.getName() + " declares @ConstructorBinding and @Autowired constructor");
}
return new Constructors(hasAutowiredConstructor, bind, deducedBindConstructor, immutableType);
}
private static boolean isAutowiredPresent(Class type) {
if (Stream.of(type.getDeclaredConstructors())
.map(MergedAnnotations::from)
.anyMatch((annotations) -> annotations.isPresent(Autowired.class))) {
return true;
}
Class userClass = ClassUtils.getUserClass(type);
return (userClass != type) ? isAutowiredPresent(userClass) : false;
}
private static Constructor[] getCandidateConstructors(Class type) {
if (isInnerClass(type)) {
return new Constructor[0];
}
return Arrays.stream(type.getDeclaredConstructors())
.filter((constructor) -> isNonSynthetic(constructor, type))
.toArray(Constructor[]::new);
}
private static boolean isInnerClass(Class type) {
try {
return type.getDeclaredField("this$0").isSynthetic();
}
catch (NoSuchFieldException ex) {
return false;
}
}
private static boolean isNonSynthetic(Constructor constructor, Class type) {
return !constructor.isSynthetic();
}
private static MergedAnnotations[] getAnnotations(Constructor[] candidates) {
MergedAnnotations[] candidateAnnotations = new MergedAnnotations[candidates.length];
for (int i = 0; i < candidates.length; i++) {
candidateAnnotations[i] = MergedAnnotations.from(candidates[i], SearchStrategy.SUPERCLASS);
}
return candidateAnnotations;
}
private static Constructor getConstructorBindingAnnotated(Class type, Constructor[] candidates,
MergedAnnotations[] mergedAnnotations) {
Constructor result = null;
for (int i = 0; i < candidates.length; i++) {
if (mergedAnnotations[i].isPresent(ConstructorBinding.class)) {
Assert.state(candidates[i].getParameterCount() > 0,
() -> type.getName() + " declares @ConstructorBinding on a no-args constructor");
Assert.state(result == null,
() -> type.getName() + " has more than one @ConstructorBinding constructor");
result = candidates[i];
}
}
return result;
}
private static Constructor deduceBindConstructor(Class type, Constructor[] candidates) {
if (candidates.length == 1 && candidates[0].getParameterCount() > 0) {
if (type.isMemberClass() && Modifier.isPrivate(candidates[0].getModifiers())) {
return null;
}
return candidates[0];
}
Constructor result = null;
for (Constructor candidate : candidates) {
if (!Modifier.isPrivate(candidate.getModifiers())) {
if (result != null) {
return null;
}
result = candidate;
}
}
return (result != null && result.getParameterCount() > 0) ? result : null;
}
private static boolean isKotlinType(Class type) {
return KotlinDetector.isKotlinPresent() && KotlinDetector.isKotlinType(type);
}
private static Constructor deduceKotlinBindConstructor(Class type) {
Constructor primaryConstructor = BeanUtils.findPrimaryConstructor(type);
if (primaryConstructor != null && primaryConstructor.getParameterCount() > 0) {
return primaryConstructor;
}
return null;
}
}
}