Guice 라 는 고급 상품
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)을 교체 하면 됩 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.