I came across the @HandlesTypes annotation while looking at Servlet 3.1 specs, and of course with lot of time to spare, I ended up trying it out.
As per Oracle documentation:
So what does the annotation do ?
Again from the docs:
I created an example class:
The text file has the same name as the interface implemented - ServletContainerInitializer - only its fully qualified version. The contents of the file is the name of the class implementing this interface
The output on running the code is as below
To be honest, the above registration code would be better of with a ContextListener.
The point is that the ServletContainerInitializer is to be placed in a jar. So the code that goes here should be something that is generic and could be used in different applications. For example a code to initialize a database logger, or a ping servlet for internal use.
For something that is very application specific I would go with the ContextListener.
As per Oracle documentation:
This annotation is used to declare the class types that a ServletContainerInitializer can handle.So the first step to understanding this annotation is to understand ServletContainerInitializer.
Interface which allows a library/runtime to be notified of a web application's startup phase and perform any required programmatic registration of servlets, filters, and listeners in response to it.As seen here, the ServletContainerInitializer is a way to hook into the application's startup phase.
So what does the annotation do ?
Again from the docs:
Implementations of this interface may be annotated with HandlesTypes, in order to receive (at their onStartup(java.util.Set>,javax.servlet.ServletContext) method) the Set of application classes that implement, extend, or have been annotated with the class types specified by the annotation.Thus the value of the annotation decides what classes should be passed on to the ContextInitializer here.
I created an example class:
@HandlesTypes({ HttpServlet.class, Filter.class }) public class ClassMetaInformation implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> classes, ServletContext context) throws ServletException { // perform any required programmatic registration of servlets, filters, // and listeners in response to it. Set<String> servlets = new HashSet<>(); Set<String> filters = new HashSet<>(); for (Class<?> classVal : classes) { if (HttpServlet.class.isAssignableFrom(classVal)) { servlets.add(classVal.getName()); } else if (Filter.class.isAssignableFrom(classVal)) { filters.add(classVal.getName()); } } System.out.println("Container detected the below Servlet Implementations"); System.out.println("Extends from Filter : " + filters); System.out.println("Extends from Servlet : " + servlets); } }Points of Note:
- The onStartup method is called during the startup phase of the web application.
- As the HandlesTypes annotation mentions HttpServlet and Filter class, all loaded classes that implement/extend either of these classes will be passed in the set parameter to the onStartup method.
- If the annotation is missing, a null Set reference will be passed to the method - leading to an Exception here.
- Same is the case if no matching classes were found.
- Both cases will result in application startup failure
The text file has the same name as the interface implemented - ServletContainerInitializer - only its fully qualified version. The contents of the file is the name of the class implementing this interface
com.web.ClassMetaInformationThis is how the class is located loaded and plugged in during initialization time.
The output on running the code is as below
INFO: At least one JAR was scanned for TLDs yet contained no TLDs.
Enable debug logging for this logger for a complete list of JARs that
were scanned but no TLDs were found in them. Skipping unneeded JARs
during scanning can improve startup time and JSP compilation time.
Container detected the below Servlet Implementations
Extends from Filter : [com.web.TestFilter]
Extends from Servlet : [com.web.TestServlet]
ServletContext null has been initialized at Fri Jan 15 00:05:18 CST 2016
TestFilter initialized as SampleFilter with init parameter value val1
TestServlet initialized as com.web.TestServlet with init parameter values val1 and val2
As seen here, the Initializer ran before loading of my Servlet/Filter or even Listeners.
Here I do not do any productive work really. But there could be uses to this class. For instance I could programmatically initialize a servlet
try { @SuppressWarnings("unchecked") Class<TestServlet> clazz = (Class<TestServlet>) Class .forName("com.web.TestServlet"); Servlet s = context.createServlet(clazz); ServletRegistration.Dynamic dynamic = context.addServlet("Sample", s); dynamic.addMapping("/baz/*"); } catch (ClassNotFoundException e) { e.printStackTrace(); }But could we not also do the same using ContextListeners ? Actually both work. Then why use the ServletContainerInitializer ?
To be honest, the above registration code would be better of with a ContextListener.
The point is that the ServletContainerInitializer is to be placed in a jar. So the code that goes here should be something that is generic and could be used in different applications. For example a code to initialize a database logger, or a ping servlet for internal use.
For something that is very application specific I would go with the ContextListener.
Nice article. Thanks.
ReplyDeleteThe text file has the same name as the interface implemented - ServletContainerInitializer - only its fully qualified version. The contents of the file is the name of the class implementing this interface.thanks for your valuable information.java training in chennai | java training in velachery
ReplyDelete
ReplyDeletevery useful info, and please keep updating........