Skip to content

Latest commit

 

History

History
180 lines (123 loc) · 5.62 KB

inheritance_full.asciidoc

File metadata and controls

180 lines (123 loc) · 5.62 KB

Inheritance and specialization in {cdi_full}

Specializing a managed bean

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 { ... }

Specializing a producer method

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;
   }

   ...

}

Specialization

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.

Direct and indirect specialization

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.