Describe the bug
I have a producer field like this:
@Produces
@Named("scoping")
@RequestScoped
Map<String, String> cache = new ConcurrentHashMap<>();
I inject the Requestscoped cache into my rest resource. However, it is always the same instance, and still contains values from the previous invocation.
The Scope is most likely not respected.
Expected behavior
The bean produced by a request scoped field producer should actually be request scoped. i.e. be a new instance on every rest request.
Actual behavior
The cache bean is already filled on the second invocation - indicating that the bean is not actually RequestScoped is not working.
Using a Scope on a Producer Method works without problems
To Reproduce
Link to a small reproducer (preferably a Maven project if the issue is not Gradle-specific).
Or attach an archive containing the reproducer to the issue.
Steps to reproduce the behavior:
Environment (please complete the following information):
uname -a or ver:mvnw --version or gradlew --version):/cc @mkouba @manovotn
I am not in front of my PC now, but for the bean to outlive a request, it would need to somehow gain a "wider" scope, something like singleton or app scoped.
You could try to do Arc.container().instance(Map<String, String>).getBean().getScope() to see what the actual scope is.
See this and this.
I know we do some automagic for scopes in certain cases, but this doesn't look like one of them. I can take a closer look tomorrow.
@Postremus The scope is not ignored at all. However, the producer field is non-static and so the container obtains an instance of the declaring bean and reads the field value whenever a new bean for Map<String, String> is created (see also Access to producer field values). In this case, the declaring bean is @ApplicationScoped and so if the field reference does not change, you'll get the same instance for each request.
If you need to create a new map for each request you'll have to (A) change the scope of the declaring bean to @RequestScoped (@Dependent would work as well) or (B) use a producer method that returns a new map.
@mkouba I tested this out in weld-se, since I believe that quarkus behaves differently than wildfly 10: https://github.com/Postremus/weldfieldproducer/blob/master/src/main/java/org/example/weldfieldproducer/Main.java
The SomeInterface bean is RequestScoped.
Also, https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#declaring_producer_field explicitly states, that a scope can be defined for a producer field.
You can declare scope on a producer field. But the spec part Martin pointed out is crucial.
If the declaring beans outlives the scope of the producer, it will just keep that value in between invocations. That's what the spec says. The reason for that is that just field declaration doesn't define how to repeatedly create new value (whereas producer method does just that), all you can do is access the field and return it's value as a bean.
Thank you!
I understant it now.
Most helpful comment
@Postremus The scope is not ignored at all. However, the producer field is non-static and so the container obtains an instance of the declaring bean and reads the field value whenever a new bean for
Map<String, String>is created (see also Access to producer field values). In this case, the declaring bean is@ApplicationScopedand so if the field reference does not change, you'll get the same instance for each request.If you need to create a new map for each request you'll have to (A) change the scope of the declaring bean to
@RequestScoped(@Dependentwould work as well) or (B) use a producer method that returns a new map.