1. 소개
Guice는 애플리케이션이 종속성 주입(DI) 패턴을 더 쉽게 사용할 수 있도록 만들어주는 프레임워크입니다.
종속성 주입(Dependency injection)은 클래스가 종속성들을 직접 생성하는 대신 종속성을 인수로 선언하는 디자인 패턴입니다. 예를 들어, 클래스 'A'는 작업을 수행하기 위해 클래스 'B'가 필요 한데, 클래스 'A'는 클래스 'B' 객체를 빌드하는 방법 - 이것은 외부에서 수행 - 에 대해 걱정할 필요가 없습니다.
다음처럼 사용자 데이터를 처리하는 애플리케이션을 구축하는 예를 살펴봅시다. 데이터베이스와 통신하는 UserService라는 서비스 클래스를 우리는 선언 했습니다.
public class UserService {
private Datastore datastore;
public UserService() {
this.datastore = new Datastore("/org/my-datastore");
}
}
위의 예에서 UserService 클래스는 Datastore를 인스턴스화하고 있습니다. 이렇게 하면 Datastore 클래스가 UserService 클래스와 밀접하게 결합됩니다.
나중에 Datastore를 변경하려한다면 UserService 클래스도 변경 해야 할 것입니다. 이러한 관계들이 이 클래스에 대한 테스트를 매우 어렵게 만듭니다.
테스트할 수 없거나 유연하지 않은 코드를 작성하는 대신 종속성 주입 패턴을 사용하여 이러한 모든 문제를 해결할 수 있습니다.
아래는 동일한 예이지만 종속성 주입을 사용 한 예입니다:
public class UserService {
private Datastore datastore;
public UserService(Datastore datastore) {
this.datastore = datastore;
}
}
위 UserService 클래스는 Datastore가 어떤 식으로 생성되는 방식을 알지 못하는 어떤 종류의UserService 클래스라는 것만 알고 있으므로, 어떤 종류의 Datastore와 함께 사용할 수 있습니다.
테스트 목적으로 물론 메모리 내 데이터베이스를 사용할 수도 있습니다.
2. 바인딩
Guice는 포함된 도메인 특정 언어(EDSL)를 사용하여 간단하고 읽기 쉬운 바인딩을 만들 수 있습니다. 이 접근 방식은 대단한 전체 사용성을 가지며, 비용이 적게 듭니다. 메서드 수준 javadoc을 읽고서 바인딩 EDSL을 사용하는 방법을 배우는 것은 어렵습니다.
간단한 클래스를 바인딩하는 것은:
bind(DefaultImpl.class);
위 구문은 기본적으로 아무 것도 하지 않습니다. "ServiceImpl 클래스를 자기 자신에 바인딩"하고 Guice의 기본 동작을 변경하지 않습니다. 모듈 클래스가 제공하는 서비스에 대한 명시적 매니페스트 역할을 하는 것을 선호하는 경우 이 방식을 그대로 고수하고 싶을 수 있습니다.
다음처럼, 우리는 특정 서비스의 구현물을 바인딩할 수 있습니다.
bind(Service.class).to(ServiceImpl.class);
이것은 바인딩 주석이 없는 Service 인스턴스에 대한 요청이 ServiceImpl 인스턴스에 대한 요청인 것처럼 처리되어야 함을 지정합니다. Guice는 이러한 주석을 찾기 시작하는 위치에 도달하기 전에 이미 ServiceImpl로 이동했을 것이기 때문에 이는 Service 에서 발견된 @ImplementedBy 또는 @ProvidedBy 주석의 기능을 재정의합니다.
bind(Service.class).toProvider(ServiceProvider.class);
위 예에서 보면, ServiceProvider는 Provider를 확장하거나 구현해야 합니다.
이 바인딩은 Guice는 첫 번째 정규화된 방식으로 ServiceProvider의 인스턴스를 리졸브 하고 난 다음 Provider 인스턴스가 Service 인스턴스를 얻은 결과로서 get() 을 호출함으로써 Service에 대한 어노테이션 되지 않은 주입 요청을 리졸브 해야만 하는 것으로 특정 할 수 있습니다.
bind(Service.class).annotatedWith(MyBinder.class).to(ServiceImpl.class);
앞선 예제와 같지만 바인딩 주석 @MyBinder를 사용하는 주입 요청에만 적용됩니다.
3. 예제
이 섹션에서는 실제 예제를 둘러볼 것입니다. maven 프로젝트를 만들고 아래와 같이 google guice에 대한 종속성을 정의해 보겠습니다:
<dependency>
<groupid>com.google.inject</groupid>
<artifactid>guice</artifactid>
<version>5.1.0</version>
</dependency>
pom.xml
<!--?xml version="1.0" encoding="UTF-8"?-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>
<groupid>org.example</groupid>
<artifactid>JavaCodeGeeks</artifactid>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupid>com.google.inject</groupid>
<artifactid>guice</artifactid>
<version>5.1.0</version>
</dependency>
</dependencies>
</project>
위 처럼 정의하면 다음 간단한 예제에 필요한 클래스들을 사용할 수 있습니다. 먼저 매우 간단한 도메인 클래스 User를 정의하겠습니다.
User.java
package org.javacodegeeks;
public record User (String username, String address) {}
JDK 14에는 새로운 종류의 유형 선언인 레코드가 도입되었습니다.
enum(열거형)과 마찬가지로 record(레코드)는 클래스의 제한된 형식입니다.
데이터를 변경하지 않고 생성자 및 접근자와 같은 가장 기본 메서드들만 포함하는 "일반 데이터 캐리어"에 이상적인 클래스 입니다.(“plain data carriers”)
이제 사용자와 관련된 요청을 처리할 컨트롤러 클래스를 정의해 봅시다. 단순하게 이 클래스에는 사용자를 등록하는 하나의 메서드만 정의합니다. 이 메서드는 이름, 성 및 주소의 세 가지 매개 변수를 사용하고 서비스 메서드를 호출합니다.( first name, surname, address) 서비스는 Guice @Inject 주석을 사용하여 컨트롤러에 주입됩니다.
UserController.java
package org.javacodegeeks;
import com.google.inject.Inject;
public class UserController {
@Inject private UserService userService;
public void registerUser(String firstname, String surname, String address) {
userService.registerUser(new User(firstname + "_" + surname, address));
}
}
이제 간단한 서비스 인터페이스를 정의해 보겠습니다.
UserService.java
package org.javacodegeeks;
public interface UserService {
void registerUser(User user);
}
다음은 서비스의 기본 구현입니다:
DefaultUserService.java
package org.javacodegeeks;
public class DefaultUserService implements UserService {
@Override
public void registerUser(User user) {
// TODO - implement
System.out.println(String.format("User %s registered successfully", user.username()));
}
}
이제 이 기본 구현을 서비스에 바인딩해 보겠습니다.
BasicModule.java
package org.javacodegeeks;
import com.google.inject.AbstractModule;
public class BasicModule extends AbstractModule {
@Override
protected void configure() {
bind(UserService.class).to(DefaultUserService.class);
}
}
메인 클래스를 생성 해 봅시다:
Application.java
package org.javacodegeeks;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class Application {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new BasicModule());
UserController userController = injector.getInstance(UserController.class);
userController.registerUser("Tom", "Cruise", "NYC");
}
}
Guice는 Guice 프레임워크의 진입점입니다. 이 클래스는 모듈에서 인젝터를 생성합니다. Guice는 API, 해당 API의 구현, 해당 구현물을 구성하는 모듈들, 마지막으로 모듈 모음으로 구성된 애플리케이션 사이에 명확한 경계를 그리는 개발 모델을 지원합니다.
Guice 클래스를 사용하여 Guice Injector를 부트스트랩하는 것은 일반적으로 main() 메서드를 애플리케이션으로 정의하는 것 입니다.
Application 클래스를 실행하면 다음과 같은 출력이 표시됩니다.:
User Tom_Cruise registered successfully
4. 마치면서
이 기사에서는 의존성 주입을 위해 Google Guice를 사용하는 방법을 살펴보았습니다. 이 예는 매우 간단한 예입니다. 이 문서에서 언급한 것 이외에도 더 많은 고급 기능이 있습니다. 예를 들어, @Named 주석으로 바인딩하는 클래스에 주석을 달 때 Named 바인딩을 사용할 수 있으며 모듈에서 다음과 같이 할 수 있습니다:
bind(MyInterface.class)
.annotatedWith(Names.named("NamedBinding"))
.to(DefaultImpl.class);
또한 클래스를 바인딩하는 다양한 방법을 살펴보았습니다. 결국, 우리는 매우 간단한 작업 예제를 살펴 보았습니다.
5. 소스 다운로드
이 글은 Google Guice의 예 였습니다.
'프로그래밍' 카테고리의 다른 글
아파치 POI로 하는 엑셀 프로그래밍-2 (0) | 2022.09.07 |
---|---|
아파치 POI로 하는 Excel 프로그래밍 (0) | 2022.09.03 |
DisconnectedContext 오류 (0) | 2022.04.28 |
[자바]특정 이미지에 투명도 입히기 (0) | 2022.04.11 |
[자바] Argument Parser (0) | 2022.04.10 |