RefreshScope is not refreshing HessianProxyFactoryBean - java

My configuration class where I initialize the hessian bean
#RefreshScope
#Configuration // #Component also not working
public class HessianConfiguration {
#Value("${sample.hessian.url}")
private String sampleUrl;
#Bean
public HessianProxyFactoryBean initSampleBean() {
HessianProxyFactoryBean invoker = new HessianProxyFactoryBean();
invoker.setServiceUrl(sampleUrl);
invoker.setServiceInterface(Sample.class);
return invoker;
}
}
And a sample component class where I use the bean.
#RefreshScope
#Component
public class SampleService {
#Autowired
Sample sample;
public String doRemoteOperation(String value){
return sample.doRemoteOperation(value);
}
}
sample.hessian.url is retrieved from spring config server.
But after changing the value of sample.hessian.url and calling "refresh" endpoint, the autowired sample bean is still trying to hit the old url value.
What am I doing wrong here ?

Related

How to handle an error when a bean does not exist and could not inject in Spring-boot?

I have a standard rest-api spring-boot application.
Controller with injected service
#RestController
#RequestMapping("/foo")
#AllArgsConstructor
public class SomeController {
private final SomeService someService;
public void someMethod(){
someService.toDoSomething();
}
}
and service with injected other beans
#Setter
public class SomeService {
private AnotherVeryImportantBean anotherVeryImportantBean;
private RestTemplateBuilder restTemplate;
public void toDoSomething() {
anotherVeryImportantBean.someAction();
}
In my case, the bean AnotherVeryImportantBean is created in another dependency, which I connect to the spring-boot application. Whether to create a bean or not is decided by a variable in the application.yml file.
like this:
another.service.enabled: true
Of course I have config class for service
#Configuration
#RequiredArgsConstructor
public class SomeConfig {
private final AnotherVeryImportantBean anotherVeryImportantBean;
#Bean
public SomeService someService(RestTemplateBuilder restTemplate) {
SomeService foo = new SomeService();
foo.setAnotherVeryImportantBean(anotherVeryImportantBean);
foo.setRestTemplate(restTemplate);
return foo;
}
The problem is that this controller and service are not the only ones in the application. I would like the application not to crash completely if this particular controller and service are not formed. If the bean is not created for some reason, I just don't use that functionality (this controller).
At this point, the application crashes because AnotherService cannot be injected into someService (In case where, for some reason, it was not created).
I tried adding an annotation to config class
#ConditionalOnBean(AnotherVeryImportantBean.class)
like this:
#ConditionalOnBean(AnotherVeryImportantBean.class)
#Configuration
#RequiredArgsConstructor
public class SomeConfig {
private AnotherVeryImportantBean anotherVeryImportantBean;
#Bean
public SomeService someService(RestTemplateBuilder restTemplate) {
SomeService foo = new SomeService();
foo.setAnotherVeryImportantBean(anotherVeryImportantBean);
foo.setRestTemplate(restTemplate);
return foo;
}
But the problem is that conditional in SomeConfig checks if the bean is in the container before it is created.
How can I handle an error when a bean cannot inject another dependency into itself?

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

Refreshing bean when underlying dependent bean gets updated in Spring boot application

I have a config as follows:
#Configuration
public class REConfiguration {
private final DBRuleLoader dbRuleLoader;
public REConfiguration(
DBRuleLoader dbRuleLoader) {
this.dbRuleLoader = dbRuleLoader;
}
#RefreshScope
#Bean
public REMgr ruleEngine() {
return REmgrFactory.getREMgr(this.dbRuleLoader);
}
}
Actually, I have added #RefreshScope here, as I want on any update of underlying DBRuleLoader bean, REMgr gets refreshed. I am not sure if it actually works.

Spring Boot application.properties custom variable in a non-controller class

How come application.properties will work in a RestController, but not in a service class?
//application.properties
test=test
Works Perfect!
#RestController
public class invitecontroller {
#Autowired inviteconfig inviteconfig;
#PostMapping("/v1/invite")
public void invite(#RequestBody XXX XXX) {
System.out.println(inviteconfig);
}
}
Returns "Null"
#Service
public class inviteservice {
#Autowired inviteconfig inviteconfig;
public void invite() {
System.out.println(inviteconfig);
}
}
#Configuration
#Data
public class inviteconfig {
private String test;
}
The inviteservice class is not configured for Spring IoC (Inversion of Control) as a bean, so Spring will not handle the inviteservice class lifecycle. In this case, #Autowired is useless.
To fix this try to add #Component annotation to invitesevice, to declare it as a component:
#Component
public class inviteservice {
#Autowired inviteconfig inviteconfig;
public void invite() {
System.out.println(inviteconfig);
}
}
In the case of the controller, with #RestController, Spring will recognize your class as a Spring component.
Finally, don't forget to inject inviteservice using Spring IoC (using #Autowired annotation, or other means)
inviteservice class should be annotated with #Component or #Service
#Component
public class inviteservice {
...

Spring Boot Autowired null

I have several classes in a Spring Boot project, some work with #Autowired, some do not. Here my code follows:
Application.java (#Autowired works):
package com.example.myproject;
#ComponentScan(basePackages = {"com.example.myproject"})
#Configuration
#EnableAutoConfiguration
#EnableJpaRepositories(basePackages = "com.example.myproject.repository")
#PropertySource({"classpath:db.properties", "classpath:soap.properties"})
public class Application {
#Autowired
private Environment environment;
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public SOAPConfiguration soapConfiguration() {
SOAPConfiguration SOAPConfiguration = new SOAPConfiguration();
SOAPConfiguration.setUsername(environment.getProperty("SOAP.username"));
SOAPConfiguration.setPassword(environment.getProperty("SOAP.password"));
SOAPConfiguration.setUrl(environment.getProperty("SOAP.root"));
return SOAPConfiguration;
}
HomeController (#Autowired works):
package com.example.myproject.controller;
#Controller
class HomeController {
#Resource
MyRepository myRepository;
MyService (#Autowired does not work):
package com.example.myproject.service;
#Service
public class MyServiceImpl implements MyService {
#Autowired
public SOAPConfiguration soapConfiguration; // is null
private void init() {
log = LogFactory.getLog(MyServiceImpl.class);
log.info("starting init, soapConfiguration: " + soapConfiguration);
url = soapConfiguration.getUrl(); // booom -> NullPointerException
I do not get the SOAPConfiguration but my application breaks with a null pointer exception when I try to access it.
I have already read many Threads here and googled around, but did not find a solution yet. I tried to deliver all necessary information, please let me know if anything misses.
I guess you call init() before the autowiring takes place. Annotate init() with #PostConstruct to make it call automatically after all the spring autowiring.
EDIT: after seeing your comment, I guess you are creating it using new MyServiceImpl(). This takes away the control of the MyServiceImpl from Spring and gives it to you. Autowiring won't work in those case
Did you created a bean for the class SOAPConfiguration in any of your configuration classes? If you want to autowire a class in your project, you need to create a bean for it. For example,
#Configuration
public class SomeConfiguration{
#Bean
public SOAPConfiguration createSOAPConfiguration(){
return new SOAPConfiguration();
}
}
public class SomeOtherClass{
#Autowired
private SOAPConfiguration soapConfiguration;
}

Categories