0

I am experimenting with Spring injection for the first time. I am surely forgetting something obvious but I don't know what it is.

Under src/main/java, I have a package 'example' containing Hello, Animal, Cat.

Under src/main/webapp/WEB-INF, I have web.xml and springapp-servlet.xml.

When I deploy my app with Tomcat, I get a:

javax.servlet.ServletException: Error instantiating servlet class example.Hello
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)

What am I missing for the injection to work?

Source below:

Hello.java

package example;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class Hello extends HttpServlet {
  private final Animal animal;

  @Autowired
  public Hello(final  Animal animal) {
    this.animal = animal;
  }

  @Override
  protected void doGet(final HttpServletRequest req,
          final HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write(animal.sound());
  }
}

Cat.java

package example;

import org.springframework.stereotype.Service;

@Service
public class Cat implements Animal {
  public String sound() {
    return "Miaou";
  }
}

Animal.java

package example;

public interface Animal {
  public String sound() ;
}

web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/springapp-servlet.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>Hello</servlet-name>
        <servlet-class>example.Hello</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>

springapp-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:component-scan base-package="example" />
    <mvc:annotation-driven />

</beans>

I initially thought that perhaps my springapp-servlet.xml was not even read, but if I make a typo on the name springapp-servlet.xml in my web.xml, I do get an error at deployment time, so I clearly have the correct path for springapp-servlet.xml. It is being but yet the injection isn't working.

UPDATE:

I am showing below the solution that worked for me thanks to the answers below. All code remains the same except for Hello:

Hello.java

public class Hello extends HttpServlet {

  @Inject
  private Animal animal;

  @Override
  public void init(final ServletConfig config) throws ServletException {
    super.init(config);
    SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
            config.getServletContext());
  }

  @Override
  protected void doGet(final HttpServletRequest req,
          final HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write(animal.sound());
  }    
}
Lolo
  • 3,935
  • 5
  • 40
  • 50
  • Are you sure this is the only exception in the logs? Have you checked the stack-trace? – Swapnil Jan 11 '13 at 17:47
  • Yes, stack trace didn't add much more info, which is why I didn't include. No other exceptions. – Lolo Jan 11 '13 at 18:06

1 Answers1

2

This is wrong:

@Service
public class Hello extends HttpServlet {

Servlet's lifecycle is controlled by the servlet container, not by Spring. Thus you can't autowire Spring beans directly to servlet. Spring should not create servlets at all. Basically Spring doesn't know anything about your servlet, it tries to instantiate it, but it's not the same instance that was created by the servlet container and that is used to handle requests.

Finally, your servlet doesn't have a no-arg constructor. Such constructor is required, but it won't make your example pass.

The solution is to fetch desired Spring beans directly from registered web application context:

WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
Animal animal = context.getBean(Animal.class);

See also (for other solutions)

Community
  • 1
  • 1
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • Ah good point. Worth pointing out if you did want to wire spring beans directly to your servlet you should use Spring MVC and wire them to your controller. – cowls Jan 11 '13 at 17:57
  • Thanks, this worked: protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); final Animal animal = (Animal)context.getBean("cat"); resp.getWriter().write(animal.sound()); } Is that the correct way of doing things? Having to invoke the web app context for every injection is a bit cumbersome so I wonder if I am not using the language correctly here. – Lolo Jan 11 '13 at 18:04
  • There is another way using the spring HttpRequestHandlerServlet. Check out the following url to see how it is setup. http://insidecoding.wordpress.com/2011/09/08/how-to-inject-spring-beans-into-servlets/ – Manuel Quinones Jan 11 '13 at 18:16
  • 1
    @Tomasz: The post that you referred to worked beautifully for me. I like the solution from Oliver using SpringBeanAutowiringSupport. – Lolo Jan 11 '13 at 18:29