英文:
How to handle @Autowire object in its parent classes?
问题
这是您提供的翻译内容:
我知道类似的问题在这里之前已经被问了很多次,但是机制仍然让我感到困惑。
这是我的问题。在CategoryService
中,CategoryDAO
对象引发了空指针异常。
@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();
}
}
在这个高评分帖子和这个帖子中,两个顶级答案都提到最可取的选项是让Spring自动装配所有的bean
。
这是否意味着一旦我需要,我也必须在其他类中自动装配CategoryService
?这是否意味着如果一个类包含自动装配的对象,我就不能使用new
运算符来初始化一个类?
如果是的话,您能解释一下背后的原因吗?
谢谢
更新
这里有一个关于使用自动装配类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, "没有记录任何类别,请先设置类别");
MainPanel.getInstance().getPanel().display(CategoryPanel.getInstance());
return;
}
}
在new CategoryService().list().size() == 0
中使用了CategoryService
。如果我在这个类中将它自动装配为属性,那么一旦我需要它,这个类也将需要被注入。我希望避免这种情况,以便事情更简单。我该如何实现这一点?
英文:
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?
答案1
得分: 1
这是否意味着一旦我需要,我还必须在其他类中也自动装配CategoryService?
是的。
这意味着如果一个类包含自动装配的对象,我就不能使用new运算符初始化一个类吗?
是的。
@Autowire
注解使您能够使用依赖注入。这是一种使得在应用程序中更容易更改接口使用的实现的技术(或实际上是一种良好的实践)。您定义将在您在属性或构造函数参数上使用@Autowire
注解时注入的bean/组件/服务。您不再在代码中到处使用new
,而是声明哪个具体类应该用于使用@Autowire
注解标注的接口(或可能是类本身)。
想象一下,您创建了一个接口RemoteAccess
和一个实现FtpRemoteAccess
,然后每次您需要它时,您都写RemoteAccess remoteAccess = new FtpRemoteAccess();
。经过一段时间,您可能会在几个地方都有这行代码。现在,如果您需要将其更改为HttpRemoteAccess
,因为您有了这个新的更好的选择,您必须检查所有的代码库。相反,如果您使用了依赖注入,您只需更改bean(使用Spring有多种方法可以实现这一点)。
为了使所有这些工作正常,Spring必须能够注入bean的所有依赖关系。如果您创建一个bean,它的所有属性也必须被注入,因为Spring将为您创建该对象。
澄清:
在您的bean内部(即将被注入的类),您可以使用new
创建对象,前提是这是有意义的,并且这些对象不是注入的类型。您已经在CategoryService::add()
中这样做了,这是可以的。依赖注入并不意味着您将不再使用new
。您只会避免在Spring依赖注入将管理的对象上使用它。
然后,还有其他良好的实践,不鼓励使用new
,比如静态工厂方法,建议在您的类中放置一个静态方法来构建完整的对象,并让构造函数为私有。但您不需要始终应用所有这些模式。
更新:
对于您的RecordListener
类,您必须添加一个CategoryService
属性,并确保它被初始化。有两种选择:您可以将RecordListener
本身转换为一个bean,并在需要的地方进行自动装配。这样,Spring将构建一个RecordListener
对象以进行注入,并且还将添加任何其他所需的bean(例如CategoryService
)。
@Component
public class RecordListener implements ActionListener {
@Autowired
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;
}
}
}
另一种选择是在当前创建RecordListener
的类中注入CategoryService
,然后将其作为构造函数参数传递。在这种情况下,RecordListener
将不会是一个bean。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论