What is the difference between using "autowired" annotation and "private final" to a repository in the service layer?

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

What is the difference between using "autowired" annotation and "private final" to a repository in the service layer?

问题

我看过一些教程,它们使用不同的语法来实现相同的功能。一旦通过控制器收到创建学生对象的POST请求,服务层就会使用以下两种方法注入存储库。

方法1:

@Service
@AllArgsConstructor
@Transactional
public class StudentService {
    private final StudentRepository studentRepo;
    // 使用studentRepo完成服务代码
}

以及方法2:

@Service
public class StudentService {
    @Autowired
    private StudentRepository studentRepo;
    // 使用studentRepo完成服务代码
}

我了解到这与构造函数注入和字段注入有关,但我真的不明白这种语法如何解决差异。是否有任何解释或资源可以帮助我更好地理解?提前感谢您!

英文:

I've seen tutorials where they use different syntax to accomplish the same thing. Once a POST request to create a student object comes in via the controller, the service layer injects the repository using these two methods.

method 1:

@Service
@AllArgsConstructor
@Transactional
public class StudentService {
    private final StudentRepository studentRepo;
    // complete service code using studentRepo
}

As well as Method 2:

@Service
public class StudentService {
    @Autowire
    private StudentRepository studentRepo;
    // complete service code using studentRepo
}

I read that it has something to do with constructor and field injection but I'm seriously not understanding how this syntax addresses the difference. Any explanations or resources for me to better understand?
Thank you in advance!

答案1

得分: 6

你现在正在叠加许多框架,这增加了混淆度。混淆可能是因为您正在使用 Lombok。@AllArgsConstructor 会自动添加一个构造函数,其中包含构建服务实例所需的所有参数。

> @AllArgsConstructor 会为类中的每个字段生成一个参数的构造函数。带有 @NonNull 标记的字段会在这些参数上进行空值检查。- 来源:Lombok 文档

使用 @AllArgsConstructor 效果上生成了以下类

@Service
@Transactional
public class StudentService {

    private final StudentRepository studentRepo;

    public StudentService(StudentRepository studentRepo) {
        this.studentRepo = studentRepo;
    }
    // 使用 studentRepo 的完整服务代码
}

现在,由于这个类只有一个构造函数,Spring 将使用它进行依赖注入。这被称为基于构造函数的依赖注入。

@Service
@Transactional
public class StudentService {
    @Autowired
    private StudentRepository studentRepo;
    // 使用 studentRepo 的完整服务代码
}

而这被称为基于字段的依赖注入。

依我个人看法,出于以下简单的原因,您应该更喜欢基于构造函数的依赖注入,因为它非常容易使用并且直截了当。您可以轻松进行测试,而在字段注入中编写单元测试较难,因为您需要使用反射来注入字段。

还请参阅 Java 中的依赖注入 以获取更详细的不同依赖注入方式的解释。

英文:

You are stacking a lot of frameworks which are adding to the confusion. The confusion is probably due to the fact you are using Lombok. The @AllArgsConstructor automatically adds a constructor with all the arguments needed to construct an instance of the service.

> @AllArgsConstructor generates a constructor with 1 parameter for each field in your class. Fields marked with @NonNull result in null checks on those parameters. - Source: Lombok Documentation

Using the @AllArgsConstructor effectively generates the following class

@Service
@Transactional
public class StudentService {

    private final StudentRepository studentRepo;

    public StudentService(StudentRepository studentRepo) {
        this.studentRepo=studentRepo;
    }
    // complete service code using studentRepo
}

Now as this class has only a single constructor Spring will use this to do dependency injection. This is called constructor based dependency injection.

@Service
@Transactional
public class StudentService {
    @Autowire
    private StudentRepository studentRepo;
    // complete service code using studentRepo
}

Whereas this is called field based dependency injection.

IMHO you should prefer constructor based dependency injection for the simple reason it is very easy to use and right in your face. You can easily test it while with field injection writing a unit test is hard(er) as you need reflection to inject the field.

See also Dependency Injection in Java for a more thorough explanation of different dependency injection styles.

答案2

得分: 2

依赖注入可以通过不同的方式实现。你所提到的两种方式,可能不太清楚的是:

  1. 字段注入
@Service
public class StudentService {
  @Autowired
  private StudentRepository studentRepo;
   // 使用studentRepo完成的服务代码
}
  1. 基于构造函数的依赖注入
@Service
//@AllArgsConstructor <-- 暂时注释掉,可能是你混淆的来源
@Transactional
public class StudentService {
    private final StudentRepository studentRepo;

     // 对于Spring Boot 1.4及以上版本,构造函数上使用@Autowired是可选的
     // 通过构造函数注入依赖
    public StudentService(StudentRepository studentRepo) {
             this.studentRepo = studentRepo;
    }
     // 使用studentRepo完成的服务代码
}

现在,如果你添加了@AllArgsConstructor(这是来自Project Lombok的一个注解),构造函数将会在.class文件中为你生成,包括所有成员字段,并进行必要的依赖注入。

英文:

Dependency injection can be achieved in different ways. The two ways that you're talking about and probably not understand clearly are:

  1. Field Injection:
@Service
public class StudentService {
  @Autowired
  private StudentRepository studentRepo;
   // complete service code using studentRepo
}
  1. Constructor Based DI:
@Service
//@AllArgsConstructor <-- commenting this for now. Probably your source of confusion
@Transactional
public class StudentService {
    private final StudentRepository studentRepo;

     // For Spring Boot 1.4 and above, using @Autowired on constructor is optional
     // Dependency being injected through constructor
    public StudentService(StudentRepository studentRepo) {
             this.studentRepo = studentRepo;
    }
     // complete service code using studentRepo
}

Now, if you put @AllArgsConstructor (an annotation from project lombok), the constructor would be generated with all the member fields for you in the .class file and required DI would take place.

huangapple
  • 本文由 发表于 2020年8月5日 13:46:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/63259116.html
匿名

发表评论

匿名网友

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

确定