Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provider<...> declaration for @Value method argument fails with TypeMismatchException [SPR-12297] #16903

Closed
spring-projects-issues opened this issue Oct 5, 2014 · 7 comments
Assignees
Labels
in: core status: backported type: bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Oct 5, 2014

Sanjay Acharya opened SPR-12297 and commented

Would be very useful if Spring allowed for the injection of javax.inject.Provider along with @Value annotation for properties that could change at Runtime.

public class Foo {
  private final Provider<Integer> changeableIntegerProperty;
 
  public Foo(Provider<Integer> changeableIntegerProperty) {
    this.changeableIntegerProperty = changeableIntegerProperty;
 }

 public void someOperation() {
     changeableIntegerProperty.get(); // Gets current value of the property
 }
}

@Configuration
public class FooConfig {
  @Bean
   public Foo foo(@Value("${fooProp}") Provider<Integer> fooProp) {
        return new Foo(fooProp);
   }
}

If the same code shown above were attempted now, a TypeMismatchException gets thrown in TypeConverterSupport due to the nesting level of the parameter not set to 1.

My request for improvement is to allow the injection of java.inject.Provider along with @Value.
Thanks.


Affects: 3.2.11, 4.0.7, 4.1.1

Issue Links:

  • #14133 MapToMapConverter cannot distinguish MultiValueMap in target
  • #10747 Provide support for javax.inject.Provider<> style injection for @Value and @Autowired

Backported to: 4.0.8, 3.2.12

1 votes, 3 watchers

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 5, 2014

Juergen Hoeller commented

As of #10747, this should work already, so I'll consider this as a bug. I suppose it just fails when type conversion is necessary... Anyway, if it's easy enough to fix, this is a candidate for a backport to 4.0.8 and 3.2.12 as well.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 5, 2014

Sanjay Acharya commented

Thanks Juergen. This appears broken in 4.1.1 as well. My dirty workaround was to trick the TypeConverter with a custom one with the nesting level:

public class CustomTypeConverter implements TypeConverter {
  private SimpleTypeConverter simpleTypeConverter;

  public CustomTypeConverter() {
    simpleTypeConverter = new SimpleTypeConverter();
  }

  public <T> T convertIfNecessary(Object newValue, Class<T> requiredType,
    MethodParameter methodParam) throws IllegalArgumentException {
    Type type = methodParam.getGenericParameterType();

    MethodParameter parameterTarget = null;

    if (type instanceof ParameterizedType) {
      ParameterizedType paramType = ParameterizedType.class.cast(type);
      Type rawType = paramType.getRawType();

      if (rawType.equals(Provider.class) && methodParam.hasParameterAnnotation(Value.class)) { // Do this only if @Value and @Provider exist on the MethodParam
        parameterTarget = new MethodParameter(methodParam); // Don't fiddle with original, send a new MethodParameter downstream
        parameterTarget.decreaseNestingLevel();
      }
    }
    return simpleTypeConverter.convertIfNecessary(newValue, requiredType, parameterTarget); 
  }

Setting the above as the TypeConverter default seems to get the code to work. I don't think this is the cleanest way to handle the same.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 5, 2014

Sanjay Acharya commented

I am surprised if the String version even worked, my simple test using 4.1.1.RELEASE, seems to fail with String as the Provider type:

public class SpringTest {
  @Test
  public void test() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(SimpleConfig.class);
    context.refresh();
    
    context.getBean(Foo.class).print();   
    
    context.close();
  }
  
  public static class SimpleConfig {
    @Bean(name = "props")
    public PropertyPlaceholderConfigurer propertyPlaceHolderConfigurer() {
      PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
      Resource location = new ClassPathResource("appProperties.properties");
      configurer.setLocation(location);
      return configurer;
    }

    @Bean
    public Foo foo(@Value("${foo}") Provider<String> propValue,
      @Value("${bar}") Provider<String> booleanVal) {
      return new Foo(propValue, booleanVal);
    }
  }
  
  public static class Foo {
    private final Provider<String> propVal;
    private final Provider<String> booleanVal;
    
    public Foo(Provider<String> propValue, Provider<String> booleanVal) {
      this.propVal = propValue;
      this.booleanVal = booleanVal;
    }
    
    public void print() {
      System.out.println(propVal.get() + "," + booleanVal.get());
    }    
  }
}

Where app.properties is simply:

foo=5
bar=false

If you set the TypeConverter to the one I mentioned via the below, the test passes:

 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getBeanFactory().setTypeConverter(new CustomTypeConverter());

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 6, 2014

Juergen Hoeller commented

This seems to be a 4.x regression introduced through #14133, only failing for Provider-declared @Value method arguments but not for fields. And as it always is, we only had tests for such scenarios with fields...

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 6, 2014

Juergen Hoeller commented

Fixing that nestingLevel assertion makes it work, so it is indeed an accidental side effect from #14133. Fixed for 4.1.2; to be backported to 4.0.8.

For the time being, it'll work fine for fields, just not for method arguments...

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 6, 2014

Sanjay Acharya commented

Great. Thanks much for the rapid turn around. We will look toward the new release.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 6, 2014

Juergen Hoeller commented

Actually, this fails in 3.2.11 too, just with a fix necessary in a different place. Backported to 3.2.12 as well now.

Juergen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core status: backported type: bug
Projects
None yet
Development

No branches or pull requests

2 participants