如何处理在其父类中的 @Autowire 对象?

huangapple go评论78阅读模式
英文:

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&lt;Category&gt; list(){
        List&lt;Category&gt; categories = categoryDAO.list();
        for (Category category : categories){
            List&lt;Record&gt; 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&lt;Category&gt; categories = sqlSession.selectList(&quot;category.selectAll&quot;);

        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, &quot;NO category is recorded, set category first&quot;);
            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, &quot;NO category is recorded, set category first&quot;);
            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.

huangapple
  • 本文由 发表于 2020年9月20日 11:40:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/63975291.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定