Transcript
@antoine_sd @cdispec#Devoxx #CDI2
CDI 2.0 is upon usAntoine Sabot-Durand
Red HatCDI spec lead
@antoine_sd @cdispec#Devoxx #CDI2
Me, Myself and I
Antoine Sabot-Durand
Software Engineer at Red Hat
CDI spec lead
CDI evangelist
Follow me: @antoine_sd
Follow CDI: @cdispec
@antoine_sd @cdispec#Devoxx #CDI2
AgendaFlashback on CDI & CDI 2.0 status
CDI 2.0 new features
Java SE support
Enhancing events
Metadata configurators
Interceptor and Decorators on Produced / custom beans (coming)
Questions and Feedback
Previously on CDI
@antoine_sd @cdispec#Devoxx #CDI2
CDI Timeline
Dec 2009 June 2013 Apr 2014 Sep 2014
CDI 1.0
(Jav
a EE
6)
CDI 1.1
(Jav
a EE
7)
CDI 1.2
(1.1 M
R)
CDI 2.0
Star
ts
jan 2017
CDI 2.0
relea
sed
@antoine_sd @cdispec#Devoxx #CDI2
CDI 1.0 - December 2009A well-defined lifecycle for stateful objects bound to lifecycle contexts
Where the set of contexts is extensible
A type-safe dependency injection mechanism without verbose configuration
Dependencies can be selected at development or deployment time
Type-safe decorators and interceptors
An event notification model
An SPI allowing portable extensions to integrate cleanly with the container
@antoine_sd @cdispec#Devoxx #CDI2
CDI 1.1 / 1.2 – June 2013 / April 2014
Add automatic enablement of CDI in Java EE
Add introspection with event, bean, decorator and interceptor meta data
Ease access to bean manager from outside CDI with CDI class
Add global enablement of interceptors using the @Priority annotation
Add Unmanaged allowing easy access to non-contexutal instances
Spec clarification
@antoine_sd @cdispec#Devoxx #CDI2
CDI 1.2 - April 2014Clarification in the spec regarding:
CDI Lifecycle
Events
Reworking Bean defining annotation to avoid conflict with other JSR 330 frameworks
Clarification on conversation resolution
OSGi official support in the API
@antoine_sd @cdispec#Devoxx #CDI2
CDI 2.0 status
JSR 365 started in September 2014
EDR1 released in 2015
EDR2 released in august.
Weld 3 Alpha 17 is the RI for EDR2
Release expected in January 2017
@antoine_sd @cdispec#Devoxx #CDI2
cdi-spec.org
@antoine_sd @cdispec#Devoxx #CDI2
CDI 2.0 new features
@antoine_sd @cdispec#Devoxx #CDI2
Java SE supportusing CDI outside Java EE
@antoine_sd @cdispec#Devoxx #CDI2
Java SE support - Why?
To align on many other Java EE spec which support Java SE bootstrapping
To boost CDI adoption for Spec and Frameworks
To provide a mean of building new stacks out of Java EE
@antoine_sd @cdispec#Devoxx #CDI2
What did we do?
CDI Specification
CDI Core CDI for Java EECDI for Java SE
@antoine_sd @cdispec#Devoxx #CDI2
Java SE bootstrap API
public static void main(String[] args) { SeContainer container = SeContainerInitializer.newInstance() .disableDiscovery() .addBeanClasses(MyService.class) .initialize(); MyService service = container.select(MyService.class).get(); service.sayHello(); container.close();}
@antoine_sd @cdispec#Devoxx #CDI2
Controlling the Request Context
In Java SE, most of our built-in contexts are unavailable:
Request context
Session context
Conversation context
That’s why we added tools to activate Request context programmatically
This feature is also available for Java EE
@antoine_sd @cdispec#Devoxx #CDI2
Controlling the Request Context
@ApplicationScopedpublic class MyBean { @Inject private RequestContextController requestContextController; public void doRequest(String body) { // activate request context requestContextController.activate(); // do work in a request context. // deactivate the request context requestContextController.deactivate(); }}
@antoine_sd @cdispec#Devoxx #CDI2
In interceptor mode
@ApplicationScopedpublic class MyBean { @ActivateRequestContext public void doRequest(String body) { // Request Context will be activated during this invocation } }
@antoine_sd @cdispec#Devoxx #CDI2
Enhancing eventsMaking popular feature even more popular
@antoine_sd @cdispec#Devoxx #CDI2
Enhancing events
CDI events are a very loved feature
We took a very long time to see how to enhance them
In CDI 2.0 we are introducing
Event ordering
Asynchronous events
@antoine_sd @cdispec#Devoxx #CDI2
Events ordering
By adding a @Priority (from commons annotations) on an observer.
The lowest value is first
Observers with no explicit priority have a middle range priority
Allowing observer to be called last
Observer with the same priority are called in an undefined order
No priority on async events
@antoine_sd @cdispec#Devoxx #CDI2
Events ordering
public void observer1(@Observes @Priority(1) Payload p) { }
public void observer2(@Observes @Priority(2) Payload p) { }
@antoine_sd @cdispec#Devoxx #CDI2
Events ordering in extensions
public class MyExtension implements Extension { public void firstPat(@Observes @Priority(1) ProcessAnnotatedType<?> pat) { … }
public void secondPat(@Observes @Priority(2) ProcessAnnotatedType<?> pat) { … }}
@antoine_sd @cdispec#Devoxx #CDI2
CDI 1.x: Sync / Async
Sync / Async is not specified
The immutable status of the payload is not specified
Implementations use a Sync model
The payload is mutated in some implementations / framework
Going async “blindly” might raise problems…
@antoine_sd @cdispec#Devoxx #CDI2
Synchronous events - firing pattern
@InjectEvent<Payload> event;
public void someBSCriticalBusinessMethod() { event.fire(new Payload());}
@antoine_sd @cdispec#Devoxx #CDI2
Synchronous events - observing pattern
public void callMe(@Observes Payload payload) { // Do something with the event }
@antoine_sd @cdispec#Devoxx #CDI2
Events are sync in CDI 1
All the observers are called in the firing thread
In no particular order (at least not specified)
The payload may be mutated
Observers may inject beans
Observers are aware of Transactional and security contexts
@antoine_sd @cdispec#Devoxx #CDI2
Asynchronous EventsAsync events can’t:
Mute Payload
Access CDI contexts states at the firing point
Be transactional
So designing backward compatible async events is more tricky than it looks:
A currently sync event should remain sync
Going sync / async should be a decision taken from the firing side
Being sync should be possible from the observing side
@antoine_sd @cdispec#Devoxx #CDI2
Async events - firing pattern
@InjectEvent<Payload> event;
public void someEvenMoreCriticalBusinessMethod() { event.fireAsync(new Payload());}
@antoine_sd @cdispec#Devoxx #CDI2
Async events - observing pattern
public void callMe(@ObservesAsync Payload payload) { // I am called in another thread}
@antoine_sd @cdispec#Devoxx #CDI2
Sync vs Async Events in a nutshell
callMe(@Observes payload) callMe(@ObservesAsync payload)
event.fire(payload) Sync call Not notified
event.fireAsync(payload) Not notified Async call
@antoine_sd @cdispec#Devoxx #CDI2
Adding an Executor to fireAsync
@InjectEvent<PanelUpdater> event;
public void someOtherCriticalBusinessMethod() { event.fireAsync(new PanelUpdater(green), executor); }
@antoine_sd @cdispec#Devoxx #CDI2
Async event in the GUI thread
@InjectEvent<PanelUpdater> event;
public void someOtherCriticalBusinessMethod() { event.fireAsync(new PanelUpdater(green), SwingUtilities::invokeLater);}
@antoine_sd @cdispec#Devoxx #CDI2
Handling exceptions
@InjectEvent<PanelUpdater> event;
public void someOtherCriticalBusinessMethod() { CompletionStage<PanelUpdater> stage = event.fireAsync(new PanelUpdater(green), SwingUtilities::invokeLater);}
@antoine_sd @cdispec#Devoxx #CDI2
Handling exceptions
Exception in one async observer doesn’t stop execution as in sync observer
One of the reasons why firing async event doesn’t trigger sync observers
Event.fireAsync returns a CompletionStage
Allowing integration of async events in standard Java 8 async pipeline
@antoine_sd @cdispec#Devoxx #CDI2
Handling exceptions
The Exception in fireAsync returned CompletionStage is
CompletionException
It holds all the exceptions in the suppressed exception set
@antoine_sd @cdispec#Devoxx #CDI2
Handling exceptions
Exception handling is done with the Standard Java 8 async api, with:
stage.whenComplete() to deal with result or exception
stage.handle() same as above but allows transformation of stage
stage.exceptionally() to only treat exception case
@antoine_sd @cdispec#Devoxx #CDI2
SPI enhancementNew configurators for meta data
@antoine_sd @cdispec#Devoxx #CDI2
Standard annotations instances
CDI makes an heavy use of Annotations
Java doesn’t provide easy way to create an annotation instance
We provided AnnotationLiteral, to help users create their instances
Annotation instance = new AnnotationLiteral<ApplicationScoped>(){};
@antoine_sd @cdispec#Devoxx #CDI2
When members are here a class has to be created
public class NamedLiteral extends AnnotationLiteral<Named> implements Named{ private String value; public NamedLiteral(String value) { this.value = value; } @Override public String value() { return null; }}
@antoine_sd @cdispec#Devoxx #CDI2
CDI 2.0 provides helpers out of the box
ApplicationScoped literal = ApplicationScoped.Literal.INSTANCE;
Named literal2 = NamedLiteral.of("myName");
@antoine_sd @cdispec#Devoxx #CDI2
Configurators for meta-dataSome meta-data are very verbose to create
AnnotatedType
Bean
InjectionPoint
ObserverMethod
If you only need to add info to an existing meta-data, it’s very boring
@antoine_sd @cdispec#Devoxx #CDI2
Example with CDI 1.2
I have a legacy framework
I want to adapt it for CDI
I need to detect all @CacheContext annotations on fields…
... And transform them in injection point
I’ll use an extension to replace @CacheContext annotation by @Inject in AnnotatedTypes
@antoine_sd @cdispec#Devoxx #CDI2
Example 1/7
First we need to implement an AnnotatedType to decorate the original one and modify AnnotatedField set
@antoine_sd @cdispec#Devoxx #CDI2
Example 2/7
public class AutoInjectingAnnotatedType<X> implements AnnotatedType<X> { private final AnnotatedType<X> delegate; private final Set<AnnotatedField<? super X>> fields; public AutoInjectingAnnotatedType(AnnotatedType<X> delegate) { this.delegate = delegate; fields = new HashSet<>(); for (AnnotatedField<? super X> field : delegate.getFields()) { if (field.isAnnotationPresent(CacheContext.class)) fields.add(new AutoInjectingAnnotatedField(field)); else fields.add(field); } }…
@antoine_sd @cdispec#Devoxx #CDI2
Example 3/7
… public Set<AnnotatedField<? super X>> getFields() { return fields; } public Class<X> getJavaClass() { return delegate.getJavaClass(); } // 7 more similar methods }
@antoine_sd @cdispec#Devoxx #CDI2
Example 4/7Then we need to do the same for AnnotatedField to add @Inject to the field annotations set
public class AutoInjectingAnnotatedField<X> implements AnnotatedField<X> { private final Set<Annotation> annotations; private final AnnotatedField<X> delegate; public AutoInjectingAnnotatedField(AnnotatedField<X> delegate) { this.delegate = delegate; annotations = new HashSet<>(delegate.getAnnotations()); annotations.add(new InjectLiteral()); }…
@antoine_sd @cdispec#Devoxx #CDI2
Example 5/7
public <T extends Annotation> T getAnnotation(Class<T> annotationType) { if (annotationType.equals(Inject.class)) return (T) new InjectLiteral(); return delegate.getAnnotation(annotationType); } public Set<Annotation> getAnnotations() { return annotations; }
...
@antoine_sd @cdispec#Devoxx #CDI2
Example 6/7
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { if (annotationType.equals(Inject.class)) return true; return delegate.isAnnotationPresent(annotationType); }
public Set<Type> getTypeClosure() { return delegate.getTypeClosure(); }
// 4 similar methods }
@antoine_sd @cdispec#Devoxx #CDI2
Example 7/7Finaly we need to write the extension to use our custom AnnotatedType and AnnotatedField
public class AutoInjectExtension implements Extension {public <T> void CreateIP( @Observes @WithAnnotations(CacheContext.class) ProcessAnnotatedType<T> pat) { pat.setAnnotatedType( new AutoInjectingAnnotatedType<T>(pat.getAnnotatedType())); }}
@antoine_sd @cdispec#Devoxx #CDI2
In CDI 2.0
We introduced configurators, helping creation of these metadata
This configurators are accessible thru container lifecycle events
They are automatically built by the container at the end of observer invocation
@antoine_sd @cdispec#Devoxx #CDI2
In CDI 2.0All the previous code fits in this extension
public class AutoInjectExtension implements Extension {public <T> void CreateIP( @Observes @WithAnnotations(CacheContext.class) ProcessAnnotatedType<T> pat) { pat.configureAnnotatedType().filterFields( f -> f.isAnnotationPresent(CacheContext.class) ) .forEach(f -> f.add(InjectLiteral.INSTANCE)); }}
@antoine_sd @cdispec#Devoxx #CDI2
AOP enhancementSupport AOP on custom or produced bean
@antoine_sd @cdispec#Devoxx #CDI2
Support AOP on producerIn CDI 1.x you cannot bind an interceptor to a produced bean
When you write:
@Transactional is applied to producer method
@Produces@Transactional public MyService produceService() {
...}
@antoine_sd @cdispec#Devoxx #CDI2
Support Interceptor on producerAnswer is probably the new InterceptionFactory
This factory uses an AnnotatedType to know where adding interceptor bindings in the class
Could also be used in Custom Bean create method
public interface InterceptionFactory<T> { InterceptionProxyFactory<T> ignoreFinalMethods(); AnnotatedTypeConfigurator<T> configure(); <T> T createInterceptedInstance(T InstanceToWrap);}
@antoine_sd @cdispec#Devoxx #CDI2
Add @transaction on one produced bean method
@Produces@RequestScopedpublic MyClass createJpaEmAssumed(InterceptionFactory<MyClass> ify) { AnnotatedTypeConfigurator<MyClass> atc = ify.configure(); atc.filterMethods(m -> m.getJavaMember().getName().equals("doSomething")) .findFirst() .ifPresent(m -> m.add(new AnnotationLiteral<Transactional>() { })); return ify.createInterceptedInstance(new MyClass());}
@antoine_sd @cdispec#Devoxx #CDI2
CDI 2.0 needs you
CDI 2.0 specification is open to everyone
Come on join us on the mailing list and IRC channel
All infos on http://cdi-spec.org or by following to @cdispec on
The more we are the more we’ll deliver
@antoine_sd @cdispec#Devoxx #CDI2
Which JSR you’ll use 365 days a year?…
JSR 365
top related