Search in sources :

Example 6 with TypeConverter

use of cn.taketoday.beans.TypeConverter in project today-framework by TAKETODAY.

the class ConstructorResolver method resolvePreparedArguments.

/**
 * Resolve the prepared arguments stored in the given bean definition.
 */
private Object[] resolvePreparedArguments(String beanName, BeanDefinition merged, Executable executable, Object[] argsToResolve, BeanWrapper bw) {
    TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
    TypeConverter converter = customConverter != null ? customConverter : bw;
    BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(beanFactory, beanName, merged, converter);
    Class<?>[] paramTypes = executable.getParameterTypes();
    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);
        } else if (argValue instanceof BeanMetadataElement) {
            argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
        } else if (argValue instanceof String) {
            argValue = beanFactory.evaluateBeanDefinitionString((String) argValue, merged);
        }
        Class<?> paramType = paramTypes[argIndex];
        try {
            resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
        } catch (TypeMismatchException ex) {
            throw new UnsatisfiedDependencyException(merged.getResourceDescription(), beanName, new InjectionPoint(methodParam), "Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) + "] to required type [" + paramType.getName() + "]: " + ex.getMessage());
        }
    }
    return resolvedArgs;
}
Also used : InjectionPoint(cn.taketoday.beans.factory.InjectionPoint) TypeMismatchException(cn.taketoday.beans.TypeMismatchException) InjectionPoint(cn.taketoday.beans.factory.InjectionPoint) TypeConverter(cn.taketoday.beans.TypeConverter) BeanMetadataElement(cn.taketoday.beans.BeanMetadataElement) UnsatisfiedDependencyException(cn.taketoday.beans.factory.UnsatisfiedDependencyException) MethodParameter(cn.taketoday.core.MethodParameter)

Example 7 with TypeConverter

use of cn.taketoday.beans.TypeConverter in project today-framework by TAKETODAY.

the class ConstructorResolver method resolveConstructorArguments.

/**
 * Resolve the constructor arguments for this bean into the resolvedValues object.
 * This may involve looking up other beans.
 * <p>This method is also used for handling invocations of static factory methods.
 */
private int resolveConstructorArguments(String beanName, BeanDefinition merged, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
    TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
    TypeConverter converter = customConverter != null ? customConverter : bw;
    var valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, merged, converter);
    int minNrOfArgs = cargs.getArgumentCount();
    for (Map.Entry<Integer, ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
        int index = entry.getKey();
        if (index < 0) {
            throw new BeanCreationException(merged.getResourceDescription(), beanName, "Invalid constructor argument index: " + index);
        }
        if (index + 1 > minNrOfArgs) {
            minNrOfArgs = index + 1;
        }
        ValueHolder valueHolder = entry.getValue();
        if (valueHolder.isConverted()) {
            resolvedValues.addIndexedArgumentValue(index, valueHolder);
        } else {
            Object resolvedValue = valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
            ValueHolder resolvedValueHolder = new ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
            resolvedValueHolder.setSource(valueHolder);
            resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
        }
    }
    for (ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
        if (valueHolder.isConverted()) {
            resolvedValues.addGenericArgumentValue(valueHolder);
        } else {
            Object resolvedValue = valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
            ValueHolder resolvedValueHolder = new ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
            resolvedValueHolder.setSource(valueHolder);
            resolvedValues.addGenericArgumentValue(resolvedValueHolder);
        }
    }
    return minNrOfArgs;
}
Also used : TypeConverter(cn.taketoday.beans.TypeConverter) BeanCreationException(cn.taketoday.beans.factory.BeanCreationException) ValueHolder(cn.taketoday.beans.factory.config.ConstructorArgumentValues.ValueHolder) Map(java.util.Map) InjectionPoint(cn.taketoday.beans.factory.InjectionPoint)

Example 8 with TypeConverter

use of cn.taketoday.beans.TypeConverter in project today-framework by TAKETODAY.

the class ConstructorResolver method createArgumentArray.

/**
 * Create an array of arguments to invoke a constructor or factory method,
 * given the resolved constructor argument values.
 */
