1. 소개

이 기사에서는 Google Guice의 기본 사항을 살펴 봅니다. Guice에서 기본 DI (Dependency Injection) 작업을 완료하는 방법을 살펴 보겠습니다.

또한 Guice 접근 방식을 Spring 및 Contexts and Dependency Injection (CDI)과 같은 기존 DI 프레임 워크의 접근 방식과 비교하고 대조 할 것입니다.

이 기사는 독자가 의존성 주입 패턴 의 기본 사항을 이해하고 있다고 가정합니다 .

2. 설정

Maven 프로젝트에서 Google Guice를 사용하려면 pom.xml에 다음 의존성을 추가해야합니다 .

<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>4.1.0</version>
</dependency>

Guice 확장의 모음 (우리가 그 나중에 조금 다룰 것이다)도있다 여기 뿐만 아니라 타사 모듈 (주로 더 설립 자바 프레임 워크에 통합을 제공함으로써) Guice의 기능을 확장 할 수는.

3. Guice를 사용한 기본 의존성 주입

3.1. 샘플 애플리케이션

헬프 데스크 비즈니스에서 이메일, SMS 및 IM의 세 가지 커뮤니케이션 수단을 지원하는 클래스를 디자인하는 시나리오를 작업 할 것입니다.

수업을 고려하십시오.

public class Communication {
 
    @Inject 
    private Logger logger;
    
    @Inject
    private Communicator communicator;

    public Communication(Boolean keepRecords) {
        if (keepRecords) {
            System.out.println("Message logging enabled");
        }
    }
 
    public boolean sendMessage(String message) {
        return communicator.sendMessage(message);
    }

}

통신 클래스는 통신 의 기본 단위입니다. 이 클래스의 인스턴스는 사용 가능한 통신 채널을 통해 메시지를 보내는 데 사용됩니다. 위에 표시된대로 Communication 에는 실제 메시지 전송을 수행하는 데 사용 하는 Communicator 가 있습니다.

Guice의 기본 진입 점은 인젝터입니다.

public static void main(String[] args){
    Injector injector = Guice.createInjector(new BasicModule());
    Communication comms = injector.getInstance(Communication.class);
}

이 메인 메서드는 Communication 클래스 의 인스턴스를 검색합니다 . 또한 Guice의 기본 개념 인 모듈 ( 이 예제에서는 BasicModule 사용 )을 소개합니다. 모듈 바인딩의 정의의 기본 단위입니다 (이 Spring에 알려진 바와 같이, 배선).

Guice는 의존성 주입 및 관리를위한 코드 우선 접근 방식을 채택 했기 때문에 많은 XML을 즉시 사용하지 않아도됩니다.

위의 예 에서 클래스에 기본 no-arg 생성자 가있는 경우 통신 의 의존성 트리는 Just-in-time binding 이라는 기능을 사용하여 암시 적으로 삽입됩니다 . 이것은 처음부터 Guice의 기능이었으며 v4.3 이후 Spring에서만 사용할 수 있습니다.

3.2. Guice 바인딩

와이어 링은 Spring에 대한 바인딩이므로 Guice에 바인딩됩니다. 바인딩을 통해 Guice가 클래스에 의존성을 주입하는 방법정의합니다 .

바인딩은 com.google.inject.AbstractModule 구현에서 정의됩니다 .

public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Communicator.class).to(DefaultCommunicatorImpl.class);
    }
}

이 모듈 구현 Communicator 변수가 발견 될 때마다 Default CommunicatorImpl 인스턴스 가 삽입되도록 지정합니다 .

이 메커니즘의 또 다른 화신은 이름이 binding 입니다. 다음 변수 선언을 고려하십시오.

@Inject @Named("DefaultCommunicator")
Communicator communicator;

이를 위해 다음과 같은 바인딩 정의가 있습니다.

@Override
protected void configure() {
    bind(Communicator.class)
      .annotatedWith(Names.named("DefaultCommunicator"))
      .to(DefaultCommunicatorImpl.class);
}

이 바인딩은 @Named ( "DefaultCommunicator") 어노테이션으로 어노테이션이 달린 변수 Communicator 의 인스턴스를 제공합니다 .

당신은 알 수 @Inject@Named 어노테이션 자카르타 EE의 CDI에서 대출 어노테이션으로 표시하고, 그들이 있습니다. 그들은에 com.google.inject *. 패키지 -에 IDE를 사용할 때 올바른 패키지에서 가져올주의해야합니다.

팁 : 방금 Guice에서 제공 한 @Inject @Named 를 사용한다고 말했지만Guice는다른 Jakarta EE 어노테이션 중에서 javax.inject.Inject javax.inject.Named에 대한 지원을 제공한다는 점에 유의할 가치가있습니다.

생성자 바인딩을 사용하여 인수가없는 기본 생성자가없는 의존성을 삽입 할 수도 있습니다 .

public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Boolean.class).toInstance(true);
        bind(Communication.class).toConstructor(
          Communication.class.getConstructor(Boolean.TYPE));
}

위의 스 니펫 부울 인수 를 사용하는 생성자를 사용하여 Communication 인스턴스를 삽입합니다 . Boolean 클래스 대상이 지정되지 않은 바인딩정의 하여 생성자에 true 인수를 제공합니다 .

대상 이 지정되지 않은 바인딩부울 매개 변수 를 허용하는 바인딩의 모든 생성자에 즉시 제공됩니다 . 이 접근 방식을 사용하면 Communication의 모든 의존성 이 주입됩니다.

생성자 별 바인딩에 대한 또 다른 접근 방식은 바인딩 에서 직접 인스턴스를 제공하는 인스턴스 바인딩입니다.

