To provide enhanced pluggability, Servlet 3.0 provides support for the programmatic declaration and registration of servlet, filter and listener components via new methods added to ServletContext. We can call these methods from within ServletContextListener#contextInitialized method or ServletContainerInitializer#onStartup method. We can summarized these new methods of ServletContext as follows:
Methods General Description
Methods
Registration of components via addXXX(). Each method on right side has multiple overloaded form. Each overloaded method needs name argument (except for Listener methods) of the component along with class name or class type or instance of the component. Each overloaded group either return ServletRegistration.Dynamic or FilterRegistration.Dynamic, (except for listeners, they return void). The returned Registration object can be used for further configuration.
addServlet(...) addFilter(...) addListener(...)
Creating instance of the component via createXXX(). We need to provide class type as argument to each method. These methods may be called before calling above addXXX method.
Getting Registration object via getXXXRegistration(). Doesn't apply to listeners. If servlet/filter has not been register in web.xml or via annotations, this call throws UnsupportedOperationException in ServletContextListener#contextInitialized.
This feature enable framework designer to add the framework specific web components during startup without asking end user on application side to provide same configurations deployment descriptor and add them manually. For example frameworks like JSF and Spring are already using ServletContainerInitializer approach for that (see source code of FacesInitializer and SpringServletContainerInitializer. The developers just need to add the framework jar dependencies and create some POJOs (using framework specific annotation or implementing some interfaces) to use the framework. They even don't have to provide /META-INF/services/javax.servlet.ServletContainerInitializer as this file already resides inside the framework jar.
The same configuration can be done in a ServletContextListener residing inside the framework jar, but disadvantage is, that will not work if the application's web.xml file has been decidedly configured metadata-complete="true" to improve performance. Setting this flag causes the container not to scan JARs for annotations. Also ServletContainerInitializer approach (SPI) is more standard way to go as it's built into Java SE.
ServletContextListener#contextInitialized should do programmatic configuration on application level only. Based on some dynamic conditional parameters we can decide to plug/register some web components selectively.
Example
This example will focus on adding servlet via ServletContextListener#contextInitialized. Assuming a servlet application can be hosted from different geographical locations. Based on different locale we can decide to choose a particular servlet to add to the container.
Prepare project
Create web application using maven-archetype-webapp, steps here.
Delete web.xml, we don't need it at all.
In pom.xml add dependency of javax.servlet-api:3.0.1
In pom.xml add tomcat7-maven-plugin to run it as embedded server.
Create a context listener class AppContextListener, annotated with @WebListener.
Create two servlet classes DefaultAppController and OffshoreAppController without any annotations.
Now we are going to run our web application from root folder: