- Antoine Sabot-Durand
- Red Hat
- CDI spec lead
- @antoine_sd
- next-presso.com
- github.com/antoinesd
Slides here http://bit.ly/newcdi2 |
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 |
CDI core |
CDI for Java SE |
CDI for Java EE |
public static void main(String[] args) {
SeContainer container = SeContainerInitializer.newInstance() (1)
.disableDiscovery()
.addBeanClasses(MyService.class)
.initialize();
MyService service = container.select(MyService.class).get(); (2)
service.sayHello();
container.close();
}
1 | SeContainerInitializer is a new api to configure and bootstrap a CDI container under Java SE. |
2 | it returns a SeContainer that implements Instance<Object> allowing programmatic lookup |
public abstract class SeContainerInitializer {
public SeContainerInitializer addBeanClasses();
public SeContainerInitializer addPackages();
public SeContainerInitializer addExtensions();
public SeContainerInitializer enableInterceptors();
public SeContainerInitializer enableDecorators();
public SeContainerInitializer selectAlternatives();
public SeContainerInitializer selectAlternativeStereotypes();
public SeContainerInitializer addProperty(); (1)
public SeContainerInitializer setProperties();
public SeContainerInitializer disableDiscovery(); (2)
public SeContainerInitializer setClassLoader(); (3)
1 | implementation specific properties can be passed to the container |
2 | By default bean discovery mode is annotated , you can disable it and add your bean classes with addBeanClasses() methods |
During servlet, filter or listener execution |
During Java EE web service invocation |
During asynchronous observer invocation |
During any remote or asynchronous method invocation of any EJB |
During any call to an EJB timeout method and delivery to any MDB |
During @Postconstruct callback invocation for any bean |
In Java SE user may want to use @RequestScoped bean |
3rd party framework developers may want to have their own request lifecycle without having to create a custom context. |
Doesn’t make sense for Application Context |
Dependent context is always active by definition |
Conversation Context is already controlled programmatically |
Session context is very special animal, we may consider adding this for it in a next CDI version |
@ApplicationScoped
public class MyBean {
@ActivateRequestContext (1)
public void doRequest(String body) {
// Request Context will be activated during this invocation
}
}
1 | CDI 2.0 provides @ActivateRequestContext interceptor to activate request during method invocation |
@ApplicationScoped
public class MyBean {
@Inject
private RequestContextController requestContextController;
(1)
public void doRequest(String body) {
// activate request context
requestContextController.activate();
// do work in a request context.
// deactivate the request context
requestContextController.deactivate();
}
}
1 | CDI 2.0 provides a new built-in bean of type RequestContextController to allow manual activation and deactivation of the Request Context. |
Events ordering |
Asynchronous events |
Better programmatic solution to fire event |
Better observers control and creation |
Observers 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 |
Observer with the same priority are called in an undefined order |
No priority on async events |
public void observer1(@Observes @Priority(1) Payload p) { ... }(1)
public void observer2(@Observes @Priority(2) Payload p) { ... }
1 | Commons Annotation 1.3 now allows using @Priority on a method parameter |
Also work on extension observers: |
public class MyExtension implements Extension {
public void firstPat(@Observes @Priority(1) ProcessAnnotatedType<?> pat) {
}
public void secondPat(@Observes @Priority(2) ProcessAnnotatedType<?> pat) {
}
}
Asynchronous events
Event
interfacepublic interface Event<T> {
...
public void fire(T event); (1)
public CompletionStage<T> fireAsync(T event); (2)
public CompletionStage<T> fireAsync(T event, NotificationOptions options); (3)
...
}
1 | CDI 1.0 synchronous event firing |
2 | Async event firing. It returns a object implementing the Java 8 CompletionStage<T> (like CompletableFuture ) |
3 | Async event firing with options (to provide a specific Executor or a implementation specific property) |
@ApplicationScoped
public class MyBean {
@Inject
Event<Payload> event;
(1)
public void someEvenMoreCriticalBusinessMethod() {
event.fireAsync(new Payload()); (2)
}
}
1 | We still inject the Event built-in bean |
2 | Here we call fireAsync in a call and forget pattern (do nothing with CompletionStage ) |
We introduced a new observer kind: @ObservesAsync |
@ApplicationScoped
public class MyOtherBean {
public void callMe(@ObservesAsync Payload payload) {
(1)
// I am called in another thread
}
}
1 | this observer will be invoked asynchronously |
Sync events (with fire() ) only trigger @Observes |
Async event (with fireAsync() ) only trigger @ObservesAsync |
Better programmatic solution to fire events
When you can’t @Inject the Event built-in bean |
BeanManager bm = CDI.current.getBeanManager(); (1)
bm.fireEvent(myPayload); (2)
1 | you need to retrieve the BeanManager (if you are in an extension, you can inject it in any lifecycle observer) |
2 | using the legacy fireEvent method |
getEvent()
More type safe approach |
Similar to Event<Object> injection so less side effect |
reusable and allows fireAsync() |
BeanManager bm = CDI.current.getBeanManager();
bm.getEvent().fireAsync(myPayload); (1)
1 | firing an asynchronous event |
Better programmatic solution to fire events
ProcessObserverMethod
enhancementYou can now configure ObserverMethod way more in Extensions |
We added the following methods to ProcessObserverMethod |
public void setObserverMethod(ObserverMethod<T> observerMethod); (1)
public ObserverMethodConfigurator<T> configureObserverMethod(); (2)
public void veto(); (3)
1 | replacing the ObserverMethod by a custom one. |
2 | configuring the observed ObserverMethod to change its behavior |
3 | vetoing the observer method (it will be ignored) |
Interceptors easier to apply
InterceptionFactory
public interface InterceptionFactory<T> {
InterceptionFactory<T> ignoreFinalMethods(); (1)
AnnotatedTypeConfigurator<T> configure(); (2)
T createInterceptedInstance(T instance); (3)
}
1 | forces creation of the interceptor even if the class to intercept has non static final methods (those shouldn’t be called in the intercepted instance of course) |
2 | allow addition of Interceptor bindings on the class and or method of the class to intercept |
3 | create an intercepted version of the instance passed in parameter |
InterceptionFactory
By injecting the InterceptionFactory built-in bean. Useful to apply interceptors on a produced bean |
@Produces
public MyClass createMyClass(InterceptionFactory<MyClass> ify) {
ify.configure().addAnnotation(new AnnotationLiteral<Transactional>() {};
return ify.createInterceptedInstance(new MyClass());
}
By calling BeanManager.createInterceptionFactory() . Useful for custom beans creation or other advanced usages |
You can add your interceptor on specific methods instead of all class |
@Produces
public MyClass createMyClass(InterceptionFactory<MyClass> ify) {
AnnotatedTypeConfigurator<MyClass> atc = ify.configure();
atc.filterMethods(m -> m.getJavaMember().getName().equals("doSomething")) (1)
.findFirst()
.ifPresent(m -> m.add(new AnnotationLiteral<Transactional>() { }));
(2)
return ify.createInterceptedInstance(new MyClass());
(3)
}
1 | retrieving the doSomething() method in MyClass |
2 | adding @Literal on it |
3 | returning an instance of MyClass whose doSomething() is now transactional |
CDI is using Annotation Literals in various places |
It’s a mean to provide an annotation instance |
So we provided AnnotationLiterals for: |
@Inject
@Qualifier
@Alternative
& @Specializes
@Typed
@Vetoed
@NonBinding
@New
If the annotation doesn’t have members it’s straightforward |
ApplicationScoped apsl = ApplicationScoped.Literal.INSTANCE; (1)
1 | for annotation included in CDI, Literal is defined the annotation class |
If the annotation have members use the of() method |
Named myNamed = NamedLiteral.of("myName"); (1)
1 | annotations coming from outside CDI have their literal in javax.enterprise.inject.literal package |
SPI made easy
Yet, it is mainly used by advanced users |
In CDI 1.x using SPI use to generate verbose and non elegant code |
In CDI 2.0 the configurators solve this |
So in there are no more excuse to avoid SPI coding |
Major element having a configurator |
AnnotatedType
hierarchyBeanAttributes
Bean
InjectionPoint
ObserverMethod
Producer
These configurators are accessible in lifecycle container event when writing extensions |
But also in the standard programming model. |
Did you notice where we used one configurator in previous slides? |
You use a legacy framework you don’t own |
This framework instantiates components using a config Map |
public class LegacyClass {
...
public void setConfig(Map<String, Object> config) {
this.config = config;
}
...
}
You’d like to inject the map instead of setting it the classical way |
You’ll use an InjectionTargetFactory to do the job (and a producer for the config Map) |
To have the map injected you’ll need to to create an AnnotatedType that simulate this |
public class LegacyClass {
...
@Inject
public void setConfig(Map<String, Object> config) {
this.config = config;
}
...
}
Injection code will look like |
public class InjectionBean {
@Inject
BeanManager bm;
public LegacyClass getInjectedLegacy(LegacyClass component) {
AnnotatedType<LegacyClass> atmc = bm.createAnnotatedType(LegacyClass.class);
bm.getInjectionTargetFactory(new LegacyAnnotatedType(atmc)) (1)
.createInjectionTarget(null)
.inject(component,bm.createCreationalContext(null));
return component;
}
}
1 | the verbose part |
AnnotatedType
public class LegacyAnnotatedType implements AnnotatedType<LegacyClass> {
AnnotatedType<LegacyClass> delegate;
Set<AnnotatedMethod<? super LegacyClass>> methods = new HashSet<>();
public LegacyAnnotatedType(AnnotatedType<LegacyClass> atmc) {
delegate = atmc;
for (AnnotatedMethod<? super LegacyClass> am : delegate.getMethods()) {
if(am.getJavaMember().getName().equals("setConfig"))
methods.add(new InjectedMethod(am));
else
methods.add(am);
}
}
public Set<AnnotatedMethod<? super LegacyClass>> getMethods() {
return methods;
}
// 8 more methods calling delegate
}
AnnotatedMethod
public class InjectedMethod<T> implements AnnotatedMethod<T> {
AnnotatedMethod<T> delegate;
Set<Annotation> annotations;
public InjectedMethod(AnnotatedMethod<T> am) {
delegate = am;
annotations = new HashSet<>(delegate.getAnnotations());
annotations.add(new AnnotationLiteral<Inject>() {
});
}
@Override
public Set<Annotation> getAnnotations() {
return annotations;
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
if(annotationType.equals(Inject.class))
return true;
return delegate.isAnnotationPresent(annotationType);
}
// 8 more methods calling delegate
}
@ApplicationScoped
public class InjectionBean {
@Inject
BeanManager bm;
public LegacyClass getInjectedLegacy(LegacyClass component) {
AnnotatedType<LegacyClass> atmc = bm.createAnnotatedType(LegacyClass.class);
InjectionTargetFactory<LegacyClass> itf = bm.getInjectionTargetFactory(atmc));
itf.configure().filterMethods(m -> m.getJavaMember()
.getName().equals("setConfig"))
.findFirst()
.ifPresent(m -> m.add(InjectLiteral.INSTANCE));
itf.createInjectionTarget(null)
.inject(component, bm.createCreationalContext(null));
return component;
}
}
Glassfish 5 preview |
CDI SE with Weld 3.0 |
Patch JBoss WildFly to update CDI |
Just add this dep to your pom.xml |
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-shaded</artifactId>
<version>3.0.0.Final</version>
</dependency>
And you’re ready to |
public class RunMe {
public static void main(String[] args) {
SeContainer container = SeContainerInitializer.newInstance()
.initialize();
// Put your code here
}
}
Get the patch for WildFly: http://download.jboss.org/weld/3.0.0.Final/ |
Get WildFly 10.1.0.Final: http://wildfly.org/downloads/ |
Unzip WildFly (not the patch) and go to $WILDFLY_HOME/bin directory |
Type the following command: |
$ ./jboss-cli.sh --command="patch apply $PATH_TO_PATCH/wildfly-10.1.0.Final-weld-3.0.0.Final-patch.zip"
Now you have a WildFly server running CDI 2.0 with Weld 3.0.0 |
Slides are accessible here http://bit.ly/newcdi2 |
Slides source https://github.com/antoinesd/Whats-new-in-CDI-2.0 |
Slides generated with Asciidoctor and DZSlides backend |
Original slide template - Dan Allen & _Sarah White |
License of this doc: CC BY-SA 4.0 |