8

I'm having trouble getting @Autowired to work in a class annotated by @Service, the autowired variable always is null. Let me explain:

@Service
public class Searcher extends Thread implements ISearcher {
@Autowired
protected ISessionProvider sessionProvider; <-- always null
...
public Searcher() {
sessionProvider.doSomeStuff();
}

sessionProvider here is always null.

The strange thing is that the same autowire in a @Controller does work:

@Controller
@RequestMapping("/search")
@Secured({ "ROLE_USER" })
public class SearchController  extends BaseController {
@Autowired
protected ISessionProvider sessionProvider; <-- does work 
@Autowired
protected ISearcher searcher;

The last line throws exception because the constructor of Searcher (implementing ISearcher) tries to access sessionProvider, which is null.

I am not sure what i might be doing wrong, it looks like spring doesn't autowire the ISessionProvider in Searcher.

It might be that spring first autowires the Searcher in SearchController, but it should first autowire SessionProvider in Searcher and next autowire Searcher in SearchController. Cause searcher cannot be autowired without a valid SessionProvider. Puzzles my brain ;-)

Can somebody offer a helping brain?

[edit]

  • component-scan includes my services, controllers and everything, just checked.
  • the I before interfaces is indeed not very nice (old habits)
  • not sure if this is a "duplicate" question, mainly because i'm not doing anything with "new", i let do spring do all the hard work, but i'll take a better peek.
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
TinusSky
  • 1,657
  • 5
  • 24
  • 32

4 Answers4

11

Spring will first create the bean instance, then inject the beans. You're trying to access to the injected bean when the current bean is created, thus the bean will be null. That's default behavior.

If you want/need to execute any logic after creating the bean, use @PostConstruct decorated method, which is invoked after the bean has been created and all the dependencies injected. Here's a sample:

@Service
public class Searcher extends Thread implements ISearcher {

    @Autowired
    protected ISessionProvider sessionProvider;

    public Searcher() {
        //nothing should be here...
    }

    @PostConstruct
    public void init() {
        sessionProvider.doSomeStuff();
    }
}
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • Cheers all, especially Luiggi and M.Deinum, using @PostConstruct works! Everything is running fine and no more nulls anymore :) – TinusSky May 07 '14 at 14:26
7

Spring can only do dependeny injection after the bean has been constructed. You are calling the method in the constructor and at that point the ISessionProvider hasn't been injected yet and hence it is null which in turn leads to a nice NullPointerException.

You have 2 solutions

  1. Move the code from the constructor to a method annotated with @PostConstruct
  2. Change the default no-arg constructor to take an argument and use that to do dependeny injection instead of the @Autowired field.

Solution 1: move that code to a method annotated with @PostConstruct.

@Service
public class Searcher extends Thread implements ISearcher {
    @Autowired
    protected ISessionProvider sessionProvider;
    ...
    public Searcher() {}

    @PostConstruct
    public void init() {
        sessionProvider.doSomeStuff();
    }

Solution 2: Use constructor based dependency injection.

@Service
public class Searcher extends Thread implements ISearcher { 

    protected final ISessionProvider sessionProvider;

    @Autowired
    public Searcher(ISessionProvider sessionProvider) {
        this.sessionProvider=sessionProvider;
        sessionProvider.doSomeStuff();
    }
}
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • Thanks for the alternative one using constructor based dependency injection, i just gave it a shot and that also works nicely. It seems to be a bit cleaner to me compared to the @PostConstruct solution. – TinusSky May 07 '14 at 14:32
  • +1 for constructor dependency injection! Makes testing so much easier than the `@PostConstruct` method – geoand May 07 '14 at 14:44
  • Advantage of constructor based injection is that you cannot (easily) create an invalid object. With `@PostConstruct` and a default constructor you still can create an object without an `ISessionProvider`. – M. Deinum May 07 '14 at 14:45
1

I didn't do test, but I think the problem is, in class Searcher, you created a no-argument constructor, and there you used the "autowired" bean. I guess there you would get NPE. Since spring would instantiate your Searcher using default constructor (by reflection) if you didn't specify one, that is, it will use the no-argument constructor you created, but at this moment, the "autowired" bean was not yet injected.

If you want to do something immediately after the bean was instantiated, you can wrap the logic code in a method, and annotate it with @PostConstruct.

Kent
  • 189,393
  • 32
  • 233
  • 301
0

I am also getting the same issue but i cannot use @PostConstruct because i am not using doinit. I have a simple method which is getting the language from lang service. My lang service is null here but in another controller it is not null, it is getting autowired.

@Service
@Configurable(preConstruction = true, autowire = Autowire.BY_NAME)  
public class FormSelectLanguageHandler extends SimpleTagSupport {

    @Autowired
    private LangService langService;

    public List<Lang> getLanguages(){
        Sessions session = (Sessions) getJspContext().findAttribute("SHLSESSION");
        List<Lang> languages=new ArrayList<Lang>();
        //List<Lang> allLanguages = langService.findAllLangs();
        List<Lang> all =new ArrayList<Lang>();
        if (isRenderLangLabel()) {
            all = langService.findByDisplayOrderAsc();
        } else {
            all = langService.findByNameOrderByNameAsc();
        }
        Long companyId = (null == session || null == session.getCompany())? null:session.getCompany().getId();
    }
}
basZero
  • 4,129
  • 9
  • 51
  • 89
KESHAV
  • 1
  • 1