How call a method in a spring bean on web context initialization - spring

I have a war file that includes the following
Spring Bean
public class DataLoader {
private static Logger log = Logger.getLogger(DataLoader.class.getName());
public void init() {
log.info("DataLoader init called");
}
}
applicationContext.xml
<bean id="dataLoader" class="com.example.DataLoader"
init-method="init" lazy-init="false">
</bean>
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
I want the init method in the DataLoader to be called when I deploy the war file to a web container. However, it does not get called.
I thought that the init method would get called after the bean is initialized because of init-method="init" in the bean configuration, and that the bean would be initialized on deployment because of lazy-init="false".
What am I doing wrong?

Are you sure your bean is being initialized at all? You probably want to make your DataLoader class extend InitializingBean, and rename init to afterPropertiesSet. The more modern way of doing this, however, would be to drop the XML configuration for the bean, and alter you class in this way:
#Component
public class DataLoader {
private static Logger log = Logger.getLogger(DataLoader.class.getName());
#PostConstruct
public void init() {
log.info("DataLoader init called");
}
}

Related

How to make a class aware of multiple application contexts in Spring MVC?

I'm trying to understand the concepts of application context in Spring MVC. To examine it I have created an application with two application contexts Test-application-context1.xml and Test-application-context2.xml and one web application context Test-dispatcher-servlet.xml. In all of these contexts I initialized a simple java bean with two fields:
1) In Test-application-context1.xml:
<bean id="testObject" class="test.TestObject">
<property name="fName" value="FirstName Context1"/>
<property name="lName" value="LastName Context1"/>
</bean>
2) In Test-application-context2.xml:
<bean id="testObject" class="test.TestObject">
<property name="fName" value="FirstName Context2"/>
<property name="lName" value="LastName Context2"/>
</bean>
3) In Test-dispatcher-servlet.xml::
<bean id="testObject" class="test.TestObject">
<property name="fName" value="FirstName WebContext"/>
<property name="lName" value="LastName WebContext"/>
</bean>
I also provided a proper configuration in the web.xml file to initialize all of these contexts when the server starts:
<servlet>
<servlet-name>dispatcherTest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/Test-dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherTest</servlet-name>
<url-pattern>/Test/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/Test-application-context1.xml /WEB-INF/Test-application-context2.xml</param-value>
</context-param>
Now I want to inject these application/web application contexts in one of my controller classes. I'm not sure how to do this for multiple contexts properly. I know when I have one context I can make my class implement ApplicationContextAware so I tried it like this:
#Controller
public class TestController implements ApplicationContextAware{
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#RequestMapping(value = "/Test")
public void Test(HttpServletRequest request) {
TestObject testObject = applicationContext.getBean("testObject", TestObject.class);
System.out.println("fName="+testObject.getfName()+"; lName="+testObject.getlName());
}
}
In the above example I always get values of testObject from Test-dispatcher-servlet.xml so it seems that applicationContext that is being injected here represents web application context, BUT if I let's say rename testObject to testObject1 in 'Test-dispatcher-servlet.xml' and run the same code I will get values from Test-application-context2.xml so here are the questions I have.
1) When we make class implement ApplicationContextAware having multiple contexts which context will be injected to that class? Is it one context that is being injected or does spring somehow combines all of contexts and inject them as one applicationContext object (that would explain why do I get values from a different context when I change the name of the bean in one of the contexts)?
2) What is the proper way to inject multiple application contexts to the class?
I know the above example is not a typical scenario and probably a bad design pattern but I'm just trying to understand how the whole thing works.
To answer your 2nd question,
Use #ImportResource as below.
From the docs,
Like #Import, this annotation provides functionality similar to the
element in Spring XML. It is typically used when designing
#Configuration classes
#Configuration
#ImportResource( { "Test-application-context1.xml", "Test-application-context2.xml" } )
public class ConfigClass { }
This will load all beans from both application contexts into the class ConfigClass.
Update:
So, there will be only one application context exists after this import.
You can access any bean from any imported *.context.xml by using #Autowired
After import, Your example in question will throw NoUniqueBeanDefinitionException, because you have more than 1 bean with the same name (testObject) in same application Context.

Strange behaviour of #Configuration with #Configurable in Spring

I've been using XML based configuration for a while - we've got a Vaadin application with Spring used as DI, but we are not interested in DispacherServlet - only root context, which we use to inject global (not user owned dependencies).
The way it works
I've defined root-context.xml file with content:
<context:annotation-config />
<context:spring-configured />
<context:load-time-weaver />
<context:component-scan base-package="com.example" />
And my web.xml has in it:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Then, some of my classes are defined with #Component annotation and others with #Configurable (the latter mostly belong to user session, so require DI for each instance created with new keyword).
I've got context.xml file with line:
<Loader delegate="false" loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" />
And spring-instrument-tomcat-3.2.1.RELEASE.jar in Tomcat's lib directory.
All the dependencies are injected (with #Autowire) to my #Configurable classes correctly.
The way it doesn't work
Recently I've tried to get rid of root-context.xml and move context initialisation to Java #Configuration class.
I've created a class as follows:
#Configuration
#EnableSpringConfigured
#EnableLoadTimeWeaving
#ComponentScan("com.example")
public class BeansConfiguration
{
}
In addition I changed web.xml entries:
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.example.spring.BeansConfiguration</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Unfortunately, strange things started to happen. Let me show you an example. Simplifying my class structure looks as follows:
#Component
public class ComponentA
{
}
#Configurable
public class BeanB
{
#Autowired
private ComponentA componentA;
}
#Configurable
public class BeanC
{
#Autowired
private ComponentA componentA;
private BeanB beanB;
public BeanC(BeanB beanB)
{
this.beanB = beanB;
}
}
#Configurable
public class Application
{
#Autowired
private ComponentA componentA;
public Application()
{
}
public void init()
{
BeanC beanC = new BeanC(new BeanB());
}
}
With the XML setup, when it does work, ComponentA is correctly injected by Spring into all my #Configurable objects.
Strangely, with annotation-only configuration BeanC doesn't get the ComponentA injected (it's always null), howewer BeanB and Application do get that!
Have you got any ideas why would it happen? As soon as I comment out lines in web.xml to go back to my previous (XML-based) configuration all starts to work.
My happy guess is that XML Spring entries register something more under the cover than their annotation based counterparts. I've spend half a day trying to find out, what could that be, but I gave up. I would appreciate any suggestions.

