Spring can't autowire class - java

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 .

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

Do not register the component if there are no dependent beans using spring-boot-starter

I create spring-boot-starter, which registers some bean:
#Configuration
#EnableConfigurationProperties(ClusterJProperties.class)
#ConditionalOnProperty(prefix = "clusterj", name = {"connectString", "dataBaseName"})
public class ClusterJAutoConfiguration {
private final ClusterJProperties clusterJConfigProperties;
#Autowired
public ClusterJAutoConfiguration(ClusterJProperties clusterJConfigProperties) {
this.clusterJConfigProperties = clusterJConfigProperties;
}
#Bean
#ConditionalOnMissingBean
public SessionFactory sessionFactory() {
Properties clusterJProperties = new Properties();
clusterJProperties.setProperty("com.mysql.clusterj.connectstring", clusterJConfigProperties.getConnectString());
clusterJProperties.setProperty("com.mysql.clusterj.database", clusterJConfigProperties.getDataBaseName());
clusterJProperties.putAll(clusterJConfigProperties.getProperties());
return ClusterJHelper.getSessionFactory(clusterJProperties);
}
}
I want using this starter in another module. For this, I created a component, and inject this bean there:
#Component
public class GetEntityHandler implements VoidTreeNodeHandler {
private final SessionFactory sessionFactory;
#Autowired
public GetEntityHandler(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
#Override
public void handle(TreeContext context, Map<String, Object> parameters) {
//do smth
}
}
But, when, properties clusterj.connectString and clusterj.connectString not present in application.yml, bean SessionFactory not create. It is ok, but GetEntityHandler want to create, and fall with error:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'getEntityHandler' defined in file [GetEntityHandler.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mysql.clusterj.SessionFactory' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
How to make my GetEnityNandler component not be created if the SessionFactory bean is not present in context?

Spring Hadoop config - No qualifying bean of type org.apache.hadoop.conf.Configuration

I am trying to configure beans for Hadoop/Hive environment. According to documentation I need Apache Hadoop Configuration class, which should be autowired. See: http://docs.spring.io/spring-hadoop/docs/2.4.0.RELEASE/reference/html/springandhadoop-store.html (section 6.2.2 Configuring the dataset support)
Yet, when I try to run my app, I get: NoSuchBeanDefinitionException: No qualifying bean of type [org.apache.hadoop.conf.Configuration] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.
My class is very simple:
#SpringBootApplication
public class HiveTestApp implements CommandLineRunner {
private
#Autowired
org.apache.hadoop.conf.Configuration hadoopConfiguration;
...
I am using Cloudera cluster, here are dependencies:
dependencies {
compile(
'org.springframework.boot:spring-boot-starter-web',
'org.springframework.data:spring-data-hadoop-hive:2.4.0.RELEASE-cdh5',
'org.apache.hive:hive-jdbc:1.1.0-cdh5.4.3',
)
Now, I might be wrong, but I can remember in the past I used autowired config, and it worked fine. Has anything changed in the latest version? Am I missing something?
OK here's the solution.
#Configuration
public class ApplicationConfiguration {
#Value("${com.domain.app.hadoop.fs-uri}")
private URI hdfsUri;
#Value("${com.domain.app.hadoop.user}")
private String user;
#Value("${com.domain.app.hadoop.hive.jdbc-uri}")
private String hiveUri;
#Autowired
private org.apache.hadoop.conf.Configuration hadoopConfiguration;
#Bean
public org.apache.hadoop.conf.Configuration hadoopConfiguration() {
return new org.apache.hadoop.conf.Configuration();
}
#Bean
public HdfsResourceLoader hdfsResourceLoader() {
return new HdfsResourceLoader(hadoopConfiguration, hdfsUri, user);
}
#Bean
public HiveTemplate hiveTemplate() {
return new HiveTemplate(() -> {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new HiveDriver(), hiveUri);
return new HiveClient(dataSource);
});
}
}
Configuration file below.
com.domain.app.hadoop:
fs-uri: "hdfs://hadoop-cluster/"
user: "hdfs-user"
hive.jdbc-uri: "jdbc:hive2://hadoop-cluster:10000/hive-db"
I've made Hadoop configuration object a bean, because I need to inject it in one of the classes. If you don't need a bean, you can just create new instance by yourself.

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

Categories