Open bsj805 opened 2 years ago
https://www.baeldung.com/project-jigsaw-java-modularity
어떤 단위로 모듈을 잡아야 할까? JAR들이 보통 모듈의 단위로 많이 여겨졌다.
JAR
들이 실제로 연관되어 있는 컴포넌트를 묶는데에는 도움이 됐지만, 이런 문제들이 있었다.
또 다른 문제로는 JAR hell
이 있었다. classpath
에 있는 여러가지 버전의 JAR
들은 ClassLoader
가 그냥 JAR
들중에 제일 첫번째로 발견되는 class를 사용하게 했다. 아주 unexpected
한 결과를 내는 일이었다.
즉, JAR이 아닌 다른 모듈 단위가 필요했고, module
기반으로 java를 구성하자는 계획을 세웠다.
모듈 시스템은 language단에서 구현되었다.
module definition file, named as module-info.java
는 이런걸 포함한다
src
|----com.baeldung.reader
| |----**module-info.java**
| |----com
| |----baeldung
| |----reader
| |----Test.java
|----com.baeldung.writer
|----**module-info.java**
|----com
|----baeldung
|----writer
|----AnotherTest.java
모듈의 일반적인 구조는 이렇다.
이 위의 구조는 두가지의 모듈을 가지고 있다. com.baeldung.reader
와 com.baeldung.writer
Let us look at some of the terminologies; we will use while defining the module (i.e., within the module-info.java):
module: the module definition file starts with this keyword followed by its name and definition
requires: is used to indicate the modules it depends on; a module name has to be specified after this keyword
transitive: is specified after the requires keyword; this means that any module that depends on the module defining requires transitive
exports: is used to indicate the packages within the module available publicly; a package name has to be specified after this keyword
opens: is used to indicate the packages that are accessible only at runtime and also available for introspection via Reflection APIs; this is quite significant to libraries like Spring and Hibernate, highly rely on Reflection APIs; opens can also be used at the module level in which case the entire module is accessible at runtime
uses: is used to indicate the service interface that this module is using; a type name, i.e., complete class/interface name, has to specified after this keyword
provides … with ...: they are used to indicate that it provides implementations, identified after the with keyword, for the service interface identified after the provides keyword
모듈 : 모듈 definition 파일이 이름이랑 정의로 시작한다.
requires: 이 모듈이 어떤 모듈에 의존하는지 나타낼 때 사용. 이 뒤에 모듈 이름이 와야한다.
transitive: requires 다음에 transitive를 쓸 수 있는데, requires transitive <module name>
을 쓴 모듈을 depend on 하는 모듈들은 implicit
명시적 dependence를 가진다. 즉 A
depend on B
하고 B의 정의에 requires transitive <module name>
이 있다면, A
는
exports: exports
opens: runtime에만 접근할 수 있는 패키지를 나타내기 위해 사용하고,
uses: 는 이 모듈이 사용하는 서비스 interface를 나타내기 위해 쓰인다. type 이름, complete class, interface name이 이 키워드 다음에 나와야 한다.
provides ... with ... 은 implementation을 제공한다고 말하기 위해서 사용된다. with 뒤에 나오는 키워드로 어떤 구현체가 있는 것인지 나타낸다. provides 다음에 나타나는 서비스 인터페이스를 위한 구현체이다.
com.baeldung.student.model이 root 모듈이다. com.baeldung.student.model.Student 클래스를 가지고 있다.
public class Student {
private String registrationId;
//other relevant fields, getters and setters
}
com.baeldung.student
에서는 com.baeldung.student.model
에 있는 클래스를 사용하게 되니까,
module-info.java
에 이렇게 정의하면 된다.
module com.baeldung.student.model {
exports com.baeldung.student.model;
}
com.baeldung.student.service
에서는 com.baeldung.student.service.StudentService
인터페이스를 통해
public interface StudentService {
public String create(Student student);
public Student read(String registrationId);
public Student update(Student student);
public String delete(String registrationId);
}
이런 CRUD를 제공하고 있다. 그럼 Student
때문에 com.baeldung.student.model' 모듈에 의존한다. 다른 모듈이
com.baeldung.student.service를 쓸 수 있게 하고,
model`이라는 모듈을 사용한다는 것을 알려줘야 한다.
module com.baeldung.student.service {
requires transitive com.baeldung.student.model;
exports com.baeldung.student.service;
}
com.baeldung.student.service.dbimpl
은 com.baeldung.student.dbimpl.StudentDbService
클래스를 저 service interface를 통해 구현했다.
public class StudentDbService implements StudentService {
public String create(Student student) {
// Creating student in DB
return student.getRegistrationId();
}
public Student read(String registrationId) {
// Reading student from DB
return new Student();
}
public Student update(Student student) {
// Updating student in DB
return student;
}
public String delete(String registrationId) {
// Deleting student in DB
return registrationId;
}
}
그럼 이모듈은 com.baeldung.student.service
에 directly 의존하고, transitively on com.baeldung.student.model
에 의존한다.
module-info.java
에 이렇게 쓸 수 있다.
module com.baeldung.student.service.dbimpl {
requires transitive com.baeldung.student.service;
requires java.logging;
exports com.baeldung.student.service.dbimpl;
}
마지막은 client module인데, module com.baeldung.student.service.dbimpl.StudentService
를 사용해서 CRUD를 한다.
그러니 depend on service.dbimpl이겠지?
public class StudentClient {
public static void main(String[] args) {
StudentService service = new StudentDbService();
service.create(new Student());
service.read("17SS0001");
service.update(new Student());
service.delete("17SS0001");
}
}
module com.baeldung.student.client {
requires com.baeldung.student.service.dbimpl;
}
이게 모듈을 쓴다고 해서 package를 안 쓰는 것은 아니고, 보통 다 만들고 모듈러라이즈 하기 위해 module-info.java
를 만드는 듯.
https://developer.okta.com/blog/2020/07/27/spring-boot-using-java-modules
spring (version 5)는 아직 modularized가 안 되어서 jar 파일들이 module-info.java
를 가지고 있지 않다.
모듈 시스템의 장점은
1. Garbage Collectors
2. Container제한 옵션 사용가능.
컨테이너에 있는지 모르고, 컨테이너의 제한 메모리가 1GB로 정해져 있는데 시스템의 메모리 16GB인 것 읽어서 더 쓰려다가 터지는 경우를 막게 되었다.
3. Project jigsaw
모듈로 변화를 다룬다.