chaos
chaos

Reputation: 400

How to handle @Autowire object in its parent classes?

I know similar questions have been asked so many times here before, but I am still confused by the mechanisms.

Here is my problem. There is a null pointer exception coming from the CategoryDAO object in the CategoryService.

@Service
public class CategoryService {
    @Autowired
    private CategoryDAO categoryDAO;

    public List<Category> list(){
        List<Category> categories = categoryDAO.list();
        for (Category category : categories){
            List<Record> rs = recordDAO.list(category.getID());
            category.setRecordNumber(rs.size());
        }
        return categories;
    }

    public void add(String name){
        Category newCategory = new Category();
        newCategory.setName(name);
        categoryDAO.add(newCategory);
    }
}
@Repository
public class CategoryDAO {
    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    public int getTotal(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<Category> categories = sqlSession.selectList("category.selectAll");

        return categories.size();
    }
}

In this top rated post and this one, both of the top answers mentioned that The most preferable option is to let Spring autowire all of your beans. Does it mean I have to also autowire the CategoryService in other classes once I need it? Which means I cannot use new operator to initialise a class if it contains autowired object? If yes, could you please explain the reason behind it?

Thanks

UPDATE

Here is an example about using the autowired class CategoryService:

public class RecordListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        RecordPanel panel = RecordPanel.getInstance();
        if (new CategoryService().list().size() == 0){
            JOptionPane.showMessageDialog(panel, "NO category is recorded, set category first");
            MainPanel.getInstance().getPanel().display(CategoryPanel.getInstance());
            return;
        }
}

The CategoryService is used in new CategoryService().list().size() == 0. If I autowire it as a property of this class here then this class will also need to be injected once I need it. I would like to avoid that so things could be easier. How can I achieve that?

Upvotes: 0

Views: 1274

Answers (1)

drkblog
drkblog

Reputation: 398

Does it mean I have to also autowire the CategoryService in other classes once I need it? Yes.

Which means I cannot use new operator to initialise a class if it contains autowired object? Yes.

The @Autowire annotation enables you to use Dependency Injection. A technique (or good practice, actually) that makes it easy to change the implementations you use for your interfaces in your application. You define beans/component/services that will get injected whenever you use the @Autowire annotation over an attribute or a constructor parameter. Instead of using new all over your code you just declare which concrete class should be used for an interface (or maybe the class itself) annotated with @Autowire. Imagine you create an interface RemoteAccess and an implementation FtpRemoteAccess and then every time you need it you write RemoteAccess remoteAccess = new FtpRemoteAccess(); After a while you might end up with that line over several places. Now, if you need to change this to HttpRemoteAccess because you have this new, better alternative, you have to review all your code base. Instead, if you used dependency injection you would just change the bean (there is more than one way to do that using Spring). For all this to work, Spring must be able to inject all the dependencies of a bean. If you create a bean, all its attributes must be injected too because Spring will create that object for you.

Clarification:

Inside your bean (namely, you classes that will be injected) you can create objects using new provided that makes sense and those object are not injected types. You are already doing that in CategoryService::add() and it is ok. Dependency injection doesn't mean you will not ever write new again. You will just avoid it for objects that will be managed by Spring dependency injection. Then, there are other good practices that disencourage using new like the static factory method that recommend putting a static method in your class to build complete objects and letting the constructor to be private. But you don't need to apply all the patterns all the time.

UPDATE:

For your RecordListener class you have to add a CategoryService attribute and then be sure it is initialized. There are two options: you can convert RecordListener in a bean itself and have it autowired where you need that. This way Spring will construct a RecordListener object for injecting it and will also add any other bean that is needed (like CategoryService)

@Component
public class RecordListener implements ActionListener {

    @Autowire
    private CategoryService categoryService;

    @Override
    public void actionPerformed(ActionEvent e) {
        RecordPanel panel = RecordPanel.getInstance();
        if (categoryService.list().size() == 0) {
            JOptionPane.showMessageDialog(panel, "NO category is recorded, set category first");
            MainPanel.getInstance().getPanel().display(CategoryPanel.getInstance());
            return;
        }
    }
}

The other option is you inject CategoryService in the class that is currently creating the RecordListener and then pass it as constructor argument. In that case RecordListener will not be a bean.

Upvotes: 1

Related Questions