Upload
tamas-rev
View
517
Download
1
Embed Size (px)
Citation preview
Practical introduction to Dependency Injection
Author: Tamas Revemail: [email protected]: tamasrev.wordpress.comTwitter: @tamasrev
What is Dependency Injection?
• It’s the callers responsibility to specify the details (which message sender).
• It’s the responsibility of the service to provide generic solutions (what message do we send)
• Hollywood principle (don’t call the framework)
Benefits of DI
• Modularity• Flexible code• Testability• Decoupling metadata from actual code• …– Similar to those of TDD
Problems with DI
• Lots of XML– Difficult, but you cannot mix metadata with code
• Bloated constructor– Ugly but provides compile-time checks
• Depending on a framework– Unless you apply the Hollywood principle
DI (IoC) frameworks
• Spring• JAVA EE CDI• Guice• Tapestry-DI• Treasure map• PicoContainer• Dagger (for Android)• …
Focus
Focus
• DI features of Dependency Injection Frameworks
• Spring, Guice, Tapestry-DI
Not in focus
• Receiving a little focus– Aspects / Decorators / Interceptors
• Definitely not in focus:– Frameworks not mentioned above
Sandbox
• FizzBuzz kata
FizzBuzz Kata
• Print the numbers 1..100 to the STDOUT. Except:– For numbers divisible by 3 you should print “Fizz”– For numbers divisible by 5 you should print “Buzz”– For numbers divisible by both 3 and 5 you should
print “FizzBuzz”• http://codingdojo.org/cgi-bin/index.pl?
KataFizzBuzz
FizzBuzz Cowboypublic class FizzBuzzCowBoy { public static void main(final String... args) { for (int i = 1; i <= 100; i++) { if (i % 15 == 0) { System.out.println("FizzBuzz"); } else if (i % 5 == 0) { System.out.println("Buzz"); } else if (i % 3 == 0) { System.out.println("Fizz"); } else { System.out.println(i); } } }}
Enterprise FizzBuzz
• Some requirements can be:– Maybe you need tests– Maybe you need to send the lines in email– Maybe you need to group the lines– Maybe you need to change the logic– …
• Dependency Injection to the rescue!
No framework – abstract logicpublic class FizzBuzzManualInjection { private final FizzBuzzNumberProvider numberProvider; private final FizzBuzzTextProvider textProvider; private final FizzBuzzPrinter printer; public FizzBuzzManualInjection(final FizzBuzzNumberProvider numberProvider, final FizzBuzzTextProvider textProvider, final FizzBuzzPrinter printer) { this.numberProvider = numberProvider; this.textProvider = textProvider; this.printer = printer; } public void doFizzBuzz() { numberProvider.getFizzBuzzNumbers().forEach(num -> { final String msg = textProvider.getFizzBuzzText(num); printer.printFizzBuzz(msg); }); }}
No framework – mainpublic class Main { public static void main(final String[] args) { final FizzBuzzNumberProvider numberProvider = new FizzBuzzNumberProvider(); final FizzBuzzTextProvider textProvider = new FizzBuzzTextProvider(); final FizzBuzzPrinter printer = new FizzBuzzPrinter(); final FizzBuzzManualInjection fizzBuzz = new FizzBuzzManualInjection(numberProvider, textProvider, printer); fizzBuzz.doFizzBuzz(); }}
Endless variations
• github.com/rev-tomi/fizzbuzz-spring-javaconf• github.com/rev-tomi/fizzbuzz-spring• github.com/rev-tomi/fizzbuzz-guice• github.com/rev-tomi/fizzbuzz
-manual-injection• github.com/rev-tomi/fizzbuzz-tapestry-di
Bootstrapping the framework
• I.e. writing the main
Spring framework
• The de facto IoC framework for java• Since 2002• Feature-rich: dependency injection, aspects,
templates and lots of other stuff
Spring - mainpublic class Main {
public static void main(final String[] args) { try (final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("fizzbuzz.spring.xml")) { final FizzBuzzService fizzBuzzService = context.getBean(FizzBuzzService.class); fizzBuzzService.doFizzBuzz(); } }
}
Spring java config - mainpublic class Main {
public static void main(final String[] args) { try (final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(FizzBuzzConfig.class)) { final FizzBuzzService fizzBuzz = context.getBean(FizzBuzzService.class); fizzBuzz.doFizzBuzz(); } }
}
This is the only difference
Google Guice
• A Dependency Injection framework by Google• Originally developed for Google Wave• Lightweight, annotation based• Since 2008
Guicepublic class Main {
public static void main(final String[] args) { // This is for the constructor with concrete classes //final Injector injector = Guice.createInjector();
final Injector injector = Guice.createInjector(new FizzBuzzModule());
final FizzBuzzGuiceService fizzBuzz = injector.getInstance(FizzBuzzGuiceService.class);
fizzBuzz.doFizzBuzz();
}
}
Tapestry-DI
• An IoC framework behind the Tapestry framework
• It makes web development easy: you can inject components and generic services too
Tapestry-DI - mainpublic class Main {
public static void main(final String[] args) { // code inspired by http://wiki.apache.org/tapestry/Tapestry5HowToIocOnly final RegistryBuilder builder = new RegistryBuilder(); builder.add(FizzBuzzModule.class);
final Registry registry = builder.build(); registry.performRegistryStartup();
final FizzBuzzTapestryService fizzBuzz = registry.getService(FizzBuzzTapestryService.class); fizzBuzz.doFizzBuzz(); }
}
DI configuration
• XML• Annotations• java statements
Spring – xml configuration<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" ... >
<bean id="fizzBuzz" class="com.trev.fizzbuzz.inject.spring.FizzBuzzService" > <property name="numberProvider" ref="fizzbuzz.numberProvider" /> <property name="textProvider" ref="fizzbuzz.textProvider" /> <property name="printer" ref="fizzbuzz.printer" /> </bean>
</beans>
Spring – xml configuration<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" ... >
<bean id="fizzBuzz" class="com.trev.fizzbuzz.inject.spring.FizzBuzzService" > <property name="numberProvider" ref="fizzbuzz.numberProvider" /> <property name="textProvider" ref="fizzbuzz.textProvider" /> <property name="printer" ref="fizzbuzz.printer" /> </bean>
</beans>
This is a bean definition
Bean ID
Bean configuration
Spring javaconfig// They call them beans – that live in a configuration@Configurationpublic class FizzBuzzConfig { // where did we see this before?
@Bean // almost <bean> public FizzBuzzPrinter fizzBuzzPrinter() { return new DefaultFizzBuzzPrinter(); }
// ...}
Spring javaconfig// They call them beans – that live in a configuration@Configurationpublic class FizzBuzzConfig { // where did we see this before?
@Bean // almost <bean> public FizzBuzzPrinter fizzBuzzPrinter() { return new DefaultFizzBuzzPrinter(); }
// ...}
So we know it defines beans
This is a bean definition
The abstract type of the bean
Bean ID
Bean configurationand other initialization
Guice config// They call them bindings – that reside in a modulepublic class FizzBuzzModule extends AbstractModule {
@Override protected void configure() { bind(FizzBuzzNumberProvider.class).to(DefaultNumberProvider.class); bind(FizzBuzzPrinter.class).to(DefaultFizzBuzzPrinter.class); bind(FizzBuzzTextProvider.class).to(DefaultTextProvider.class); }
}
Guice config// They call them bindings – that reside in a modulepublic class FizzBuzzModule extends AbstractModule {
@Override protected void configure() { bind(FizzBuzzNumberProvider.class).to(DefaultNumberProvider.class); bind(FizzBuzzPrinter.class).to(DefaultFizzBuzzPrinter.class); bind(FizzBuzzTextProvider.class).to(DefaultTextProvider.class); }
}
So we know it defines services
The abstract type of the serviceThis is the service id too
Service configuration across the bindingsConcrete type of the service.I.e. what do we inject.
Tapestry-DI config// They call them bindings – that reside in a modulepublic class FizzBuzzModule {
public static void bind(final ServiceBinder binder) { binder.bind(FizzBuzzNumberProvider.class, DefaultNumberProvider.class); binder.bind(FizzBuzzPrinter.class, DefaultFizzBuzzPrinter.class); binder.bind(FizzBuzzTextProvider.class, DefaultTextProvider.class); binder.bind(FizzBuzzTapestryService.class); // concrete service class }
}
Tapestry-DI config// They call them bindings – that reside in a modulepublic class FizzBuzzModule {
public static void bind(final ServiceBinder binder) { binder.bind(FizzBuzzNumberProvider.class, DefaultNumberProvider.class); binder.bind(FizzBuzzPrinter.class, DefaultFizzBuzzPrinter.class); binder.bind(FizzBuzzTextProvider.class, DefaultTextProvider.class); binder.bind(FizzBuzzTapestryService.class); // concrete service class }
}
Must place to the right package,so we know it defines services
The abstract type of the serviceThis is the service id too
Service configuration across the bindingsConcrete type of the service.I.e. what do we inject.
Magic
Aspects
• Advices• Interceptors• Typically used for– Transaction– Performance measurement– Logging
• Problem: too much magic
What magic?
• Proxy– Proxy Pattern (GoF)– java.lang.reflect.Proxy
• Difficult to find the aspect implementation• Stack traces from hell
Stack traces from hell• http://
www.nurkiewicz.com/2012/03/filtering-irrelevant-stack-trace-lines.html
…at sun.reflect.NativeMethodAccessorImpl.invoke0()at sun.reflect.NativeMethodAccessorImpl.invoke()at sun.reflect.DelegatingMethodAccessorImpl.invoke()at java.lang.reflect.Method.invoke()at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs()at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod()…
Tapestry – @CommitAfter• In HibernateModule:@Contribute(ComponentClassTransformWorker2.class)@Primarypublic static void provideCommitAfterAnnotationSupport(OrderedConfiguration configuration) { // If logging is enabled, we want logging to be the first advice, wrapping around the commit advice. configuration.addInstance("CommitAfter", CommitAfterWorker.class, "after:Log");}• Need to do a few more steps to
Tapestry - CommitAfterWorkerpublic class CommitAfterMethodAdvice implements MethodAdvice { @Override public void advise(final MethodInvocation invocation) { //... skipped some stuff for brevity if (transaction != null && !transaction.isActive()) { transaction.begin(); } try { invocation.proceed(); // going on with the method being annotated } catch (final RuntimeException e) { if (transaction != null && transaction.isActive()) { rollbackTransaction(transaction); } throw e; } if (transaction != null && transaction.isActive()) { transaction.commit(); } //... skipped some stuff for brevity }}
Spring - @Transactional
• TransactionalInterceptor• Won’t fit into this slide• Can deal with distributed transactions too
Wrapping up
• All the frameworks are very similar, but they call the same thing differently.
• Bean / Service are equivalent• Module / Config are equivalent• Aspect / Interceptors / Decorators / Method
advices are equivalent
Questions