Share
Embed
Transcript
100 slides
-
Dependency Injection • ⼀一種 Design Pattern • 最快速,且不⽤用任何 Liberary 即可達成
• 其實每個 app 中都已經有實作 DJ -
public class Tweeter { public void tweet(String tweet) { TwitterApi
api = new TwitterApi(); api.postTweet("JakeWharton", tweet); } } public class TwitterApi { public void postTweet(String user, String tweet) { OkHttpClient client = new OkHttpClient(); Request request = // TODO build POST request… client.newCall(request).execute(); } Tweeter tweeter = new Tweeter(); tweeter.tweet("Hello, #Devoxx 2014!"); -
public class TwitterApi { private final OkHttpClient client = new
OkHttpClient(); public void postTweet(String user, String tweet) { Request request = // TODO build POST request client.newCall(request).execute(); } } -
public class TwitterApi { private final OkHttpClient client; public TwitterApi(OkHttpClient
client) { this.client = client; } public void postTweet(String user, String tweet) { Request request = // TODO build POST request client.newCall(request).execute(); } } -
public class Tweeter { private final TwitterApi api = new
TwitterApi(new OkHttpClient()); private final String user; public Tweeter(String user) { this.user = user; } public void tweet(String tweet) { api.postTweet(user, tweet); } } -
public class Tweeter { private final TwitterApi api = new
TwitterApi(new OkHttpClient()); private final String user; public Tweeter(String user) { this.user = user; } public void tweet(String tweet) { api.postTweet(user, tweet); } } Tweeter tweeter = new Tweeter("JakeWharton"); tweeter.tweet("Hello, #Devoxx 2014!"); tweeter.tweet("#Hungover #Dagger"); -
Guice • Developed at Google by Bob Lee, later Jesse
Wilson, others. • Adopted and maintained by Java Core Libraries team. Powerful, dynamic, well-tested, wide- spread, etc... • Configuration problems occur at runtime. • Slow initialization, slow injection, memory concerns. -
Dagger (v1) • Developed at Square by Jesse Wilson advised
by Bob Lee. • Initially targeted at highly resource constrained environments. • Static analysis of all dependencies and injection points. • Fail as early as possible (compile-time, not runtime) • Eliminate reflection on methods, fields, and annotations. -
Dagger (v2) • Proposed and implemented by Java Core Libraries
team. • Eliminate runtime library and generated code overhead. • Shift remaining runtime analysis to compile time. • Scoping with annotations and associated static analysis. -
Dagger API • @Module + @Provides: 提供相依性的機制 • @Inject: 要求提供相依性
• @Component: modules 與 injections 之間的橋 樑,讓兩者能互通 • 另外有⼀一些語法糖可以使⽤用,使⽤用起來更⽅方便 • 被設計成可以拆成許多⼩小元件,並組合起來使⽤用 -
Providing Dependencies • Modules 是⼀一些 classes,⽽而這些 classes 中的 methods 提供相依性。
• 必須在 class 上加上 @Module • 必須在每個 method 上加上 @Provides -
public class NetworkModule { OkHttpClient provideOkHttpClient() { return new OkHttpClient();
} TwitterApi provideTwitterApi(OkHttpClient client) { return new TwitterApi(client); } } -
@Module public class NetworkModule { OkHttpClient provideOkHttpClient() { return new
OkHttpClient(); } TwitterApi provideTwitterApi(OkHttpClient client) { return new TwitterApi(client); } } -
@Module public class NetworkModule { !@Provides OkHttpClient provideOkHttpClient() { return
new OkHttpClient(); } @Provides TwitterApi provideTwitterApi(OkHttpClient client) { return new TwitterApi(client); } } -
@Module public class NetworkModule { !@Provides @Singleton OkHttpClient provideOkHttpClient() {
return new OkHttpClient(); } @Provides @Singleton TwitterApi provideTwitterApi(OkHttpClient client) { return new TwitterApi(client); } } -
Providing Dependencies • Modules 是⼀一些 classes,⽽而這些 classes 中的 methods 提供相依性。
• 必須在 class 上加上 @Module • 必須在每個 method 上加上 @Provides • Modules 可以拆開成很多 module,也可以組合在 ⼀一起使⽤用 -
@Module public class TwitterModule { private final String user; public
TwitterModule(String user) { this.user = user; } @Provides @Singleton Tweeter provideTweeter(TwitterApi api) { return new Tweeter(api, user); } @Provides @Singleton Timeline provideTimeline(TwitterApi api) { return new Tweeter(api, user); } } -
Requesting Dependencies • 必須要有 @Inject 的 annotation • 有三種 inject
⽅方式 • Constructor, field, and method injection. -
Constructor Injection • 在 Constructor 加上 @Inject • 表⽰示 Constructor
的參數需要 dependency • 這些參數可以被使⽤用在 private 或 final fields. -
public class TwitterApplication { private final Tweeter tweeter; private final
Timeline timeline; @Inject public TwitterApplication(Tweeter tweeter, Timeline timeline) { this.tweeter = tweeter; this.timeline = timeline; } //… } -
Method Injection • 在 methods 上加上 @Inject • 表⽰示 method
的參數需要 dependency • Injection 發⽣生在物件被完全建⽴立之後 • 只有⼀一個合理的 use case: passing ‘this’ to a dependency. -
public class TwitterApplication { private final Tweeter tweeter; private final
Timeline timeline; @Inject public TwitterApplication(Tweeter tweeter, Timeline timeline) { this.tweeter = tweeter; this.timeline = timeline; } @Inject public void enableStreaming(Streaming streaming) { streaming.register(this); } } -
Field Injection • 在 fields 上加上 @Inject • Field 不能為
private 或是 final • Injection 發⽣生在物件被完全建⽴立之後 • 在 android 中最常⾒見到 -
public class TwitterApplication extends Activity { @Inject Tweeter tweeter; @Inject
Timeline timeline; // ... } -
Components • Modules 與 injections 之間的橋樑,讓兩者能互 通 • Component 為
injector,實際發⽣生 inject 的⼈人 • Scope 的概念 -
@Singleton @Component(modules = { NetworkModule.class, TwitterModule.class, !}) public interface TwitterComponent
{ Tweeter tweeter(); Timeline timeline(); } -
TwitterComponent component = DaggerTwitterComponent.builder() .twitterModule(new TwitterModule(“JakeWharton")) .build(); @Module public class
TwitterModule { private final String user; public TwitterModule(String user) { this.user = user; } // … -
TwitterComponent component = DaggerTwitterComponent.builder() .twitterModule(new TwitterModule(“JakeWharton")) .build(); Tweeter tweeter =
component.tweeter(); tweeter.tweet("Hello, #Devoxx 2014!"); -
public class TwitterApplication implements Runnable { private final Tweeter tweeter;
private final Timeline timeline; @Inject public TwitterApplication(Tweeter tweeter, Timeline timeline) { this.tweeter = tweeter; this.timeline = timeline;} @Override public void run() { tweeter.tweet("Hello #Devoxx 2014!”); timeline.loadMore(20); for (Tweet tweet : timeline.get()) { System.out.println(tweet); } } } -
@Singleton @Component(modules = { NetworkModule.class, TwitterModule.class, !}) public interface TwitterComponent
{ TwitterApplication app(); } -
@Singleton @Component(modules = { NetworkModule.class, TwitterModule.class, !}) public interface TwitterComponent
{ TwitterApplication app(); } TwitterComponent component = DaggerTwitterComponent.builder() .twitterModule(newTwitterModule(“JakeWharton")) .build(); component.app().run(); -
public class TwitterApplication implements Runnable { @Inject Tweeter tweeter; @Inject
Timeline timeline; @Inject public TwitterApplication(Tweeter tweeter, Timeline timeline) { this.tweeter = tweeter; this.timeline = timeline;} @Override public void run() { tweeter.tweet("Hello #Devoxx 2014!”); timeline.loadMore(20); for (Tweet tweet : timeline.get()) { System.out.println(tweet); } } } -
@Singleton @Component(modules = { NetworkModule.class, TwitterModule.class, !}) public interface TwitterComponent
{ void injectApp(TwitterApplication app); } -
@Singleton @Component(modules = { NetworkModule.class, TwitterModule.class, !}) public interface TwitterComponent
{ void injectApp(TwitterApplication app); } TwitterComponent component = DaggerTwitterComponent.builder() .twitterModule(newTwitterModule(“JakeWharton")) .build(); TwitterApplication app = new TwitterApplication(); component.injectApp(app); app.run(); -
@Singleton @Component(modules = { NetworkModule.class, TwitterModule.class, !}) public interface TwitterComponent
{ void injectActivity(TwitterActivity activity); } TwitterComponent component = DaggerTwitterComponent.builder() .twitterModule(newTwitterModule(“JakeWharton")) .build(); TwitterActivity activity = // Android creates instance... component.injectActivity(activity); // use tweet and timeline in activity… -
Components • Modules 與 injections 之間的橋樑,讓兩者能互 通 • Component 為
injector,實際發⽣生 inject 的⼈人 • Scope 的概念 -
@Component( dependencies = ApiComponent.class, modules = TwitterModule.class ) public interface
TwitterComponent { void injectActivity(TwitterActivity activity); } @Singleton @Component(modules = NetworkModule.class) public interface ApiComponent { } -
@Singleton @Component(modules = NetworkModule.class) public interface ApiComponent { TwitterApi api();
} @Component( dependencies = ApiComponent.class, modules = TwitterModule.class ) public interface TwitterComponent { void injectActivity(TwitterActivity activity); } -
@Module public class TwitterModule { private final String user; public
TwitterModule(String user) { this.user = user; } @Provides @Singleton Tweeter provideTweeter(TwitterApi api) { return new Tweeter(api, user); } @Provides @Singleton Timeline provideTimeline(TwitterApi api) { return new Tweeter(api, user); } } -
ApiComponent apiComponent = Dagger_ApiComponent.create(); TwitterComponent twitterComponent = DaggerTwitterComponent.builder() .apiComponent(apiComponent) .twitterModule(new
TwitterModule("JakeWharton")) .build(); TwitterActivity activity = // Android creates instance... component.injectActivity(activity); -
Scope Annotations • Only create a single instance. • @Singleton
是最⼤大的 scope (可以想像是最上層的 scope) • ⾃自定 scope,可以讓程式更清楚,也可以有較短 的 lifecycle @Scope public @interface User { } -
@Module public class TwitterModule { private final String user; public
TwitterModule(String user) { this.user = user; } @Provides Tweeter provideTweeter(TwitterApi api) { return new Tweeter(api, user); } @Provides Timeline provideTimeline(TwitterApi api) { return new Tweeter(api, user); } } -
@Module public class TwitterModule { private final String user; public
TwitterModule(String user) { this.user = user; } @Provides @User Tweeter provideTweeter(TwitterApi api) { return new Tweeter(api, user); } @Provides @User Timeline provideTimeline(TwitterApi api) { return new Tweeter(api, user); } } -
@Component( dependencies = ApiComponent.class, modules = TwitterModule.class ) public interface
TwitterComponent { void injectActivity(TwitterActivity activity); } -
@User @Component( dependencies = ApiComponent.class, modules = TwitterModule.class ) public
interface TwitterComponent { void injectActivity(TwitterActivity activity); } -
Dagger 1 public abstract class ObjectGraph { public static ObjectGraph
create(Object... modules) {} public abstract ObjectGraph plus(Object... modules); public abstract void validate(); public abstract void injectStatics(); public abstract <T> T get(Class<T> type); public abstract <T> T inject(T instance); } public @interface Module { Class<?>[] injects() default { }; Class<?>[] staticInjections() default { }; Class<?>[] includes() default { }; Class<?> addsTo() default Void.class; boolean overrides() default false; boolean complete() default true; boolean library() default true; } public @interface Provides { enum Type { UNIQUE, SET } Type type() default Type.UNIQUE; } public interface Lazy<T> { T get(); } public interface MembersInjector<T> { void injectMembers(T instance); } -
Dagger 2 public @interface Component { Class<?>[] modules() default {};
Class<?>[] dependencies()default {}; } public @interface Module { Class<?>[] includes() default {}; } public @interface Provides { } public @interface MapKey { boolean unwrapValue(); } public interface Lazy<T> { T get(); } -
Issues • Gained 13% of processor performance at google scale
• No reflection at all • Less flexible -
Reference • DAGGER 2 - A New Type of dependency
injection https://www.youtube.com/watch?v=oK_XtfXPkqw • Dependency Injection with Dagger 2 https://speakerdeck.com/jakewharton/ dependency-injection-with-dagger-2-devoxx-2014 • The Future of Dependency Injection with Dagger 2 https://www.parleys.com/tutorial/ 5471cdd1e4b065ebcfa1d557/