Skip to content

Commit

Permalink
Incorporate additional changes - move specialization to Full; change …
Browse files Browse the repository at this point in the history
…Modularity paragraph.
  • Loading branch information
manovotn committed Apr 29, 2021
1 parent 893141b commit a45fbe3
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 140 deletions.
124 changes: 2 additions & 122 deletions spec/src/main/asciidoc/core/inheritance.asciidoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[[inheritance]]

== Inheritance and specialization
== Inheritance

A bean may inherit type-level metadata and members from its superclasses.

Expand Down Expand Up @@ -97,124 +97,4 @@ This injection point is inherited by `UserDaoClient`, but the type of the inheri
----
public class UserDaoClient
extends DaoClient<User> { ... }
----

[[specialization]]

=== 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:

[source, java]
----
@Default @Asynchronous
public class AsynchronousService implements Service {
...
}
----

[source, java]
----
@Alternative
public class MockAsynchronousService extends AsynchronousService {
...
}
----

Suppose that the `MockAsynchronousService` alternative is selected, as defined in <<selection>>:

[source, java]
----
@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`:

[source, java]
----
@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`:

[source, java]
----
@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.

[source, java]
----
@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]]

==== Direct and indirect specialization

The annotation `@jakarta.enterprise.inject.Specializes` is used to indicate that one bean _directly specializes_ another bean, as defined in <<specialize_managed_bean>> and <<specialize_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`:

[source, java]
----
@Mock @Specializes
public class MockAsynchronousService extends AsynchronousService {
...
}
----

If `AsynchronousService` declared a bean name:

[source, java]
----
@Default @Asynchronous @Named("asyncService")
public class AsynchronousService implements Service{
...
}
----

Then the bean name would also automatically be inherited by `MockAsynchronousService`.

If an interceptor is annotated `@Specializes`, non-portable behavior results.

----
119 changes: 113 additions & 6 deletions spec/src/main/asciidoc/core/inheritance_full.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,122 @@

== Inheritance and specialization in {cdi_full}

[[specialization_full]]
[[specialization]]

=== Specialization in {cdi_full}
=== Specialization

[[direct_and_indirect_specialization_full]]
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.

==== Direct and indirect specialization in {cdi_full}
Consider the following beans:

In addition to rules defined in <<direct_and_indirect_specialization>>, the following apply when running in {cdi_full}:
[source, java]
----
@Default @Asynchronous
public class AsynchronousService implements Service {
...
}
----

If decorator is annotated `@Specializes`, non-portable behavior results.
[source, java]
----
@Alternative
public class MockAsynchronousService extends AsynchronousService {
...
}
----

Suppose that the `MockAsynchronousService` alternative is selected, as defined in <<selection>>:

[source, java]
----
@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`:

[source, java]
----
@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`:

[source, java]
----
@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.

[source, java]
----
@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]]

==== Direct and indirect specialization

The annotation `@jakarta.enterprise.inject.Specializes` is used to indicate that one bean _directly specializes_ another bean, as defined in <<specialize_managed_bean>> and <<specialize_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`:

[source, java]
----
@Mock @Specializes
public class MockAsynchronousService extends AsynchronousService {
...
}
----

If `AsynchronousService` declared a bean name:

[source, java]
----
@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.

12 changes: 1 addition & 11 deletions spec/src/main/asciidoc/core/injectionandresolution.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,10 @@ The container is not required to support circular chains of dependencies where e

Beans and their clients may be deployed in _modules_ in a module architecture.
In a module architecture, certain modules are considered _bean archives_.
The library may be an explicit bean archive or an implicit bean archive, as defined in <<bean_archive>>.
In CDI Lite, libraries that are bean archives are always implicit bean archives.

A bean packaged in a certain module is available for injection, lookup and name resolution to classes packaged in some other module if and only if the bean class of the bean is required to be _accessible_ to the other module by the class accessibility requirements of the module architecture.

An alternative is not available for injection, lookup or name resolution to classes in a module unless the module is a bean archive and the alternative is explicitly _selected_ for the bean archive or the application.

[[declaring_selected_alternatives]]

==== Declaring selected alternatives
Expand Down Expand Up @@ -60,14 +58,6 @@ A bean is said to be _enabled_ if:

Otherwise, the bean is said to be disabled.

[[inconsistent_specialization]]

==== Inconsistent specialization

Suppose an enabled bean X specializes a second bean Y.
If there is another enabled bean that specializes Y we say that _inconsistent specialization_ exists.
The container automatically detects inconsistent specialization and treats it as a deployment problem.

[[inter_module_injection]]

==== Inter-module injection
Expand Down
10 changes: 10 additions & 0 deletions spec/src/main/asciidoc/core/injectionandresolution_full.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

=== Modularity in {cdi_full}

In module architecture, a library may be an explicit bean archive or an implicit bean archive, as defined in <<bean_archive>>.

Additionally, an alternative is not available for injection, lookup or name resolution to classes in a module unless the module is a bean archive and the alternative is explicitly _selected_ for the bean archive or the application.

[[declaring_selected_alternatives_full]]

==== Declaring selected alternatives in {cdi_full}
Expand Down Expand Up @@ -54,7 +58,13 @@ If the same type is listed twice under the `<alternatives>` element, the contain

For a custom implementation of the `Bean` interface defined in <<bean>>, the container calls `isAlternative()` to determine whether the bean is an alternative, and `getBeanClass()` and `getStereotypes()` to determine whether an alternative is selected in a certain bean archive.

[[inconsistent_specialization]]

==== Inconsistent specialization

Suppose an enabled bean X specializes a second bean Y.
If there is another enabled bean that specializes Y we say that _inconsistent specialization_ exists.
The container automatically detects inconsistent specialization and treats it as a deployment problem.

[[inter_module_injection_full]]

Expand Down
1 change: 1 addition & 0 deletions spec/src/main/asciidoc/core/scopescontexts.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ The `@Dependent` scope is always active.

Many instances of beans with scope `@Dependent` belong to some other bean and are called _dependent objects_.

* Instances interceptors are dependent objects of the bean instance they decorate.
* An instance of a bean with scope `@Dependent` injected into a field, bean constructor or initializer method is a dependent object of the bean into which it was injected.
* An instance of a bean with scope `@Dependent` injected into a producer method is a dependent object of the producer method bean instance that is being produced.
* An instance of a bean with scope `@Dependent` obtained by direct invocation of an `Instance` is a dependent object of the instance of `Instance`.
Expand Down
2 changes: 1 addition & 1 deletion spec/src/main/asciidoc/core/scopescontexts_full.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

In addition to rules defined in <<dependent_objects>>, when running in {cdi_full}, the following apply:

* Instances of decorators and interceptors are dependent objects of the bean instance they decorate.
* Instances of decorators are dependent objects of the bean instance they decorate.


[[passivating_scope_full]]
Expand Down

0 comments on commit a45fbe3

Please sign in to comment.