have anyone a idea why the Service is asking for a bean ? I'm building a small microservice with a Article- and Authorservice. Where is the issue here ?
I'm using feign-reactor-spring-cloud-starter dependency for the communication between the services.
Service:
#Slf4j
#Service
#RequiredArgsConstructor
public class ArticleService{
private final IArticleRepository articleRepository;
private final IAuthorResponse authorResponse;
public Mono<ArticleRequest> createArticle(
ArticleRequest articleRequest,
String authorEmail
){
Mono<AuthorResponse> requestedAuthor = authorResponse
.getAuthorByEmail(authorEmail)
.switchIfEmpty(Mono.error(new BadRequestException(valueOf(AUTHOR_NOT_FOUND))))
.map(author -> new AuthorResponse(
author.firstName(),
author.lastName(),
author.email()))
.log();
Article articleModel = Article.builder()
.articleId(UUID.randomUUID())
.articleTitle(articleRequest.title())
.articleContent(articleRequest.content())
.publishedAt(IDateTimeCreator.createDateTime())
.authorEmail(requestedAuthor.map(AuthorResponse::email).block()) //TODO: implement current loggedIn AuthorEmail
.build();
return articleRepository
.existsArticleByAuthorEmailAndAndArticleTitle(
articleModel.getAuthorEmail(),
articleModel.getArticleTitle())
.flatMap(exists -> {
if (exists) {
return Mono.error(
new BadRequestException(valueOf(ARTICLE_ALREADY_EXISTS)));
} else {
return articleRepository
.save(articleModel)
.map(article -> new ArticleRequest(
article.getArticleTitle(),
article.getArticleContent()))
.log();
}
});
}
}
Interface:
#ReactiveFeignClient(name = "authorservice", configuration = ReactiveFeignConfig.class)
public interface IAuthorResponse {
#GetMapping(path = "/api/v1/author/{authorEmail}")
Mono<AuthorResponse> getAuthorByEmail(#PathVariable("authorEmail") String authorEmail);
}
Config
#Configuration
public class ReactiveFeignConfig {
#Bean
public ReactiveLoggerListener loggerListener(){
return new DefaultReactiveLogger(Clock.systemUTC(), LoggerFactory.getLogger("articleservice"));
}
// Here we specify our Exception between the articleservice and authorservice
#Bean
public ReactiveStatusHandler reactiveStatusHandler() {
return ReactiveStatusHandlers.throwOnStatus(
(status) -> (status == 500),
(methodKey, response) ->
new RetryableException(
response.status(),
"Failed. The ArticleService couldn't reach the Authorservice. Please try again!",
null,
Date.from(Instant.EPOCH),
null));
}
// In case of an error such as a simple timeout occurred, we've the opportunity with this methode to try again.
#Bean
public ReactiveRetryPolicy reactiveRetryPolicy() {
return BasicReactiveRetryPolicy.retryWithBackoff(3, 2500);
}
}
This is a part of my pom.xml:
<dependency>
<groupId>com.salenaluu</groupId>
<artifactId>clients</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
The Interface is in a seperate module with no main class.
Mainclass:
#EnableReactiveFeignClients(basePackages = "com.salenaluu.clients}")
#EnableEurekaClient
#SpringBootApplication
public class ArticleServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ArticleServiceApplication.class,args);
}
}
Message:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 1 of constructor in ...ArticleService required a bean of type '....IAuthorResponse' that could not be found.
Action:
Consider defining a bean of type '....IAuthorResponse' in your configuration.
Process finished with exit code 0
Related
I'm trying to get my microservice java spring boot to communicate with another microservice using Feign but I'm getting this message when starting the application:
APPLICATION FAILED TO START
***************************
Description: Parameter 0 of constructor in ProductService required a bean of type ProductClientRepository' that could not be found.
Action: Consider defining a bean of type 'ProductClientRepository' in your configuration.
I don't know what could be wrong, I already checked if all the declared variables are in the project's properties and I already checked the imports, I don't know why it is saying that something is missing in the bean.
pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
SaleService:
#Service
#RequiredArgsConstructor
public class SaleService {
private final ProductService productService;
#Transactional
public Sale createSale(Sale sale) {
Set<Long> codesFromRequest = sale.getProducts().stream().map(p -> p.getCode())
.collect(Collectors.toSet());
validateProduct(codesFromRequest);
return saleRepository.save(sale);
}
public void validateProduct(Set<Long> codesFromRequest) {
List<SaleProductDTO> products = productService.findProduct(codesFromRequest);
Set<Long> returnedCodes = products.stream().map(p -> p.getCode()).collect(Collectors.toSet());
throwExceptionIf(validateSizeList(codesFromRequest, returnedCodes),
new ProductNotFoundException());
}
public boolean validateSizeList(Collection<?> codesFromRequest, Collection<?> returnedCodes) {
return codesFromRequest.size() != returnedCodes.size();
}
}
ProductService:
#Service
#Slf4j
#AllArgsConstructor
public class ProductService {
private final ProductClientRepository productRepository;
#Retryable(value = { Exception.class }, maxAttempts = 3, backoff = #Backoff(delay = 50))
public List<SaleProductDTO> findProduct(Set<Long> codes) {
Page<SaleProductDTO> resultPage;
try {
var search = SearchProductDTO
.builder()
.codes(codes)
.build();
resultPage = productRepository.getProducts(search);
} catch (FeignException f) {
return Collections.emptyList();
}
return resultPage.getContent();
}
}
ProductClientRepository:
#FeignClient(name = "product-service", url = "${ms-product.url}", configuration = ProductOAuth2FeignConfig.class)
public interface ProductClientRepository {
#GetMapping(value = "/chunk")
Page<SaleProductDTO> getProducts(#RequestBody SearchProductDTO searchDTO);
}
ProductOAuth2FeignConfig:
public class ProductOAuth2FeignConfig {
#Autowired
private ProductConfig productConfig;
#Bean
public RequestInterceptor stockOAuth2RequestInterceptor() {
return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), resource());
}
private OAuth2ProtectedResourceDetails resource() {
ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails();
resource.setAccessTokenUri(productConfig.getTokenUri());
resource.setClientId(productConfig.getTokenClientId());
resource.setClientSecret(productConfig.getTokenClientSecret());
resource.setGrantType(productConfig.getTokenGrantType());
resource.setScope(List.of(productConfig.getTokenScope()));
return resource;
}
}
ProductConfig:
#Data
#Configuration
#ConfigurationProperties(prefix = "ms-product")
public class ProductConfig {
private String tokenUri;
private String tokenGrantType;
private String tokenClientId;
private String tokenClientSecret;
private String tokenScope;
}
application.properties:
external.api=https://myadress.com/api
ms-product.url=${external.api}/products
ms-product.token-uri=${external.api}/products/oauth2/token
ms-product.token-grant-type=client_credentials
ms-product.token-client-id=client-id-value
ms-product.token-client-secret=secret-value
ms-product.token-scope=read
feign.client.config.default.connect-timeout=3000
feign.client.config.default.read-timeout=7000
Seems you need to add #EnableFeignClients annotation. Please refer Spring Boot OpenFeign
Attempting to create a class that will be used by JavaConfig as a source of bean definitions.
#Configuration
public class MyClass {
#Bean
#ConditionalOnProperty(name = "property")
public A fuction1() {
doSomething1(); // may return an exception
}
#Bean
#ConditionalOnMissingBean(name = "fuction1")
public A fuction2() {
doSomething2();
}
#Bean
public B fuction3(A a) {
doSomething3(a);
}
}
The third bean definition has the error "could not autowire. There is more than one bean of A type." How do I tell Spring to try to autowire the first A and if it is missing then to try the second A, i.e. following the conditional process described. Hopefully that makes sense.
Thanks!
You are defining two bean with same name hence, autowiring issue is obvious. Try using #Primary on the bean you want to give priority.
How about this approach?
#Configuration
public class MyClass {
#Bean
#ConditionalOnProperty(name = "property", havingValue="true")
public A fuction1() {
doSomething1(); // may return an exception
}
#Bean
#ConditionalOnProperty(name = "property", havingValue="false")
public A fuction2() {
doSomething2();
}
#Bean
public B fuction3(A a) {
doSomething3(a);
}
}
Maybe this config will be helpful:
#Configuration
public class MyClass {
#Bean
#ConditionalOnProperty(name = "property")
public A fuction1() {
try {
doSomething1(); // may return an exception
} catch(Exception e) {
return null;
}
}
#Bean
#ConditionalOnMissingBean(name = "fuction1")
public A fuction2() {
doSomething2();
}
#Bean
public B fuction3(#Autowired(required = false) #Qualifier("function1") A a1, #Qualifier("function2") A a2) {
if (a1 == null) {
doSomething3(a2);
} else {
doSomething3(a1);
}
}
}
The sample code you've provided is conceptually correct. Of course there are compilation errors but annotations are correct.
Here is a sample that compiles and works.
#Configuration
public class ConditionalConfiguration {
public static class A {
private final String name;
public A(String name) {
this.name = name;
}
}
public static class B {
private final A a;
public B(A a) {
this.a = a;
}
}
#Bean
#ConditionalOnProperty(name = "a1-property")
public A a1() {
return new A("a1");
}
#Bean
#ConditionalOnMissingBean(A.class)
public A a2() {
return new A("a2");
}
#Bean
public B b(A a) {
System.out.println("########## " + a.name);
return new B(a);
}
}
When the property a1-property is passed to the application
./gradlew clean bootRun -i -Da1-property
########## a1
the bean a1 is created.
When the property is missing
./gradlew clean bootRun -i
########## a2
The bean a2 is created.
Make sure to configure Gradle to pass system properties to the bootRun task by adding in the build.gradle
bootRun {
systemProperties System.properties
}
To be decoupled from a1 bean name, condition on bean type is used: #ConditionalOnMissingBean(A.class) instead of #ConditionalOnMissingBean(name = "a1").
If you expect an exception during bean creation it is another case.
When factory method annotated with #Bean throws an exception, Spring application crashes with BeanInstantiationException.
So, exceptions should be handled in the factory method:
#Bean
#ConditionalOnProperty(name = "a1-property")
public A a1() {
try {
//return new A("a1");
throw new RuntimeException("Sample exception");
} catch (Exception e) {
return fallbackA();
}
}
#Bean
#ConditionalOnMissingBean(A.class)
public A a2() {
return fallbackA();
}
private A fallbackA() {
return new A("a2");
}
I have created one scheduler class
public class TestSchedulderNew {
#Scheduled(fixedDelay = 3000)
public void fixedRateJob1() {
System.out.println("Job 1 running");
}
#Scheduled(fixedDelay = 3000)
public void fixedRateJob2() {
System.out.println("Job 2 running");
}
}
In configuration i have put #ConditionalOnProperty annotation to enable this on conditional purpose.
#Bean
#ConditionalOnProperty(value = "jobs.enabled")
public TestSchedulderNew testSchedulderNew() {
return new TestSchedulderNew();
}
Now in controller, i have created "stopScheduler" method to stop those scheduler , in this controller i have autowired
TestSchedulderNew class
#RestController
#RequestMapping("/api")
public class TestCont {
private static final String SCHEDULED_TASKS = "testSchedulderNew";
#Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor; /]
#Autowired
private TestSchedulderNew testSchedulderNew;
#GetMapping(value = "/stopScheduler")
public String stopSchedule(){
postProcessor.postProcessBeforeDestruction(testSchedulderNew,
SCHEDULED_TASKS);
return "OK";
}
}
Now the problem is if conditional property is false then i get below exception
Field testSchedulderNew in com.sbill.app.web.rest.TestCont required a bean of type 'com.sbill.app.schedulerJob.TestSchedulderNew
In case of true everything works fine,
Do we have any option to solve this ?
You can use #Autowired(required=false) and null check in stopScheduler method.
#Autowired(required=false)
private TestSchedulderNew testSchedulderNew;
#GetMapping(value = "/stopScheduler")
public String stopSchedule() {
if (testSchedulderNew != null) {
postProcessor.postProcessBeforeDestruction(testSchedulderNew,
SCHEDULED_TASKS);
return "OK";
}
return "NOT_OK";
}
my service:
#Service
public class ForgetService{
#Async
public CompletableFuture<Object> getTokenService() {
Map<String, Object> map = new HashMap<>(8);
map.put("status", ErrorEnum.TOKEN_SUSSCESS.getStatus());
map.put("message", ErrorEnum.TOKEN_SUSSCESS.getMessage());
return CompletableFuture.completedFuture(map);
}
}
my controller:
#RestController
public class ForgetController {
private final ForgetService forgetService;
#Autowired
private ForgetController(ForgetService forgetService) {
this.forgetService = forgetService;
}
#PostMapping(value = "/forget/token")
#Async
public CompletableFuture<Object> getTokenController() {
return CompletableFuture.completedFuture(forgetService.getTokenService());
}
}
spring boot application class:
#SpringBootApplication
#EnableAsync
public class ApitestApplication {
public static void main(String[] args) {
SpringApplication.run(ApitestApplication.class, args);
}
}
when i run my application, console log :
The bean 'forgetService' could not be injected as a 'com.apitest.service.ForgetService' because it is a JDK dynamic proxy that implements:
com.apitest.inf.ForgetServiceInf
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on #EnableAsync and/or #EnableCaching.
i tried setting 'spring.aop.proxy-target-class=true' in application.properties and setting '#EnableAsync(proxyTargetClass=true), but it's useless,so what's wrong? how to resolve it?
please use below approach, it might help you to fix this issue.
#Service
public class ForgetService{
#Bean("GetTokenService")
public CompletableFuture<Object> getTokenService() {
Map<String, Object> map = new HashMap<>(8);
map.put("status", ErrorEnum.TOKEN_SUSSCESS.getStatus());
map.put("message", ErrorEnum.TOKEN_SUSSCESS.getMessage());
return CompletableFuture.completedFuture(map);
}
#RestController
public class ForgetController {
private final ForgetService forgetService;
#Autowired
private ForgetController(ForgetService forgetService) {
this.forgetService = forgetService;
}
#PostMapping(value = "/forget/token")
#Async("GetTokenService")
public CompletableFuture<Object> getTokenController() {
return CompletableFuture.completedFuture(forgetService.getTokenService());
}
}
I have Spring service, which is actually actor, it is received info, but I cant pass it to another Spring service, because injection fails.
#Service("mailContainer")
#Scope("prototype")
#Component
public class MailContainer extends UntypedActor {
private final LoggingAdapter LOG = Logging.getLogger(getContext().system(), this);
private Mail value;
private List<Mail> mailList = new ArrayList<Mail>();
private Integer size;
#Autowired
#Qualifier("springService")
private SpringService springService;
//#Autowired
public void setSpringService(SpringService springService) {
this.springService = springService;
}
public MailContainer(Mail value) {
this.value = value;
}
#Override
public void onReceive(Object message) throws Exception {
// LOG.debug("+ MailContainer message: {} ", message);
if (message instanceof Mail) {
value = (Mail) message;
System.out.println("MailContainer get message with id " + value.getId());
System.out.println("With time " + value.getDateSend());
//getSender().tell(value, getSelf()); //heta uxarkum
//this.saveIt(value);
springService.add(value);
}
}
and second service
#Service("springService")
//#Component
#Scope("session")
public class SpringService {
private List<Mail> mailList = new ArrayList<Mail>();
public void add(Mail mail) {
System.out.println("Saving mail from Spring " +mail.getId());
mailList.add(mail);
}
public List<Mail> getMailList() {
return mailList;
}
}
Spring config, this is from akka spring example
#Configuration
//#EnableScheduling
//EnableAsync
#ComponentScan(basePackages = {"com"}, excludeFilters = {
#ComponentScan.Filter(Configuration.class)})
//#ImportResource("classpath:META-INF/spring/spring-data-context.xml")
//#EnableTransactionManagement
//#EnableMBeanExport
//#EnableWebMvc
public class CommonCoreConfig {
// the application context is needed to initialize the Akka Spring Extension
#Autowired
private ApplicationContext applicationContext;
/**
* Actor system singleton for this application.
*/
#Bean
public ActorSystem actorSystem() {
ActorSystem system = ActorSystem.create("AkkaJavaSpring");
// initialize the application context in the Akka Spring Extension
SpringExtProvider.get(system).initialize(applicationContext);
return system;
}
}
So, how I can inject just another Spring service?????????
Based on our discussions, I think it is due to the way you create the MailContainer actor. You aren't using the SpringExtProvider and instead are using Props.create directly. This means that Spring doesn't get the opportunity to perform dependency injection on your new actor.
Try changing this code:
#Override
public void preStart() throws Exception {
System.out.println("Mail collector preStart: {} ");
getContext().actorOf(Props.create(MailContainer.class, result), "one");
}
to use the the SpringExtProvider like this:
#Override
public void preStart() throws Exception {
System.out.println("Mail collector preStart: {} ");
getContext().actorOf(SpringExtProvider.get(getContext().system()).props("mailContainer"), "one");
}
This way you are asking the Spring extension to create the new actor and inject any required dependecnies.