guice web dataflow:
Basic Design Concept:
1) HttpRequest/HttpResponse will always be created by WebContainer(jetty/tomcat) and Guice will reuse/wrap them.
2) For Servlet that match the url pattern in ServletModule, they will be created by Guice instead, their service() will called by guice filtering.
3) For Servlet that doesn't match the url pattern defined in ServletModule, they will be created by WebContainer directly.
1) Listener:
<listener> <listener-class>edu.xmu.webapp.core.MyListener</listener-class> </listener>
public class MyListener extends GuiceServletContextListener { @Override protected Injector getInjector() { return Guice.createInjector(new MyServletModule()); } }
public class MyServletModule extends ServletModule { @Override protected void configureServlets() { serve("*.html").with(MyServlet.class); } }
2) Listener startup:
public abstract class GuiceServletContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent servletContextEvent) { final ServletContext servletContext = servletContextEvent.getServletContext(); GuiceFilter.servletContext = new WeakReference<ServletContext>(servletContext); Injector injector = getInjector(); // will invoke our own getInjector() in listener; MyServletModule.configureServlets() will be invoked injector.getInstance(InternalServletModule.BackwardsCompatibleServletContextProvider.class) .set(servletContext); servletContext.setAttribute(INJECTOR_NAME, injector); } }
// MyListener @Override protected Injector getInjector() { return Guice.createInjector(new MyServletModule()); } // Guice public static Injector createInjector(Stage stage, Iterable<? extends Module> modules) { return new InternalInjectorCreator() .stage(stage) .addModules(modules) .build(); } // Elements public static List<Element> getElements(Stage stage, Iterable<? extends Module> modules) { RecordingBinder binder = new RecordingBinder(stage); for (Module module : modules) { binder.install(module); // here our own module(MyServletModule) will be installed } return Collections.unmodifiableList(binder.elements); } // MyServletModule @Override protected void configureServlets() { serve("*.html").with(MyServlet.class); }
3) Filter:
<filter> <filter-name>guiceFilter</filter-name> <filter-class>com.google.inject.servlet.GuiceFilter</filter-class> </filter> <filter-mapping> <filter-name>guiceFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
4) Serve Request with guice created servlet:
Request URL:
http://localhost:8080/webapp-core/aaa.html
// GuiceFilter public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) { filterPipeline.dispatch(servletRequest, servletResponse, filterChain); } // ManagedFilterPipeline public void dispatch(ServletRequest request, ServletResponse response, FilterChain proceedingFilterChain) { new FilterChainInvocation(filterDefinitions, servletPipeline, proceedingFilterChain) .doFilter(withDispatcher(request, servletPipeline), response); } // FilterChainInvocation public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse){ servletPipeline.service(servletRequest, servletResponse); } // ServletPipeline public boolean service(ServletRequest request, ServletResponse response) { //stop at the first matching servlet and service for (ServletDefinition servletDefinition : servletDefinitions) { if (servletDefinition.service(request, response)) { // Here our own servlet is invoked return true; } } //there was no match... return false; }
5) Serve Request with web container created servlet:
http://localhost:8080/webapp-core/formalServlet
<servlet> <servlet-name>formalServlet</servlet-name> <servlet-class>edu.xmu.webapp.servlet.FormalServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>formalServlet</servlet-name> <url-pattern>/formalServlet</url-pattern> </servlet-mapping>
// GuiceFilter public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) { filterPipeline.dispatch(servletRequest, servletResponse, filterChain); } // ManagedFilterPipeline public void dispatch(ServletRequest request, ServletResponse response, FilterChain proceedingFilterChain) { new FilterChainInvocation(filterDefinitions, servletPipeline, proceedingFilterChain) .doFilter(withDispatcher(request, servletPipeline), response); } // FilterChainInvocation public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException, ServletException { index++; // Our url doesn't match any of defined guice serve url, thus serviced is false final boolean serviced = servletPipeline.service(servletRequest, servletResponse); //dispatch to the normal filter chain only if one of our servlets did not match if (!serviced) { proceedingChain.doFilter(servletRequest, servletResponse); } } // proceedingChain is an instance of "FilterChain", it will invoke all the following filters configed in web.xml, and then invoke correspond service() in target Servlet. // From here, guice has transferred the filter/servlet control to web container(tomcat/jetty).
Reference Links:
1) http://insidecoding.com/2013/02/05/using-google-guice-in-web-applications/