How to autowire a collection of a particular size - java

I am trying autowire a list of objects for a particular size but inspite searching through all stackoverflow related questions I am unable to see anything like this discussed before.I am surprised that this would never be needed?
In below example I see that the size of myListeners is always 1. How do i make myListeners list having 10 instances of MyListener
#Component
#Scope("prototype")
public class ListenerThreadPool {
private static final Logger LOGGER = LoggerFactory.getLogger(ObjectPool.class);
private ExecutorService threadPool;
private int poolSize;
#VisibleForTesting
final AtomicBoolean isStarted = new AtomicBoolean(false);
#Autowired
public void setMyObject(List<MyListener> myListeners) {
this.myListeners = myListeners;
}
#Autowired
#VisibleForTesting
List<MyListener> myListeners;
public void setPoolSize(int poolSize) {
this.poolSize=poolSize;
this.threadPool = Executors.newFixedThreadPool(poolSize);
}
public void start() {
if (isStarted.compareAndSet(false, true)) {
for (int i = 0; i < myListeners.size(); ++i) {
LOGGER.info("Starting listeners pool " + myListeners.get(i).toString());
threadPool.submit(myListeners.get(i));
}
} else {
LOGGER.warn("Cannot start listeners pool because it's already started");
}
}
}
I tried something like below but I get error
#Bean
public List listMyListeners(#Value("${workflow.threads:10}") int threads) {
return new ArrayList(10);
}
#Autowired
#Qualifier("listMyListeners")
List myListener;
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: java.util.List com.groupon.mailman.messaging.MyListenerPool.myListener; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.groupon.mailman.messaging.MyListener] found for dependency [collection of com.groupon.mailman.messaging.MyListener]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=listMyListeners)}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.groupon.mailman.messaging.MyListener] found for dependency [collection of com.groupon.mailman.messaging.MyListener]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=listMyListeners)}

