In addition to rules defined in [managed_beans], the following rules apply.
If a bean class of a managed bean X is annotated @Specializes
, then the bean class of X must directly extend the bean class of another managed bean Y.
Then X directly specializes Y, as defined in Specialization.
If the bean class of X does not directly extend the bean class of another managed bean, the container automatically detects the problem and treats it as a definition error.
For example, MockLoginAction
directly specializes LoginAction
:
public class LoginAction { ... }
@Mock @Specializes
public class MockLoginAction extends LoginAction { ... }
In addition to rules defined in [producer_method], the following rules apply.
If a producer method X is annotated @Specializes
, then it must be non-static and directly override another producer method Y.
Then X directly specializes Y, as defined in Specialization.
If the method is static or does not directly override another producer method, the container automatically detects the problem and treats it as a definition error.
@Mock
public class MockShop extends Shop {
@Override @Specializes
@Produces
PaymentProcessor getPaymentProcessor() {
return new MockPaymentProcessor();
}
@Override @Specializes
@Produces
List<Product> getProducts() {
return PRODUCTS;
}
...
}
If two beans both support a certain bean type, and share at least one qualifier, then they are both eligible for injection to any injection point with that declared type and qualifier.
Consider the following beans:
@Default @Asynchronous
public class AsynchronousService implements Service {
...
}
@Alternative
public class MockAsynchronousService extends AsynchronousService {
...
}
Suppose that the MockAsynchronousService
alternative is selected, as defined in [selection]:
@Alternative @Priority(jakarta.interceptor.Interceptor.Priority.APPLICATION+100)
public class MockAsynchronousService extends AsynchronousService {
...
}
Then, according to the rules of [unsatisfied_and_ambig_dependencies], the following ambiguous dependency is resolvable, and so the attribute will receive an instance of MockAsynchronousService
:
@Inject Service service;
However, the following attribute will receive an instance of AsynchronousService
, even though MockAsynchronousService
is a selected alternative, because MockAsynchronousService
does not have the qualifier @Asynchronous
:
@Inject @Asynchronous Service service;
This is a useful behavior in some circumstances, however, it is not always what is intended by the developer.
The only way one bean can completely override a second bean at all injection points is if it implements all the bean types and declares all the qualifiers of the second bean. However, if the second bean declares a producer method or observer method, then even this is not enough to ensure that the second bean is never called!
To help prevent developer error, the first bean may:
-
directly extend the bean class of the second bean, or
-
directly override the producer method, in the case that the second bean is a producer method, and then
explicitly declare that it specializes the second bean.
@Specializes
public class MockAsynchronousService extends AsynchronousService {
...
}
When an enabled bean, as defined in [enablement], specializes a second bean, we can be certain that the second bean is never instantiated or called by the container. Even if the second bean defines a producer or observer method, the method will never be called.
The annotation @jakarta.enterprise.inject.Specializes
is used to indicate that one bean directly specializes another bean, as defined in Specializing a managed bean and Specializing a producer method.
Formally, a bean X is said to specialize another bean Y if there is either:
-
direct specialization, where X directly specializes Y, or
-
transitive specialization, where a bean Z exists, such that X directly specializes Z and Z specializes Y.
Then X will inherit the qualifiers and bean name of Y:
-
the qualifiers of X include all qualifiers of Y, together with all qualifiers declared explicitly by X, and
-
if Y has a bean name, the bean name of X is the same as the bean name of Y.
Furthermore, X must have all the bean types of Y. If X does not have some bean type of Y, the container automatically detects the problem and treats it as a definition error.
If Y has a bean name and X declares a bean name explicitly the container automatically detects the problem and treats it as a definition error.
For example, the following bean would have the inherited qualifiers @Default
and @Asynchronous
:
@Mock @Specializes
public class MockAsynchronousService extends AsynchronousService {
...
}
If AsynchronousService
declared a bean name:
@Default @Asynchronous @Named("asyncService")
public class AsynchronousService implements Service{
...
}
Then the bean name would also automatically be inherited by MockAsynchronousService
.
If an interceptor or decorator is annotated @Specializes
, non-portable behavior results.