Web Beans defines the following injection points:
Any injected field of a Web Bean implementation class
Any parameter of a Web Bean constructor, Web Bean remove method, initializer method, producer method or disposal method
Any parameter of an observer method, except for the event parameter
Web Bean instances may also be obtained by evaluating EL expressions which refer to the Web Bean by name.
In general, an API type or Web Bean name does not uniquely identify a Web Bean. When resolving a Web Bean at an injection point, the Web Bean manager considers API type, binding annotations and Web Bean deployment type precedence. When resolving a Web Bean name in EL, the Web Bean manager considers name and deployment type precedence. This allows Web Bean developers to decouple type from implementation.
The Web Bean manager is required to ensure that any injected reference to a Web Bean instance may be cast to any API type of the Web Bean.
An unsatisfied dependency exists at an injection point when no enabled Web Bean has the API type and binding types declared by the injection point.
An ambiguous dependency exists at an injection point when in the set of enabled Web Beans with the API type and binding types declared by the injection point there exists no unique Web Bean with a higher precedence than all other Web Beans in the set.
The Web Bean manager must validate all injection points of all enabled Web Beans at initialization time to ensure that there are no unsatisfied or ambiguous dependencies. If an unsatisfied or ambiguous dependency exists, an UnsatisfiedDependencyException or AmbiguousDependencyException is thrown by the Web Bean manager at initialization time, as defined in Section 4.9, “Instance resolution”.
For the purposes of typesafe resolution and dependency injection, primitive types and their corresponding wrapper types in the package java.lang are considered identical and assignable. If necessary, the Web Bean manager performs boxing or unboxing when it injects a value to a field or parameter of primitive or wrapper type.
However, if an injection point of primitive type resolves to a Web Bean that may be null, such as a producer method with a nullable (non-primitive) return type, a NullableDependencyException is thrown by the Web Bean manager at initialization time.
The method Bean.isNullable() may be used to detect if a Web Bean has null values.
References to Web Bean instances are valid only for a certain period of time. The application should not invoke a method of an invalid reference.
The validity of an injected reference depends upon whether the scope of the injected Web Bean is a normal scope or a pseudo-scope.
Any reference to a Web Bean with a normal scope is valid as long as the application maintains a hard reference to it. However, it may only be invoked when the context associated with the normal scope is active. If it is invoked when the context is inactive, a ContextNotActiveException is thrown by the Web Bean manager.
Any reference to a Web Bean with a pseudo-scope (such as @Dependent) is valid until the Web Bean instance to which it refers is destroyed. It may be invoked even if the context associated with the pseudo-scope is not active. If the application invokes a method of a reference to an instance that has already been destroyed, the behavior is undefined.
Clients of a Web Bean with a normal scope type, as defined in Section 8.2, “Normal scopes and pseudo-scopes”, do not hold a direct reference to the instance of the Web Bean (the object returned by Bean.create()). Instead, their reference is to a client proxy object. A client proxy implements/extends all API types of the Web Bean and delegates all method calls to the current instance (as defined in Section 8.2, “Normal scopes and pseudo-scopes”) of the Web Bean.
There are a number of reasons for this indirection:
The Web Bean manager must guarantee that when any valid injected reference to a Web Bean of normal scope is invoked, the invocation is always processed by the current instance of the injected Web Bean. In certain scenarios, for example if a request scoped Web Bean is injected into a session scoped Web Bean, or into a Servlet, this rule requires an indirect reference. (Note that the @Dependent pseudo-scope is not a normal scope.)
The Web Bean manager may use a client proxy when creating Web Beans with circular dependencies. This is only necessary when the circular dependencies are initialized via a simple Web Bean constructor or producer method parameter. (Web Beans with scope @Dependent never have circular dependencies.)
Finally, client proxies are serializable, even when the Web Bean itself is not. Therefore the Web Bean manager must use a client proxy whenever a Web Bean with normal scope is injected into a Web Bean with a passivating scope, as defined in Section 8.4, “Passivating scopes and serialization”. (On the other hand, Web Beans with scope @Dependent must be serialized along with their client.)
Client proxies are never required for a Web Bean whose scope is a pseudo-scope such as @Dependent.
All client proxies must be serializable.
Client proxies may be shared between multiple injection points. For example, a particular Web Bean manager might instantiate exactly one client proxy object per Web Bean. (However, this strategy is not required by the Web Beans specification.)
Certain legal API types cannot be proxied by the Web Bean manager:
classes without a non-private constructor with no parameters,
classes which are declared final or have final methods,
primitive types,
and array types.
If an injection point whose declared type cannot be proxied by the Web Bean manager resolves to a Web Bean with a normal scope type, an UnproxyableDependencyException is thrown by the Web Bean manager at initialization time.
Every time a method of the Web Bean is invoked upon a client proxy, the client proxy must:
obtain the context object by calling Manager.getContext(), passing the Web Bean scope, then
obtain an instance of the Web Bean by calling Context.get(), passing the Bean instance representing the Web Bean and true as the value of the create parameter, and
invoke the method upon the Web Bean.
If an injection point declares no binding type, the default binding type @Current is assumed.
The following are equivalent:
@ConversationScoped public class Order { private Product product; private User customer; @Initializer public void init(@Selected Product product, User customer) { this.product = product; this.customer = customer; } }
@ConversationScoped public class Order { private Product product; private User customer; @Initializer public void init(@Selected Product product, @Current User customer) { this.product = product; this.customer = customer; } }
As are the following:
<myapp:Order> <ConversationScoped/> <myapp:init> <Initializer/> <myapp:Product> <myapp:Selected/> </myapp:Product> <myapp:User/> </myapp:init> </myapp:Order>
<myapp:Order> <ConversationScoped/> <myapp:init> <Initializer/> <myapp:Product> <myapp:Selected/> </myapp:Product> <myapp:User> <Current/> </myapp:User> </myapp:init> </myapp:Order>
The following definitions are equivalent:
public class Payment { public Payment(BigDecimal amount) { ... } @Initializer Payment(Order order) { this(order.getAmount(); } }
public class Payment { public Payment(BigDecimal amount) { ... } @Initializer Payment(@Current Order order) { this(order.getAmount(); } }
As are the following:
<myapp:Payment> <myapp:Order/> </myapp:Payment>
<myapp:Payment> <myapp:Order> <Current/> </myapp:Order> </myapp:Payment>
The Java language does not currently support a literal syntax for parameterized types. Therefore, Web Beans provides the following helper class to allow inline instantiation of an object that represents a parameterized type.
public abstract class TypeLiteral<T> { protected TypeLiteral() { if (!(getClass().getSuperclass() == TypeLiteral.class)) { throw new RuntimeException("Not a direct subclass of TypeLiteral"); } if (!(getClass().getGenericSuperclass() instanceof ParameterizedType)) { throw new RuntimeException("Missing type parameter in TypeLiteral"); } } public final Type getType() { ParameterizedType parameterized = (ParameterizedType) getClass() .getGenericSuperclass(); return parameterized.getActualTypeArguments()[0]; } @SuppressWarnings("unchecked") public final Class<T> getRawType() { Type type = getType(); if (type instanceof Class) { return (Class<T>) type; } else if (type instanceof ParameterizedType) { return (Class<T>) ((ParameterizedType) type).getRawType(); } else if (type instanceof GenericArrayType) { return (Class<T>) Object[].class; } else { throw new RuntimeException("Illegal type parameter in TypeLiteral"); } } }
An object that represents any parameterized type may be obtained by subclassing TypeLiteral.
TypeLiteral type = new TypeLiteral<List<String>>() {};
This object may be passed to Web Beans APIs that perform typesafe resolution.
The Java language does not currently support a literal syntax for inline instantiation of annotation values. Therefore, Web Beans provides the following helper class to allow inline instantiation of annotation type instances.
public abstract class AnnotationLiteral<T extends Annotation> implements Annotation { protected AnnotationLiteral() { if (!(getClass().getSuperclass() == AnnotationLiteral.class)) { throw new RuntimeException( "Not a direct subclass of AnnotationLiteral"); } if (!(getClass().getGenericSuperclass() instanceof ParameterizedType)) { throw new RuntimeException( "Missing type parameter in AnnotationLiteral"); } } @SuppressWarnings("unchecked") public final Class<T> annotationType() { ParameterizedType parameterized = (ParameterizedType) getClass() .getGenericSuperclass(); return (Class<T>) parameterized.getActualTypeArguments()[0]; } }
An instance of an annotation type may be obtained by subclassing AnnotationLiteral.
public abstract class PayByBinding extends AnnotationLiteral<PayBy> implements PayBy {}
PayBy payby = new PayByBinding() { public value() { return CHEQUE; } };
Annotation values are often passed to Web Beans APIs that perform typesafe resolution.
Occasionally, the application might need to obtain a Web Bean instance via programmatic API call. This is useful when the type or binding types vary dynamically—in generic framework code, for example. Thus, the Manager interface provides operations for resolving a Web Bean by type or name. The Web Bean manager provides an implementation of this interface to the application.
The Web Bean manager provides a built-in Web Bean with API type javax.webbeans.Manager, scope @Dependent, deployment type @Standard and binding type @Current. Thus, any Web Bean may obtain an instance of Manager by injecting it:
@Current Manager manager;
Alternatively, the application may obtain the Manager object from JNDI. The Web Bean manager must register an instance of Manager with name java:comp/Manager in JNDI at initialization time.
A contextual instance of a Web Bean may be obtained by calling Manager.getInstance(), passing the Bean object representing the Web Bean.
public interface Manager { public <T> T getInstance(Bean<T> bean); ... }
Manager.getInstance() returns a Web Bean instance or client proxy.
If the given Bean instance represents a Web Bean with a normal scope, as defined in Section 8.2, “Normal scopes and pseudo-scopes”, Manager.getInstance() must return a client proxy.
obtain the context object by calling Manager.getContext(), passing the Web Bean scope, then
obtain an instance of the Web Bean by calling Context.get(), passing the Bean instance representing the Web Bean and true as the value of the create parameter, and return it.
The Manager.getInstanceByType() methods obtain a contextual instance of a Web Bean:
public interface Manager { public <T> T getInstanceByType(Class<T> type, Annotation... bindingTypes); public <T> T getInstanceByType(TypeLiteral<T> type, Annotation... bindingTypes); ... }
The first argument is a Web Bean API type, the remaining arguments are instances of binding annotation types.
For example:
PaymentProcessor pp = manager.getInstanceByType(PaymentProcessor.class, synchronousAnnotation, payByAnnotation);
If no binding annotations are passed to getInstanceByType(), the default binding type @Current is assumed.
If two instances of the same binding type are passed to getInstanceByType(), a DuplicateBindingTypeException is thrown.
If an instance of an annotation that is not a binding type is passed to getInstanceByType(), an IllegalArgumentException is thrown.
The getInstanceByType() method must:
Identify the Web Bean by calling Manager.resolveByType(), passing the type and binding annotations of the injection point.
If resolveByType() did not return a Web Bean, throw an UnsatisfiedDependencyException or, if resolveByType() returned more than one Web Bean, throw an AmbiguousDependencyException.
If the Web Bean has a normal scope type and the type cannot be proxied by the Web Bean manager, as defined in Section 4.4.1, “Unproxyable API types”, throw an UnproxyableDependencyException.
Otherwise, obtain an instance of the Web Bean (or a client proxy) by calling Manager.getInstance(), passing the Bean object representing the Web Bean, and return it.
The getInstanceByType() method is called whenever the Web Bean manager injects a Web Bean instance into another Web Bean.
In certain situations, injection is not the most convenient way to obtain a reference to a Web Bean instance. For example, it may not be used when the API type or and/binding types vary dynamically at runtime. In these situations, the application may directly call Manager.getInstanceByType().
When the application calls getInstanceByType() to obtain a Web Bean instance dynamically, it may need to pass instances of the binding annotation types.
The helper class javax.webbeans.AnnotationLiteral makes it easier to implement binding annotation types:
public class SynchronousBinding extends AnnotationLiteral<Synchronous> implements Synchronous {}
public abstract class PayByBinding extends AnnotationLiteral<PayBy> implements PayBy {}
Then the application may easily instantiate instances of the binding type:
PaymentProcessor pp = manager.getInstanceByType(PaymentProcessor.class, new SynchronousBinding(), new PayByBinding() { public PaymentMethod value() { return CHEQUE; } });
Parameterized API types may be specified by passing a subclass of TypeLiteral:
PaymentProcessor pp = manager.getInstanceByType( new TypeLiteral<List<String>>() {}, new WishListBinding() );
The process of matching a Web Bean to an injection point is called typesafe resolution. The Web Bean manager considers API type, binding annotations, and Web Bean precedences when resolving a Web Bean to be injected to an injection point.
Typesafe resolution usually occurs at Web Bean manager initialization time, allowing the Web Bean manager to warn the user if any enabled Web Beans have unsatisfied or ambiguous dependencies.
The resolveByType() method of the Manager interface returns the result of the typesafe resolution.
public interface Manager { public <T> Set<Bean<T>> resolveByType(Class<T> apiType, Annotation... bindingTypes); public <T> Set<Bean<T>> resolveByType(TypeLiteral<T> apiType, Annotation... bindingTypes); ... }
If no binding annotations are passed to resolveByType(), the default binding annotation @Current is assumed.
If two instances of the same binding type are passed to resolveByType(), a DuplicateBindingTypeException is thrown.
If an instance of an annotation that is not a binding type is passed to resolveByType(), an IllegalArgumentException is thrown.
The following algorithm must be used by the Web Bean manager when resolving a Web Bean by type:
First, the Web Bean manager identifies the set of matching enabled Web Beans which have the given API type. For this purpose, primitive types are considered to be identical to their corresponding wrapper types in java.lang, array types are considered identical only if their element types are identical and parameterized types are considered identical only if both the type and all type parameters are identical.
Next, the Web Bean manager considers the given binding annotations. If no binding annotations were passed to resolveByType(), the Web Bean manager assumes the binding annotation @Current. The Web Bean manager narrows the set of matching Web Beans to just those where for each given binding annotation, the Web Bean declares a binding annotation with (a) the same type and (b) the same annotation member value for each member which is not annotated @NonBinding (see Section 4.9.2.1, “Binding annotations with members”).
Next, the Web Bean manager examines the deployment types of the matching Web Beans, as defined in Section 2.5.7, “Deployment type precedence”, and returns the set of Web Beans with the highest precedence deployment type that occurs in the set. If there are no matching Web Beans, an empty set is returned.
If resolveByType() is called with the API type java.lang.Object and no binding annotations, it returns the set of all enabled Web Beans.
According to the algorithm above, binding annotations with members are supported:
@PayBy(CHEQUE) class ChequePaymentProcessor implements PaymentProcessor { ... }
@PayBy(CREDIT_CARD) class CreditCardPaymentProcessor implements PaymentProcessor { ... }
Then only ChequePaymentProcessor is a candidate for injection to the following attribute:
@PayBy(CHEQUE) PaymentProcessor paymentProcessor;
On the other hand, only CreditCardPaymentProcessor is a candidate for injection to this attribute:
@PayBy(CREDIT_CARD) PaymentProcessor paymentProcessor;
The Web Bean manager calls the equals() method of the annotation member value to compare values.
An annotation member may be excluded from consideration using the @NonBinding annotation.
@BindingType @Retention(RUNTIME) @Target({METHOD, FIELD, PARAMETER, TYPE}) public @interface PayBy { PaymentMethod value(); @NonBinding String comment(); }
Array-valued or annotation-values members of a binding annotation must be annotated @NonBinding. If an array-valued or annotation-valued member of a binding annotation is not annotated @NonBinding, a DefinitionException is thrown by the Web Bean manager at initialization time.
According to the algorithm above, a Web Bean implementation class or producer method may declare multiple binding annotations:
@Synchronous @PayBy(CHEQUE) class ChequePaymentProcessor implements PaymentProcessor { ... }
Then ChequePaymentProcessor would be considered a candidate for injection into any of the following attributes:
@PayBy(CHEQUE) PaymentProcessor paymentProcessor;
@Synchronous PaymentProcessor paymentProcessor;
@Synchronous @PayBy(CHEQUE) PaymentProcessor paymentProcessor;
A Web Bean must declare all of the binding annotations that are specified at the injection point to be considered a candidate for injection.
The Web Bean manager provides a Unified EL ELResolver. When this resolver is called with a null base object, it calls the method Manager.getInstanceByName() to obtain an instance of the Web Bean named in the EL expression:
public interface Manager { public Object getInstanceByName(String name); ... }
For example:
Object pp = manager.getInstanceByName("paymentProcessor");
The getInstanceByName() method must:
Identify the Web Bean by calling Manager.resolveByName(), passing the name.
If resolveByName() returned an empty set, return a null value.
Otherwise, if resolveByName() returned more than one Web Bean, throw an AmbiguousDependencyException.
Otherwise, if exactly one Web Bean was returned, obtain an instance of the Web Bean by calling Manager.getInstance(), passing the Bean instance representing the Web Bean.
For each distinct name that appears in the EL expression, getInstanceByName() must be called at most once. Even if a name appears more than once in the same expression, the Web Bean manager may not call getInstanceByName() multiple times with that name. This restriction ensures that there is a unique instance of each Web Bean with scope @Dependent in any EL evaluation.
Open issue: Web Beans supports qualified names. The ELResolver implements support for qualified names in Unified EL. How exactly does this work?
The process of matching a Web Bean to a name used in EL is called name resolution. Since there is no typing information available in EL, the Web Bean manager may consider only Web Bean names.
The resolveByName() method of the Manager interface performs name resolution.
public interface Manager { public Set<Bean<?>> resolveByName(String name); ... }
The following algorithm must be used by the Web Bean manager when resolving a Web Bean by name:
The Web Bean manager identifies the set of matching enabled Web Beans which have the given name.
Next, the Web Bean manager examines the deployment types of the matching Web Beans, as defined in Section 2.5.7, “Deployment type precedence”, and returns the set of Web Beans with the highest precedence deployment type that occurs in the set. If there are no matching Web Beans, an empty set is returned.
The name resolution algorithm usually occurs at runtime.
In a Servlet or JSF application, the Web Bean manager must register the ELResolver with the web container.
In a JSF environment, the Web Bean manager calls Application.addELResolver() at initialization time (or provides a faces-config.xml file with an <el-resolver> element).
If necessary, the Web Bean manager will register the ELResolver directly with the JSP engine by calling JspFactory.getDefaultFactory(), JspFactory.getJspApplicationContext() and finally JspApplicationContext.addELResolver() at initialization time.