If you're using #Autowired for java.util.List it will inject a list of all Java Beans matching the List’s Generic type.
So, obviously, you need more beans of type MyListener in your context(now it's only one).

Related

Spring #Bean Inject Enum

In my Config I define two beans of the same class that differ in the value of an enum:
#Bean
public MyClass myClassNew() {
return new MyClass(Source.NEW);
}
#Bean
public MyClass myClassOld() {
return new MyClass(Source.OLD);
}
The Class looks like this:
#Component
#RequiredArgsConstructor
public class MyClass {
private final Source source; // Source is an enum
}
When I autowire a bean in a test:
public class MyClassTest {
#Autowired
private MyClass myClassNew;
}
Spring gives me the following error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type '...AClass$Source' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
This is totally weird. Why does Spring trying to find a bean of an enum?

Autowire class with arguments in constructor fails

I have the following class :
public class ProducerWrapper<K, V> {
Producer<K, V> producer;
ThreadPoolExecutor threadPoolExecutor;
#Autowired
public ProducerWrapper(Properties p, int poolsize) {
......
log.info("Created kafka producer");
}
....
I try to inject it in a different service :
#Service
public class mainService{
#Qualifier("ProducerX")
#Autowired
private ProducerWrapper<Long,CustomObject1> p1;
#Autowired
#Qualifier("ProducerY")
private ProducerWrapper<Long,CustomObject2> p2;
And I created the following configuration :
#Configuration
#ComponentScan("main_package..")
public class MyConf {
#Bean(name = "ProducerX")
public ProducerWrapper<Long, CustomObject1> createProducerWrapper() throws IOException {
FileInputStream propertiesFile = new FileInputStream("producerx.properties");
properties = new Properties();
properties.load(propertiesFile);
return new ProducerWrapper<>(properties,5);
}
#Bean(name = "ProducerY")
public ProducerWrapper<Long, CustomObject2> createProducerWrapper() throws IOException {
FileInputStream propertiesFile = new FileInputStream("producery.properties");
properties = new Properties();
properties.load(propertiesFile);
return new ProducerWrapper<>(properties,5);
}
}
As you can see I have a different properties file for each producer. The error I'm getting is the following :
Error creating bean with name 'ProducerWrapper' defined in file [..../ProducerWrapper.class]: Unsatisfied dependency expressed through constructor parameter 1;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'int' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Parameter 1 of constructor in com.xx.xx.ProducerWrapper required a bean of type 'int' that could not be found.
If I remove the autowired annotation on top of the constructor, I'm getting a different error that the default constructor can't be found by Spring.
In addition, in the logs I see the following message that indicates that everything in the constructor was run :
2020-06-24 12:14:49.331 INFO 30912 --- [ main] c.a.a.ProducerWrapper : Created kafka producer
What am I doing wrong ?
You have this signature:
#Autowired
public ProducerWrapper(Properties p, int poolsize) {
...
You have not provided the "poolsize" parameter to be autowired. i.e. there's no integer that exists in your config that can be autowired into this variable.
To resolve this: Create a PoolSize class that wraps an int value. Then create a PoolSize object in your config to be autowired.
It says in the error output:
...No qualifying bean of type 'int' available: expected at least 1 bean which qualifies as autowire candidate.
I found the solution in the following stackoverflow post
Bottom line :
The constructor in the ProducerWrapper class shouldn't have any annotation :
//None annotation above constructor
public ProducerWrapper(Properties p, int poolsize) {
......
log.info("Created kafka producer");
}
Create a configuration class for the beans just like I pasted in my main comment.
3.Remove the Service annoatipn from the ProducerWrapper

NoSuchBeanDefinitionException: No qualifying bean of type for inner class

I try to add third-party bean to my application:
#Configuration
#ComponentScan(...)
public class ApplicationConfiguration {
#Bean(name = "mqSocket")
public ZMQ.Socket startServer() {
try (ZMQ.Context ctx = ZMQ.context(1);
ZMQ.Socket publisher = ctx.socket(ZMQ.PUB)) {
publisher.bind("tcp://*:5556");
return publisher;
}
}
}
and I try to autowire this like this:
#RestController
public class MyRestController {
#Autowired
private ZMQ.Socket mqSocket;
but it prints following:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myRestController': Unsatisfied dependency expressed through field 'mqSocket'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.zeromq.ZMQ$Socket' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.zeromq.ZMQ$Socket' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
...
You should add the #Import annotation on your Application like class.
Eg.:
#Import(ApplicationConfiguration.class)
public class Application {
}
Note: cf. #M.Prokhorov comments, the ZMQ.Socket is closed by the try-with-resource statement
You created a bean with name mqSocket so I guess you have to use the #Qualifier annotation; try to change your code in this way:
#RestController
public class MyRestController {
#Autowired
#Qualifier("mqSocket")
private ZMQ.Socket mqSocket;
I hope it's usefull
Angelo

Why is spring not injecting using generic qualifiers?

this is my configuration class
#Configuration
class Factories {
#Bean
Collection<Log> logs() {
List<Log> logs = new ArrayList<>();
for ( int i = 0; i < 10; i++ ) { // gross example code
Log log = new Log();
log.setEntry( RandomStringUtils.randomAlphabetic( 10 ) );
logs.add( log );
}
return logs;
}
}
and here's how I'm trying to autowire it
#Service
#Transactional
public class LogService {
private final LogRepository repository;
private final ObjectFactory<Instant> now;
private final Collection<Log> logs;
#Autowired
LogService( final LogRepository repository, final ObjectFactory<Instant> now, final Collection<Log> logs ) {
this.repository = repository;
this.now = now;
this.logs = logs;
}
but I get this exception
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.xenoterracide.example.log.Log] found for dependency [collection of com.xenoterracide.example.log.Log]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1326)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1024)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:967)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
... 24 more
according to the documentation I think this should work. Here's my full code in case you need it. Am I misunderstanding the documentation?
Given #Autowired a List<Something>, Spring will look for as many Something beans as you have defined in your ApplicationContext and attempt to autowire them all into the target. The documentation states
As a specific consequence of this semantic difference, beans that are
themselves defined as a collection or map type cannot be injected
through #Autowired, because type matching is not properly applicable
to them. Use #Resource for such beans, referring to the specific
collection or map bean by unique name.
In your case, you have
#Autowired
LogService(/* ... */ final Collection<Log> logs ) {
but no Log beans. So it complains
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.xenoterracide.example.log.Log] found for dependency [collection of com.xenoterracide.example.log.Log]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
What you seem to want is a bean of type Collection<Log> injected directly. Spring can do this with the javax.annotation.Resource annotation. Unfortunately, that annotation doesn't work on constructors.
You'll need to either annotate your (changed to non-final) field or add a setter method and annotate that.
#Resource
private Collection<Log> logs;
or
#Resource
public void setLogs(Collection<Log> logs) {
this.logs = logs;
}

Spring can't autowire class

I have the following Spring #Configuration:
#Bean
#Qualifier("mongo")
public MongoFacade mongo(Environment env){
final String host = env.getProperty("database.host");
final MongoClient mongoClient = new MongoClient(host);
return new MongoFacade(mongoClient, "test-db");
}
#Bean
public MessageStore<Event> eventStore(#Qualifier("mongo") MongoFacade mongo, ObjectMapper mapper) {
return new MongoMessageStore<>(mongo, mapper);
}
When I launch my application an exception is thrown and the cause is:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'eventStore' defined in it.m.MyConfiguration: Unsatisfied dependency expressed through constructor argument with index 0 of type [base.backend.persistence.impl.mongo.MongoFacade]: : No qualifying bean of type [base.backend.persistence.impl.mongo.MongoFacade] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Qualifier(value=mongo)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [base.backend.persistence.impl.mongo.MongoFacade] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Qualifier(value=mongo)}
MongoFacade class is a real class, and don't implements interfaces, I've also tried to remove the Environment dependency from the method, but still doesn't work.
Removing the #Qualifier annotation the error is:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [base.backend.persistence.impl.mongo.MongoFacade] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
MongoFacade is a simple facade for MongoClient class that works with my PersistenceId class:
public class MongoFacade {
public static final String DEFAULT_NAME = "default";
private final MongoClient mongo;
private final String defaultName;
public MongoFacade(MongoClient mongoClient) {
this(mongoClient, DEFAULT_NAME);
}
public MongoFacade(MongoClient mongoClient, String defaultName) {
this.mongo = mongoClient;
this.defaultName = defaultName;
}
public Document findOne(PersistenceId id){
final MongoCollection<Document> collection = collection(id);
final String documentId = documentId(id);
return collection.find(eq("_id", documentId)).first();
}
public UpdateResult updateOne(PersistenceId id, Document update) {
final MongoCollection<Document> collection = collection(id);
final String documentId = documentId(id);
return collection.updateOne(eq("_id", documentId), update);
}
The environment can be injected as a field instead of the constructor also I would suggest using method calls instead of auto wiring for the dependencies.
Something like the following
#Autowired
private Environment env;
#Bean
public MongoFacade mongoFacade(){
final String host = env.getProperty("database.host");
final MongoClient mongoClient = new MongoClient(host);
return new MongoFacade(mongoClient, "test-db");
}
#Bean
public MessageStore<Event> eventStore(ObjectMapper mapper) {
return new MongoMessageStore<>(mongoFacade(), mapper);
}
Edit: Added Spring Boot MongoDB config
From the comments it is clear you are using Spring Boot, this already auto configures a MongoClient for you. Instead of doing it yourself you can reduce your configuration a little further. (Se also MongoAutoConfiguration).
In your application.properties add
spring.data.mongodb.host=<value of current database.host>
or if it is a full URI
spring.data.mongodb.uri=<value of current database.host>
Then change your configuration class.
#Bean
public MongoFacade mongoFacade(MongoClient mongoClient){
return new MongoFacade(mongoClient, "test-db");
}
#Bean
public MessageStore<Event> eventStore(ObjectMapper mapper) {
return new MongoMessageStore<>(mongoFacade(null), mapper);
}
Spring Boot will now construct a MongoClient.
can you please use #Service instead of #qualifier while declaring bean definition
#Service("mongo")
public MongoFacade mongoFacade()
let me know if it worked for you or not .

Categories