public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Communication.class)
          .toInstance(new Communication(true));
    }    
}

이 바인딩은 Communication 변수가 선언 될 때마다 Communication 클래스 의 인스턴스를 제공합니다 .

그러나이 경우 클래스의 의존성 트리는 자동으로 연결되지 않습니다. 과도한 초기화 또는 의존성 주입이 필요하지 않은 경우이 모드의 사용을 제한해야합니다.

4. 의존성 주입 유형

Guice는 DI 패턴에서 기대할 수있는 표준 유형의 주입을 지원합니다. 에서 커뮤니케이터 클래스, 우리는 다른 종류의 주입 할 필요가 CommunicationMode을 .

4.1. Field 주입

@Inject @Named("SMSComms")
CommunicationMode smsComms;

선택적 @Named 어노테이션을 한정자로 사용하여 이름을 기반으로 대상 주입을 구현합니다.

4.2. 방법 주입

여기에서는 주입을 달성하기 위해 setter 메서드를 사용합니다.

@Inject
public void setEmailCommunicator(@Named("EmailComms") CommunicationMode emailComms) {
    this.emailComms = emailComms;
}

4.3. 생성자 주입

생성자를 사용하여 의존성을 삽입 할 수도 있습니다.

@Inject
public Communication(@Named("IMComms") CommunicationMode imComms) {
    this.imComms= imComms;
}

4.4. 암시 적 주입

Guice는 Injectorjava.util.Logger 인스턴스와 같은 일부 범용 구성 요소를 암시 적으로 주입 합니다. 샘플 전체에서 로거를 사용하고 있지만 실제 바인딩을 찾을 수는 없습니다.

5. Guice 범위 지정

Guice는 우리가 다른 DI 프레임 워크에서 사용했던 범위 및 범위 지정 메커니즘을 지원합니다. Guice는 기본적으로 정의 된 의존성의 새 인스턴스를 제공합니다.

5.1. 하나씩 일어나는 것

애플리케이션에 싱글 톤을 주입 해 보겠습니다.

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class).in(Scopes.SINGLETON);

에서 (Scopes.SINGLETON) 어느 것을 지정 통신기 필드 @Named ( "AnotherCommunicator")는 싱글를 얻을 것이다 주입. 이 싱글 톤은 기본적으로 느리게 시작됩니다.

5.2. 열망하는 싱글 톤

이제 eager singleton을 주입 해 보겠습니다.

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class)
  .asEagerSingleton();

asEagerSingleton () 호출은 열심히 인스턴스로 싱글을 정의합니다.

이 두 범위 외에도 Guice는 사용자 지정 범위와 Jakarta EE에서 제공 하는 웹 전용 @RequestScoped@SessionScoped 어노테이션을 지원합니다 (해당 어노테이션의 Guice에서 제공하는 버전은 없음).

6. Guice의 측면 지향 프로그래밍

Guice는 측면 지향 프로그래밍에 대한 AOPAlliance의 사양을 준수합니다. 예제에서 메시지 전송을 추적하는 데 사용할 전형적인 로깅 인터셉터를 4 단계로 구현할 수 있습니다.

1 단계 – AOPAlliance의 MethodInterceptor 구현 :

public class MessageLogger implements MethodInterceptor {

    @Inject
    Logger logger;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object[] objectArray = invocation.getArguments();
        for (Object object : objectArray) {
            logger.info("Sending message: " + object.toString());
        }
        return invocation.proceed();
    }
}

2 단계 – 일반 Java 어노테이션 정의 :

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MessageSentLoggable {
}

3 단계 – Matcher에 대한 바인딩 정의 :

Matcher 는 AOP 어노테이션이 적용될 구성 요소를 지정하는 데 사용하는 Guice 클래스입니다. 이 경우 CommunicationMode 구현에 어노테이션을 적용하려고합니다 .

public class AOPModule extends AbstractModule {

    @Override
    protected void configure() {
        bindInterceptor(
            Matchers.any(),
            Matchers.annotatedWith(MessageSentLoggable.class),
            new MessageLogger()
        );
    }
}

여기에서 MessageLogger 인터셉터를 메서드에 적용된 MessageSentLoggable 어노테이션 이있는 모든 클래스에 적용 할 Matcher를 지정했습니다 .

4 단계 – 통신 모드에 어노테이션을 적용하고 모듈로드

@Override
@MessageSentLoggable
public boolean sendMessage(String message) {
    logger.info("SMS message sent");
    return true;
}

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new BasicModule(), new AOPModule());
    Communication comms = injector.getInstance(Communication.class);
}

7. 결론

기본적인 Guice 기능을 살펴보면 Guice의 영감이 Spring에서 어디에서 왔는지 알 수 있습니다.

JSR-330에 대한 지원과 함께 Guice는 DI 유연성을 원하는 개발자를 대상으로하는 주입 중심 DI 프레임 워크 (Spring은 프로그래밍 편의성을위한 전체 에코 시스템을 제공하는 반면, DI에만 국한되지 않음)를 목표로합니다.

Guice는 또한 고도로 확장 가능 하므로 프로그래머가 프레임 워크를 유연하고 창의적으로 사용할 수있는 휴대용 플러그인을 작성할 수 있습니다. 이것은 Guice가 이미 서블릿, JSF, JPA 및 OSGi와 같은 가장 인기있는 프레임 워크 및 플랫폼에 대해 제공하는 광범위한 통합에 추가됩니다.

이 예제에 사용 된 모든 소스 코드는 GitHub 프로젝트 에서 찾을 수 있습니다 .