class required a single bean, but 2 were found: - java

I have the below interface:
public interface MailSender {
void sender(String to, String subject,String body);
}
With 2 imlementation of it:
public class SmtpkMailSender implements MailSender {
static Log log=LogFactory.getLog(MailSender.class);
public void sender(String to, String subject,String body){
log.info("SMTP To: "+to);
log.info("SMTP Subjecy: "+subject);
log.info("SMTP body: "+body);
}
}
and the second one is:
#Primary
public class MockMailSender implements MailSender {
static Log log=LogFactory.getLog(MailSender.class);
public void sender(String to, String subject,String body){
log.info("To: "+to);
log.info("Subject: "+subject);
log.info("body: "+body);
}
}
I used the dependency injection into the controller class which is as following:
#RestController
public class MailController {
#Autowired
private MailSender smtpkMailSender;
#RequestMapping("/send")
public String send(){
smtpkMailSender.sender("Person", "Important", "Take Care");
return "mail is sent";
}
}
At the end i have a configuration class which contains my Beans:
#Configuration
public class MailConfig {
#Bean
public SmtpkMailSender getSmtpkMailSender(){
return new SmtpkMailSender();
}
#Bean
public MockMailSender getMockMailSender(){
return new MockMailSender();
}
}
Unfortunatly, when i run my application it complains with:
Description:
Field smtpkMailSender in com.example.demo.MailController required a single bean, but 2 were found:
- getSmtpkMailSender: defined by method 'getSmtpkMailSender' in class path resource [com/example/mail/MailConfig.class]
- getMockMailSender: defined by method 'getMockMailSender' in class path resource [com/example/mail/MailConfig.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
As you see, although i have specified the MockMailSender as Primary the spring still complains, and cannot identify it

Since you're using java config you should mark config method with #Primary annotation, and not a class:
#Configuration
public class MailConfig {
#Bean
public SmtpkMailSender getSmtpkMailSender(){
return new SmtpkMailSender();
}
#Bean
#Primary
public MockMailSender getMockMailSender(){
return new MockMailSender();
}
}

You can use #Qualifier annotation for specify which specific type of your implementation, you want to autowire.
#RestController
public class MailController {
#Autowired
#Qualifier("smtpkMailSender")
private MailSender smtpkMailSender;
#RequestMapping("/send")
public String send(){
smtpkMailSender.sender("Person", "Important", "Take Care");
return "mail is sent";
}
}

You may simply name your bean using
#Bean(name="mockMailSender")
public MockMailSender getMockMailSender() { }
and then decorate the autowired field with
#Autowired
#Qualifier("mockMailSender")
private MailSender smtpkMailSender;

Another solution for this error is to exclude the Bean that is not used (the duplicated bean), from the autoconfiguration stage within "application.yml" file, as explained in this thread: How to exclude AutoConfiguration classes in Spring Boot JUnit tests?
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
- org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration
This is applicable if the error appears after you added a new Maven/Gradle dependency, which duplicates an existing bean that you are using. For example, when both beans come from external JARs, so the source code cannot be updated with #Primary or #Qualifier annotations to solve the error.

Related

Spring: Implement two instances of a #Service with two instances of #Configuration linked to each respective service

An existing utility exists that I am importing to my project. It is written similarly as these two classes
#Service
public class ServiceAccessorImpl implements ServiceAccessor {
#Autowired private ServiceConfiguration serviceConfiguration;
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
#Configuration
#Data //automatically adds getters and setters
#ConfigurationProperties(prefix="config")//pulls serviceEndPoint value from external config
//Assume external config has config.serviceEndPoint = "www.endpoint1.com"
public class ServiceConfiguration {
private String serviceEndPoint;
}
In a separate project below I am importing the above into my project. I would like to have two instances of the same service with two unique and respective configuration classes. so that service1 is linked to config1 and service2 is linked to config2. My reasoning is I want one instance that only pulls the endpoint from the external configuration and another instance that I can use to set the endpoint. I have tried using things like #Qualifier but I cant figure out how to link the correct config with the correct service. I have a feeling that this may not be possible because ServiceConfiguration is privately scoped within ServiceAccessorImpl and I have no access through setters or constructors.
Controller Endpoint. The below is psuedo code of how I would like to implement my design. Autowiring in a single instance and using either endpoint works for that endpoint but not for both as shown below.
#ComponentScan(basePackageClass = ServiceAccessorImpl.class)
public class ServiceAccessorController {
#Autowired private ServiceAccessor serviceAccessor1;
#Autowired private ServiceConfiguration serviceConfiguration1;
#Autowired private ServiceAccessor serviceAccessor2;
#Autowired private ServiceConfiguration serviceConfiguration2;
Response CallEndpoint1(){
//www.endpoint1.com is already set here from external config
return serviceAccessor1.executeCall();
}
Response CallEndpoint1(){
serviceConfiguration2.setServiceEndPoint("www.endpoint2.com")
return serviceAccessor2.executeCall();
}
}
Thank you in advance
If you need multiple instances of the same implementation, it's easier to not annotate it as a bean, and have a #Configuration provide the beans instead.
public class ServiceAccessorImpl implements ServiceAccessor {
private ServiceConfiguration serviceConfiguration;
public ServiceAccessorImpl(ServiceConfiguration configuration) {
this.serviceConfiguration = configuration;
}
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
// this one should just have #ConfigurationProperties, not #Configuration
#Data
#ConfigurationProperties(prefix="config")
public class ServiceConfiguration {
private String serviceEndPoint;
}
then in your service you can have a Configuration providing both instances:
#Configuration
public class BeansConfiguration {
#Qualifier("service1")
#Primary // optional, Spring will autowire this instance by default if no qualifier is given
#Bean
public service1(#Autowired ServiceConfiguration config) {
// use the default config bean
return new ServiceAccessorImpl(config);
}
#Qualifier("service2")
#Bean
public service2() {
return new ServiceAccessorImpl(new ServiceConfiguration("www.endpoint2.com"));
}
}
then you may consume both by using the qualifiers (note that you don't have to inject the configs here):
public class ServiceAccessorController {
#Autowired
private ServiceAccessor serviceAccessor1;
#Autowired
#Qualifier("service2")
private ServiceAccessor serviceAccessor2;
Response CallEndpoint1(){
return serviceAccessor1.executeCall();
}
Response CallEndpoint2(){
return serviceAccessor2.executeCall();
}
}

Java Spring Boot #Autowired values are null

When I try to print the variable that I have autowired, it prints "null" instead of the value I set it to, "Example." I can't quite figure out what I'm missing
In my AppConfig class I have:
#Configuration
public class ApplicationConfiguration {
#Bean
public String tableName(){
return "Example";
}
}
In my other class, DAOMethods, that I want to autowire the variable in:
#Component
public class DAOMethods {
#Autowired
private String tableName;
public void print(){
System.out.println(tableName);
}
}
They exist in different packages; With AppConfig living in a config
folder and DAOMethods in client->dynamodb->util folder. Config and
Client are folders under the main->java folder
The added #Configuration annotation scans for the beans in the current and its subpackages. You need to explicitly tell the application to scan the required packages. So you can do:
#SpringBootApplication (scanBasePackages = {"config", "client"})
OR,
You need to keep the config and other classes which uses that config in same root package. You can put the config folder and client folder under same package, say com.application and then add the following in your main class:
#SpringBootApplication(scanBasePackages = "com.application")
Now run the application.
You problem have many solutions
F.e. you can create config-class with required set of parameters and next autowire it (Injecting values with #Value annotation from application configuration file is good practice):
#Component
public class CustomConfiguration {
#Value("${table.name}")
private String tableName;
#Value("${some.value}")
private Integer someValue;
public String getTableName() {
return tableName;
}
public Integer getsomeValue() {
return someValue;
}
}
And you application.properties will looks like:
some.value=1
table.name=Example
Or you can simply inject single value from configuration with #Value annotation
One of solutions is using bean name in #Value annotation:
#Configuration
public class ApplicationConfiguration {
#Bean
public String tableName(){
return "Example";
}
}
#Component
public class DAOMethods {
#Value(#{tableName})
private String tableName;
}
More examples you can see in this question: Autowire a string from Spring #Configuration class?

How to use spring #autowired annotation in the class having void methods?

I have an interface and service implements it. It has some void methods.
I am using spring java bean configuration. But unable to create bean object because of void methods.How to handle this problem.
I tried to use #PostConstruct instead of #Bean after reading some blogs, but it didn't work out.
public interface MyInterface {
void someData(List<MyClass> list, String somedata);
}
#Service("myInterface")
public DummyClass implements MyInterface {
public void someData(List<MyClass> list, String somedata){
// my business logic
}
}
public AppConfig {
#Bean
public MyInterface myInterface {
return new DummyClass(); // but gives error void cannot return value
}
}
My Junit looks like this
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
classes = {AppConfig.class},
loader = AnnotationConfigContextLoader.class
)
public class MyTest {
#Autowired
DummyClass dummyClass;
// If I don't use AppConfig and simply autowire then I get
"Error creating bean name, unsatisfied dependency
}
How do I achieve dependency injection here?
Use #Configuration annotation on AppConfig class, with this all the beans defined on this class will be loaded on spring context.
If you use #Service annotation on DummyClass, you do not need to declare #Bean annotation because you are already saying to spring to detect this class for dependency injection. On the other hand use #Bean annotation to specify the instantiation of the class. Normally I let the #Bean to complex classes for dependency injection or to override configurations.

#Bean declaration with #Qualifier doesn't work

Lets say I have a config class (JmsQueueConfig, see below). In this class, I want to configure multiple queues for my entire application. For one queue, there is no problem. However when I add a second queue and try to use one of these queues from a service (MemberService), then Spring-boot tells me
Parameter 1 of constructor in
com.example.notification.application.jms.JmsEventPublisher
required a single bean, but 2 were found:
- queueAccountToNotification: defined by method 'queueAccountToNotification' in class path resource
[com/example/notification/application/jms/JmsQueueConfig.class]
- queueNotificationToAccount: defined by method 'queueNotificationToAccount' in class path resource
[com/example/notification/application/jms/JmsQueueConfig.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer
to accept multiple beans, or using #Qualifier to identify the bean
that should be consumed
Here is my Config-Class:
#Configuration
#EnableJms
#ImportAutoConfiguration(classes = {
JmsAutoConfiguration.class,
ActiveMQAutoConfiguration.class
})
public class JmsQueueConfig {
#Value("${APP_QUEUE_ACCOUNT_TO_NOTIFICATION}")
private String queueAccountToNotificationName;
#Value("${APP_QUEUE_NOTIFICATION_TO_ACCOUNT}")
private String queueNotificationNameToAccount;
#Bean
#Qualifier("q1")
public Queue queueAccountToNotification() {
return new ActiveMQQueue(queueAccountToNotificationName);
}
#Bean
#Qualifier("q2")
public Queue queueNotificationToAccount() {
return new ActiveMQQueue(queueNotificationNameToAccount);
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
#Bean
#Qualifier("p1")
public EventPublisher eventPublisher(JmsTemplate jmsTemplate) {
return new JmsEventPublisher(jmsTemplate, new ActiveMQQueue(queueAccountToNotificationName));
}
#Bean
public MessageConverter messageConverter() {
return new JmsMessageConverter();
}
}
My Service:
#Service
#FieldDefaults(level = AccessLevel.PRIVATE)
#AllArgsConstructor
#Slf4j
public class MemberService {
#Autowired
#Qualifier("q1")
Queue q;
#Qualifier("p1")
EventPublisher eventPublisher;
public void createMemberSubscription(final Member member) {
final MembershipSubscriptionEvent event = new MembershipSubscriptionEvent(UUID.randomUUID().toString(), member.getEmail());
//eventPublisher.publish(event);
log.info("createMemberSubscription");
}
public void removeMemberSubscription(final Member member) {
final MembershipRemovalEvent event = new MembershipRemovalEvent(UUID.randomUUID().toString());
//eventPublisher.publish(event);
log.info("removeMemberSubscription");
}
}
I am new to Spring ecosystem and I might not have understand well the #Autowired and bindings. Any good documentation or example would be much appreciated.
On Spring and SoF, I haven't found any such documentation.
Updated:
JmsEventPublisher class
#Component
#FieldDefaults(level = AccessLevel.PRIVATE)
#Slf4j
#AllArgsConstructor
public class JmsEventPublisher implements EventPublisher {
final JmsTemplate jmsTemplate;
final Destination destination;
#Override
public void publish(DomainEvent event) {
jmsTemplate.convertAndSend(destination, event);
log.trace("Sent event. [destination={}, event={}]", destination, event);
}
}
To correct the accepted answer,your understanding for the use of #Qualifier is correct. It can be used in two contexts. It can be used with a #Bean configuration method to provide a qualifier for the bean. If not provided this defaults to the bean name.
It can also be used at an injection target i.e method or field with #Autowired or #Inject annotation. At this context it assist Spring autowiring infrastructure to filter the bean candidates matching the injection target based on the qualifier(provided with #Bean method) if multiple beans are found
The cause of the error was due to
#AllArgsConstructor
public class JmsEventPublisher implements EventPublisher
The #AllArgsConstructor will generate the following constructor
public JmsEventPublisher(JmsTemplate jmsTemplate, Destination destination){
//body
}
Spring will attempt to autowire JmsEventPublisher by constructor since it has one constructor which is not a no argument constructor. However the parameter of type Destination matches two beans of type Queue.
The solution is to either use an explicit constructor. ie remove #AllArgsConstructor and define constrctor as follows
public JmsEventPublisher(JmsTemplate jmsTemplate, #Qualifier("q1")Destination destination){
//body
}
or use field or setter injection instead i.e remove #AllArgsConstructor and inject fields or setter methods
public class JmsEventPublisher implements EventPublisher {
private JmsTemplate jmsTemplate;
#Qualifier("q1")
private Destination destination;
}
I think you have misunderstood #Qualifier
From the documentation, "This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring."
In your case #Qualifier is of no meaning.
#Bean
#Qualifier("q1")
public Queue queueAccountToNotification() {
return new ActiveMQQueue(queueAccountToNotificationName);
}
Instead you should be doing like this
#Bean(name = "q1")
public Queue queueAccountToNotification() {
return new ActiveMQQueue(queueAccountToNotificationName);
}
#Bean(name = "q2")
public Queue queueNotificationToAccount() {
return new ActiveMQQueue(queueNotificationNameToAccount);
}
Similarly remove #Qualifier on eventPublisher(...)
That doesn't solve all the problem. :)
As the exception indicates, spring is not able to autowire Destination field in JmsEventPublisher. Because it has two beans of type Destination(q1 and q2).
To solve this what you can do is .
Put #Primary on one of the bean declaration and then use #Qualifier.
#Primary
#Bean(name = "q1")
public Queue queueAccountToNotification() {
return new ActiveMQQueue(queueAccountToNotificationName);
}
public class JmsEventPublisher implements EventPublisher {
final JmsTemplate jmsTemplate;
#Qualifier("q1")
final Destination destination;
..........
}
Bottom line is For #Qualifier to work in case of multiple beans of same type, you need to put #Primary
Another option is instead of using #Primary, you can name the variables exactly as Bean names, then spring will automagically inject correct beans for you. i.e.
public class JmsEventPublisher implements EventPublisher {
final JmsTemplate jmsTemplate;
final Destination q1; // q1 or q2
.....
}
similiarly in MemberService
public class MemberService {
#Autowired
Queue q1; // q1 or q2
.....
}
Change final Destination destination to final Destination q1 and it should work. I had the same problem and it worked for me.

How do I express a dependency on a bean defined in an imported configuration in Spring?

I recently started working at a place that uses Java configuration for Spring as opposed to XML and so far I'm loving it.
My question is the following:
If we have a #Configuration annotated class A that imports another #Configuration annotated class B, what is the proper, type-safe way for a bean defined in A to depend on a bean defined in B.
Here's an example I saw in a blog (https://blog.codecentric.de/en/2012/07/spring-dependency-injection-styles-why-i-love-java-based-configuration/):
#Configuration
public class PartnerConfig {
#Bean
public PartnerService partnerService() {
return new PartnerServiceImpl();
}
}
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Autowired
private PartnerConfig partnerConfig;
#Bean
public CashingService cashingService() {
return new CashingServiceImpl(partnerConfig.partnerService());
}
}
As a second part to my question, if I was to do the above, would Spring interpret as a bean dependency? That is, when I do
partnerConfig.partnerService()
in the example above, am I getting Spring to fetch me the partnerService bean, or am I just calling a regular java method and creating a new instance of the PartherService (which is NOT what I want, since the bean should be a singleton) ?
EDIT:
It has been suggested to use a #Qualifier. Would this work?
#Configuration
public class PartnerConfig {
#Bean
#MyCustomQualifier
public PartnerService partnerService() {
return new PartnerServiceImpl();
}
}
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Bean
public CashingService cashingService(#MyCustomQualifier PartnerService partnerService) {
return new CashingServiceImpl(partnerService);
}
}
I recommend giving the docs a read: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html
Refer to the section:
#Bean Methods in #Configuration Classes
This sums it up very well.
Typically, #Bean methods are declared within #Configuration classes. In this case, bean methods may reference other #Bean methods in the same class by calling them directly. This ensures that references between beans are strongly typed and navigable.
Also take a look at: http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html
Section:
Composing #Configuration classes
Just add the dependency as an argument to the #Bean annotated method and remove the autowiring of the configuration.
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Bean
public CashingService cashingService(PartnerService partnerService) {
return new CashingServiceImpl(partnerService);
}
}
or simply autowire the PartnerService instead of the configuration.
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Autowire
private PartnerService partnerService;
#Bean
public CashingService cashingService() {
return new CashingServiceImpl(partnerService);
}
}

Categories