Repository: micronaut-projects/micronaut-core
File: http/src/main/java/io/micronaut/http/bind/binders/QueryValueArgumentBinder.java (Java)

Code before:
            Object value;
            // Only maps and POJOs will "bindAll", lists work like normal
            if (Iterable.class.isAssignableFrom(argument.getType())) {
                value = doResolve(context, parameters);
                if (value == null) {
                    value = Collections.emptyList();
                }
            } else {
                value = parameters.asMap();
            }
            return doConvert(value, context);
        }
        return doBind(context, parameters, BindingResult.unsatisfied());
    }

    private BindingResult<T> bindPojo(ArgumentConversionContext<T> context,
                                      HttpRequest<?> source,
                                      AnnotationMetadata annotationMetadata,
                                      ConvertibleMultiValues<String> parameters,
                                      Argument<T> argument) {
        Optional<BeanIntrospection<T>> introspectionOpt = BeanIntrospector.SHARED.findIntrospection(argument.getType());
        if (introspectionOpt.isEmpty()) {
            return BindingResult.unsatisfied();
        }

        BeanIntrospection<T> introspection = introspectionOpt.get();
        T instance;
        try {
            instance = introspection.instantiate();
        } catch (Exception e) {
            return BindingResult.unsatisfied();
        }

        for (BeanProperty<T, Object> property : introspection.getBeanProperties()) {
            String name = property.getName();
            Optional<String> value = parameters.getFirst(name);

            if (value.isEmpty()) {
                value = property.asArgument()
                    .getAnnotationMetadata()
                    .stringValue(Bindable.class, "defaultValue");
            }

            if (value.isPresent()) {
                Optional<Object> converted = conversionService.convert(value.get(), context.with(property.asArgument()));
                if (converted.isPresent()) {
                    try {
                        property.set(instance, converted.get());
                    } catch (Exception e) {
                        return BindingResult.unsatisfied();
                    }

Review comment (suggestion):
this should be altered to use `introspection.builder()` so that records, immutable types, types with custom constructors etc. are supported.

Code after:

        if (bindAll) {
            Object value;
            // Only maps and POJOs will "bindAll", lists work like normal
            if (Iterable.class.isAssignableFrom(argument.getType())) {
                value = doResolve(context, parameters);
                if (value == null) {
                    value = Collections.emptyList();
                }
            } else {
                value = parameters.asMap();
            }
            return doConvert(value, context);
        }
        return doBind(context, parameters, BindingResult.unsatisfied());
    }

    private BindingResult<T> bindPojo(ArgumentConversionContext<T> context,
                                      ConvertibleMultiValues<String> parameters,
                                      Argument<T> argument) {
        Optional<BeanIntrospection<T>> introspectionOpt = BeanIntrospector.SHARED.findIntrospection(argument.getType());
        if (introspectionOpt.isEmpty()) {
            return BindingResult.unsatisfied();
        }

        BeanIntrospection<T> introspection = introspectionOpt.get();
        BeanIntrospection.Builder<T> introspectionBuilder = introspection.builder();
        Argument<?>[] builderArguments = introspectionBuilder.getBuilderArguments();

        for (int index = 0; index < builderArguments.length; index++) {
            Argument<?> builderArg = builderArguments[index];
            String propertyName = builderArg.getName();
            List<String> values = parameters.getAll(propertyName);
            boolean hasNoValue = values.isEmpty();
            @Nullable String defaultValue = hasNoValue ? builderArg
                .getAnnotationMetadata()
                .stringValue(Bindable.class, "defaultValue").orElse(null) : null;

            ArgumentConversionContext<?> conversionContext = context.with(builderArg);
            Optional<?> converted = hasNoValue ? conversionService.convert(defaultValue, conversionContext) : conversionService.convert(values, conversionContext);
            if (converted.isPresent()) {
                try {
                    @SuppressWarnings({"unchecked"})
                    Argument<Object> rawArg = (Argument<Object>) builderArg;
                    introspectionBuilder.with(index, rawArg, converted.get());
                } catch (Exception e) {
                    context.reject(builderArg, e);
                    return BindingResult.unsatisfied();
                }
            } else if (conversionContext.hasErrors()) {
                ConversionError conversionError = conversionContext.getLastError().orElse(null);