각 컴포넌트는 @Scope 애노테이션과 함께 범위를 지정할 수 있다.예를 들어 안드로이드에서는 애플리케이션, 액티비티, 프래그먼트 인스턴스에 대한 범위 지정을 다르게 관리함으로써 오브젝트 그래프의 생성과 소멸을 각자 관리할 수 있다.
@Singleton
@Component(modules = MyModule.class)
public interface MyComponent{
Object getObject();
}
@Module
public class MyModule{
@Provides
@Singleton
Object provideObject(){
return new Object();
}
}
@Test
public void testObjectIdentity(){
MyComponent myComponent = DaggerMyComponent.create();
Object temp1 = myComponent.getObject();
Object temp2 = myComponent.getObject();
asertNotNull(temp1);
asertNotNull(temp2);
assertSame(temp1,temp2);
}
실행 결과
232653
232653
true
특정 컴포넌트에 스코프에 종속되지 않아 컴포넌트에 @Reusable을 선언하지 않아도 된다. 즉 다른 스코프 애노테이션 처럼 인스턴스의 동일성을 보장하진 않지만 항상 동일한 인스턴스를 사용해야 하는것이 아니라면 메모리 관리 측면에서 조금 더 효율적이다.
모듈 내의 추상 메서드에 붙일 수 있으며 , 이 메서드는 반드시 하나의 매개 변수만을 가져야 한다. 매개 변수를 반환형으로 바인드 할 수 있으며 ,@Provide 메서드 대신 좀 더 효율적으로 사용할 수 있다.
@Binds
abstract Random bindRandom(SecureRandom secureRandom);
모듈 내의 추상 메서드에 붙일 수 있으며 , 이 메서드의 매개변수는 항상 가질 수 없다. void 가 아닌 특정타입을 반환형으로 가져야 하며, 예외사항을 던질수도 없다.
@Module
public abstract class CommonModule{
@BindsOptionalOf
abstract String bindsOptionalOfString();
}
@Module
public class HelloModule{
@Provides
String provideString(){
return "Hello";
}
}
@BindsOptionalOf 메서드를 통한 의존성 주입은 다음과 같인 Optional 타입 등으로 주입된다.
public class Foo{
@Inject
public Optional<String> str;
@Inject
public Optional<Provider<String>> str2;
@Inject
public Optional<Lazy<String>> str2;
}
만약 컴포넌트 내에 Foo가 바인드 된적이 있다면 Optional 의 상태는 Present 아니면 absent이다.
어떤 타입의 의존성이 바인드외었는지 여부와 관계없이 @Inject를 이용해 주입할 수 있는것이 특징
Optional은 Null을 포함하는것을 허용하지 않는다. 그러기에 @Nullable바인딩에 대해서는 컴파일 타임에 에러를 발생함
바인드 유무에 따른 Optional 상태 테스트를 위해 두개의 컴포넌트를 생성한다
@Component(modules ={CommonModule.class ,HelloModule.class})
public interface StrComponent{
void inject(Foo foo);
}
@Component(modules = {CommonModule.class})
public interface NoStrComponent{
void inject(Foo foo);
}
@Test
public void testFoo(){
Foo foo = new Foo();
DaggerStrComponent.create().inject(foo);
System.out.println(foo.str.isPresent());
System.out.println(foo.str.get());
DaggerNoStrComponent.create().inject(foo);
System.out.println(foo.str.isPresent);
System.out.println9foo.str.get());
}
실행 결과
true
Hello
false
java.util.noSuchElemntException:No Value Present
@Component
public interface BindsComponent{
void inject(Foo foo);
@Component.Builder
interface Builder{
@BindsInstance
Builder setString(String str);
BindsComponent build();
}
}
public class Foo{
@Inject
public String str;
}
@Test
public void testBindsInstance(){
String hello = "Hello World";
Foo foo = new Foo();
BindsComponent component = DaggerBindsComponent.builder()
.setString(hello)
.build();
component.inject(foo);
assertEquals("Hello World",foo.str);
}
실행 결과
Hello World
Dagger의 멀티바인딩을 이용해 여러 모듈에 있는 같은 타입의 객체를 하나의 Set 또는 Map에 관리할 수 있다.
Set로 멀티바인딩을 구현하려면 @IntoSet , @ElementsIntoSet 애노테이션을 @Provides 메서드에 함께 사용할 수 있다.
@Module
public class SetModule{
@Provides
@IntoSet
String provideHello(){
return "Hello;
}
@Provides
@IntoSet
String provideWorld(){
return "World";
}
}
간단히 @IntoSet하는 것으로도 Set<String>
타입으로 멀티바인딩이 구현된다.
객체를 하나씩 Set에 추가하는것이 아닌 Set<String>
의 일부분을 한거번에 추가하는 방법은 @ElementsIntoSet 애노테이션을 사용하는것이다.
@Module
public class SetModule{
...
@Provides
@ElementsIntoSet
Set<String> provideSet(){
return new HashSet<>(Arrays.asList("Charles","Runa"));
}
}
@Component(modules =SetModule.class)
public interface SetComponent{
void inject(Foo foo);
}
public class Foo{
@Inject
Set<String> strings;
public void print(){
for(Iterfator itr = strings.iterator();itr.hasNext();){
System.out.println(itr.next());
}
}
}
pulic class MultibindingTest{
@Test
public void testMultibindingSet(){
Foo foo = new Foo();
DaggerSetComponent.create.inject(foo);
foo.print();
}
}
기본 제공 키의 종류
-@StringKey
-@ClassKey
-@IntKey
-@LongKey
public clas Foo{
}
@Module
public class MapModule{
@Provides
@IntoMap
@StringKey("foo")
static Long provideFooValue(){
return 100L;
}
@Provides
@IntoMap
@ClassKey(Foo.class)
static String provideFooStr(){
return "Foo String";
}
}
@Component(modules=MapModule.class)
public interface MapComponent{
Map<String,Long> getLongByString();
Map<Class<?>,String> getStringByClass();
}
public class MultibindingMapTest{
@Test
public void testMultibindingMap(){
MapComponent component = DaggerMapComponent.create();
long value = component.getLongByString().get("foo");
String str = component.getStringByClass().get(Foo.class);
System.out.println(value);
System.out.println(str);
}
}
실행 결과
100
Foo String
public enum Animal{
CAT,DOG;
}
@MapKey
public @interface AnimalKey{
Animal value();
}
@MapKey
public @interface Numberkey{
Class<? extends Number> value();
}
@Component(modules = MapModule.class)
public interface MapKeyComponent{
Map<Animal,String> getStringByAnimal();
Map<Class<? extends Number> , Stirng> getStringByNumber();
}
@Module
public class MapModule{
@IntoMap
@AnimalKey(Animal.CAT)
@Provides
String provideCat(){
return "Meow";
}
@IntoMap
@AnimalKey(Animal.DOG)
@Provides
String provideDog(){
return "Bow-wow";
}
@IntoMap
@NumberKey(Float.class)
@Provides
String provideFloatValue(){
return "100f";
}
@IntoMap
@NumberKey(Integer.class)
@Provides
String provideIntegerValue(){
return "1";
}
}
@Test
public void testCustomMapKey(){
MapKeyComponent component = DaggerMapKeyComponent.create();
String cat = component.getStringsByAnimal().get(Animal.CAT);
String dog = component.getStringsByAnimal().get(Animal.DOG);
String number = component.getStringByNumber().get(Float.class);
String Integer = component.getStringByNumber().get(Integer.class);
System.out.println(cat);
System.out.println(dog);
System.out.println(number);
System.out.println(Integer);
}
실행 결과
meow
bow-wow
100.0f
1