Upload
virtual-jboss-user-group
View
715
Download
0
Embed Size (px)
Citation preview
INTRODUCTION TO CONTEXTS AND DEPENDENCY INJECTION (CDI)
@antoine_sd
ANTOINE SABOT-DURAND
• Senior Software Engineer @Red Hat
• Java & OSS :
• CDI co-spec lead
• CDI community development
• Tech Lead on Agorava
• @antoine_sd
WHAT IS CDI ?
• Java EE dependency injection standard• Strong typed and type safe• Context management• Observer pattern included (Event bus)• Highly extensible
A BIT OF HISTORY
Dec 2009 June 2013 Apr 2014 Sep 2014
CDI 1.0 (Java EE
6)
CDI 1.1 (Java EE
7)
CDI 1.2 (1.1 M
R)
CDI 2.0 Starts
Q1 2016
CDI 2.0 released
IMPLEMENTATIONSJBoss Weld (Reference Implementation) Apache Open WebBeans
CDI ACTIVATION
• In CDI 1.0, you must add a beans.xml file to your archive
• Since CDI 1.1, it’s activated by default:
• All classes having a “bean defining annotation” become a bean
• You can still use beans.xml file to activate CDI explicitly or deactivate it
THE CDI BEAN• In Java EE 6 and 7 everything is a Bean including (EJB session beans)
• beans are basic components
• They are managed by the container
• They all have a lifecycle
• They can be intercepted (AOP)
• They can be injected
• Accessible from outside CDI code.
BASIC DEPENDENCY INJECTION
@Inject
THIS IS A BEAN
public class HelloService { public String hello() { return "Hello World!"; }}
DI IN CONSTRUCTORpublic class MyBean {
private HelloService service;
@Inject public MyBean(HelloService service) { this.service = service; }}
DI IN INITIALISER METHODpublic class MyBean {
private HelloService service;
@Inject public void initService(HelloService service) { this.service = service; }}
DI IN FIELDpublic class MyBean { @Inject private HelloService service;
public void displayHello() { display(service.hello(); }}
NO TYPE ERASURE IN CDI
public class MyBean {
@Inject Service<User> userService;
@Inject Service<Staff> staffService; }
NO TYPE ERASURE IN CDI
public class MyBean {
@Inject Service<User> userService;
@Inject Service<Staff> staffService; }
This w
orks
TYPES OF A BEAN
• A bean will be a candidate for all “injection point” whose type belongs to its types set
• Bean types set contains its type, all its implementing interfaces and all its ancestors including Object.
• Bean types can be restricted by using @Typed annotation (Object is always in the set)
TYPES OF A BEANpublic class HelloService { //Types set : HelloService, Object} public class FrenchHelloService extends GenericService implements HelloService { //Types set : FrenchHelloService, GenericService, HelloService, Object } @Typed({HelloService.class,GenericService.class}) public class FrenchHelloService extends GenericService implements HelloService { //Types set : GenericService, HelloService, Object }
USING QUALIFIERS TO DISTINGUISH BEANS OF THE SAME TYPE
TWO SERVICE IMPLEMENTATIONS…public interface HelloService { public String hello();}public class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; }}public class EnglishHelloService implements HelloService { public String hello() { return "Hello World!"; }}
…NEED QUALIFIERS…@Qualifier@Retention(RUNTIME)@Target({FIELD, TYPE, METHOD, PARAMETER}) public @interface French {}
@Qualifier@Retention(RUNTIME)@Target({FIELD, TYPE, METHOD, PARAMETER}) public @interface English {}
…TO BE DISTINGUISHED.
@Frenchpublic class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; }}
@Englishpublic class EnglishHelloService implements HelloService { public String hello() { return "Hello World!"; }}
QUALIFIED INJECTION POINTSpublic class MyBean { @Inject @French HelloService service; public void displayHello() { display( service.hello(); }}public class MyBean { @Inject @English HelloService service; public void displayHello() { display( service.hello(); }}
QUALIFIERS CAN HAVE MEMBERS@Qualifier@Retention(RUNTIME)@Target({FIELD, TYPE, METHOD, PARAMETER}) public @interface Language {
Languages value(); @Nonbinding String description() default "";
public enum Languages { FRENCH, ENGLISH }}
QUALIFIERS WITH MEMBERS 1/2@Language(FRENCH)public class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; }}@Language(ENGLISH)public class EnglishHelloService implements HelloService { public String hello() { return "Hello World!"; }}
QUALIFIERS WITH MEMBERS 2/2public class MyBean { @Inject @Language(ENGLISH) HelloService service; public void displayHello() { display( service.hello(); }}
public class MyBean { @Inject @Language(FRENCH) HelloService service; public void displayHello() { display( service.hello(); }}
MULTIPLE QUALIFIERS
public class MyBean { @Inject @French HelloService service;}
@French @Console @Securedpublic class FrenchHelloService implements HelloService {
}
MULTIPLE QUALIFIERS
public class MyBean { @Inject @French @Console HelloService service;}
@French @Console @Securedpublic class FrenchHelloService implements HelloService {
}
MULTIPLE QUALIFIERS
public class MyBean { @Inject @French @Console @Secured HelloService service;}
@French @Console @Securedpublic class FrenchHelloService implements HelloService {
}
MULTIPLE QUALIFIERS
public class MyBean { @Inject @French @Console @Secured HelloService service;}
@French @Securedpublic class FrenchHelloService implements HelloService {
}
MULTIPLE QUALIFIERS
public class MyBean { @Inject @French @Console @Secured HelloService service;}
@French @Securedpublic class FrenchHelloService implements HelloService {
}
RESERVED QUALIFIERS
@Default@Any@Named
PROGRAMMATIC LOOKUP
SOMETIMES CALLED “LAZY INJECTION”
public class MyBean {
@Inject Instance<HelloService> service;
public void displayHello() { display( service.get().hello() ); }}
CHECK BEAN EXISTENCE AT RUNTIMEpublic class MyBean {
@Inject Instance<HelloService> service;
public void displayHello() { if (!service.isUnsatisfied()) { display( service.get().hello() ); } }}
LOOP ON ALL BEANS OF A GIVEN TYPEpublic class MyBean {
@Inject @Any Instance<HelloService> services;
public void displayHello() { for (HelloService service : services) { display( service.hello() ); } }}
SELECT A QUALIFIER AT RUNTIMEpublic class MyBean {
@Inject @Any Instance<HelloService> services;
public void displayHello() { display( service.select( new AnnotationLiteral()<French> {}) .get() ); }}
CONTEXTS
CONTEXTS MANAGE BEANS LIFECYCLE• They helps container to choose when a bean should be instantiated and destroyed
• They enforce the fact that a given bean is a singleton for a given context
• Built-in CDI contexts :
• @Dependent (default)
• @ApplicationScoped, @SessionScoped, @RequestScoped
• @ConversationScoped
• @Singleton
• You can create your own scope
CHOOSING THE RIGHT CONTEXT
@SessionScopedpublic class CartBean {
public void addItem(Item item) { ... } }
CHOOSING THE RIGHT CONTEXT
@ApplicationScopedpublic class CartBean {
public void addItem(Item item) { ... } }
CHOOSING THE RIGHT CONTEXT
@ApplicationScopedpublic class CartBean {
public void addItem(Item item) { ... } } FAI
L !!!
CONVERSATION IS MANAGE BY DEV
@ConversationScopedpublic class CartBean {
public void addItem(Item item) { ... } }
NEW CONTEXTS CAN BE CREATED
@ThreadScopedpublic class CartBean {
public void addItem(Item item) { ... } }
PRODUCERS
CREATING BEAN FROM ANY CLASS
@Producespublic MyNonCDIClass myProducer() {return new MyNonCdiClass();}...@InjectMyNonCDIClass bean;
DISPOSER ARE OPTIONAL
@Producespublic MyNonCDIClass myProducer() {return new MyNonCdiClass();}
// Will be call at the instance end of lifepublic void releaseMyInstance(@Disposes MyNonCdiClass inst) {inst.clean();}
PRODUCERS MAY HAVE A SCOPE
@Produces@RequestScopedpublic FacesContext produceFacesContext() { return FacesContext.getCurrentInstance();}
GETTING INFO FROM INJECTION POINT
@Producespublic Logger produceLog(InjectionPoint injectionPoint) { return Logger.getLogger(injectionPoint.getMember() .getDeclaringClass().getName());}
REMEMBER : “NO TYPE ERASURE”
@Producespublic <K, V> Map<K, V> produceMap(InjectionPoint ip) { if (valueIsNumber(ip.getType())) { return new TreeMap<K, V>(); } return new HashMap<K, V>();}
EVENTS
A NICE WAY TO ADD DECOUPLINGpublic class FirstBean { @Inject Event<Post> postEvent;
public void saveNewPost(Post myPost) { postEvent.fire(myPost); }}
public class SecondBean { public void listenPost(@Observes Post post) { System.out.println("Received : " + evt.message()); }}
EVENTS CAN BE QUALIFIEDpublic class FirstBean { @Inject Event<Post> postEvent;
public void saveNewPost(Post myPost) { postEvent.select( new AnnotationLiteral()<French> {}).fire(myPost); }}
public class SecondBean { // these 3 observers will be called public void listenFrPost(@Observes @French Post post) {} public void listenPost(@Observes Post post) {} public void listenObject(@Observes Object obj) {}
// This one won’t be called public void listenEnPost(@Observes @English Post post) {}}
AS ALWAYS “NO TYPE ERASURE”
public class SecondBean { // these observers will be resolved depending // on parameter in event payload type public void listenStrPost(@Observes Post<String> post) {} public void listenNumPost(@Observes Post<Number> post) {}}
SOME BUILT-IN EVENTSpublic class SecondBean { public void beginRequest(@Observes @Initialized(RequestScoped.class) ServletRequest req) {} public void endRequest(@Observes @Destroyed(RequestScoped.class) ServletRequest req) {}
public void beginSession(@Observes @Initialized(SessionScoped.class) HttpSession session) {} public void endSession(@Observes @Destroyed(SessionScoped.class) HttpSession session) {}}
DECORATORS & INTERCEPTORS
INTERCEPTOR VS DECORATOR• They are two Aspect Oriented mechanism
• Interceptor is technical oriented : transaction, security, logging
• Interceptor can be bound to any bean or bean method
• Decorator is business oriented : change the behaviour of a bean
• Decorator is for a given bean class
DECORATOR
• To use a decorator, your bean should implement an interface
• The decorator will implement the same interface and will be annotated with @Decorator annotation
• It can be an abstract class (to avoid choosing methods to decorate)
• A decorator injects the bean it decorates with @Delegate annotation
A DECORATOR
@Decorator@Priority(Interceptor.Priority.APPLICATION)public abstract class HelloDecorator implements HelloService {
// The decorated service may be restricted with qualifiers @Inject @Delegate HelloService service;
public String hello() { return service.hello() + "-decorated"; }}
INTERCEPTOR
Interceptor Binding Annotation
Interceptor Class
+
INTERCEPTOR BINDING…
@InterceptorBinding@Target({METHOD, TYPE}) @Retention(RUNTIME)public @interface Loggable {}
…IS USED TO BIND AN INTERCEPTOR
@Interceptor @Loggable@Priority(Interceptor.Priority.APPLICATION) public class LogInterceptor { @AroundInvoke public Object log(InvocationContext ic) throws Exception { System.out.println("Entering " + ic.getMethod().getName()); try { return ic.proceed(); } finally { System.out.println("Exiting " + ic.getMethod().getName()); } } }
IT CAN BE PUT ON CLASS OR METHOD
@Loggablepublic class MyBean {
@Inject HelloService service;
public void displayHello() { display( service.hello(); }}
OTHER FEATURES
• Stereotypes: to create annotation gathering multiple annotations
• Alternatives: to provide alternative to a bean for tests or specific environments
• Specialization : to completely override an existing bean
• Check the spec on http://cdi-spec.org
THAT’S ALL FOR BASIC CDI
• If you want to learn advanced stuff come to check my over talk : CDI advanced.
• follow @cdispec and @antoine_sd on twitter
• Questions ?