private ArgumentsHolder createArgumentArray(String beanName, RootBeanDefinition merged, @Nullable ConstructorArgumentValues resolvedValues, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable, BeanWrapper wrapper, boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
    TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
    TypeConverter converter = customConverter != null ? customConverter : wrapper;
    ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
    HashSet<ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
    LinkedHashSet<String> autowiredBeanNames = new LinkedHashSet<>(4);
    for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
        Class<?> paramType = paramTypes[paramIndex];
        String paramName = paramNames != null ? paramNames[paramIndex] : "";
        // Try to find matching constructor argument value, either indexed or generic.
        ValueHolder valueHolder = null;
        if (resolvedValues != null) {
            valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
            // it could match after type conversion (for example, String -> int).
            if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) {
                valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
            }
        }
        if (valueHolder != null) {
            // We found a potential match - let's give it a try.
            // Do not consider the same value definition multiple times!
            usedValueHolders.add(valueHolder);
            Object originalValue = valueHolder.getValue();
            Object convertedValue;
            if (valueHolder.isConverted()) {
                convertedValue = valueHolder.getConvertedValue();
                args.preparedArguments[paramIndex] = convertedValue;
            } else {
                MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
                try {
                    convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam);
                } catch (TypeMismatchException ex) {
                    throw new UnsatisfiedDependencyException(merged.getResourceDescription(), beanName, new InjectionPoint(methodParam), "Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(valueHolder.getValue()) + "] to required type [" + paramType.getName() + "]: " + ex.getMessage());
                }
                Object sourceHolder = valueHolder.getSource();
                if (sourceHolder instanceof ValueHolder) {
                    Object sourceValue = ((ValueHolder) sourceHolder).getValue();
                    args.resolveNecessary = true;
                    args.preparedArguments[paramIndex] = sourceValue;
                }
            }
            args.arguments[paramIndex] = convertedValue;
            args.rawArguments[paramIndex] = originalValue;
        } else {
            MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
            // have to fail creating an argument array for the given constructor.
            if (!autowiring) {
                throw new UnsatisfiedDependencyException(merged.getResourceDescription(), beanName, new InjectionPoint(methodParam), "Ambiguous argument values for parameter of type [" + paramType.getName() + "] - did you specify the correct bean references as arguments?");
            }
            try {
                Object autowiredArgument = resolveAutowiredArgument(methodParam, beanName, autowiredBeanNames, converter, fallback);
                args.resolveNecessary = true;
                args.arguments[paramIndex] = autowiredArgument;
                args.rawArguments[paramIndex] = autowiredArgument;
                args.preparedArguments[paramIndex] = autowiredArgumentMarker;
            } catch (BeansException ex) {
                throw new UnsatisfiedDependencyException(merged.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
            }
        }
    }
    for (String autowiredBeanName : autowiredBeanNames) {
        beanFactory.registerDependentBean(autowiredBeanName, beanName);
        if (log.isDebugEnabled()) {
            log.debug("Autowiring by type from bean name '{}' via {} to bean named '{}'", beanName, (executable instanceof Constructor ? "constructor" : "factory method"), autowiredBeanName);
        }
    }
    return args;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) InjectionPoint(cn.taketoday.beans.factory.InjectionPoint) Constructor(java.lang.reflect.Constructor) TypeMismatchException(cn.taketoday.beans.TypeMismatchException) ValueHolder(cn.taketoday.beans.factory.config.ConstructorArgumentValues.ValueHolder) InjectionPoint(cn.taketoday.beans.factory.InjectionPoint) TypeConverter(cn.taketoday.beans.TypeConverter) UnsatisfiedDependencyException(cn.taketoday.beans.factory.UnsatisfiedDependencyException) MethodParameter(cn.taketoday.core.MethodParameter) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) BeansException(cn.taketoday.beans.BeansException)

Example 9 with TypeConverter

use of cn.taketoday.beans.TypeConverter in project today-framework by TAKETODAY.

the class ArgumentConvertingMethodInvoker method registerCustomEditor.

/**
 * Register the given custom property editor for all properties of the given type.
 * <p>Typically used in conjunction with the default
 * {@link cn.taketoday.beans.SimpleTypeConverter}; will work with any
 * TypeConverter that implements the PropertyEditorRegistry interface as well.
 *
 * @param requiredType type of the property
 * @param propertyEditor editor to register
 * @see #setTypeConverter
 * @see cn.taketoday.beans.PropertyEditorRegistry#registerCustomEditor
 */
