Injecting Spring Beans into Java Servlets

February 23rd, 2012 by Lal Sah Leave a reply »

If you are working in a Java Web Application and you are using Spring IoC Container in your application, there is a chance that you might have to inject Spring Beans into a Java Servlet.

Since there is not a direct way to inject Spring Beans into a Java Servlet, you might try to lookup the Spring Beans from the Spring Context within your servlet and assign the dependencies which means that part of injection would no more be IoC and you would be looking for some concise way of doing this.

To solve this problem, Spring provides a trick which is a class called  org.springframework.web.context.support.HttpRequestHandlerServlet which consists of two behaviors:

  1. It is-a javax.servlet.http.HttpServlet - completes one of the requirements.
  2. and, it is a wrapper around org.springframework.web.HttpRequestHandler which has to be Spring Bean configured with bean injections, if any, to achieve the second goal (dependency injection). This is an interface which has a method called handleRequest(HttpServletRequest request, HttpServletResponse response) which the HttpRequestHandlerServlet delegates to while serving a request. So you need to implement this interface and have the dependencies wired in that class.

Note! Your servlet name (1) and you bean id (2) must match because HttpRequestHandlerServlet uses the servlet name to look up the beans form the context.

Now let’s look at an example:

  • Write your Spring bean (which is also a request handler / implements HttpRequestHandler). This bean should be configured to be component scanned. In the example below, this bean has a service called HelloService wired using Spring DI annotation @Autowired which will be injected by the Spring IoC container.
/**
* AnnotatedHttpServletRequestHandler.java
* Feb 15, 2012
*/
package com.sourceallies.spring.noxml.demo.web.servlet.handler;
 
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Logger;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestHandler;
 
import com.sourceallies.spring.noxml.demo.service.HelloService;
 
/**
* @author Lal
*
*/
@Component("annotatedServletHandler")
public class AnnotatedHttpServletRequestHandler implements HttpRequestHandler {
 
private static final Logger LOGGER = Logger.getLogger(AnnotatedHttpServletRequestHandler.class.getName());
 
@Autowired
private HelloService helloService;
 
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.write("<h1>Spring Beans Injection into Java Servlets!</h1><h2>" + helloService.sayHello("World") + "</h2>");
}
  • Write your Servlet. This servlet class extends org.springframework.web.context.support.HttpRequestHandlerServlet.
package com.sourceallies.spring.noxml.demo.web.servlet;
 
import javax.servlet.annotation.WebServlet;
 
import org.springframework.web.context.support.HttpRequestHandlerServlet;
 
/**
* Servlet implementation class AnnotatedHttpServlet
*/
@WebServlet(description = "Http Servlet using pure java / annotations", urlPatterns = { "/annotatedServlet" }, name = "annotatedServletHandler")
public class AnnotatedHttpServlet extends HttpRequestHandlerServlet {
 
private static final long serialVersionUID = 1L;
}

Notice the @Component(“annotatedServletHandler”) and @WebServlet(…, name = “annotatedServletHandler”). The bean id and the servlet name are exactly same.

Now, this will absolutely work and in fact you got access to Spring Beans from the Servlet provided that your Spring bean annotatedServletHandler was registered in the Spring Root Context (the context that is setup using org.springframework.web.context.ContextLoaderListener). However, it could be possible that your Web related beans, annotatedServletHandler for instance, are registered in the Spring Dispatcher Context. If this is the case, the previous example would not work. This leads to a situation where you have to implement your own HttpRequestHandlerServlet that could lookup both root and dispatcher contexts.

Here is an implementation of such a HttpRequestHandlerServlet which is pretty much similar to what Spring provides but with added functionality to support dispatcher context as well.

package com.sourceallies.spring.noxml.demo.web.servlet.framework;
 
import java.io.IOException;
import java.util.logging.Logger;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.FrameworkServlet;
 
import com.sourceallies.spring.noxml.demo.initializer.ApplicationContextInitializer;
 
@SuppressWarnings("serial")
public class MyHttpRequestHandlerServlet extends HttpServlet {
 
private static final Logger LOGGER = Logger.getLogger(MyHttpRequestHandlerServlet.class.getName());
 
// Replace ApplicationContextInitializer.DISPATCHER_SERVLET_NAME with the
// name of your dispatcher servlet
private static final String DISPATCHER_CONTEXT_ATTRIBUTE_NAME = FrameworkServlet.SERVLET_CONTEXT_PREFIX + ApplicationContextInitializer.DISPATCHER_SERVLET_NAME;
 
private HttpRequestHandler target;
 
@Override
public void init() throws ServletException {
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
try {
this.target = (HttpRequestHandler) wac.getBean(getServletName(), HttpRequestHandler.class);
} catch (NoSuchBeanDefinitionException e) {
LOGGER.info("HTTP Request Handler bean was not found in Spring Root Context! Now looking up in the Dispatcher Context...");
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), DISPATCHER_CONTEXT_ATTRIBUTE_NAME);
this.target = (HttpRequestHandler) context.getBean(getServletName(), HttpRequestHandler.class);
}
}
 
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
 
LocaleContextHolder.setLocale(request.getLocale());
try {
this.target.handleRequest(request, response);
} catch (HttpRequestMethodNotSupportedException ex) {
String[] supportedMethods = ((HttpRequestMethodNotSupportedException) ex).getSupportedMethods();
if (supportedMethods != null) {
response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
}
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
} finally {
LocaleContextHolder.resetLocaleContext();
}
}
}

The rest are normal Spring configurations.

References

Advertisement

4 comments

  1. Adriana says:

    thanks for share!

  2. Cole says:

    Very cool article. I have become impressed with this site

  3. Eric Wilson says:

    This seems to have the potential to be very helpful, but I can’t understand why I’m getting:

    “HTTP method GET is not supported by this URL”

    Obviously I have not overridden the doGet in my servlet, but I thought this post was saying that the request would be passed to the handler anyway.

  4. Shalom says:

    thanks for your help.
    I this got me started solving my problem – and of course – it worked :-)

Leave a Reply

*