Transcript
Page 1: Big Modular Java with Guice
Page 2: Big Modular Java with Guice

2

Big Modular Java™ with GuiceJesse WilsonDhanji Prasanna

May 28, 2009

Post your questions for this talk on Google Moderator:code.google.com/events/io/questionsClick on the Tech Talks Q&A link.

Page 3: Big Modular Java with Guice

How does my code change with Guice?

> Objects come to you• Instead of ʻnewʼ and factories

> Reusable modules> First-class scopes> Easier tests

• and more confidence> Less boilerplate

3photo courtesy of http://flickr.com/photos/xero79/378837837

Page 4: Big Modular Java with Guice

Overview

> Example• Ugh, factories arenʼt fun

> Using Guice• @Inject is the new new

> Leveraging Guice• A tour of extensions and advanced features

4

Page 5: Big Modular Java with Guice

Exampletweet tweet

5

> Setting the stage> Constructors> Factories> Dependency Injection

• by hand• with Guice

photo courtesy ofhttp://flickr.com/photos/jessicafm/62271212

Page 6: Big Modular Java with Guice

Code you might writeA tweets client public void postButtonClicked() {

String text = textField.getText();

if (text.length() > 140) {

Shortener shortener = new TinyUrlShortener();

text = shortener.shorten(text);

}

if (text.length() <= 140) {

Tweeter tweeter = new SmsTweeter();

tweeter.send(text);

textField.clear();

}

}

6

Page 7: Big Modular Java with Guice

Calling Dependenciesʼ Constructors Directly

photo courtesy of http://flickr.com/photos/salford_ian/3134250986

Page 8: Big Modular Java with Guice

public void postButtonClicked() {

String text = textField.getText();

if (text.length() > 140) {

Shortener shortener = new TinyUrlShortener();

text = shortener.shorten(text);

}

if (text.length() <= 140) {

Tweeter tweeter = new SmsTweeter();

tweeter.send(text);

textField.clear();

}

}

Getting dependencies via their constructors...calling new directly doesnʼt afford testing

8

We post to tinyurl.com and send an SMS for each test! This is neither fast nor reliable.

Page 9: Big Modular Java with Guice

Getting Dependencies from Factories

photo courtesy of http://flickr.com/photos/abulic_monkey/130899453

Page 10: Big Modular Java with Guice

public void postButtonClicked() {

String text = textField.getText();

if (text.length() > 140) {

Shortener shortener = ShortenerFactory.get();

text = shortener.shorten(text);

}

if (text.length() <= 140) {

Tweeter tweeter = TweeterFactory.get();

tweeter.send(text);

textField.clear();

}

}

Getting dependencies from factories

10

Page 11: Big Modular Java with Guice

public class TweeterFactory {

private static Tweeter testValue;

public static Tweeter get() {

if (testValue != null) {

return testValue;

}

return new SmsTweeter();

}

public static void setForTesting(Tweeter tweeter) {

testValue = tweeter;

}

}

Implementing the factoryall of this boilerplate slows you down. Ugh!

11

We still have to write a factory for the URL shortener.

Page 12: Big Modular Java with Guice

Factory dependency graphthe static dependency causes monolithic compiles

12

TweetClient

TweeterFactory

SmsTweeter

depends

depends

Page 13: Big Modular Java with Guice

public void testSendTweet() {

MockTweeter tweeter = new MockTweeter();

TweeterFactory.setForTesting(tweeter);

try {

TweetClient tweetClient = new TweetClient();

tweetClient.getEditor().setText("Hello!");

tweetClient.postButtonClicked();

assertEquals("Hello!", tweeter.getSent());

} finally {

TweeterFactory.setForTesting(null);

}

}

Testing with a factorydonʼt forget to clean up afterwards!

13

Page 14: Big Modular Java with Guice

Dependency Injection (DI) by hand

photo courtesy of http://flickr.com/photos/dan4th/1657850829

Page 15: Big Modular Java with Guice

