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
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.
I need to initialize beans in the Spring context after my application has started; currently, I initialize beans in a class with annotation #Configuration like this:
#Configuration
public class AppConfig {
#Inject
#Bean
public BeanA init(param1, param2, etc...) {
--- Code to construct bean A ---
}
#Inject
#Bean
public BeanB init(param1, param2, etc...) {
--- Code to construct bean B ---
}
}
But some beans I need to initialize after application startup so my approach is create a class listen to ApplicationReadyEvent event in Spring and put the code to initialize beans in that class.
#Configuration
class ApplicationStartingListener implements ApplicationListener<ApplicationReadyEvent>{
---- Code to init bean here ----
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
--- If I put init bean code in here, is it correct? ----
}
}
Is this the best way? Or there are some other better solutions?
I will enumerate other approaches in order to init beans, I grouped the approach in Standard Approach and Spring Boot Approach.
Standard Approach
#PostConstruct: it is just an annotation that triggers a method after bean is create, it doesn't allow input parameters.
#Bean(init-method="somInitMehotd"): this approach is totally related to Spring bean lifecycle and it is called after bean creation, if you are using another method with #PostConstruct annotation, then the #PostConstruct will be called first. This approach doesn't allow input parameters.
ApplicationListener: this interface allows to listen the standard events related to the Context Lifecycle, also it can listen customized events. For example: create a class MyAppListener and implements ApplicationListener<ContextRefreshedEvent> in this case the MyAppListener will implement an onApplicationEvent method that receives a ContextRefreshedEvent
Spring Boot Approach
The runners: There are two very useful interfaces CommandLineRunner and ApplicationRunner both of them will run after ApplicationContext is created both of them allows to inject beans as input parameters.
Spring boot listeners: Spring Application gives some additional events than the standards events that comes from the Application Context. One of the event is ApplicationReadyEvent and it is fire when the application is ready to receive request. In order to listen this events just implements the ApplicationListener using ApplicationReadyEvent as generic.
Here is the example:
MyBean class has different methods that will be called for each approach listed above, every method will call a print method and that method has a Thread.sleep in order to validate the order that every listener is called.
import javax.annotation.PostConstruct;
public class MyBean {
private String myVar="";
public MyBean(){
}
#PostConstruct
public void postConstructInit(){
this.myVar="Post init called";
print();
}
public void beanInit(){
this.myVar="Bean init called";
print();
}
public void contextInit(){
this.myVar="Context init called";
print();
}
public void runnerInit(){
this.myVar="Runner init called";
print();
}
public void bootListenerInit(){
this.myVar="Boot init called";
print();
}
public void print(){
System.out.println(this.myVar);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Here is the ContextRefreshListener class that will listen the ContextRefreshedEvent and handle it.
public class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit();
}
}
And it is the BootListener that will receive the ApplicationReadyEvent that comes from Spring Application.
public class MyBootListener implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit();
}
}
And finally the Spring Boot Application
#SpringBootApplication
public class StackoverflowBootApplication {
public static void main(String[] args) {
SpringApplication.run(StackoverflowBootApplication.class, args);
}
#Bean(name = "myBean", initMethod = "beanInit")
public MyBean getMyBean(){
return new MyBean();
}
#Bean
public ContextRefreshListener getContextRefreshedListener(){return new ContextRefreshListener();}
#Bean
public MyBootListener getBootListener(){return new MyBootListener();}
#Bean
public CommandLineRunner getRunner(ApplicationContext ctx){
return (args) -> {
ctx.getBean(MyBean.class).runnerInit();
};
}
}
The output is:
Post init called
Bean init called
Context init called
Runner init called
Boot init called
Post init called output comes from
#PostConstruct
public void init(){
this.initByPostconstruct="Post init called";
Bean init called comes from the initMethod value
#Bean(name = "myBean", initMethod = "beanInit")
public MyBean getMyBean(){
return new MyBean();
}
}
Context init called comes from ContextRefreshedEvent
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit();
}
Runner init called comes from CommandLineRunner
#Bean
public CommandLineRunner getRunner(ApplicationContext ctx){
return (args) -> {
ctx.getBean(MyBean.class).runnerInit();
};
}
Boot init called comes from ApplicationReadyEvent
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit();
}
All the listed scenarios were triggered by Spring, I didi'nt call any of the events directly, all of them were called by Spring Framework.
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
I have following classes:
#Component
public class TestParentClass{
#Value("${service.timeout}")
protected String timeout;
#PostConstruct
protected void init() {
//some code
}
protected Boolean someMethod(Agr agr0, Agr agr2){
//some code
}
}
#Component
#Primary
public class TestSubClass extends TestParentClass{
#PostConstruct
#Override
protected void init() {
System.out.println("Test0:" + timeout);
super.init();
}
#Override
protected Boolean someMethod(Agr agr0, Agr agr2){
System.out.println("Test1:" + timeout);
return super.someMethod(agr0,agr1);
}
}
Properties file has entry as
service.timeout=30
and is included in import resources in spring configuration.
When I run the application having these classes, during deployment log shows
Test0:30
but when the someMethod called log shows
Test1:null
Any help in resolving this issue is appreciated, thanks in advance!
i hava aclass InitApp
#Component
public class InitApp implements ServletContextListener {
#Autowired
ConfigrationService weatherConfService;
/** Creates a new instance of InitApp */
public InitApp() {
}
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println(weatherConfService);
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
and listener in web.xml:
<listener>
<listener-class>com.web.Utils.InitApp</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
the confService print --> null
what the problem?
A couple of ideas came to me as I was having the same issue.
First one is to use Spring utils to retrieve the bean from the Spring context within the listener:
Ex:
#WebListener
public class CacheInitializationListener implements ServletContextListener {
/**
* Initialize the Cache Manager once the application has started
*/
#Override
public void contextInitialized(ServletContextEvent sce) {
CacheManager cacheManager = WebApplicationContextUtils.getRequiredWebApplicationContext(
sce.getServletContext()).getBean(CacheManager.class);
try {
cacheManager.init();
} catch (Exception e) {
// rethrow as a runtime exception
throw new IllegalStateException(e);
}
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
}
This works fine if you only have one or two beans. Otherwise it can get tedious. The other option is to explicitly call upon Spring's Autowire utilities:
#WebListener
public class CacheInitializationListener implements ServletContextListener {
#Autowired
private CacheManager cacheManager;
/**
* Initialize the Cache once the application has started
*/
#Override
public void contextInitialized(ServletContextEvent sce) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
try {
cacheManager.init();
} catch (Exception e) {
// rethrow as a runtime exception
throw new IllegalStateException(e);
}
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
}
The caveat in both these solutions, is that the Spring context must by loaded first before either of these can work. Given that there is no way to define the Listener order using #WebListener, ensure that the Spring ContextLoaderListener is defined in web.xml to force it to be loaded first (listeners defined in the web descriptor are loaded prior to those defined by annotation).
By declaring
<listener>
<listener-class>com.web.Utils.InitApp</listener-class>
</listener>
in your web.xml, you're telling your container to initialize and register an instance of InitApp. As such, that object is not managed by Spring and you cannot #Autowired anything into it.
If your application context is set up to component-scan the com.web.Utils package, then you will have a InitApp bean that isn't registered as a Listener with the container. So even though your other bean will be #Autowired, the servlet container won't ever hit it.
That is the trade-off.
There are workarounds to this if you use programmatic configuration with a ServletContainerInitializer or a WebApplicationInitializer for servlet 3.0. You wouldn't use #Autowired, you would just have setter/getter that you would use to set the instance.
Here's an example
class SpringExample implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = ...;
SomeBean someBean = context.getBean(SomeBean.class);
CustomListener listener = new CustomListener();
listener.setSomeBean(someBean);
// register the listener instance
servletContext.addListener(listener);
// then register DispatcherServlet and ContextLoaderListener, as appropriate
...
}
}
class CustomListener implements ServletContextListener {
private SomeBean someBean;
public SomeBean getSomeBean() {
return someBean;
}
public void setSomeBean(SomeBean someBean) {
this.someBean = someBean;
}
#Override
public void contextInitialized(ServletContextEvent sce) {
// do something with someBean
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
Note that Spring has some custom implementation of WebApplicationInitializer that are quite sophisticated. You really shouldn't need to implement it directly yourself. The idea remains the same, just deeper in the inheritance hierarchy.
#WebListener
public class StartupListener implements ServletContextListener {
#Autowired
private MyRepository repository;
#Override
public void contextDestroyed(ServletContextEvent event) {
}
#Override
public void contextInitialized(ServletContextEvent event) {
AutowireCapableBeanFactory autowireCapableBeanFactory = WebApplicationContextUtils.getRequiredWebApplicationContext(event.getServletContext()).getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(this);
repository.doSomething();
}
}
As others have said this listener observes by the web servlet(tomcat) context (Not the Spring Container) and is notified of servlet startup/shutdown.
Since it is created by the servlet outside of the Spring container it is not managed by Spring hence #Autowire members is not possible.
If you setup your bean as a managed #Component then Spring will create the instance and the listener wont register with the external servlet.
You cannot have it both ways..
One solution is the remove the Spring annotations and manually retrieve your member from the Spring Application context and set your members that way.
ie
public class InitApp implements ServletContextListener {
private ConfigrationService weatherConfService;
private static ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:web-context.xml");
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
weatherConfService = applicationContext.getBean(ConfigrationService.class);
System.out.println(weatherConfService);
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
With current versions of Spring Boot 2, you can also register Servlets, filters, and listeners as Spring beans and use autowired components normally:
Registering Servlets, Filters, and Listeners as Spring Beans
Any Servlet, Filter, or servlet *Listener instance that is a Spring bean is registered with the embedded container. This can be particularly convenient if you want to refer to a value from your application.properties during configuration.
More info here Register #WebListeners in a way that allows them to register servlets and filters.
This means that you simply have to annotate your ServletContextListener as #Comonent.
Since Spring Boot 2.4 using #WebListener does not work anymore, mentioned in the release notes.
A side-effect of this change is that the Servlet container now creates the instance of the WebListener and, therefore, dependency injection such as with #Autowired can no longer be used. In such cases, #Component should be used instead.