Using Google Guice in web applications can raise some issues. First of all when we talk about web applications we talk about servlets and we talk about managed environments. The creation of servlets in not controlled by us, when we write the application, it is controlled by the web application container. This generates some problems:
The key element to bootstrap Google Guice to a java servlet based application is ServletContext. For each web application there is only one ServletContext instance per JVM. ServletContext is used to manage data which is accessible to all the servlets, and it can be used by the servlets to share data between them. Starting with Servlet 2.3 specification Sun provides a listener mechanism which can be used to instantiate objects at the application start up, outside of any servlet scope in the ServletContext.
This techniques can not be used in distributed environments. If we use it we'll have one injector instance on each JVM because on distributed environments for each JVM there is one ServletContext. ServletContext on one JVM is not visible from another JVM, so if we can not use guice scopes distributed on multiple machines.
As we said there is only one instance of javax.servlet.ServletContext(as long as we don't have a distributed application). We can trigger the initialization of the ServletContext using ServletContextListener. All we have to do is to implement the ServletContextListener.contextInitialized to create the google guice injector, and to set it as an attribute in the ServletContext:
package com.exam.web.guice; import java.lang.reflect.InvocationTargetException; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; public class GuiceServletContextListener implements ServletContextListener { public static final String KEY = Injector.class.getName(); public void contextInitialized(ServletContextEvent servletContextEvent) { servletContextEvent.getServletContext() .setAttribute(KEY, getInjector(servletContextEvent.getServletContext())); } public void contextDestroyed(ServletContextEvent servletContextEvent) { servletContextEvent.getServletContext().removeAttribute(KEY); } @SuppressWarnings("unchecked") private Injector getInjector(ServletContext servletContext) { String className = servletContext.getInitParameter("module"); try { Class extends Module> module = (Class extends Module>) Class.forName(className); return Guice.createInjector(module.getConstructor().newInstance()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } } }
Then we have to change the web.xml to define the listener to be used by the web application container:
<listener> <listener-class> com.exam.web.guice.GuiceServletContextListener </listener-class> </listener>
We use web.xml to define which module to be used by Guice in
String className = servletContext.getInitParameter("module");
The class name is defined like this in web.xml:
<context-param>module</param-name> com.webapplication.bus.HibernateDaoModule</param-value> <description>Guice Module to be used for the servlets app</description> </context-param>
Finally in the servlet we can use this expression to get the injector:
(Injector) servletContext.getAttribute(GuiceServletContextListener.KEY)
We can add an injector field to the servlet and "inject" the injector into it when the servlet is initialized(a super servlet class can be created to define this code in only one place):
public class MyServlet extends HttpServlet { Injector injector; ... @Override public void init(ServletConfig config) throws ServletException { HibernateUtil.Configure(true); ServletContext servletContext = config.getServletContext(); injector = (Injector) servletContext.getAttribute(GuiceServletContextListener.KEY); injector.injectMembers(this); managersRegistry = injector.getInstance(ManagersRegistry.class); super.init(config); } }