Guice 라 는 고급 상품

11747 단어 JavaDB2SpringSQL
더 읽 기
Guice 는 대부분의 시간 을 편리 하고 사용 하기 쉽다.구 이 스 와 스프링 등 다른 용기 의 가장 큰 차이 점 은 구 이 스 는 주입 이 대부분 이름 에 따 른 것 이 아니 라 유형 에 따 른 것 이 라 고 믿는다 는 점 이다.Guice 는 사용 하기에 편리 하 며 대부분 유형 에 따라 주입 된다.
Bind 여러 실현
하지만 한 유형 에 대해 내 가 여러 가지 실현 이 있다 면 어 떡 하지?가장 흔히 볼 수 있 는 문 제 는 두 개의 데이터베이스 가 있다 는 것 이다.

bind(sqlMapClient.class).toInstance(createSqlMapClientForDB1());

만약 두 개의 데이터베이스 가 있다 면,이 binding 은 어떻게 쓸 수 있 습 니까?

bind(sqlMapClient.class).toInstance(createSqlMapClientForDB1());
bind(sqlMapClient.class).toInstance(createSqlMapClientForDB2());

이렇게 쓰 면 Guice 는 sqlMapClient 에 binding 이 존재 한다 고 보고 합 니 다.그리고 사용 하 는 곳.

public class Service1 {
  @Inject
  SqlMapClient sqlMapClient;
}

도대체 db1 을 주입 한 거 야,db2 를 주입 한 거 야?가장 직관 적 인 해결책 은 DB1 과 DB2 가 다른 키 에 binding 을 하 는 것 이다.세 가지 선택 이 있어 요.
선택 1:다른 interface

public class Db1SqlMapClient extends SqlMapClient {
  private final SqlMapClient delegate;
  // delegate all methods of sql map client
}
public class Service1 {
  @Inject
  Db1SqlMapClient sqlMapClient;
}

선택 2:바 인 딩 주석

@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME
public @interface DB1 {
}

bind(SqlMapClient.class)
.annotatedWith(DB1.class)
.with(createSqlMapClientForDB1());

public class Service1 {
  @Inject @DB1
  SqlMapClient sqlMapClient;
}

선택 3:Named binding

bind(SqlMapClient.class)
.annotatedWith(Names.named("DB1"))
.with(createSqlMapClientForDB1());

public class Service1 {
  @Inject @Named("DB1")
  SqlMapClient sqlMapClient;
}

세 가지 방안 중 완벽 한 것 은 하나 도 없다.첫 번 째 는 매우 번 거 롭 고 사용 하 는 곳 은 구체 적 인 유형 을 알 아야 한다.방안 2 가 훨씬 좋 지만 annotation 을 추가 로 정의 해 야 합 니 다.동시에 사용 할 때 자신 이 필요 로 하 는 의존 이 어떤 annotation 에 의 해 표시 되 었 는 지 알 아야 합 니 다.이것 은 약간 serviceLocator.locateService(db1connection)냄새 가 난다.세 번 째 는 더 나 빠 졌 다.사용 하 는 곳 은 자신 이 필요 로 하 는 의존 하 는 이름 을 알 고 이름 은 문자열 이 며 더 이상 안전 을 재 구성 하 는 것 이 아니 라 인용 도 찾 을 수 없다.
Guice 2.0 에 서 는 네 번 째 방안 이 나 왔 다.
선택 4:Child Injector

db1Injector = injector.createChildInjector(new AbstractModule() {
  public void configure() {
    bind(SqlMapClient.class).toInstance(createSqlMapClientForDB1());
  }
});
db2Injector = injector.createChildInjector(new AbstractModule() {
  public void configure() {
    bind(SqlMapClient.class).toInstance(createSqlMapClientForDB2());
  }
});

서로 다른 데이터 베 이 스 를 사용 하려 면 서로 다른 injector 로 주입 해 야 합 니 다.그러나 이런 방식 의 제한 은 사실 더욱 크다.모든 시스템 이 자 연 스 럽 게 여러 개의 구역 으로 나 눌 수 있 는 것 이 아니 라 각 구역 은 서로 다른 용 기 를 사용한다.
종합 적 으로 Guice 는 bid 여러 가지 실현 을 지원 하 는 네 가지 방식 이 있 는데 Guice 가 추천 하 는 방식 은 bidingAnnotation 을 사용 하 는 것 이다.
Bind Set
이것 은 보기에 매우 간단 한 문제 인 것 같은 데,바로 이렇게 쓰 는 것 이 아닌가?

bind(Set.class).toInstance(new HashSet());

하지만 Integer 와 String 두 개의 다른 set 가 필요 합 니 다.어 떻 습 니까?간단 하 다

bind(new TypeLiteral>(){}).toInstance(new HashSet(){{
  add("Hello");
  add("World");
}});

4.567913.무엇 입 니까?어,자바 문법 공부 하 러 가자.
아니면 Guice 2 에 추 가 된 Types 를 이용 할 수 있 습 니 다.
{{}}