public class TweetClient {

private final Shortener shortener;

private final Tweeter tweeter;

public TweetClient(Shortener shortener, Tweeter tweeter) {

this.shortener = shortener;

this.tweeter = tweeter;

}

public void postButtonClicked() {

...

if (text.length() <= 140) {

tweeter.send(text);

textField.clear();

}

Dependency injection by handobjects come to you

15

Dependency Injection:rather than looking it up, get it passed in.

Page 16: Big Modular Java with Guice

public void testSendTweet() {

MockShortener shortener = new MockShortener();

MockTweeter tweeter = new MockTweeter();

TweetClient tweetClient

= new TweetClient(shortener, tweeter);

tweetClient.getEditor().setText("Hello!");

tweetClient.postButtonClicked();

assertEquals("Hello!", tweeter.getSent());

}

Testing with dependency injectionno cleanup required

16

Page 17: Big Modular Java with Guice

public class TweetClientFactory {

private static TweetClient testValue;

public static TweetClient get() {

if (testValue != null) {

return testValue;

}

Shortener shortener = ShortenerFactory.get();

Tweeter tweeter = TweeterFactory.get();

return new TweetClient(shortener, tweeter);

}

Where does the dependency go?ugh, you still have to write boilerplate code to build stuff

17

DI motto:Push dependencies from the core to the edges

Page 18: Big Modular Java with Guice

Where does the dependency go?your application code sheds its heavyweight dependencies

18

TweetClient

SmsTweeterTinyUrlShortener

depends

creates

TweeterFactoryShortenerFactory

dependsTweetClientFactory

Page 19: Big Modular Java with Guice

Dependency Injection with Guice

photo courtesy of http://flickr.com/photos/randysonofrobert/347327376

Page 20: Big Modular Java with Guice

Dependency injection with Guice

20

TweetClient

SmsTweeterTinyUrlShortener

depends

createsInjector

uses

TweetModule

Page 21: Big Modular Java with Guice

import com.google.inject.AbstractModule;

public class TweetModule extends AbstractModule {

protected void configure() {

bind(Tweeter.class).to(SmsTweeter.class);

bind(Shortener.class).to(TinyUrlShortener.class);

}

}

Configuring the injector using modules

21

Page 22: Big Modular Java with Guice

import com.google.inject.Inject;

public class TweetClient {

private final Shortener shortener;

private final Tweeter tweeter;

@Inject

public TweetClient(Shortener shortener, Tweeter tweeter) {

this.shortener = shortener;

this.tweeter = tweeter;

}

Telling Guice to use your constructorannotate a constructor with @Inject

22

Page 23: Big Modular Java with Guice

public static void main(String[] args) {

Injector injector = Guice.createInjector(new TweetModule());

TweetClient tweetClient = injector.getInstance(TweetClient.class);

tweetClient.show();

}

Bootstrapping Guice

23

Page 24: Big Modular Java with Guice

Using Guice

> Why?> Bindings

• Instances• Constructors• Linked Bindings• Provider Methods

> Scopes> Injections

24photo courtesy of http://flickr.com/photos/theogeo/2211326536

Page 25: Big Modular Java with Guice

Why use a framework?

> Writing boilerplate slows you down> More up front type checking> It makes it easier to write better code

> Plus...• Scopes• AOP• Tight integration with web, data access APIs, etc.

25

Page 26: Big Modular Java with Guice

Guice in a nutshell

> Types have dependencies• these are passed in automatically• identified with annotations

> Modules define how dependencies are resolved

26

Page 27: Big Modular Java with Guice

Bindings

photo courtesy of http://flickr.com/photos/uwehermann/3417729678

Page 28: Big Modular Java with Guice

Bindingsmap types to their implementations

28

public class TweetModule extends AbstractModule {

protected void configure() {

bind(TweetClient.class);

bind(Tweeter.class) .to(SmsTweeter.class);

bind(String.class).annotatedWith(Username.class) .toInstance("jesse");

}

@Provides Shortener provideShortener() {

return new TinyUrlShortener();

}

}

Page 29: Big Modular Java with Guice

Binding Annotationsuniquely identify a binding

29

protected void configure() {

bind(String.class).annotatedWith(Username.class) .toInstance("jesse");

...

}

“jesse”@Username String

@Inject

public SmsTweeter(@Username String username) {

this.username = username;

}

Page 30: Big Modular Java with Guice

Defining your own binding annotations

30

@BindingAnnotation

@Retention(RUNTIME)

@Target({FIELD, PARAMETER, METHOD})

public @interface Username {}

> This boilerplate defines a binding annotation> Everything is compile-time checked• IDE autocomplete, import, find usages• Avoids clumsy string matching

Page 31: Big Modular Java with Guice

Instance Bindingsalways use the same value

31

protected void configure() {

bind(Integer.class).annotatedWith(Port.class) .toInstance(8080);

...

}

8080@Port Integer

> Best suited for value objects such as a database name, or webserver port

Page 32: Big Modular Java with Guice

Constructor Bindingsto resolve a type, call its constructor

32

public class TweetModule extends AbstractModule {

protected void configure() {

bind(TweetClient.class);

...

}

}

new TweetClient(...)TweetClient

> Requires @Inject on the constructor• Dependencies are passed in as parameters

Page 33: Big Modular Java with Guice

Linked Bindingsto resolve a type, use another binding

33

public class TweetModule extends AbstractModule {

protected void configure() {

bind(Tweeter.class).to(SmsTweeter.class);

...

}

}

> Requires a binding for the target type• If none exists, one will be created automatically

SmsTweeterTweeter

Page 34: Big Modular Java with Guice

Linked Bindingsto resolve a type, use another binding

34

public class TweetModule extends AbstractModule {

protected void configure() {

bind(Tweeter.class).to(SmsTweeter.class);

...

}

}

new SmsTweeter(...)Tweeter

> Requires a binding for the target type• If none exists, one may be created automatically...

SmsTweeter

Page 35: Big Modular Java with Guice

Provider methodsto resolve a type, call this method

35

public class TweetModule extends AbstractModule {

protected void configure() {...}

@Provides Shortener provideShortener() {

return new TinyUrlShortener();

}

}

provideShortener(...)Shortener

> Annotate a module method with @Provides• The return type is bound• Dependencies are passed in as parameters

Page 36: Big Modular Java with Guice

Using Scopes

photo courtesy of http://flickr.com/photos/houseofsims/3139640931

Page 37: Big Modular Java with Guice

Scopesmanage how many

> Scopes manage how instances are reused• because theyʼre stateful• or expensive to construct or lookup• or expensive to maintain

37

Page 38: Big Modular Java with Guice

Common scopes

> Unscoped: one per use• create it, use it, and toss it!• often the best choice

> @Singleton: one per application• for heavyweight resources• and application state

> @RequestScoped: one per web or RPC request> @SessionScoped: one per HTTP session

38

Everything is unscoped by default in Guice.

Page 39: Big Modular Java with Guice

Applying scopesthe best way is to annotate a class with its scope

39

@Singleton

public class TweetClient {

...

@Inject

public TweetClient(Shortener shortener, Tweeter tweeter) {

this.shortener = shortener;

this.tweeter = tweeter;

}

Page 40: Big Modular Java with Guice

Applying scopesyou can specify scopes in a module

40

public class TweetModule extends AbstractModule {

protected void configure() {

bind(Tweeter.class) .to(SmsTweeter.class) .in(Singleton.class);

}

@Provides @Singleton Shortener provideShortener() {

return new TinyUrlShortener();

}

}

Page 41: Big Modular Java with Guice

Defining Injections

photo courtesy of http://flickr.com/photos/gaetanlee/631004864

Page 42: Big Modular Java with Guice

Constructor injectionto supply dependencies when creating an object

42

public class TweetClient {

private final Shortener shortener;

private final Tweeter tweeter;

@Inject

public TweetClient(Shortener shortener, Tweeter tweeter) {

this.shortener = shortener;

this.tweeter = tweeter;

}

Immutable

Page 43: Big Modular Java with Guice

Method injectionsets dependencies into a new or existing instance

43

public class TweetClient {

private Shortener shortener;

private Tweeter tweeter;

@Inject void setShortener(Shortener shortener) {

this.shortener = shortener;

}

@Inject void setTweeter(Tweeter tweeter) {

this.tweeter = tweeter;

}

> Plays nice with inheritance

Page 44: Big Modular Java with Guice

Field injectionsets dependencies into a new or existing instance

44

public class TweetClient {

@Inject Shortener shortener;

@Inject Tweeter tweeter;

public TweetClient() {}

> Concise, but difficult to test

Page 45: Big Modular Java with Guice

Injecting Providers

photo courtesy of http://flickr.com/photos/toasty/535851893

Page 46: Big Modular Java with Guice

The Provider interface

46

public interface Provider<T> {

T get();

}

Page 47: Big Modular Java with Guice

Injecting a Provider

47

public class TweetClient {

@Inject Provider<Shortener> shortenerProvider;

@Inject Tweeter tweeter;

public void postButtonClicked() {

String text = textField.getText();

if (text.length() > 140) {

Shortener shortener = shortenerProvider.get();

text = shortener.shorten(text);

}

...

}

Page 48: Big Modular Java with Guice

Why inject Providers?

> to get multiple instances• for example, if you need multiple

DatabaseConnections

> to load lazily

> to mix scopes• for example, to access request-scoped objects from

a singleton-scoped object

48

Page 49: Big Modular Java with Guice

Leveraging Guice

> AJAX via GWT> Servlets and App Engine> AOP> Type Literals> Introspection SPI

49photo courtesy of http://flickr.com/photos/niff/87501285

Page 50: Big Modular Java with Guice

AJAX via GWTand GIN

> Zero runtime cost: GIN generates JavaScript from modules

> API compatibility with Guice• Great for GWT-RPC• Test without a browser

50

public class MyWidgetClientModule extends AbstractGinModule {

protected void configure() {

bind(MyWidgetMainPanel.class).in(Singleton.class);

bind(MyRemoteService.class) .toProvider(MyRemoteServiceProvider.class);

}

}

Page 51: Big Modular Java with Guice

Servlets and App Engine

> Configure your servlets programatically

> Use @RequestScoped and @SessionScoped to manage application state safely and easily

51

public class TweetSearchServletModule extends ServletModule {

protected void configureServlets() {

serve("/search").with(TweetSearchServlet.class);

}

}

Page 52: Big Modular Java with Guice

AOPaspect oriented programming

> You can apply method interceptors to injected objects• This is fantastic for cross-cutting concerns like

transactions, security, and performance

52

public class DatabaseTweetStorage implements TweetStorage {

@Transactional

public void saveTweet(String message) {

...

}

}

Page 53: Big Modular Java with Guice

Type Literals

> Like the language itʼs built on, Guice loves types• Set<User> is distinct from Set<Tweet>

> Guice can defeat erasure!

53

@Inject

public SocialView(

Set<User> followers,

Set<Tweet> tweets) {

...

}

Page 54: Big Modular Java with Guice

Introspection SPIservice provider interface

54

TweetClient

<init>

Tweeter Shortener

SmsTweeter

<init>

@Username

String

TweetModule.java:34

"jesse"

TweetModule.java:38

#provideShortener()

> Module and injector internals are available via a mirror SPI• Inspect, analyze and rewrite

bindings

> Guice uses it internally for...• createInjector()• module overrides• graphing

Page 55: Big Modular Java with Guice

Wrapping up...

> Dependency injection leads to testable and reusable code

> Guice makes dependency injection easy• plus it enables scopes• and it integrates neatly with the other APIs you use

> It works on both Java™ SE and Java™ EE• Plus Android, App Engine and GWT (via GIN)

55

Page 56: Big Modular Java with Guice

For more information...

> The Guice website documents usage, extensions, and best practices• http://code.google.com/p/google-guice/

> Plus, Dhanji and Robbieʼs books:

56

Page 57: Big Modular Java with Guice

Q & A

photo courtesy of http://flickr.com/photos/toasty

Post your questions for this talk on Google Moderator:code.google.com/events/io/questionsClick on the Tech Talks Q&A link.

Page 58: Big Modular Java with Guice

Top Related