1

I've been trying to get Spring to inject an @Autowired dependency into my application without avail. What am I doing wrong?

I created a bean called TimeService. Its job is to return the current time to anyone that asks.

package com.foxbomb.springtest.domain.time;

import java.util.Date;
import org.springframework.stereotype.Service;

@Service
public class TimeService {

    public TimeService() {
        super();
        System.out.println("Instantiating TimeService...");
    }

    public Date getTime() {
        return new Date();
    }
}

Of course, I need to tell Spring about this, so I added the following to web.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring-context.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>  

Great, and some Spring configuration:

<?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:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="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:annotation-config base-package="com.foxbomb.springtest.domain" />

</beans>

Now all we need is a calling class that would like to use this dependency. Unfortunately, the @Autowired here seems to do nothing:

package com.foxbomb.springtest;

import...

@Configurable
public class TimeManager {

    public TimeManager() {
        super();
        System.out.println("Instantiating TimeManager...");
    }

    @Autowired
    private TimeService timeService;

    public Date getTime() {
        return timeService.getTime();
    }

}

And lastly, a JSP that wants to display the time:

<%@page import="com.foxbomb.springtest.ApplicationContextImpl"%>
<%@page import="com.foxbomb.springtest.TimeManager"%>

<html>
    <head>
        <title>Spring Test</title>
    </head>
    <body>
        <h1>Autowired Dependencies....</h1>
        <p>
            Time is now <%=new TimeManager().getTime()%>!
        </p>

    </body>
</html>

But all I get is:

java.lang.NullPointerException
    com.foxbomb.springtest.TimeManager.getTime(TimeManager.java:26)
halfer
  • 19,824
  • 17
  • 99
  • 186
sparkyspider
  • 13,195
  • 10
  • 89
  • 133

5 Answers5

4

When you access TimeManager object like this:

Time is now <%=new TimeManager().getTime()%>!

Spring does not know anything about this class. You are basically creating a new object, that's it!

Probably Spring creates an instance of TimeManager and injects dependencies properly (however you should use @Service rather than @Configuration annotation), but you are not using this instance in your JSP. Instead you are creating new, unmanaged instance, that is completely independent and unaware of Spring and dependencies...

I think this will work:

<%= WebApplicationContextUtils.
  getWebApplicationContext(application).
  getBean(TimeManager.class).
  getTime() %>

Ugly? Sure. Because the whole approach of accessing services from JSP (view) is ugly. You should at least have a servlet that accesses Spring beans and puts result in request attributes. These attributes can then be accessed in JSP that you forward to - without ugly scriptlets.

If you really want to do this "the right" way, try Spring MVC or other popular Java web framework.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • Does the @Configuration annotation not tell Spring that this class is going to try and access Spring-managed beans? – sparkyspider Oct 01 '11 at 15:30
  • I'm simply using the JSP as a really simple way of interrogating TimeManager. I have no intention of using JSP's like this in "real life". – sparkyspider Oct 01 '11 at 15:31
  • try what he wrote, he's certainly right about you not acquiring the injected bean from the spring context when you instanciate the TimeManager like you did in your jsp – plus- Oct 01 '11 at 17:34
  • `@Configuration` annotation is used to configure application context, but by coincidence it might work. You should always use `@Service` or similar stereotype. Also as *DwB* noticed, you are scanning the wrong package. P.S.: Care to explain the downvote, anybody? – Tomasz Nurkiewicz Oct 01 '11 at 18:26
  • In your example above, where you use WebApplicationContextUtils, what is the "application" parameter? – sparkyspider Oct 03 '11 at 09:46
  • `application` is an instance of `javax.servlet.ServletContext`. See: http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/JSPIntro7.html. Haven't tried it though. – Tomasz Nurkiewicz Oct 03 '11 at 10:15
1

The @Autowire annotation in TimeManager was not recognized, because you told spring to start searching for annotation configuration information in the wrong package.

TimeManager is in com.foxbomb.springtest.

You have this configuration:

<context:annotation-config base-package="com.foxbomb.springtest.domain"/>

Notice that "com.foxbomb.springtest != com.foxbomb.springtest.domain".

Change your configuration to include this:

<context:annotation-config base-package="com.foxbomb.springtest"/>

Edit: Spring must instantiate TimeManager.

You will need to:

  1. Have TimeManager be a data member of the controller class.
  2. @Autowire TimeManager into the controller class (or get it from spring somehow).
  3. Add the TimeManager to some appropriate scope (maybe session, maybe request, maybe application).
  4. Access this TimeManager on your jsp page. Maybe something like this: It is now ${MyTimeManager.time} balonia watch time.
DwB
  • 37,124
  • 11
  • 56
  • 82
0

I tend to agree with @TomaszNurkiewicz; use a framework. However, a framework is way overkill for a lot of applications. If it's overkill for you, this answer details how to do it without all the manual work of getting the bean yourself. IMO doing it manually defeats the purpose.

Community
  • 1
  • 1
Dave Newton
  • 158,873
  • 26
  • 254
  • 302
  • I agree, but the answer you point to describes how to inject Spring beans into servlets, not JSPs. – Tomasz Nurkiewicz Oct 01 '11 at 14:34
  • @TomaszNurkiewicz I can only support doing things the wrong way so much ;) – Dave Newton Oct 01 '11 at 14:37
  • I'm just trying to use it as an independent example to understand how to manage dependencies. I thought that using [@]Configuration and [@]Autowired in TimeManager.java was sufficient for Spring to inject TimeService when the class is instantiated, however it's instantiated. – sparkyspider Oct 01 '11 at 15:36
  • @MarkvanWyk Nope--in order to use Spring, you have to use Spring. It doesn't go in and manipulate byte code to make instance creation automatically Spring-aware. – Dave Newton Oct 01 '11 at 15:41
  • @Dave Newton The example I've worked on before used Aspect/J and the [@]Configurable annotation allowed me to insantiate a class anywhere and Spring would automatically Autowire the beans in regardless. It was amazing - but I totally don't understand it :) – sparkyspider Oct 02 '11 at 20:36
  • Yep, using AspectJ and `@Configurable` is also possible, but you need to use spring-aspects explicitly, along with an `aop.xml` file (normal AspectJ stuff) and so on. – Dave Newton Oct 02 '11 at 20:47
0

You should be using @Component (or a child stereotype annotation) rather than @Configuration, and annotating your TimeManager with that. @Configuration is used for Java based container configuration, which is not what you are doing here.

nicholas.hauschild
  • 42,483
  • 9
  • 127
  • 120
0

Finally!

I got it working with the code in the above example. The trick was to set up Aspect/J and Weaving. This allows the @Configurable annotated class to automatically realize that its dependent on Spring to inject dependencies.

There wasn't much wrong with the base package :) I accidentally made a xml typo, trying to add the base-package attribute to the context:annotation-config tag. The correct definition was:

<context:annotation-config/>
<context:component-scan base-package="com.foxbomb.springtest"/>

I need actually eventually need to change it to com.foxbomb.springtext to include the class that was trying to access the bean too - as in the end it's also annotated.

Thanks everyone for your help!

sparkyspider
  • 13,195
  • 10
  • 89
  • 133