public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
    TypeConverter converter = getTypeConverter();
    if (!(converter instanceof PropertyEditorRegistry)) {
        throw new IllegalStateException("TypeConverter does not implement PropertyEditorRegistry interface: " + converter);
    }
    ((PropertyEditorRegistry) converter).registerCustomEditor(requiredType, propertyEditor);
}
Also used : SimpleTypeConverter(cn.taketoday.beans.SimpleTypeConverter) TypeConverter(cn.taketoday.beans.TypeConverter) PropertyEditorRegistry(cn.taketoday.beans.PropertyEditorRegistry)

Example 10 with TypeConverter

use of cn.taketoday.beans.TypeConverter in project today-framework by TAKETODAY.

the class ArgumentConvertingMethodInvoker method doFindMatchingMethod.

/**
 * Actually find a method with matching parameter type, i.e. where each
 * argument value is assignable to the corresponding parameter type.
 *
 * @param arguments the argument values to match against method parameters
 * @return a matching method, or {@code null} if none
 */
@Nullable
protected Method doFindMatchingMethod(Object[] arguments) {
    TypeConverter converter = getTypeConverter();
    if (converter != null) {
        String targetMethod = getTargetMethod();
        Method matchingMethod = null;
        int argCount = arguments.length;
        Class<?> targetClass = getTargetClass();
        Assert.state(targetClass != null, "No target class set");
        Method[] candidates = ReflectionUtils.getAllDeclaredMethods(targetClass);
        int minTypeDiffWeight = Integer.MAX_VALUE;
        Object[] argumentsToUse = null;
        for (Method candidate : candidates) {
            if (candidate.getName().equals(targetMethod)) {
                // Check if the inspected method has the correct number of parameters.
                int parameterCount = candidate.getParameterCount();
                if (parameterCount == argCount) {
                    Class<?>[] paramTypes = candidate.getParameterTypes();
                    Object[] convertedArguments = new Object[argCount];
                    boolean match = true;
                    for (int j = 0; j < argCount && match; j++) {
                        // Verify that the supplied argument is assignable to the method parameter.
                        try {
                            convertedArguments[j] = converter.convertIfNecessary(arguments[j], paramTypes[j]);
                        } catch (TypeMismatchException ex) {
                            // Ignore -> simply doesn't match.
                            match = false;
                        }
                    }
                    if (match) {
                        int typeDiffWeight = getTypeDifferenceWeight(paramTypes, convertedArguments);
                        if (typeDiffWeight < minTypeDiffWeight) {
                            minTypeDiffWeight = typeDiffWeight;
                            matchingMethod = candidate;
                            argumentsToUse = convertedArguments;
                        }
                    }
                }
            }
        }
        if (matchingMethod != null) {
            setArguments(argumentsToUse);
            return matchingMethod;
        }
    }
    return null;
}
Also used : SimpleTypeConverter(cn.taketoday.beans.SimpleTypeConverter) TypeConverter(cn.taketoday.beans.TypeConverter) TypeMismatchException(cn.taketoday.beans.TypeMismatchException) Method(java.lang.reflect.Method) Nullable(cn.taketoday.lang.Nullable)

Aggregations

TypeConverter (cn.taketoday.beans.TypeConverter)18 InjectionPoint (cn.taketoday.beans.factory.InjectionPoint)8 TypeMismatchException (cn.taketoday.beans.TypeMismatchException)7 ValueHolder (cn.taketoday.beans.factory.config.ConstructorArgumentValues.ValueHolder)6 UnsatisfiedDependencyException (cn.taketoday.beans.factory.UnsatisfiedDependencyException)5 MethodParameter (cn.taketoday.core.MethodParameter)5 Map (java.util.Map)5 SimpleTypeConverter (cn.taketoday.beans.SimpleTypeConverter)4 BeansException (cn.taketoday.beans.BeansException)3 BeanCreationException (cn.taketoday.beans.factory.BeanCreationException)3 Constructor (java.lang.reflect.Constructor)3 HashSet (java.util.HashSet)3 LinkedHashSet (java.util.LinkedHashSet)3 BeanMetadataElement (cn.taketoday.beans.BeanMetadataElement)2 PropertyEditorRegistry (cn.taketoday.beans.PropertyEditorRegistry)2 ResolvableType (cn.taketoday.core.ResolvableType)2 Nullable (cn.taketoday.lang.Nullable)2 Method (java.lang.reflect.Method)2