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.
Comment From: sbrannen
What happens if you used @Autowired
instead of @Resource
?
Comment From: beancracker
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;
}
Comment From: beancracker
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;
}
Comment From: jeffreycrump01880
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<>();
}
Comment From: snicoll
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.
Comment From: spring-projects-issues
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.
Comment From: spring-projects-issues
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.