보기 도 쉬 워 보이 고.그런데 만약 에 이 Set 멤버 가 쉬 운 String 이 아니라면?예 를 들 어 저 는 OrderProcessor 라 는 인터페이스 가 있 습 니 다.

bind(Types.setOf(String.class)).toInstance(new HashSet(){{
  add("Hello");
  add("World");
}});

Order 프로세서 에 따라 Order 를 다 르 게 처리 해 야 합 니 다(메 일 을 보 내 고 데이터 베 이 스 를 저장 합 니 다).각각 대응 하 는 것 은 다음 과 같다.

public interface OrderProcessor {
  void processOrder(Order order);
}

public class MailOrderProcessor implements OrderProcessor {
  @Inject
  EmailSender emailSender
  // send mail
}

이때 이 세트 는 어떻게 바 인 딩 합 니까?우 리 는 이렇게 할 수 없다.

public class DbOrderProcessor implements OrderProcessor {
  @Inject
  SqlMapClient sqlMapClient;
  // save order to database
}

이렇게 하 는 문 제 는 new MailOrderProcessor()가 MailSender 에 주입 할 수 없다 는 것 이다.모듈 에 서 는 injector.getInstance(MailOrderProcessor.class)를 사용 할 수 없습니다.이런 상황 에 대해 네 가지 선택 이 있다.
선택 1:injectMemebers 수 동 주입 의존

bind(Types.of(Set.class)).toInstance(new HashSet(){{
  add(new MailOrderProcessor());
  add(new DbOrderProcessor());
}});

선택 2:포장 세트

@Inject
Injector injector;
for (OrderProcessor orderProcessor : orderProcessors) {
  injector.injectMemebers(orderProcess);
}

선택 3:getProvider

public class OrderProcessors {
  private final Set processors = new HashSet();
  @Inject
  public void setDbOrderProcessor(DbOrderProcessor processor) {
    processors.add(processor);
  }
  @Inject
  public void setMailOrderProcessor(MailOrderProcessor processor) {
    processors.add(processor);
  }
}

여기 서 이용 하 는 특성 은 AbstractModule 에서 getProvider 라 는 방법 을 제공 하 는 것 입 니 다.이렇게 하면 Module 에서 injector.getInstance 를 호출 할 수 없 지만 인 스 턴 스 의 provider 를 얻 을 수 있 습 니 다.이렇게 해서 우 리 는 order processor 의 provider set 를 얻 었 고 order processor 의 set 를 직접 받 지 않 았 습 니 다.
선택 4:getProvider+ProvidedOrderProcessor

