false_azure
false_azure

Reputation: 1503

Autowiring a DAO into a Service

I'm using Spring and am trying to autowire (using annotations) a DAO into a Service, which is then wired into a controller. Having

@Autowired
Movie movieDao;

on its own doesn't work, as I think the new method gets called, so that DAO isn't managed by Spring. The following does work, but it will look messy if I have to copy and paste that context configuration into each method

    @Autowired
    MovieDao movieDao;

    @Override
    public List<Movie> findAll() {
        GenericXmlApplicationContext context = new GenericXmlApplicationContext();
        context.load("classpath:app-context.xml");
        context.refresh();
        MovieDao movieDao = (MovieDao) context.getBean("movieDao", MovieDao.class);
        return movieDao.findAll();
    }

where this code is in my Service class. Is there a more elegant way to ensure that my DAO is initialised properly, rather than copying and pasting the first 4 lines of that method into each Service method?

[edit] The class that contains the code above is a class called MovieServiceImpl, and it essentially corresponds to the DataServicesImpl class in the architecture described on this page. (I'll add a summary/description of that architecture and what I'm trying to do soon). This is the code: http://pastebin.com/EiTC3bkj

Upvotes: 2

Views: 6272

Answers (1)

Kuhess
Kuhess

Reputation: 2591

I think that the main problem is that you want to instantiate your service directly (with new) and not with Spring:

MovieService movieService = new MovieServiceImpl();

When you do this, your MovieServiceImpl instance is constructed but not initialised (the field @Autowired MovieDao is null).

If you want to instantiate properly your object with field injection, you need to use Spring. As explained in the documentation or in this example, you can automatically detect all your annotated beans and initialize them in your context with the component scanning.

Example

In your case, using annotiations on (@Component, @Service, etc) and in (@Autowired, @Inject, etc) your beans, your project could look like this:

Spring configuration app-context.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Use component scanning to auto-discover your beans (by annotation) and initialize them -->
    <context:component-scan base-package="com.se325.a01" />

    <!-- No need to declare manually your beans, because beans are auto-discovered thanks to <context:component-scan/> -->

</beans>

Entry point of your application App.java

package com.se325.a01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.se325.a01.model.Movie;
import com.se325.a01.service.MovieService;

public class App {
    public static void main(String[] args) {
        // Let's create the Spring context based on your app-context.xml
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"app-context.xml"});
        // Now your context is ready. All beans are initialised.
        // You can retrieve and use your MovieService
        MovieService movieService = context.getBean("movieService");

        Movie matrixMovie = new Movie("Matrix");
        movieService.create(matrixMovie);
    }
}

In fact, when you are using Spring, it is really important to understand how the context is initialized. In the example above, it can be sum up as:

  1. Your entry point App#main is called.
  2. The configuration app-context.xml is loaded by ClassPathXmlApplicationContext.
  3. The package com.se325.a01 is scanned thanks to the line <context:component-scan base-package="com.se325.a01" />. All annotated beans (@Component, @Service, etc) are contructed but not yet initialised.
  4. When all the beans are constructed, Spring initialises them by injecting dependencies. In the example, the @Autowired annotations which mark the dependencies are also discovered thanks to the line <context:component-scan ... \>.
  5. The context is ready with all beans :)

Notes

All this answer explains how you can use component scanning and annotations to use Spring in a main entry point. However, if you are developing a server application, the entry point is the WEB-INF/web.xml.

As @chrylis said, field injection is error prone. Prefer using constructor-based injection.

Upvotes: 1

Related Questions