spring-framework
spring-framework copied to clipboard
When trying to inject collection with generic type, some beans are not injected
Spring Boot version: 2.5.5 Spring core version: 5.3.10
When trying to inject collection with generic type, some beans are not injected.
- All beans are proxied by interface
- Bean not injected into the collection is also autowired as another field
public class TestObject { }
public interface TestBoc<T> { }
public class TestObjectA extends TestObject { }
public class TestObjectC extends TestObject { }
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Component("testObjectABoc")
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
@Transactional(value = "transactionManager", propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 300)
public class TestObjectABocImp implements TestBoc<TestObjectA> { }
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Component("testObjectCBoc")
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
@Transactional(value = "transactionManager", propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 300)
public class TestObjectCBocImp implements TestBoc<TestObjectC> { }
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
@Component
public class TestMntBocImp implements TestMntBoc {
@Resource
TestBoc<TestObjectA> testObjectABoc;
@Resource
List<TestBoc<? extends TestObject>> testObjectBocs;
}
import javax.annotation.Resource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class Demo2ApplicationTests {
@Resource
TestMntBocImp testMntBocImp;
@Test
void contextLoads() {
Assertions.assertEquals(2, testMntBocImp.testObjectBocs.size());
}
}
Result:
org.opentest4j.AssertionFailedError: expected: <2> but was: <1>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:150)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:145)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:510)
at com.example.demo.Demo2ApplicationTests.contextLoads(Demo2ApplicationTests.java:19)
TestMntBocImp.testObjectBocs
contain proxied bean of TestObjectCBocImp
only.
If remove testObjectABoc
field from TestMntBocImp
, then testMntBocImp.testObjectBocs.size()
will return 2.
What happens if you used @Autowired
instead of @Resource
?
After changing the @Resource
to @Autowired
for testObjectABoc field (or change to @Autowired
for both testObjectABoc and testObjectBocs fields), the test case passed with TestMntBocImp.testObjectBocs.size() equals to 2.
However, @Resource
and @Autowired
are used interchangeably throughout the whole project, with @Resource
more widely used.
What it difference between @Resource
and @Autowired
in this scenario?
import java.util.List;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TestMntBocImp implements TestMntBoc {
@Autowired
TestBoc<TestObjectA> testObjectABoc;
@Resource
List<TestBoc<? extends TestObject>> testObjectBocs;
}
I add a new interface TestObjectABoc to TestObjectABocImp, then change the data type of field testObjectABoc under TestMntBocImp class to TestObjectABoc, and test case fail again with org.opentest4j.AssertionFailedError: expected: <2> but was: <1>
, even when @Autowired
is used.
@Component("testObjectABoc")
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
@Transactional(value = "transactionManager", propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 300)
public class TestObjectABocImp implements TestBoc<TestObjectA>, TestObjectABoc { }
@Component
public class TestMntBocImp implements TestMntBoc {
@Autowired
TestObjectABoc testObjectABoc;
@Autowired
List<TestBoc<? extends TestObject>> testObjectBocs;
}
I'm seeing this too with Spring 5.3.20 and Boot 2.7.1. When I add an @Bean
to return an empty list, it's working:
Field descriptionProviders in com.se.gwa.rest.intercept.PathInterceptor required a bean of type 'java.util.Collection' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'java.util.Collection' in your configuration.
@Bean
public Collection<MappingDescriptionProvider> descriptionProviders() {
return new ArrayList<>();
}
However, @Resource and @Autowired are used interchangeably throughout the whole project
They should not. @Resource
has a by-name semantic while @Autowired
has a by-type semantic.
@beancracker can you please simplify the example to the minimum set of beans and share it as a small project we can run ourselves? You can do that by attaching a zip to this issue or pushing the code to a GitHub repository.
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.