I have this simple component class:
package jason;
import org.springframework.stereotype.Component;
#Component
public class Messenger {
private String message;
public Messenger(String message) {
this.message = message;
}
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
}
In the argument for the constructor, IntelliJ reports: Could not autowire. No beans of 'String' type found.
There are two more classes in this small toy project: Config:
package jason;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackageClasses = Messenger.class)
public class Config {
#Bean
public Messenger helloWorld(){
return new Messenger("Hello World!");
}
}
and MainApp:
package jason;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Messenger obj = context.getBean("helloWorld", Messenger.class);
obj.getMessage();
}
}
Curiously, besides the seemingly compile-time error, the project builds, but fails at runtime with:
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'messenger' defined in file [C:\Users\jasonfil\code\green-taxi\target\classes\jason\Messenger.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
What am I doing wrong here? New to SpringBoot. Might have IOC misconception(s).
You are mixing two ways of bean injection with Messenger class, annotation-based injection with #Component, and you are declaring it as a bean, with #Bean, in the configuration class.
When you try to inject Messenger using the AnnotationConfigApplicationContext with an activated component scan, Spring will use the annotation-based injection first, so #Component.
So Spring will call the default constructor to inject your bean, of course, if there is no constructor based autowiring (that's your case), so you need to add the default constructor to Messenger. if there is no default constructor Spring will use the available constructor, so will get the error above. Of course, you need to delete the #Bean configuration because you are not using it:
package jason;
import org.springframework.stereotype.Component;
#Component
public class Messenger {
private String message;
public Messenger() {
}
public Messenger(String message) {
this.message = message;
}
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
}
Or, if you want to use the bean configuration, you can remove #Component from Messenger, and also remove #ComponentScan:
package jason;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
public class Config {
#Bean
public Messenger helloWorld(){
return new Messenger("Hello World!");
}
}
You are using Java Config type Bean registration as well as Component Scan type Bean registration.
the quickest solution is to remove #Component from the Messenger class.
Related
This question already has an answer here:
Is it possible to set a bean name using annotations in Spring Framework?
(1 answer)
Closed 4 years ago.
I am testing out simple AOP use case in Spring but am getting the below error,
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'bean1' is defined
Below are my source files,
DemoConfig.java
package com.luv2code.aopdemo;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import com.luv2code.aopdemo.aspect.MyDemoLoggingAspect;
import com.luv2code.aopdemo.dao.AccountDAO;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan("com.luv2code.aopdemo")
public class DemoConfig {
#Bean
#Qualifier("bean1")
public AccountDAO accDao() {
return new AccountDAO();
}
#Bean
#Qualifier("bean2")
public MyDemoLoggingAspect myAscpect() {
return new MyDemoLoggingAspect();
}
}
MyDemoLoggingAspect.java
package com.luv2code.aopdemo.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class MyDemoLoggingAspect {
// this is where we add all of our related advices for logging
// let's start with an #Before advice
#Before("execution(** com.luv2code.aopdemo.dao.AccountDAO.addAccount(..))")
public void beforeAddAccountAdvice() {
System.out.println("\n=====>>> Executing #Before advice on addAccount()");
}
}
MainDemoApp.java
package com.luv2code.aopdemo;
import com.luv2code.aopdemo.dao.AccountDAO;
public class MainDemoApp {
public static void main(String[] args) {
// read spring config java class
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
// get the bean from spring container
AccountDAO theAccountDAO = context.getBean("bean1", AccountDAO.class);
// call the business method
theAccountDAO.addAccount();
// do it again!
System.out.println("\nlet's call it again!\n");
// call the business method again
theAccountDAO.addAccount();
// close the context
context.close();
}
}
I have given my bean ID "bean1", even after that Spring is not able to find my bean in the context. Why am I getting this error and how to resolve this?
The #Qualifier tag is used with the #Autowired annotation.
What you need is
#Bean(name="bean1")
public AccountDAO accDao() {
return new AccountDAO();
}
I have the following configuration class:
#Configuration
public class StartupConfig {
private final Logger log = LoggerFactory.getLogger(this.getClass().getSimpleName());
#PostConstruct
public void init() {
log.debug("Start up config initialized.");
}
#Bean
public SchedulerService schedulerService() {
return new SchedulerService();
}
}
I want to be able to load the schedulerService bean from the applications main method. Something like this:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.crm.config.StartupConfig;
import com.crm.service.SchedulerService;
#SpringBootApplication
#EnableCaching
public class Server {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(StartupConfig.class);
context.refresh();
SpringApplication.run(Server.class, args);
SchedulerService schedulerService = (SchedulerService) context.getBean("schedulerService");
schedulerService.start();
}
}
The schedulerService class has an Autowired dependency:
#Service
#Transactional
public class SchedulerService {
#Autowired
private SchedulerTriggerJpaDao schedulerTriggerJpaDao;
...
Here is the definition of SchedulerTriggerJpaDao:
package com.crm.dao;
import java.util.Collection;
import javax.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import com.crm.entity.SchedulerTrigger;
#Transactional
public interface SchedulerTriggerJpaDao extends JpaRepository<SchedulerTrigger, Integer> {
public Collection<SchedulerTrigger> findByEnabledTrueAndDeletedFalse();
}
When I run up the application I get the following error:
Exception in thread "main"
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'schedulerService': Unsatisfied
dependency expressed through field 'schedulerTriggerJpaDao'; nested
exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'com.crm.dao.SchedulerTriggerJpaDao'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
at
org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
What do I need to change to correctly initialise the schedulerService bean so it can also initialise the schedulerTriggerJpaDao dependency?
If your SchedulerTriggerJpaDao class has the following annotation
#Repository
then it should be recognised as a bean.
Your SchedulerTriggerJpaDao is just an interface. You need
either provide a Dao implementation yourself and annotate it with #Component
(FYI #Repository and #Service automatically mark class as component)
or use some framework that will generate a Dao implementation for you based on your interface. E.g. Spring Data JPA (http://projects.spring.io/spring-data-jpa/)
The problem is that you are returning a new instance of the SchedulerService, which is not managed by spring. You are annotating the class as a #Service but spring is only managing the one injected by #Inject and/or #Autowire.
I would like for Spring Boot to throw an exception if any of my beans are not fully configured during initialization. I thought that the correct way to do that would be to annotate the relevance bean methods with #Required, but it does not behave as I expect.
application.yml:
my_field: 100
Simple bean class:
package com.example.demo;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Component;
#Component
public class MyProperties {
private int myField;
public MyProperties(){}
#Required
public void setMyField(int myField) {
this.myField = myField;
}
#Override
public String toString() {
return "{myField=" + myField + '}';
}
}
My application class:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import javax.annotation.PostConstruct;
#SpringBootApplication
public class DemoApplication {
#Bean
#ConfigurationProperties
public MyProperties getMyProperties() {
return new MyProperties();
}
#PostConstruct
public void init() {
MyProperties myProperties = getMyProperties();
System.out.println(myProperties);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
In the init method of DemoApplication I am printing the resulting bean object. Without the #Required annotation it is loaded correctly and prints {myField=100}. However, when I add the annotation it throws this exception:
org.springframework.beans.factory.BeanInitializationException: Property 'myField' is required for bean 'myProperties'
This is despite the fact that the config file contains the required value.
What is the correct to tell Spring that a field is required?
From the docs
Spring Boot will attempt to validate #ConfigurationProperties classes whenever they are annotated with Spring’s #Validated annotation. You can use JSR-303 javax.validation constraint annotations directly on your configuration class. Simply ensure that a compliant JSR-303 implementation is on your classpath, then add constraint annotations to your fields
You should declare myField as follows:
#NonNull
private int myField;
I have a SqsQueueSender to send messages to AWS. I want to test this class. My thought is that it should be a #Component that is injected in to the classes that need it. Importantly, I want to configure the endpoint of the SqsQueueSender to be different in testing vs. production environments.
I've been moving #Autowired and #Component around the classes various different ways but must have some basic misunderstanding. Here's my latest configuration:
package com.foo.fulfillmentApi.dao;
import com.amazonaws.services.sqs.AmazonSQSAsyncClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.messaging.support.MessageBuilder;
#Component
public class SqsQueueSender {
private static final Logger LOGGER = LoggerFactory.getLogger(SqsQueueSender.class);
private final QueueMessagingTemplate queueMessagingTemplate;
#Autowired
AmazonSQSAsyncClient amazonSQSAsyncClient;
//This value is from /resources/application.properties
private #Value("${sqs.endpoint}") String endpoint;
public SqsQueueSender(AmazonSQSAsyncClient amazonSqsAsyncClient) {
amazonSqsAsyncClient.setEndpoint(endpoint);
this.queueMessagingTemplate = new QueueMessagingTemplate(amazonSqsAsyncClient);
}
public void send(String queueName, String message) {
this.queueMessagingTemplate.convertAndSend(queueName, MessageBuilder.withPayload(message).build());
}
}
The error message on startup states
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.amazonaws.services.sqs.AmazonSQSAsyncClient' available: expected
at least 1 bean which qualifies as autowire candidate. Dependency
annotations: {}
To implement SqsQueueSender you must pass an AmazonSQSAsyncClient. How do I make sure this component can access an existing bean of that type?
You need to create a configuration class. In your case it would be something like this:
#Configuration
public class AWSConfig {
#Bean(name ="awsClient")
public AmazonSQSAsync amazonSQSClient() {
AmazonSQSAsyncClient awsSQSAsyncClient
= new AmazonSQSAsyncClient();
// set up the client
return awsSQSAsyncClient;
}
If it has problems with injecting then add qualifier in qsQueueSender:
#Autowired
#Qualifier("awsClient")
AmazonSQSAsyncClient amazonSQSAsyncClient;
You can also do this using the xml configuration but as you are using annotations then this is more advisable approach.
Add #Component/#Service in com.amazonaws.services.sqs.AmazonSQSAsyncClient or return an object of that using #Bean annotation from configuration class.
If you use springboot - define into your startup application file as below
#Bean
public AmazonSNSAsync amazonSNSClient() {
ClientConfiguration config = new ClientConfiguration();
return AmazonSNSAsyncClient.asyncBuilder().withClientConfiguration(config)
.withRegion(Regions.fromName(region))
.withCredentials(new DefaultAWSCredentialsProviderChain())
.build();
}
I'm trying to learn how to read properties file using spring. After an internet searching I found that I can use #value and #PropertySource annotations to achieve that. I created a project which has the following structure and classes codes:
Structure of the project:
AppConfigMongoDB.java implementation:
package com.mongodb.properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
#PropertySource("classpath:config/config.properties")
public class AppConfigMongoDB {
#Value("#{mongodb.url}")
private String mongodbUrl;
#Value("#{mongodb.db}")
private String defaultDb;
public String getMongoDb()
{
return defaultDb;
}
public String getMongoDbUrl()
{
return mongodbUrl;
}
}
SpringConfiguration.java implementation:
package com.mongodb.properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class SpringConfiguration {
#Bean
public AppConfigMongoDB getAppConfigMongoDB(){
return new AppConfigMongoDB();
}
}
Main.java
package com.mongodb.properties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
AppConfigMongoDB mongo = applicationContext.getBean(AppConfigMongoDB.class);
System.out.println("db= "+mongo.getMongoDb());
System.out.println("URL= "+mongo.getMongoDbUrl());
}
}
The properties file that I'm reading from called config.properties, it contains the following lines:
mongodb.url=1.2.3.4
mongodb.db=dataBase
I tested this small project and I got a stack trace that contains the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getAppConfigMongoDB': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.lang.String com.mongodb.properties.AppConfigMongoDB.mongodbUrl; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field 'mongodb' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1202)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
at com.mongodb.properties.Main.main(Main.java:9)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field 'mongodb' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:226)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:93)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:81)
at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:51)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:87)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:120)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:242)
at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:161)
... 18 more
Is it a problem of Spring calling beans? Or maybe is it a problem of properties file path or something else?
I can see several issues in the code.
1) Your place holders for values should be in the form ${mogodb.url}, not #{mongodb.url}. The "#" has a different meaning (See Spring Expressions).
2) You are going to need a PropertySourcesPlaceholderConfigurer bean to do the injection of the values
3) Sooner or later you are going to have a number of Beans floating around, and in I would use #ComponentScan to allow the context to know these without you having to mention them one by one
4) If you use ComponentScan to get the beans, you are going to have to provide AppConfigMongoDB bean once
I end up with these classes after doing all that:
Main.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
AppConfigMongoDB mongo = applicationContext.getBean(AppConfigMongoDB.class);
System.out.println("db= "+mongo.getMongoDb());
System.out.println("URL= "+mongo.getMongoDbUrl());
}
}
SpringConfiguration.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
#Configuration
#ComponentScan
public class SpringConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
AppConfigMongoDB.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:config/config.properties")
public class AppConfigMongoDB {
#Value("${mongodb.url}")
private String mongodbUrl;
#Value("${mongodb.db}")
private String defaultDb;
public String getMongoDb() {
return defaultDb;
}
public String getMongoDbUrl() {
return mongodbUrl;
}
}
Nice reply given by #Ian Sparkes . Adding some of my inputs here. Configuring the PropertySourcesPlaceholderConfigurer is not mandatory. I am able to get the value from my properties file and set it to the filed variable of my main Configuration class without it.
There is also another way to get the values of your keys in the properties file. Use org.springframework.core.env.Environment class . This class automatically loads all the key-value pairs in properties file that is in class path.
Example :
#Configuration
#ComponentScan(basePackages="com.easy.buy")
#PropertySource({"classpath:config.properties","classpath:props/db-${env}-config.properties",
"classpath:props/app-${env}-config.properties"})
public class CatalogServiceConfiguration {
Logger logger = LoggerFactory.getLogger(CatalogServiceConfiguration.class);
//This object loads & holds all the properties in it as key-pair values
#Autowired
private Environment env;
/**
* Instantiate the DataSource bean & initialize it with db connection properties
* #return
*/
#Bean(name="basicDataSource")
public BasicDataSource basicDataSource() {
String dbUrl = env.getProperty("db.url");
String dbUser = env.getProperty("db.user");
String dbPwd = env.getProperty("db.pwd");
String driver = env.getProperty("db.driver");
logger.info("Initializing CatalogServiceConfiguration");
logger.info("dbUrl=" + dbUrl);
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(driver);
ds.setUrl(dbUrl);
ds.setUsername(dbUser);
ds.setPassword(dbPwd);
return ds;
}
}