I'm trying to initialize some beans during application start that will be reading from static shared in-memory structures. I was previously using #PostContruct but would like to shift to a more event-based initialization so that I can make use of Spring AOP features (Config, Resources, etc.) and avoid repeating myself.
All data beans implement this interface:
public interface DataInterface {
public void loadData();
public List<String> getResults(String input);
}
I have tried implementing both ServletContextListener and WebApplicationInitializer interfaces, but neither seem to get called.
#Service
public class AppInit implements WebApplicationInitializer {
#Autowired
DataInterface[] dataInterfaces;
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
// This does not get called
for (DataInterface interface : dataInterfaces)
interface.loadData();
}
}
#WebListener
public class AppContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// does not get called
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
// does not get called
}
}
I could also try to initialize these classes at the end of the main() function that returns after I start the SpringApplication.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
// Can I initialize the DataInterfaces here???
}
}
Seems like there should be a better way.
Edit:
I ended up using the following solution since I wasn't able to receive any of the Context* events listed in the Spring docs.
#Component
public class DalInitializer implements ApplicationListener {
#Autowired
DataInterface[] dataInterfaces;
#Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
if (applicationEvent.getClass() == ApplicationReadyEvent.class) {
for (DataInterface interface : dataInterfaces)
interface.loadData();
}
}
}
Use spring application event listeners, see Better application events in Spring Framework
Related
I tried constructor based Dependency injection in TestAppListener class which implements ServletContextListener.
I got this error
Exception sending context initialized event to listener instance of class [com.example.listener.TestAppListener].
I searched stack overflow but I couldn't find any solution for this scenario. Please any one help me in sort out this. I placed Implementation classes in META-INF.services folder also. My understanding is there is some problem in constructor dependency injection but this way of DI is need for my situation, because in real time I want to create datasource connection inside startup method.
Find all my classes below which i'm using,
#WebListener
public class TestAppListener implements ServletContextListener {
private static TestDao dao;
public TestAppListener(TestDao dao){
this.dao = dao;
}
public TestAppListener(){}
#Override
public void contextInitialized(ServletContextEvent sce) {
dao = ServiceLoader.load(TestDao.class).iterator().next();
dao.startUp();
System.out.println("Context initialized method called");
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Context destroyed method called");
dao.shutDown();
}
}
public interface TestDao {
void startUp();
void shutDown();
}
public class TestDaoImpl implements TestDao {
#Override
public void startUp() {
System.out.println("Database is initialized");
}
#Override
public void shutDown() {
System.out.println("Database is initialized");
}
}
#Configuration
public class SpringConfig {
public SpringConfig() {
}
#Bean
public ServletListenerRegistrationBean<ServletContextListener> listenerRegistrationBean() {
ServletListenerRegistrationBean<ServletContextListener> bean = new ServletListenerRegistrationBean<>();
bean.setListener(new TestAppListener());
return bean;
}
}
The Servlet #WebListeners are handled by Servlet containers(tomcat/Jetty) when the container is starting. So they know nothing about Spring.
For more details, see discussion in this issue.
A workaround solution is replace the #WebListener with Spring's #Component, thus you can inject Spring beans(declare your Dao as spring beans) into the listeners directly.
BTW, you have to add #ServletComponentScan on your Spring Boot application class to activate it.
I created an example project to demo #WebServlet, #WebFilter and #WebListener.
Change private static TestDao dao to private final TestDao dao. Spring doesn't allow statics to be used as targets for injection. Also, your TestDaoImpl class needs to be a component or a bean defined in a Spring configuration file.
if I have duplicate bean name Spring boot will override it. I want to disable this feature. I saw many discussions to this where it disables the overriding inside the main method. but in web app the main method will not be invoked at all. How can I disable it ?
In your main method you need to create a new ApplicationContextInitializer and override its initialize() method for disabling the bean definition override. See below:
new SpringApplicationBuilder(SpringBootApp.class)
.initializers(new ApplicationContextInitializer<GenericApplicationContext>()
{
#Override
public void initialize(GenericApplicationContext applicationContext)
{
applicationContext.setAllowBeanDefinitionOverriding(false);
}
})
.run(args);
--UPDATE--
Since you deploy as web app, the above method will not work, of course.
You can implement the WebApplicationInitializer interface and override its startup() method, providing a custom ApplicationContextInitializer:
public class CustomInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>
{
#Override
public void initialize(ConfigurableApplicationContext applicationContext)
{
applicationContext.setAllowBeanDefinitionOverriding(false);
}
}
public class WebInitializer implements WebApplicationInitializer
{
#Override
public void onStartup(ServletContext servletContext) throws ServletException
{
DispatcherServlet servlet = new DispatcherServlet();
servlet.setContextInitializers(new CustomInitializer());
}
}
I'm using Kinesis Client Library (KCL) and Spring boot. To use KCL, I have to implement a class (I named it RecordProcessor) for interface IRecordProcessor. And KCL will call this class and process records from kinesis. But when I tried to use dependency injection, I found it was not succeeded.
Here's the snippet for RecordProcessor:
#Component
public class RecordProcessor implements IRecordProcessor {
#Autowired
private SingleRecordProcessor singleRecordProcessor;
#Override
public void initialize(String shardId) {
...
}
#Override
public void processRecords(List<Record> records, IRecordProcessorCheckpointer checkpointer) {
...
}
}
I use Class SingleRecordProcessor to process single each record from kinesis. And this is my SingleRecordProcessor class snippet:
#Component
public class SingleRecordProcessor {
private Parser parser;
private Map<String, Table> tables;
public SingleRecordProcessor() {
}
#Autowired
private void setParser(Parser parser) {
this.parser = parser;
}
#Autowired
private void setTables(Map<String, Table> tables) {
this.tables = tables;
}
public void process(String record) {
...
}
}
I want to let spring framework automatically inject the SingleRecordProcessor instance into the class and use it. But I found that the field singleRecordProcessor is null.
Any idea why the dependency injection is failed? Or is it impossible to inject dependencies into a class which is called by other framework (in this case it's KCL)? Any suggestions will be appreciated! Really need some help please!!
[UPDATE]:
Sorry for not expressing the error clearly. The error was NullPointerException. I tried to inject singleRecordProcessor and call method process() on it. I think the injection was not successful so the instance singleRecordProcessor is null and there comes the NullPointerException.
More information is as follows:
I have a major class called Application
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./app.pid"));
ConfigurableApplicationContext ctx = application.run(args);
}
}
And I have the MainProcessor class which will call KCL.
#Service
public final class MainProcessor {
#EventListener(ApplicationReadyEvent.class)
public static void startConsumer() throws Exception {
init();
IRecordProcessorFactory recordProcessorFactory = new RecordProcessorFactory();
Worker worker = new Worker(recordProcessorFactory, kinesisClientLibConfiguration);
...
worker.run(); // this line will call KCL library and eventually call ProcessorRecord class.
}
}
[UPDATE2]
RecordProcessorFactory only has one method like this
#Component
public class RecordProcessorFactory implements IRecordProcessorFactory {
#Autowired
RecordProcessor recordProcessor;
#Override
public IRecordProcessor createProcessor() {
return recordProcessor;
}
}
It creates a new RecordProcessor instance for KCL to use it.
You should autowire an instance of this into your MainProcessor:
#Component
public class RecordProcessorFactory {
#Lookup IRecordProcessor createProcessor() { return null; }
}
Spring will instantiate a RecordProcessorFactory for you, and replace the implementation of createProcessor() in it with one that will return a new IRecordProcessor each time it's called. Both the factory and the processors will be Spring beans - which is what you want.
I am trying to get the spring application context on a ServletContextListener. I am using Spring with annotation configuration. Using this code i get "context null". What I am doing wrong?
#WebListener
public class Initializer implements ServletContextListener
{
public void contextInitialized(ServletContextEvent event)
{
System.out.println("context " + WebApplicationContextUtils.getWebApplicationContext(event.getServletContext()));
}
#Override
public void contextDestroyed(ServletContextEvent sce)
{
}
}
Thanks
The key for fix the problem has been delete the annotation #WebListener and on WebAppInitializer override onStartup to ensure that the ContextLoaderListener is loaded before Initializer
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
.
.
.
#Override
public void onStartup(ServletContext container) throws ServletException
{
super.onStartup(container);
container.addListener(Initializer.class);
}
}
I have to update a table entry when application is started and stopped.
I have a service which has the calls to the DAO method but when this DAO method is called the SessionFactory which is autowired is null.
I have used 2 methods:
#PostConstruct, #PreDestroy
ApplicationListener onApplicationEvent()
In both the cases I am getting the SessionFactory as null in the DAO class. I am using Setter-Injection in the DAO class for injecting the SessionFactory.
Environment: JDBC-Datasource, Hibernate 3.4, Spring 3.1.2, Weblogic 10.3
It would be great if you could point me in the right direction.
Update:
Thanks for all your comments I got it resolved.Our app is a EAR and my DAO bean config was in a different WAR's applicationContext.xml. I moved the DAO bean config to my shared configuration (appConfig.xml) and it worked like charm. I used #PostConstruct and #PreDestroy
You can do it using SmartLifecycle interface and then configuring it as a bean:
<bean class="com.my.package.MySmartlifecycle">
And your implementation:
public class MySmartLifecycle implements SmartLifecycle{
//autowire anything you need from context
#Override
public void start() {
//do stuff on startup here
}
#Override
public void stop() {
//do stuff on shutdown here
}
#Override
public boolean isRunning() {
return false;
}
#Override
public int getPhase() {
return 0;
}
#Override
public boolean isAutoStartup() {
return true;
}
#Override
public void stop(Runnable callback) {
}
}
If you want to execute method after the context is initialized, you have to use ContextRefreshedEvent. In case of context destroying you should use ContextStoppedEvent, but you also have to remebmer that there is no guarantee that this event will be posted.
#Component
public class SpringStartedListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// do something
}
}
#Component
public class SpringClosedListener implements ApplicationListener<ContextStoppedEvent> {
#Override
public void onApplicationEvent(ContextStoppedEvent event) {
// do something
}
}
If you need some more details please refer to http://www.tutorialspoint.com/spring/event_handling_in_spring.htm