How to load applicationContext.xml

I trying to load an applicationContext.xml from java class in a web application using
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
and
ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml");
My question is how to load applicationContext.xml from a java class. The applicationContext.xml is WEB-INF. Is that possible?
You can use the ContextLoaderListener in your web.xml file:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Then you can use the WebApplicationContext to load the context:
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(servlet.getServletContext());
Hope this helps.
A ContextLoaderListener is used when you want to load a specific context that will act as your context root. If you want to load additional contexts for whatever reason, you can define your own ServletContextListener, create your ApplicationContext instances, and put them in the ServletContext attributes so that they are available to the web application
public class AdditionalContextListener implements ServletContextListener {
#Override
public void contextDestroyed(ServletContextEvent sce) {
// destroy those contexts maybe
}
#Override
public void contextInitialized(ServletContextEvent sce) {
ApplicationContext context = ...; // get your context
sce.getServletContext().setAttribute("someContextIdentifier", context);
}
}

SimpleJdbcTemplate not serializable

I use JSF and Spring. The JSF beans must be serializable by design. The JSF beans get injected with Spring beans that handle the db access. Thus these beans must be serializable as well. However simpleJdbcTemplate is not serializable so I get stack traces when restarting the servlet and sessions are lost. If I declare simpleJdbcTemplate to be transient, I don't get stack traces about "not serializable" but then simpleJdbcTemplate is sometimes NULL after a restart, which is even worse.
So what can I do so that the spring beans get injected with a "fresh" simpleJdbcTemplate when the servlet is restarted?
The classes look like this:
#Repository
public class Users implements Serializable
{
#Autowired
private SimpleJdbcTemplate simpleJdbcTemplate;
// ...
}
#ManagedBean
#SessionScoped
public class ThisBean implements Serializable
{
#ManagedProperty(value = "#{users}")
private Users users;
public void setUsers(Users users)
{
this.users = users;
}
// ...
}
Now some config files, just so that you see I didn't miss something standard.
The web.xml has this:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>Listeners.SessionListener</listener-class>
</listener>
The applicationContext.xml has this:
<context:annotation-config />
<context:component-scan base-package="SpringDB"/>
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/mysql"/>
<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg>
<ref bean="dataSource"/>
</constructor-arg>
</bean>
faces-config.xml has this:
<application>
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
<application>
<message-bundle>jsf</message-bundle>
</application>
</application>
Why don't you try to pass in the DataSource reference instead of the JdbcTemplate. The common practice is to inject the data source and create the JdbcTemplate within the setter for the data source. You'll find the following example in the spring documentation:
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
You can also use #Autowired on the setDataSource method if you don't want to set up your beans with XML. For further details have a look at the JdbcTemplate best practices section of the excellent spring documentation.
Add scope="prototype" definition for simpleJDBCTemplate bean. Then Spring Framework will create new instance for every servlet instance. More info about bean scopes: http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes

Spring setting WebApplicationContext in ServletContextAware bean

I'm converting existing code to Spring 3 JDBC. I've put it into a class (SpringDB.Users) that implements ServletContextAware. In setServletContext(), the following code doesn't work:
public void setServletContext(ServletContext sc)
{
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
simpleJdbcTemplate = (SimpleJdbcTemplate) wac.getBean("simpleJdbcTemplate");
}
The reason is: exception is java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
However I did register a ContextLoaderListener in web.xml:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
The applicationContext.xml has this:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/mysql"/>
<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg>
<ref bean="dataSource"/>
</constructor-arg>
</bean>
<bean class="SpringDB.Users"/>
which results in getting the call to setServletContext(). The class SpringDB.Users is mostly static stuff. It is never instantiated by the java code.
Apparently, the call to WebApplicationContextUtils.getRequiredWebApplicationContext() is "too early". Because what does work without any trouble is to get the WebApplicationContext at a later time, i.e. when database work really starts - so what I do is to call a private function getSimpleJdbcTemplate() instead of a private variable simpleJdbcTemplate:
static private SimpleJdbcTemplate getSimpleJdbcTemplate ()
{
if (simpleJdbcTemplate == null)
{
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
simpleJdbcTemplate = (SimpleJdbcTemplate) wac.getBean("simpleJdbcTemplate");
}
return simpleJdbcTemplate;
}
Is there any solution so that the variable simpleJdbcTemplate can be initialized within setServletContext() ?
Am I missing something obvious, or just expecting too much?
Why do you need that in the first place?
If your class is a bean, then you can simply inject (with #Inject, #Autowired or xml) the jdbc template:
#Inject
private SimpleJdbcTemplate template;

Resources