The new #RefreshScope in Spring Cloud is great. But a side effect is that beans become lazily loaded. Most of the time this is a non-issue, but eagerly loaded beans allow for DI, property value setting, etc. to be assured at startup time rather than later at runtime. Is there any way to employ #RefreshScope AND cause the affected beans to be eagerly loaded?
I solved this "problem" by implementing a listener that catches the Refresh event.
Then I iterate over every bean in the context and call the getClass() method. (Any other method call on the bean will trigger its instantiation - getClass() is convenient since it exists for all beans.)
#Service
public class RefreshListener {
private final Logger logger = LoggerFactory.getLogger(RefreshListener.class);
#Autowired
ApplicationContext applicationContext;
#EventListener
public void onRefreshScopeRefreshed(final RefreshScopeRefreshedEvent event) {
logger.info("Received Refresh event. Refreshing all beans...");
for (String beanName : applicationContext.getBeanDefinitionNames()) {
logger.info("Refreshing bean " + beanName);
applicationContext.getBean(beanName).getClass();
}
}
}
Hope it helps.
Related
I once read that for setter injection, the dependencies are not injected until they are needed. However, when I run a little test on that, I see that in using setter injection, dependencies are injected at application startup time. Actually, when is setter injection being called in the Spring bean life cycle ? and what does it mean by "dependencies are not injected until they are needed" ?
#Service
public class MainService {
private DependencyService dependencyService;
#Autowired
public void setDependencyService(DependencyService dependencyService) {
this.dependencyService = dependencyService;
}
#PostConstruct
public void afterConstruct() {
System.out.println("Created MainService bean");
if (service != null) {
System.out.println("DependencyService is injected");
}
}
}
#Service
public class DependencyService {
#PostConstruct
public void afterConstruct() {
System.out.println("created DependencyService bean");
}
}
On application startup, the console result:
Created DependencyService bean
Created MainService bean
DependencyService is injected
Dependencies are always injected right after or during the bean is instantiated no matter you use field injection , setter injection or constructor injection .
So it depends on when the bean is initialised. By default all beans will be initialised eagerly at startup which means that their dependencies are also injected at startup
It is generally a desirable behaviour as it allows you to discover error due to bean configuration at startup rather than several hours or even days later.
You can change a bean to be lazy initialised until they are needed by annotating it as #Lazy. So if you want MainService to be lazy initialised until it is accessed (i.e. its setter injection does not happen at start up) , you have to :
#Service
#Lazy
public class MainService {
}
I want to find all beans that are not injected into other beans, thus I can remove them to make spring start up faster. Any ideas? Thanks in advance.
From ConfigurableBeanFactory#getDependentBeans Javadoc, I see that there's a method we can invoke to get an array of beans that depends on the bean name we provide. Tracing backwards to how we can get the bean factory. You could probably do the following if you can get a hold of the GenericApplicationContext:
Get the bean factory from the context.
Iterate through the bean definition names in the bean factory.
Call ConfigurableBeanFactory::getDependentBeans to see if anything depends on it.
#Component
public class Example {
#EventListener
public void contextRefreshed(ContextRefreshedEvent event) {
// Could also just autowire the context directly
GenericApplicationContext context = (GenericApplicationContext) event.getApplicationContext();
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] beanNames = beanFactory.getBeanDefinitionNames();
for(String beanName : beanNames) {
String[] dependentBeanNames = beanFactory.getDependentBeans(beanName);
if (dependentBeanNames.length <= 0) {
// bean with nothing depending on it
}
}
}
}
Edit:
This solution isn't perfect, but would probably be useful as a starting point. There are beans that could have nothing depend on it, but are used in the application. A good example would be your controllers (classes annotated with #Controller). From what I tested out, it had 0 dependent beans but the request mapping methods it holds is clearly being executed and referenced somehow.
I have a Spring service with #PreDestroy method which calls another service
#Service
public class DestroyMe {
public final ValidService service;
private SomeDto dto;
#PreDestroy
public void destroy() {
service.stop(dto);
}
}
The ValidService its an interface with #Validated class level annotation
#Validated
public interface ValidService {
public void stop(#Valid SomeDto dto);
}
Also there's implementation class of this interface (with #Service annotation).
Validation works correctly in runtime, but when SprinBoot application terminates, it calls the destroy method which calls the service.stop(dto) and I get this error:
Error creating bean with name 'defaultValidator': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!).
Clicking on defaultValidator in IDE brings me into public static LocalValidatorFactoryBean defaultValidator() of ValidationAutoConfiguration class.
If I remove the #Validated annotation from the ValidatedService all works correctly but there's no service's input validation.
Looks like Spring is trying to create the validator bean ad-hoc to validate the input to stop method but it's not possible to do this in destroy phase.
How to solve this problem?
EDIT 1
Here is the code to reproduce this behavior: https://github.com/smacz/predestroy
A fix would be to replace the #PreDestroy annotation with #EventListener(ContextClosedEvent.class), if this makes sense in the context of your application.
Background
Beans are destroyed one by one. So when your Bean DestroyMe is being destroyed, it might happen, that the Validator-bean has already been destroyed. But a Validator bean is required, as the ValidService is annotated with #Validated. So Spring tries to load that bean from the application context. As the bean is not there, it tries to create a new instance for that bean, but this is not allowed while the application context is being shut down, as the exception states.
By switching from a pre-destroy method to an event listener which listens for the context closed event, one can run code, before any beans are destroyed.
I have a spring application. I am autowiring classes and they are working fine.
For e.g
#Controller
public class SearchController {
#Autowired
private EnvironmentControl envControl;
#Autowired
private SearchControl searchControl;
...
But now i have on server startup class called ScheduleServlet which uses init method to schedule something...
public class SchedulerServlet extends HttpServlet {
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.LOGGER.info("timer servlet is initialized ");
try {
InitialContext ic = new InitialContext();
TimerManager tm = (TimerManager) ic.lookup("java:comp/env/tm/TimerManager");
Timer timer = tm.schedule(new GlobalTemplateScheduler(), 0, 3600000);// one hour interval
System.out.println("Timer..... " + timer);
}
...
In this my GlobalTemplateScheduler class has timerExpired method which is scheduled to execute after every one hour interval.
public class GlobalTemplateScheduler implements TimerListener {
#Autowired
private TemplateControl templateControl;
#Override
public void timerExpired(Timer timer) {
try {
templateControl.updateMappings(names);
} catch (Exception e) {
this.LOGGER.error(e.getMessage());
e.printStackTrace();
}
...
So i have to autowire templateControl which i am getting null. This should happen on server startup.
Further inside updateMappings there's a datasource object which is also autowired as constructor-arg(This is working fine on browser request but need to do it on server startup).
Note: I cannot use the ApplicationListener interface.
Any suggestions would really help.
Thankyou.
On application startup beans initialization would not be completed, beans can be used after the application context refresh or after the intialization of the bean, it will make no sense to execute a logic which requires the bean on the startup unless you detect whether the bean is ready or not.
You can execute some logic using #PostConstruct in the bean which will be executed after the initialization of the bean so you can manipulate your logic in a way to do so after the intialization of the bean or you could detect and execute logic after the ContextRefreshedEvent by impelementing applicationListener and put your logic in onAppplicationEvent method.
One solution would be to use Spring's Container within your servlet. There are many implementations for this purpose, for instance the AnnotationConfigApplicationContext. Spring's documentation describes how to use the ClassPathXmlApplicationContext
Suppose GlobalTemplateScheduler is also a bean, then the key point is this:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
GlobalTemplateScheduler sheduler = context.getBean("sheduler", GlobalTemplateScheduler.class);
sheduler.someMethod();
The content of the XML, which is used by the ClassPathXmlApplicationContext is small. But you need to enable component scan:
<context:component-scan base-package="foo.bar.baz" />
Another approach, I could suggest, is to use Spring's DispatcherServlet to wire all the beans together. It can use the same XML, it is just a matter of loading it. The benefit is that is you don't need to load the application context by yourself and launch a bean as an entry point
There are plenty of tutorials how to use this servlet.
If you dont't like to write XML, you could use the WebApplicationInitializer
As i said the beans which i was autowiring were working fine. I just needed those beans in my Scheduler Servlet.
Here's the solution which worked...
In my scheduler servlet i got the application context xml and used the beans which were required...
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
GlobalTemplateControl globalTemplateControlObject = context.getBean("globalTemplateControl", GlobalTemplateControl.class);
Thanks #duffymo #Amer Qarabsa #Spindizzy for your suggestions :)
I need to inject list of already initialized beans into some another one.
I have class with definitions of some lazy beans which are used depending on environment - like on server 1 only impl1 and impl2 will be used and on server 2 impl3 and impl1
#Component
class Definitions {
#Bean
#Lazy
public A impl1() { /* ... */ }
#Bean
#Lazy
public A impl2() { /* ... */ }
#Bean
#Lazy
public A impl3() { /* ... */ }
}
And I have some monitoring bean which don't know anything about environment and just collects all those A beans exposing some health information for actuator:
#Component
class Monitoring implements HealthIndicator {
#Autowired
private List<A> monitored;
}
Problem is that spring wires all beans into monitored even if they were not initialized before (which crashes the whole thing, cause there is no suitable environment). And I need to somehow explain to spring that I only need already initialized beans - something like #AutowireOnlyThoseLazyBeansWhichAlreadyBeenUsedSomewhereElse
P.S. I know that I can use dirty hack and declare a list property inside Definitions, fill it in bean factory methods and register another one bean with reference to that list but it is too dirty.
Solution was to just write custom OSGi-like "ServiceTracker" through monitoring beans of some specific type via BeanPostProcessor and registering dynamic bean with all tracked beans in concurrent map into context.