bind(new TypeLiteral>>(){}).toInstance(new HashSet>(){{
  add(getProvider(DbOrderProcessor.class);
  add(getProvider(MailOrderProcessor.class);
}});

이렇게 하면 위의 Set>를 Set 로 바 꿀 수 있 습 니 다.

public class ProvidedOrderProcessor implements OrderProcessor {
  private final Provider provider;
  public ProvidedOrderProcessor(Provider provider) {
    this.provider = provider;
  }
  public void processOrder(Order order) {
    provider.get().processOrder(order);
  }
}

다 중 모듈 공동 Bind 컬 렉 션
만약 Set 이 여러 개의 Module 이 공동으로 추가 한 것 이 라면 어떻게 해 야 합 니까?Guice 의 코어 만 사용 하면 하나의 Binding 은 하나의 Module 에서 이 루어 져 야 합 니 다.그러나 Guice 의 확장 Multibings 에 의존 하면 여러 개의 Module 공동 Bind 하나의 Set 를 할 수 있 습 니 다.

bind(Types.setOf(OrderProcessor.class)).toInstance(new HashSet(){{
  add(getLazyInstance(DbOrderProcessor.class));
  add(getLazyInstance(MailOrderProcessor.class));
}});
OrderProcessor getLazyInstance(Class extends OrderProcessor> clazz) {
  return new ProvidedOrderProcessor(getProvider(clazz));
}

문제 가 해 결 된 것 같은 데?그리고 Multibings 확장 은 맵 도 지원 합 니 다.
제한 하 다.
근 데 리스트 어 떡 해?아직 까지 이 문 제 를 해결 하기 위 한 공식 적 인 지지 가 없다.또 다른 문 제 는 어떻게 Bind 하나의 Chain,즉 Decorator 모델 입 니까?

public class Module1 extends AbstractModule {
  public void configure() {
    Multibinder multibinder
         = Multibinder.newSetBinder(binder(), OrderProcessor.class);
    multibinder.addBinding().to(MailOrderProcessor.class);
  }
}
public class Module2 extends AbstractModule {
  public void configure() {
    Multibinder multibinder
         = Multibinder.newSetBinder(binder(), OrderProcessor.class);
    multibinder.addBinding().to(DbOrderProcessor.class);
  }
}

만약 여러 개의 Decorator 가 서로 포 함 된 Chain 을 형성 했다 면 비교적 복잡 할 것 이다.하나의 Module 의 경우 앞에서 말 한 ProvidedOrderProcessor 방식 을 이용 할 수 있 습 니 다.그러나 모듈 개 를 통 해 이 체인 을 구축 하려 면 공식 적 인 방법 이 없다.
   
플러그 가능 한 Module 을 확장 점 으로 하 다
이상 적 인 상 태 는 응용 프로그램 에 Xmodule,YModule,ZModule 이 있 습 니 다.ZModule 이 없 을 때 도 프로그램 은 작 동 할 수 있 습 니 다.하지만 ZModule 을 꽂 으 면 일부 행동 이 확 장 됩 니 다.이렇게 하면 서로 다른 모듈 을 추가 함으로써 확장 의 목적 을 달성 할 수 있다.어떻게 하지?Guice 는 이러한 효 과 를 지원 할 수 있 는 다음 과 같은 능력 을 제공 했다.
방법 1:@ImplementedBy,@ProvidedBy

public class DecoratedOrderProcessor implements OrderProcessor {
  private final OrderProcessor decorated;
  public OrderProcessor(OrderProcessor decorated) {
    this.decorated = decorated;
  }
  public void processOrder(Order order) {
    try {
      decorated.processOrder(order);
    } finally {
      // do something;
    }
  }
}

이렇게 하면 모든 Module 이 OrderProcessor 의 실현 을 지정 하지 않 았 을 때 기본 값 으로 MailOrderProcessor 를 사용 합 니 다.bind(OrderProcessor.class).to(DbOrderProcessor.class)는 DbOrderProcessor 를 사용 합 니 다.이 특성 은 매우 편리 하 다.특히 일부 서 비 스 는 테스트 할 때 만 바뀐다.

@ImplementedBy(MailOrderProcessor.class)
public interface OrderProcessor {
}

모든 제품 코드 에서 CurrentTimeProvider 는 DefaultImpl 입 니 다.그러나 테스트 에서 우 리 는 bind(CurrentTimeProvider.class).toInstance(new FixedTimeProvider(2008,5,12)를 지정 할 수 있 습 니 다.이렇게 시간 은 그 순간 으로 정 해 졌 다.
방법 2:@Inject(optional=true)

@ImplementedBy
public interface CurrentTimeProvider {
  DateTime getNow();
  public static class DefaultImpl implements CurrentTimeProvider {
    public DateTime getNow() {
      return new DateTime();
    }
  }
}

이런 방식 은 의존 해 야 할 곳 에서 스스로 부족 한 실현 을 제공 할 수 있 는 상황 에 적용 된다.processor 를 optional 로 표시 하면 processor 는 ProcessOrder Service 의 확장 점 이 됩 니 다.사용자 가 원한 다 면 언제든지 모듈 을 추가 하 는 방식 으로 ProcessOrderService 행위 의 목적 을 바 꿀 수 있다.
방법 3:Multibindings
앞에서 우 리 는 Multibinder 를 통 해 여러 개의 Module 로 하나의 set 를 공동으로 Bind 할 수 있 는 것 을 보 았 다.이 특성 을 이용 하면 모듈 을 추가 하고 서로 다른 실현 목적 을 추가 할 수 있다.확장 점 을 매우 실현 하 는 방식 이기 도 하 다.
방법 4:Override
Guice 2 에 추 가 된 기능 입 니 다.사용 이 간단 하 다

public class ProcessOrderService {
  @Inject(Optional = true)
  OrderProcessor processor = new DummyOrderProcessor();
}

이렇게 하면 모두 같은 key 라면 Default Module 에서 정의 하 는 binding 은 CustomizationModule 에 의 해 고 쳐 집 니 다.이 특성 은 매우 폭력 적 이어서 이미 복잡 한 binding 해석 과정 을 더욱 복잡 하 게 만 들 었 다.최종 적 으로 사용 하 는 binding 이 어느 Module 에서 왔 는 지 알 기 가 더욱 어렵다.일반적으로 추천 하지 않 습 니 다.그러나 이러한 폭력 적 인 특성 으로 인해 여러 Module 이 공동으로 Bind 와 같은 List 또는 Decorator Chain 을 가능 하 게 만 들 었 습 니 다.

Module finalModule = Modules
.override(new DefaultModule())
.with(new CustomizationModule());

Before 는 Binding Annotation 입 니 다.다른 모듈 에 서 는 bid CUSTOMIZABLE 만 반복 하면키 면 돼.

Key CUSTOMIZABLE_KEY = Key.get(OrderProcessor.class, new Before(MailOrderProcessor.class));
bind(Types.listOf(OrderProcessor.class)).toInstance(new ArrayList(){{
  add(new ProvidedOrderProcessor(getProvider(CUSTOMIZABLE_KEY));
  add(getLazyInstance(MailOrderProcessor.class);
}});
bind(CUSTOMIZABLE_KEY).toInstance(new DummyOrderProcessor());

사용 할 때 Modules.over ride 로 결 성 실현(Dummy OrderProcessor)을 교체 하면 됩 니 다.

좋은 웹페이지 즐겨찾기