The document outlines the extension of Java EE with CDI, focusing on the development of a web application to manage expenses utilizing various Java technologies. It details the architecture, features of CDI, and its programming model, including dependency injection, producers, interceptors, and decorators. The presentation emphasizes CDI's advantages in providing a modern and flexible framework for integrating third-party technologies into Java applications.
Introduces Java EE with CDI, presenting speakers Antoine Sabot-Durand and Antonio Goncalves, and outlines the agenda.
Features a web app named 'ExpensEEs' for expense management, based on Java EE 7 and several technologies.
Describes the model architecture of 'ExpensEEs' and introduces JBoss Forge as a scaffolding tool.
Highlights the current app status, mentions incomplete features, and discusses the refactoring plan.Introduces CDI, its powerful programming model, various lifecycle management of beans, and injection specifics.
Discusses producer methods and their role in creating beans, along with the concept of disposing resources.
Explains interceptors and decorators as AOP tools, including their definitions, uses, and examples.
Introduces qualifiers for differentiating beans of the same type, detailing how to use them with examples.
Explains the lifecycle of beans, scopes, and how contexts manage bean instances within defined lifecycles.
Discusses portable extensions in CDI, how they extend capabilities of CDI and integration with 3rd party libraries.
Announces ongoing work on CDI 2.0 with an emphasis on new features like asynchronous events.
Concludes the presentation and provides references to resources for further information.
Architecture - Model
User
String login
String password
Stringname
String email
UserRole role
Reimbursement
Date date
Set<Expense> expenses
Currency currency
User user
Conference conference
Expense
description
Date date
Float amount
ExpenseType expenseType
private Currency currency
Conference
String name
Date date
String country
String city
1
*
1 *
1
*
Declaring a Producer
public class ProducingBean{
@Produces
private List<Integer> mapInt = new ArrayList<>();
@Produces
@French
public List<String> FrenchListStrProducer() {
List<String> res = Arrays.asList("bonjour");
return res;
}
}
Producers should be declared in a Bean class
It’s a field or a method annotated with @Produces (static methods supported)
Producers can have qualifiers
value of field or method returned value is the bean instance
1
2
2
3
4
1
2
3
4
Interceptor example
@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());
}
}
}
We make the class an interceptor ( @Interceptor comes from Interceptor spec)
We bind this interceptor to the @Loggable interceptor binding
We activate and give a priority to the interceptor (can be done in beans.xml config file as well)
This intercepting method will be invoked instead of the intercepted one.
1
2
3
4
1
2
3
4
49.
Interceptor Usage
@Loggable
public class MyBean{ ... }
public class MyOtherBean {
@Loggable
public String hello() { ... }
}
All bean’s method will be intercepted
This method will be intercepted
1
2
1
2
Decorator Example
@Decorator
@Priority(Interceptor.Priority.APPLICATION)
public abstract classHelloDecorator implements HelloService {
@Inject @Delegate HelloService service;
public String hello() {
return service.hello() + "-decorated";
}
}
Declares that the class is a decorator
Activates and gives a priority to the decorator (could be done via beans.xml file)
Decorators can be abstract and should share the same interface than decorated beans
Decorated bean is annotated with @Delegate (other beans could be injected in decorator)
Decorating method is called instead of decorated bean method. Delegate can be used in it.
1
2
3
4
5
1
2
3
4
5
52.
Back to demo Create a @Loggable interceptor binding
Create a LoggingInterceptor
Add LoggingInterceptor to each service
Enable interceptor in the beans.xml
When an injection point resolves to multiple beans…
public class MyBean{
@Inject
HelloService service;
}
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!";
}
}
deployment fails with an Ambiguous dependencies error.
Binding members limit the number of annotations to add to your
code
@Language(FRENCH)
public class FrenchHelloServiceimplements HelloService {
public String hello() {
return "Bonjour tout le monde!";
}
}
@Language(ENGLISH)
public class EnglishHelloService implements HelloService {
public String hello() {
return "Hello World!";
}
}
public class MyBean {
@Inject
@Language(value = FRENCH, description = "ici on parle français")
HelloService serviceFr;
@Inject
@Language(value = ENGLISH, description = "english spoken here")
HelloService serviceEn;
}
Examples
public class MyBean{ ... }
@Named
public class MyBean2 { ... }
@Named @Language(FRENCH)
public class MyBean2 {
@Inject
MyBean2 bean;
}
this bean has @Default and @Any qualifiers
this bean has @Default , @Named and @Any qualifiers
this bean has @Language(FRENCH) , @Named and @Any qualifiers
this injection point has @Default qualifier
1
2
3
4
1
2
3
4
Back to demo Create a @Clear and @Encrypted qualifiers
Create a DigestPassword interface
Two implementations ClearPassword and EncryptPassword
Inject encrypted password digest into AccountBean and
UserService
Meet Instance interface
Instance interface lets perform typesafe resolution at runtime
publicclass MyBean {
@Inject
Instance<HelloService> service;
public void displayHello() {
display(service.get().hello());
}
}
Instance<T> injection points are always satisfied and never fail at deployment time
with Instance<T> you control when bean a instance is requested with the get() method
1
2
1
2
Looping on all beans in the Instance
Instance<T> is iterable
It’s where we use the @Any qualifier present on all beans
public class MyBean {
@Inject
@Any
Instance<HelloService> services;
public void displayHello() {
for (HelloService service : services) {
display(service.hello());
}
}
}
Available scopes
CDI provides the following built-in scopes (and associated contexts):
@Dependent (default) bean has the same scope than its injector
@ApplicationScoped instance is linked to application lifecycle
@SessionScoped instance is linked to http session lifecycle
@RequestScoped instance is liked to http request lifecycle
@ConversationScoped lifecycle manually controlled within session
Instance is created at first request and destroyed with its bound
context
CDI SPI allows third party to create their custom contexts
79.
Examples
public class BaseHelloServiceimplements HelloService { ... }
@RequestScoped
public class RequestService {
@Inject HelloService service;
}
@ApplicationScoped
public class ApplicationService {
@Inject RequestService service;
}
Bean has default scope @Dependent , instances are created for each injection
Bean is @RequestScoped . Instance is created by request context and destroyed with request context
Bean is @ApplicationScoped . Instance is created by application context and will live during all
application
No problem to inject bean from an other scope: CDI will provide the right bean
1
2
3
4
1
2
3
4
Example - Logout and session invalidation
@Inject FacesContext facesContext;
publicString logout(){
try{
facesContext.getExternalContext().invalidateSession();
return "success?faces-redirect=true";
}
catch(Exception e){
return "error";
}
}
redirect is important to end current request and kill the session effectively
we might want to keep the current session and logout user
not a good practice to call UI layer from service to perform business
1
1
86.
Example - Do logout in a CDI way
@Named
@SessionScoped
public class AccountBeanimplements Serializable {
@Inject
private Instance<AccountBean> myInstance;
public String doLogout() {
myInstance.destroy(myInstance.get());
return "/index";
}
Since CDI 1.1, Instance provides a destroy() method to remove an instance from its context
1
1
Alternative Example
public SapService implementsErpService {
}
@Alternative
public MockSapService implements ErpService {
}
@ApplicationScoped
public OrderService {
@Inject
ErpService service;
}
A service doing heavy or costly operation
Can be mocked with an alternative
Beans injecting the service have nothing to do. Alternative will replace the original bean if selected
1
2
3
1
2
3
91.
Selecting the Alternative
With @Priority annotaion (for the whole app)
@Alternative
@Priority(Interceptor.Priority.APPLICATION)
public MockSapService implements ErpService {
}
With beans.xml file (for the current module)
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
<alternatives>
<class>com.acme.MockSapService</class>
</alternatives>
</beans>
1
Stereotypes
we have a lot of bean with @Conversation and @Named
we can create a stereotype gathering both
@Stereotype
@Named
@ConversationScoped
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Wizard {
}
We are delcaring a Stereotype
All the annotations we want to put in the stereotype
1
2
2
1
2
Events in action
public 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());
}
}
Event<T> is injected at the firing side. T is the event payload type (here my Post class)
When needed, we fire the event with our instance of T (here the Post object to save)
At the consuming side, we define an observer for a given payload type with @Observes annotation. If
observed type match fired event type, the observer is called.
1
2
3
1
2
3
97.
Observer resolution mimics typesafe resolution (in a looser way)
public class FirstBean{
@Inject Event<Post> postEvent;
public void saveNewPost(Post myPost) {
postEvent.select(new AnnotationLiteral()<French> {}).fire(myPost);
}
}
public class SecondBean {
public void listenFrPost(@Observes @French Post post) {}
public void listenPost(@Observes Post post) {}
public void listenObject(@Observes Object obj) {}
public void listenEnPost(@Observes @English Post post) {}
}
These observers will be triggered (empty subset is accepted for qualifiers when resolving observers)
This observer won’t be triggered
1
1
1
2
1
2
98.
Hooking on context lifecycle with events
public 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) {}
public void beginApplication(@Observes @Initialized(ApplicationScoped.class) ServlerContext ctx) {}
public void endApplication(@Observes @Destroyed(ApplicationScoped.class) ServlerContext ctx) {}
}
Observer must be defined in a bean
Observer resolution may trigger bean instance creation.
So observers above are a nice way to initailize bean with its context
Internal Step HappenOnce Loop on Elements
Bean manager lifecycle
Deployment
Start
Before
Bean
Discovery
Scan
Archive
Process
Annotated
Type
After
Type
Discovery
Bean
Eligibility
Check
Process
Injection
Point
Process
Injection
Target
Process
Bean
Attributes
Process
Bean
Process
Producer
Process
Observer
Method
After
Bean
Discovery
After
Deployment
Validation
Application
Running
Before
Shutdown
Undeploy
Application
SPI for type meta-model
Annotated
Type getBaseType()
Set<Type> getTypeClosure()
<Textends Annotation> getAnnotation(Class<T>)
Set<Annotation> getAnnotations()
boolean isAnnotationPresent(Class<? extends Annotation>)
AnnotatedMember
X
Member getJavaMember()
boolean isStatic()
AnnotatedType<X> getDeclaringType()
AnnotatedParameter
X
int getPosition()
AnnotatedCallable<X> getDeclaringCallable()
AnnotatedType
X
Class<X> getJavaClass()
Set<AnnotatedConstructor<X>> getConstructors()
Set<AnnotatedMethod<? super X>> getMethods()
Set<AnnotatedField<? super X>> getFields()
AnnotatedCallable
X
List<AnnotatedParameter<X>> getParameters()
AnnotatedField
X
Field getJavaMember()
AnnotatedConstructor
X
Constructor<X> getJavaMember()
AnnotatedMethod
X
Method getJavaMember()
116.
SPI dedicated to CDI meta-model
BeanAttributes
T
Set<Type> getTypes()
Set<Annotation> getQualifiers()
Class<?extends Annotation> getScope()
String getName()
Set<Class<? extends Annotation>> getStereotypes()
boolean isAlternative()
Bean
T
Class<?> getBeanClass()
Set<InjectionPoint> getInjectionPoints()
boolean isNullable()
Interceptor
T
Set<Annotation> getInterceptorBindings()
boolean intercepts(InterceptionType type)
Object intercept(InterceptionType, T, InvocationContext)
Decorator
T
Type getDelegateType()
Set<Annotation> getDelegateQualifiers()
Set<Type> getDecoratedTypes()
Producer
T
T produce(CreationalContext<T>)
void dispose(T)
Set<InjectionPoint> getInjectionPoints()
InjectionTarget
T
void inject(T, CreationalContext<T>)
void postConstruct(T)
void preDestroy(T)
InjectionPoint
Type getType()
Set<Annotation> getQualifiers()
Bean<?> getBean()
Member getMember()
Annotated getAnnotated()
boolean isDelegate()
boolean isTransient()
ObserverMethod
T
Class<?> getBeanClass()
Type getObservedType()
Set<Annotation> getObservedQualifiers()
Reception getReception()
TransactionPhase getTransactionPhase()
void notify(T)
EventMetadata
Set<Annotation> getQualifiers()
InjectionPoint getInjectionPoint()
Type getType()
Asynchronous event example
public class FirstBean{
@Inject Event<Post> postEvent;
public void saveNewPost(Post myPost) {
postEvent.fireAsync(myPost);
}
}
public class SecondBean {
public void listenPost(@ObservesAsync Post post) {
System.out.println("Received : " + evt.message());
}
}
We introduced a fireAsync() method
For backward compatibility we had to add an @ObservesAsync observer
1
2
1
2