Reputation: 400
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
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