spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

When trying to inject collection with generic type, some beans are not injected

Open beancracker opened this issue 2 years ago • 4 comments

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.

beancracker avatar Dec 05 '21 19:12 beancracker

What happens if you used @Autowired instead of @Resource?

sbrannen avatar Dec 06 '21 10:12 sbrannen

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;
	
}

beancracker avatar Dec 06 '21 18:12 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;
}

beancracker avatar Dec 06 '21 20:12 beancracker

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<>();
}

jeffreycrump01880 avatar Jul 14 '22 14:07 jeffreycrump01880

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.

snicoll avatar Oct 02 '23 08:10 snicoll

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.

spring-projects-issues avatar Oct 09 '23 08:10 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.

spring-projects-issues avatar Oct 16 '23 08:10 spring-projects-issues