#RefreshScope can't get along with #Bean - java

I have a controller which need refresh config from config server so I add #RefreshScope on it. Meanwhile this controller need to calling a backend API so that I defined the restTemplate Bean. But once I start this application, exception occur. Can anyone tell me why these two annotation make circulate reference?
Error: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'scopedTarget.frontEndApplication':
Unsatisfied dependency expressed through field 'restTemplate'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'restTemplate': Requested bean is currently in creation: Is there an unresolvable circular reference?
#SpringBootApplication
#RestController
#EnableDiscoveryClient
#RefreshScope
public class FrontEndApplication {
#Value("${msg:Hello default}")
private String message;
public static void main(String[] args) {
SpringApplication.run(FrontEndApplication.class, args);
}
#Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
#Autowired
RestTemplate restTemplate;
}

First, don't put #RefreshScope on the controller. Typically you'd want to do it in a class that stores state. If it's a configuration property it's better to use a #ConfigurationProperty annotation on a POJO and call #EnableConfigurationProperties.
Also your main class does everything, can you please just separate it into separate classes and try again? It's not a good idea to have your main class also be a controller, repository and a service at the same time.

Related

Create a bean from PathVariable in Spring

In my controller, i get a Pathvariable called "realm".
I want to use this variable in service layer. I don't want to set realm in every method.
This is my controller:
#RestController
#RequestMapping("/user-managment/{realm}/")
#PreAuthorize("#someBean.test(#requestHelper.getPathVariableByName('realm'))")
public class KeycloackUserController {
#Autowired
private KeyCloakAdminAPIService keyCloakAdminAPIService;
As you can see, i get a "realm" in request. I tried to initialize a new bean with that variable to use it in service but it didn't work.
This is my service:
#Service
public class KeyCloakAdminAPIService {
#Autowired
#Value("${someBean.test}")
private String realm;
I get an error:
Error creating bean with name 'keycloackUserController': Unsatisfied dependency expressed through field 'keyCloakAdminAPIService';
Do you have any advice for this problem?

How to initialize bean method defined in Main class into test context

I have created a bean method in the main class:
#SpringBootApplication
#EnableScheduling
public class SpringApplication{
#Bean
Public String getCronValue(ServiceImpl service){
return service.getConfig().get("cron duration");
}
}
using this bean in a scheduled task:
#Component
public Class MySch{
#Scheduled(cron="#{getCronValue}")
public void schedulerMethod(){
//Do something
}
}
Now the problem is when I try to run JUnit tests #Bean GetCronValue is not initialized in test context and #Scheduled annotation throws an exception:
Update:-
It throws an exception:-
BeanCreationException: Error creating bean with name
'SchedulerMethod' : Initialization of bean failed; nested
exception is ' org. springframework.beans.
factory.Beanexpressio exception: Expression parsing
failed; nested exception is org. springframework.
expression.spel.SpelEvaluationException: EL1021E: A
problem occurred whilst attempting to access the
property ' getCronValue' : Error creating bean with name
'getCronValue' : Unsetisfied dependency expressed
through method 'getCronValue' parameter 0; nested
exception is org. springframework. beans. factory.
NoSuchBeanDefinitionException: No qualifying bean of
type 'com.pkg.service.ServiceImpl' available: expected at
least 1 bean which qualifies as a autowire candidate.
Dependemcy annotations: {}'
My Controller test class looks like:-
#Transactional
public class ControllerTest{
#MockBean
private Service service;
.
.
// test cases
}
How to resolve this issue.
I assume that you're using #SpringBootTest annotation.
When you test a Controller you may want narrow the tests to only the web layer by using #WebMvcTest. Any other dependencies required by the controller will be then mocked using #MockBean.
When #WebMvcTest is used Spring Boot instantiates only the web layer rather than the whole context. In an application with multiple controllers, you can even ask for only one to be instantiated for example.
#WebMvcTest(controllers =Controller.class)
public class ControllerTest{
#MockBean
private Service service;
#Autowired
private MockMvc mockMvc;
// test cases
}
I noticed that you have the #Transactional annotation in your example. This can indicate that you maybe giving too match responsibilities to your controller and may consider passing Database access related logic to a service/repostory/DAO
See https://spring.io/guides/gs/testing-web/

Scope_prototyped bean is instantiated at startup and fails because it can't autowire

I want to create a prototyped bean with some custom parameter using an ObjectFactory. At the place where I need my bean I have the following:
private final ObjectProvider<Installation> installationProvider;
public void test() {
Installation installation = installationProvider.getObject("url");
}
and I have it configured like that:
#Configuration
public class MyConfiguration {
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public Installation createInstallation(String url) {
return new Installation(url);
}
}
But when I start my application I get the following exception:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method createInstallation in com.mycompany.MyConfiguration required a bean of type 'java.lang.String' that could not be found.
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.
Given that this is occurring at startup it seems like Spring tries to autowire this bean at startup. However, my understand is that a a bean of scope SCOPE_PROTOTYPE should not be autowire at runtime, as I want to create the beans during runtime with different parameters.
I found the problem. I had a separate service which I created for testing a while ago (and forgot about):
#Service
public class TestSomething {
private final Installation installation;
#Autowired
public TestSomething(Installation installation) {
this.installation = installation;
}
}
As this service is wired at startup, obviously it will also try to autowire the dependen bean.
I've setup a sample project like yours and it works as expected. Every call to installationProvier.getObject("url") return a new instance of the installtaion bean.
Are you sure, that your application don't access the bean via #Autowire in another class?

two conversionService bean created

I am getting an error while trying to up my application
Description:
Field conversionService in com.profectus.dashboard.service.impl.DashBoardSettingsServiceimpl required a single bean, but 2 were found:
- mvcConversionService: defined by method 'mvcConversionService' in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]
- defaultConversionService: defined by method 'defaultConversionService' in class path resource [org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.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
Why 2 beans are created and how to keep only one converter, I just want only spring core converter who can convert entity to pojo or pojo to entity.
I am stuck because of this issue, any lead would be helpful.
Service class code:-
import org.springframework.core.convert.ConversionService;
//other imports
#Service
public class DashBoardSettingsServiceimpl implements DashBoardSettingsService {
#Autowired
private DashBoardSettingJpaRepository dashBoardSettingRepo;
#Autowired
private ConversionService conversionService;
#Override
public DashBoardSettingResponse save(UserInfo userInfo, DashBoardSettingRequest request) {
//other coded
DashBoardSettigEntity entity = conversionService.convert(request.getDashBoardSetting(),
DashBoardSettigEntity.class);
DashBoardSettigEntity entityRetrieve = dashBoardSettingRepo.save(entity);
DashBoardSetting setting = conversionService.convert(entityRetrieve, DashBoardSetting.class);
DashBoardSettingResponse response = new DashBoardSettingResponse();
response.addDashBoardSetting(setting);
return response;
}
}
Autowire type DefaultConversionService instead of ConversionService
Seems like it has something to do with spring data rest having its own ConversionService instance.
Can you try this:
#Autowired #Qualifier("mvcConversionService") ConversionService conversionService;
It worked for me.
Don't forget to add your converters to a WebMvcConfigurer implementation.

Spring Autowire with Bean creation in same class results in :Requested bean is currently in creation Error*

I know the error is self explanatory, but when I remove the setting of rest template from constructor to #Autowired #Qualifier("myRestTemplate") private RestTemplate restTemplate, it works.
Just want to know how can I do that in constructor if the same class has bean definition of what I am trying to autowire?
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'xxx': Requested bean is currently in
creation: Is there an unresolvable circular reference?
#Component
public class xxx {
private RestTemplate restTemplate;
#Autowired
public xxx(#Qualifier("myRestTemplate") RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Bean(name="myRestTemplate")
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
#Bean methods in a regular #Component annotated class are processed in what is known as lite-mode.
I don't know why you'd want to do this. If your xxx class controls the instantiation of the RestTemplate, there's not much reason not to do it yourself in the constructor (unless you mean to expose it to the rest of the context, but then there are better solutions).
In any case, for Spring to invoke your getRestTemplate factory method, it needs an instance of xxx. To create an instance of xxx, it needs to invoke its constructor which expects a RestTemplate, but your RestTemplate is currently in construction.
You can avoid this error by making getRestTemplate static.
#Bean(name="myRestTemplate")
public static RestTemplate getRestTemplate() {
return new RestTemplate();
}
In this case, Spring doesn't need an xxx instance to invoke the getRestTemplate factory method.

Categories

Resources