Spring injection Into Servlet
I wanted to leverage on the solution provided by Sotirios Delimanolis but adding transparent autowiring to the mix. The idea is to turn plain servlets into autowire-aware objects.
So I created a parent abstract servlet class that retrieves the Spring context, gets and autowiring-capable factory and uses that factory to autowire the servlet instances (the subclasess, actually). I also store the factory as an instance variable in case the subclasses need it.
So the parent abstract servlet looks like this:
public abstract class AbstractServlet extends HttpServlet {
protected AutowireCapableBeanFactory ctx;
@Override
public void init() throws ServletException {
super.init();
ctx = ((ApplicationContext) getServletContext().getAttribute(
"applicationContext")).getAutowireCapableBeanFactory();
//The following line does the magic
ctx.autowireBean(this);
}
}
And a sevlet subclass looks like this:
public class EchoServlet extends AbstractServlet {
@Autowired
private MyService service;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
response.getWriter().println("Hello! "+ service.getMyParam());
}
}
Notice the only thing EchoServlet needs to do is to declare a bean just in common Spring practice. The magic is done in the init() method of the superclass.
I haven't tested it thoroughly. But it worked with a simple bean MyService that also gets a property autowired from a Spring-managed properties file.
Enjoy!
Note:
It's best to load the application context with Spring's own context listener like this:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Then retrieve it like this:
WebApplicationContext context = WebApplicationContextUtils
.getWebApplicationContext(getServletContext());
ctx = context.getAutowireCapableBeanFactory();
ctx.autowireBean(this);
Only spring-web library needs to be imported, not spring-mvc.
What you are trying to do will make every Servlet
have its own ApplicationContext
instance. Maybe this is what you want, but I doubt it. An ApplicationContext
should be unique to an application.
The appropriate way to do this is to setup your ApplicationContext
in a ServletContextListener
.
public class SpringApplicationContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
sce.getServletContext().setAttribute("applicationContext", ac);
}
... // contextDestroyed
}
Now all your servlets have access to the same ApplicationContext
through the ServletContext
attributes.
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
ApplicationContext ac = (ApplicationContext) config.getServletContext().getAttribute("applicationContext");
this.apiData = (ApiData)ac.getBean("apiData");
this.apiLogger = (ApiLogger)ac.getBean("apiLogger");
}