Autowire class with arguments in constructor fails - java

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

Related

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: use properties read from a file

i'm new to the spring framework and i'm having some problems trying to read and use properties from a file.
To summarize, what i want to do is to define a class which stores all the properties read, a second class that uses those properties to do something and a third class that uses the results.
The class that stores the properties is:
#Configuration
public class PropertyClass {
#Value("${propertyName")
private Integer propertyName;
#Bean(name = "propertyName")
public Integer getPropertyName() {
return propertyName;
}
}
The class that reads and uses those properties:
#Component
public class PropertyReader {
private Integer myProperty;
#Autowire
#Qualifier("propertyName")
public void setMyProperty(
Integer myProperty) {
this.myProperty = myProperty;
}
public Integer getValue() {
//do something with myProperty
return result;
}
}
And the class that uses PropertyReader:
public class Utilizer {
private PropertyReader getPropertyReader() {
ApplicationContext context = new AnnotationConfigApplicationContext(PropertyReader.class);
PropertyReader reader = (BakerStorageClassConfigHelper)context.getBean("PropertyReader");
return reader;
}
}
I've registered the classes as beans in the application-config.xml file:
<bean class="property.class.package.PropertyClass" depends-on="Environment" />
<bean class="reader.class.package.PropertyReader" />
And i have an environment.xml file where the "Environment" bean is defined with location rules to find the property files.
Now what happens that in the class "Utilizer" when i try to get the "ApplicationContext" object an exception is thrown:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'PropertyReader':
Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire method: public void reader.class.package.PropertyReader.setMyProperty(java.lang.Integer);
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.Integer]
found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
I've tried to change the annotation of PropertyReader class to #Repository or #Service and tried to add a #ComponentScan with the PropertyClass package specified but none of that worked for me..
Could someone give me some advices?
Thank you!
I do not quite get why do you need to declare propertyName as Integer.
If what you need is just get the properties from file, then you can define a PropertiesFactoryBean and autowire it to any other beans you like.
Let's say you have a myValues.properties file containing values:
key1=value1
key2=value2
Define Bean:
#Bean(name = "myProperties")
public PropertiesFactoryBean detailQueriesFactoryBean()
{
PropertiesFactoryBean pfb = new PropertiesFactoryBean();
pfb.setLocation(new ClassPathResource("com/xxx/myValues.properties"));
return pfb;
}
Now wherever you need it, do:
#Autowired
#Qualifier("myProperties")
private Properties myValuesContainer;
public void myMethod(){
//this will get you "value1"
String value1 = myValuesContainer.getProperty("key1");
}
Hope this works for you.
--------------------- For your case----------------
If it is already in the application context, you can use #Value to inject value directly in your PropertyReader and add getter/setter for them. No need a PropertyClass, right?
Or you can add a #PostConstruct method to PropertyReader. Inside the method, you can retrieve the values you need from the existing context.
#PostContstruct
public void extractValues(){
//retrieve value from context and assign to whichever var.
}

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 .

Can Spring Autowire beans created in a BeanFactoryPostProcessor

I have a standard bean with some properties that need to be autowired.
#Service
public class MyServiceImpl implements MyService {
#Autowired
private FirstRepository first;
public MyServiceImpl() {
}
I use a Java Config to find the beans:
#Configuration
#ComponentScan(basePackages = "com.company", excludeFilters = { #Filter(Configuration.class) })
public class MainConfig {
}
However, the FirstRepository Bean doesn't exist so I create it in a BeanFactoryPostProcessor:
public class RepoGeneratorPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
GenericBeanDefinition jpaR = new GenericBeanDefinition();
jpaR.setBeanClass(JpaRepositoryFactoryBean.class);
jpaR.setAutowireCandidate(true);
jpaR.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
jpaR.setLazyInit(false);
jpaR.setPropertyValues(new MutablePropertyValues().add("repositoryInterface", FirstRepository.class));
RootBeanDefinition definition = new RootBeanDefinition();
definition.setBeanClass(FirstRepository.class);
definition.setAutowireCandidate(true);
definition.setFactoryBeanName("&jpaR");
definition.setFactoryMethodName("getObject");
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_NAME);
definition.setLazyInit(false);
definition.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory;
registry.registerBeanDefinition("jpaR", jpaR);
registry.registerBeanDefinition("first", definition);
}
When I start my application I get the following exception which seems to suggest that Spring can't find the FirstRepository bean.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.company.FirstRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.
If I remove the #Autowired annotation I can see after start up that the FirstRepository bean is properly created.
Any suggestions?
This exception is saying that there is no bean defined for the FirstRepository class when the project is being built. Which I cannot see it here either.
The simplest solution would be to have a bean definition in your application-context.xml like this:
<bean id="firstRepository" class="your.package.FirstRepository" autowire="byName"/>
In this case, at the start up, there will be that bean definition.
I don't think you need the & before the beanname in
definition.setFactoryBeanName("&jpaR");
I used something like that in my project
definition.setFactoryBeanName("jpaR");
and it worked as expected
The & is needed if you need to get the factory bean of the bean named first.
&first should return jpaR.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-extension-factorybean

Categories