How to configure #Component class appropriately - java

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

Related

How to keep duplicate spring bean in container. [ConflictingBeanDefinitionException]

I am working on an application which uses some third java libraries which are build on top of core spring framework.
Now when my application uses these libraries I am getting ConflictingBeanDefinitionException because two libraries have the same bean name.
Now as these libraries are external I cannot change the bean name. Is there a way by which in my application I can use both the beans in same container?
#Component
class ApplicationLogic {
#Autowire FetcherAndResolver fetchFromLibraryA;
#Autowire FetcherAndResolver fetchFromLibraryB; //Because both bean names are same here comes the exception.
}
I think this would require a bit of a hack.
First of all, structure your packages in a way so that external beans are not added automatically.
So, if external class is located in package a.b. Then you have to move your own classes in either a.c or a.b.c. THis will ensure that you are in control of how beans are initialized.
Once this is done, you can add a #Configuration class where you can create Beans of both type:
#Configuration
public class ExternalBeanConfiguration {
#Bean("internal-resolver" )
public FetcherAndResolver internalResolver() {
return new FetcherAndResolver();
}
#Bean("external-resolver" )
public a.b.c.FetcherAndResolver externalResolver() {
return new a.b.c.FetcherAndResolver();
}
}
I am assuming that FetcherAndResolver is a class rather than an interface. If it is an interface, it is easier to do it as you won't have to use fully-qualified name for classes.
Then you can simply autowire with qualifiers.
#Component
public class SomeComponent {
#Qualifier( "internal-resolver" )
FetcherAndResolver internalResolver;
#Qualifier( "external-resolver" )
FetcherAndResolver externalResolver;
}
First component from third java libraries:
package com.example.component1;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
public String makeSomeWork() {
return "Component 1";
}
}
Second component from third java libraries:
package com.example.component2;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
public String makeSomeWork() {
return "Component 2";
}
}
Controller:
package com.example.controller;
import com.example.component1.MyComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class MyController {
#Autowired
private MyComponent myComponent;
#Autowired
private com.example.component2.MyComponent myComponent2;
#RequestMapping("/components")
public String getComponents() {
return myComponent.makeSomeWork() + "_" + myComponent2.makeSomeWork();
}
}

[SpringBoot]: Simple component cannot autowire String class

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.

calling method with Rest Template Builder

I created this rest template with the rest template builder and set connection and read timeouts. I need to call this rest template from other methods in the program, but am unsure how to do so. please help, thanks in advance!
//create rest template with rest template builder and set connection and read timeouts #Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.setConnectTimeout(Duration.ofMillis(connectTimeout))
.setReadTimeout(Duration.ofMillis(readTimeout))
.build();
}
// this is an example method that calls rest template, unsure what goes in the parameter section
#Bean
public example example() {
return new restTemplate(what goes here)
);
}
if you have created your customised RestTemplate, you may autowire it any class where you want to call it and use the one. if you more than 1 RestTemplates you can use #Qualifier above RestTemplate Bean and use the same in the calling class.
RestTemplateBuilder is a bean provided by Spring boot. You can inject that into any of your Spring bean classes.
Then you just want to configure your restTemplate at the creation of your Spring bean class and store it as a field. You can do something like below (This is not the one and only way).
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
#Configuration
public class MyExampleRestClientConfiguration {
private final RestTemplateBuilder restTemplateBuilder;
#Autowired
public MyExampleRestClient(RestTemplateBuilder restTemplateBuilder) {
this.restTemplateBuilder = restTemplateBuilder;
}
#Bean
public RestTemplate restTemplate() {
return restTemplateBuilder
.setConnectTimeout(Duration.ofMillis(connectTimeout))
.setReadTimeout(Duration.ofMillis(readTimeout))
.build();
}
}
Now in Some other spring bean class, you can simply wire the restTemplate bean and re-use.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
public class MyExampleRestClient {
private final RestTemplate restTemplate;
#Autowired
public MyExampleRestClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
//Now You can call restTemplate in any method
}
You may refer this for more.

How to correctly initialise a Bean in the main method of a Spring Boot application?

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.

Using Java configuration and constructor injection

I have a spring bean class with a constructor with multiple parameters and #Inject annotation.
Is there a way to use spring Java configuration class to create a bean for the class without actually writing code for creating the object? Something like using #Bean on a field?
#Bean(MyClassName.class) private MyInterfaceName myBean;
Or maybe by making the configuration class abstract and the bean method abstract, like:
#Bean(MyClassName.class) abstract MyInterfaceName myBean();
It's quite annoying (and pointless) to write each time the whole method that only creates a new object if you know that you only have 1 implementation of the class and you want to use auto wiring and constructor injection.
You can use #Component annotation. According to Spring documentation:
#Component indicates that an annotated class is a "component". Such classes are
considered as candidates for auto-detection when using
annotation-based configuration and classpath scanning.
Use the #Component annotation.
Indicates that an annotated class is a "component". Such classes are
considered as candidates for auto-detection when using
annotation-based configuration and classpath scanning. Other
class-level annotations may be considered as identifying a component
as well, typically a special kind of component: e.g. the #Repository
annotation or AspectJ's #Aspect annotation.
Here is an example:
import org.springframework.stereotype.Component;
#Component
public class CustomerDAO
{
#Override
public String toString() {
return "Hello , This is CustomerDAO";
}
}
A DAO class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class CustomerService
{
#Autowired
CustomerDAO customerDAO;
#Override
public String toString() {
return "CustomerService [customerDAO=" + customerDAO + "]";
}
}
And a runner class:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
public static void main( String[] args )
{
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"Spring-AutoScan.xml"});
CustomerService cust = (CustomerService)context.getBean("customerService");
System.out.println(cust);
}
}
And your output:
CustomerService [customerDAO=Hello , This is CustomerDAO]

Categories

Resources