I am trying to inject a configuration bean into my Service. For this i created a separate class EncryptionProps
public class EncryptionProps {
#Getter
#Setter
#Value("${kafka.properties.security.keyreuseinterval}")
private long keyReuseInterval;
}
Then, i am creating another config class to register this as a bean
#Configuration
public class EncryptionConfig {
#Bean
public EncryptionProps encryptionProps() {
return new EncryptionProps();
}}
And this is how, i am calling this in my service class:
#Service
public class EncryptionService {
private final long keyReuseInterval;
//some more declarations
#Autowired
public EncryptionService(SchemaProcessor schemaProcessor, CryptoLib crypto, EncryptionProps encryptionProps) {
this.schemaProcessor = Preconditions.checkNotNull(schemaProcessor, "schemaProcessor cannot be null.");
this.crypto = Preconditions.checkNotNull(crypto, "CryptoLib must be provided");
this.keyReuseInterval = encryptionProps.getKeyReuseInterval();
}
However, when i run this, i am getting the following error - org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.sinkad.kafka.util.EncryptionProps' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I am stuck on this since last day. I have tried tons of stackoverflow questions on this, but none helped so far. Can anybody please help me in why Spring is not finding this bean. I even tried adding #ComponentScan over the EncryptionConfig class. But that also didn't worked. Or if you can just point me to some resource related to it.
Thanks!
UPDATE:
Here is my main class:
package com.example.sinkad;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}}
Try like below:
#Component
class EncryptionProps {
final String keyreuseinterval;
// Use #Autowired to get #Value to work.
#Autowired
EncryptionProps(
#Value("${kafka.properties.security.keyreuseinterval}") final String keyreuseinterval) {
this.keyreuseinterval = keyreuseinterval;
}
}
I was finally able to solve this issue with a team member's help. I am still not sure what was the issue, But here's what we did:
1) Moved the keyReuseInterval value to another config class, which will create the object for EncryptionProps object
public class EncryptionProps {
#Getter
#Setter
private long keyReuseInterval;
}
Here 's the config class
#Configuration
public class CryptoConfig {
#Value("${kafka.properties.security.keyreuseinterval}")
private long keyReuseInterval;
#Bean
public EncryptionProps encryptionProps() {
EncryptionProps encryptionProps = new EncryptionProps();
encryptionProps.setKeyReuseInterval(keyReuseInterval);
return encryptionProps;
}
}
2) And we are calling encryptionProps in the service class similar to before:
#Autowired
public EncryptionService(SchemaProcessor schemaProcessor, CryptoLib crypto, EncryptionProps encryptionProps) {
this.schemaProcessor = Preconditions.checkNotNull(schemaProcessor, "schemaProcessor cannot be null.");
this.crypto = Preconditions.checkNotNull(crypto, "CryptoLib must be provided");
this.keyReuseInterval = encryptionProps.getKeyReuseInterval();
}
I am not sure how this fixed the issue. But thought will share it with you guys. Thanks again guys for your suggestions.
Related
I have a spring batch application in which the writer has an #Autowired field (it is a service class). When running tests for the writer step, I am met with the error:
Field batchTrackingService in com.ally.cr.miscinfo.batch.writer.AdvantageClientItemWriter required a bean of type 'com.test.miscinfo.service.TestService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.test.miscinfo.service.batchTrackingService ' in your configuration.
I've looked at a few answers to related questions, and most of them are caused by the fact that the class being injected has not been annotated with #Component, #Service, #Repository, etc. However mine is. I also read questions where the supposed solution was to add the #ComponentScan() annotation to the Main class of my application. After trying this, it gave the same error. Can someone please help me? Any help is appreciated.
Here are the relevant classes:
Main class:
#SpringBootApplication
#EnableBatchProcessing
#EnableJpaRepositories("com.test.miscinfo.repository")
#EntityScan("com.test.miscinfo.entity")
#ComponentScan("com.test.miscinfo.service")
public class MiscInfoServiceApplication {
public static void main(String[] args) {
SpringApplication.run(MiscInfoServiceApplication.class, args);
}
}
Writer class:
#Slf4j
#Component
#AllArgsConstructor
#NoArgsConstructor
public class AdvantageClientItemWriter implements ItemWriter<MiscInfo> {
#Autowired private AdvantageClientConfig advantageClientConfig;
#Autowired WebClient advantageClientWebClient;
#Autowired private BatchTrackingService batchTrackingService;
#Override
public void write(List<? extends MiscInfo> miscInfos) throws Exception {
/* some call to a method in the injected service */
}
}
Service class:
#AllArgsConstructor
#NoArgsConstructor
#Slf4j
#Transactional
#Service
public class BatchTrackingService {
public void someMethod() {}
}
Please let me know if I am missing relevant info.
EDIT:
Adding test method:
#ExtendWith(SpringExtension.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#EnableConfigurationProperties(value = AdvantageClientConfig.class)
#SpringBootTest
#ActiveProfiles("test")
#ContextConfiguration(classes = { AdvantageClientItemWriter.class })
public class AdvantageClientItemWriterTest {
#MockBean RestTemplate advantageClientRestTemplate;
#MockBean WebClient advantageWebClient;
WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class);
WebClient.RequestBodySpec requestBodySpec = mock(WebClient.RequestBodySpec.class);
WebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class);
WebClient.RequestHeadersSpec requestHeadersSpec = mock(WebClient.RequestHeadersSpec.class);
#Autowired AdvantageClientConfig advantageClientConfig;
#Autowired AdvantageClientItemWriter advantageClientItemWriter;
ArgumentCaptor<String> uriCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<MediaType> mediaTypeCaptor= ArgumentCaptor.forClass(MediaType.class);
ArgumentCaptor<String> headerNameCaptor= ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> headerValueCaptor= ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> bodyCaptor= ArgumentCaptor.forClass(String.class);
private MemoryAppender memoryAppender;
#BeforeEach
public void init(){
Logger logger = (Logger) LoggerFactory.getLogger("com.test");
memoryAppender = new MemoryAppender();
memoryAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
logger.setLevel(Level.DEBUG);
logger.addAppender(memoryAppender);
memoryAppender.start();
}
#Test
public void successfulAdvantageClientWrite() throws Exception {
setupMockReturns();
when(responseSpec.toBodilessEntity()).thenReturn(Mono.just(new ResponseEntity(null, HttpStatus.OK)));
List<MiscInfo> miscInfos = new ArrayList<>();
final MiscInfo miscInfo = createMiscInfo1();
miscInfos.add(miscInfo);
advantageClientItemWriter.write(miscInfos);
Assertions.assertEquals(advantageClientConfig.getEndpoint(), uriCaptor.getValue());
Assertions.assertEquals(MediaType.APPLICATION_JSON, mediaTypeCaptor.getValue());
Assertions.assertEquals( advantageClientConfig.getHeaderName(), headerNameCaptor.getValue());
Assertions.assertEquals(advantageClientConfig.getApiKey(), headerValueCaptor.getValue());
Assertions.assertEquals(new ObjectMapper().writer().withDefaultPrettyPrinter().writeValueAsString(miscInfos), bodyCaptor.getValue());
assertThat(memoryAppender.search("Write to Advantage status: ", Level.DEBUG).size()).isEqualTo(1);
}
}
This error means that Spring is trying to autowire of bean of type BatchTrackingService in your AdvantageClientItemWriter but it could not find one in the application context. In other words, your test context does not contain a bean definition of type BatchTrackingService, which could be due to one of the following causes:
Either the configuration class that defines that bean is not imported in the test class (in #ContextConfiguration(classes = { AdvantageClientItemWriter.class })
or the class of that bean is not in the package that is scanned by Spring (Boot).
Make sure that:
the class public class BatchTrackingService {} is defined in the package referenced in #ComponentScan("com.test.miscinfo.service")
the #ContextConfiguration annotation imports the class of that bean (something like #ContextConfiguration(classes = { AdvantageClientItemWriter.class, BatchTrackingService.class }), or that it imports a configuration class which defines an instance of that bean.
I have a class which has following autowiring
public class XYZ {
#Autowired
private Principal principal;
public void main() {
AlexandriaDownloadSignatureUtilityV1 downloadSignatureUtilV1 =
new AlexandriaDownloadSignatureUtilityV1(
getMaterialsetNameProvider(principal),
);
}
}
I want to autowire AlexandriaDownloadSignatureUtilityV1 dependency, but since it is dependent on pricipal bean, can you please tell me how to do so?
#Component
public class XYZ {
#Autowired
private Principal principal;
public void main() {
AlexandriaDownloadSignatureUtilityV1 downloadSignatureUtilV1 =
new AlexandriaDownloadSignatureUtilityV1(
getMaterialsetNameProvider(principal),
);
}
}
Add #Component at the Top of the class this will create a bean of this object and inject their dependency also
I see a lot of posts surrounding this error message, but none with a solution that's worked for me.
I'm writing a Spring application that has required me to configure multiple data sources. I got all of those working, but now I need to import a module my teammates built that uses an additional data source and several repositories. They've been using this module in their code and it seems to be working for them, but when I try to run my code I get the following error (some details obscured for work reasons):
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.company.teammodule.repositories.StatsRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
//I can't paste code, so there may be typos...
Spring Application Class:
package com.company.mymodule;
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages={
"com.company.mymodule",
"com.company.mymodule.utils",
"com.company.mymodule.configuration",
"com.company.teammodule.repositories",
//I've been fiddling with packages here; I've added every package from the teammatemodule app and still get the error described})
#public class MyModuleApplication
{
public static void main(String[] args) { SpringApplication.run(MyModuleApplication.class, args); }
}
My class that uses the teammatemodule:
package com.company.mymodule.utils;
#Component
public class TeammateModuleUtil {
#Autowired
private TeammateModuleConfiguration config; //This class contains an object of another class which references the problem repository
#Value("${applicationName})
private final String applicationName;
public TeammateModuleUtil()
{
try {
config.setApplicationName(applicationName);
config.loadConfiguration();
}
catch (Exception e) {
//Error handling
}
}
}
The TeammateModuleConfiguration class:
package com.company.teammatemodule
#Component
public class TeammateModuleConfiguration extends ConfigurationBase {
#Autowired
TeammateModuleServiceData data; //This class contains a reference to the problem repository
#Autowired
Utilities util;
#Autowired
ConfigurationRepository configurationRepository;
public void loadConfiguration() throws Exception {
try {
this.alConfigure = this.configurationRepository.findByConfigureIdApplicationName(this.applicationName);
} catch (Exception e) {
//Error handling
}
}
}
Here's the class that has the problem reference:
package com.company.teammatemodule;
#Component
public class TeammateModuleServiceData {
#Autowired
StatsRepository statsRepository //This is the repo the code can't find a bean for
#Autowired
MessageAuthorizationRepository messageAuthorizationRepository;
#Autowired
LogMessagesRepository logMessagesRepository;
#Autowired
Utilities util;
//Class methods
}
And here's the repository class for good measure:
package com.company.teammatemodule.repositories;
#Repository
public interface StatsRepository extends JpaRepository<Stats, StatsId> {
#Procedure(
procedureName = "schema.log_service_stats",
outputParameterName = "o_stats_id"
)
String logServiceStats(#Param("i_request_payload") String var1, #Param("i_response_payload") String var2, #Param("i_operation_name") String var3, #Param("i_stats_id") String var4);
#Procedure(procedureName = "schema.update_service_stats)
void updateServiceStats(#Param("i_request_payload") String var1, #Param("i_response_payload") String var2, #Param("i_operation_name") String var3, #Param("i_stats_id) String var4);
}
The above repository is the one that can't be found when I try to launch the application. What I've checked and tried:
The main application has the #SpringApplication annotation, and all other involved classes have either #Component, #RestController, or #Repository, so I don't think it's an issue of some component not being labeled a bean...
None of the other repositories in teammatemodule are giving me a problem, but I'm guessing StatsRepository is just the first repository to be referenced
The only properties in my properties file are applicationName and data sources unrelated to the teammatemodule. I'm not listing any packages or exclusions there.
I've specified all packages in #ComponentScan ( basepackages = {//packages} ) as well as #SpringApplication( scanBasePackages = {//packages} )
I've tried excluding spring's autoconfigure by using the following annotations in my SpringAplication class:
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
#EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
So I don't think it's a missing #Component or other bean annotation, and to the best of my ability I've specified the package in which the repository bean is located and overridden Spring's built-in autoconfiguration tools... But the app still can't find the bean.
Thanks for your help!
It looks like this might just be a simple misspelling:
com.company.teammodule.repositories <- Component Scan
com.company.teammatemodule.repositories <- Package specified in Repository class
Edited to add proc structure:
main/java
hello(package)
Application(main app)
TestRestController
models(package)
Test
services(package)
TestRepo(interface)
I'm currently looking at component scan as just released the 'repo.Test' in the exception is a clue.
I've been through numerous tutorials and questions and still cannot find the answer to my particular issue, which is most likely to be down to my lack of understanding.
I have a spring boot application that I'm adding a db to. I've been following this tutorial : https://www.callicoder.com/spring-boot-rest-api-tutorial-with-mysql-jpa-hibernate/
However when I try and run my application (following identical steps) I get an exception:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testRestController': Unsatisfied dependency expressed through field 'testRepo'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'repo.TestRepo' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I do have another bean autowired in, the main difference is that this bean(the one that works) has a service that implements the interface and a bean configuration class. None of the examples for JPA follow that model and it seems daft to create a service to re-implement the methods from the JPARepo.
This is the controller I'm using:
#RestController
public class TestRestController {
#Autowired
GreetingService greetingService;
#Autowired
TestRepo testRepo;
#RequestMapping("/hello")
public String home() {
return greetingService.greet();
}
#RequestMapping("/testrepo")
public String testrepo() {
Test test = new Test("steve");
testRepo.save(test);
Long idOftest = test.getId();
test = null;
test = testRepo.findById(idOftest).get();
return "DID THIS WORK::::: + "+ test.toString();
}
with the interface being
#Repository
public interface TestRepo extends JpaRepository<Test, Long> {
}
and the model:
#Entity
#Data
public class Test {
private final String name;
public Test(String name){
this.name = name;
}
#Id
#GeneratedValue
private Long id;
public Long getId(){
return this.id;
}
}
The application main is:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
// #Bean
// public CommandLineRunner commandLineRunner(ApplicationContext ctx ) {
// return args -> {
//
// System.out.println("Let's inspect the beans provided by Spring Boot:");
//
// String[] beanNames = ctx.getBeanDefinitionNames();
// Arrays.sort(beanNames);
// for (String beanName : beanNames) {
// System.out.println(beanName);
// }
//
//
// };
// }
}
I recently commented out the bean annotation to see if that was causing an issue.
Thank you for any help in advance!
You are getting this exception because your class TestRepo is part of repo package, which is not the sub-package hierarchy of Application class package. #SpringBootApplication defines an automatic component scan on the packages which are subpackages of your main class i.e Application. If you want to resolve this issue without changing your package hierarchy add below line with #SpringBootApplication:
#ComponentScan({"repo","your.application.class.package"}) //// add the names of the packages where the controllers, services, repositories beans are stored
I do have ServiceImpl which looks like this:
#Service
#RequiredArgsConstructor
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
And I would like to inject a field value to fieldA in an Application.java from application.yml like this:
#EnableSwagger2
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
But I receive the following error when running SpringBoot app:
Error creating bean with name 'serviceAImpl' defined in URLNo qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Do you have any solution for that?
You annotated your class with #Service and defined it manually as a bean with the #Bean annotation. I do think the second is the way you planned to use it.
The #Service annotation will make this class get picked up by Spring's component scan and additionally create an instance of it.
Of course it tries to resolve the parameters and fails when it tries to find a matching "bean" for the String field because there is no simple String bean (and should not :) ).
Remove the #Service annotation and everything should work as expected.
Try this
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowire
public ServiceAImpl(#Value("${fieldA}") String fieldA){
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
and this
#EnableSwagger2
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You should not use #Service and #Bean for the same class!
Spring is not so smart :)
You should annotate your bean like:
#RequiredArgsConstructor
public class ServiceAImpl {
#Value("${fieldA}")
private final String something;
...
But I'm not sure it will work with the #RequiredFieldsConstructor, it would be simpler for you write down the constructor annotated with #Autowired and using the #Value annotation for the String parameter:
#Autowired
public ServiceAImpl(#Value("${aProp}") String string) {
You're using two bean declaration mechanisms:
You're registering your bean using #Service
You're registering a bean using #Bean
This means that your service will be created twice. The one defined using #Bean works properly, since it uses the #Value annotation to inject the proper value in your service.
However, the service created due to #Service doesn't know about the #Value annotation and will try to find any bean of type String, which it can't find, and thus it will throw the exception you're seeing.
Now, the solution is to pick either one of these. If you want to keep the #Bean configuration, you should remove the #Service annotation from ServiceAImpl and that will do the trick.
Alternatively, if you want to keep the #Service annotation, you should remove the #Bean declaration, and you should write your own constructor rather than relying on Lombok because this allows you to use the #Value annotation within the constructor:
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
/**
* This constructor works as well
*/
public ServiceAImpl(#Value("${fieldA}") String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
If you want to declare ServiceAImpl as a Spring bean in your Java Configuration file, you should remove the #Service annotation from the class declaration. These annotations doesn't work well together.
ServiceAImpl.java
import org.springframework.beans.factory.annotation.Autowired;
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowired
public ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
Application.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
}
Your application.properties
fieldA=value
The below implementation works well for me. You have two issues, first you have to choose between #Service and #Bean and the other issue I've seen in your code was the #Value annotation, you have to use only to inject a value from the properties.
#SpringBootApplication
public class TestedValueApplication {
#Autowired
void printServiceInstance(ServiceA service) {
System.out.println("Service instance: " + service);
System.out.println("value==value? " + service.isFieldA("value"));
}
public static void main(String[] args) {
SpringApplication.run(TestedValueApplication.class, args);
}
#Bean
public ServiceA serviceA(#Value("${fieldA}") String fieldA) {
return new ServiceAImpl(fieldA);
}
}
Service:
public class ServiceAImpl implements ServiceA {
private String fieldA;
ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
application.properties:
fieldA=value