specialization.xml
8.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ ]>
<chapter id="specialization">
<title>Specialization, inheritance and alternatives</title>
<para>
When you first start developing with CDI, you'll likely be dealing only with a single bean implementation for each
bean type. In this case, it's easy to understand how beans get selected for injection. As the complexity of your
application grows, multiple occurrences of the same bean type start appearing, either because you have multiple
implementations or two beans share a common (Java) inheritance. That's when you have to begin studying the
specialization, inheritance and alternative rules to work through unsatisfied or ambiguous dependencies or to
avoid certain beans from being called.
</para>
<para>
The CDI specification recognizes two distinct scenarios in which one bean extends another:
</para>
<blockquote>
<itemizedlist>
<listitem>
<para>
The second bean <emphasis>specializes</emphasis> the first bean in certain deployment scenarios. In these
deployments, the second bean completely replaces the first, fulfilling the same role in the system.
</para>
</listitem>
<listitem>
<para>
The second bean is simply reusing the Java implementation, and otherwise bears no relation to the first
bean. The first bean may not even have been designed for use as a contextual object.
</para>
</listitem>
</itemizedlist>
</blockquote>
<para>
The second case is the default assumed by CDI. It's possible to have two beans in the system with the same part
bean type (interface or parent class). As you've learned, you select between the two implementations using
qualifiers.
</para>
<para>
The first case is the exception, and also requires more care. In any given deployment, only one bean can fulfill a
given role at a time. That means one bean needs to be enabled and the other disabled. There are a two modifiers
involved: <literal>@Alternative</literal> and <literal>@Specializes</literal>. We'll start by looking at
alternatives and then show the guarantees that specialization adds.
</para>
<section id="alternativestereotypes">
<title>Using alternative stereotypes</title>
<para>
CDI lets you <emphasis>override</emphasis> the implementation of a bean type at deployment time using an
alternative. For example, the following bean provides a default implementation of the
<literal>PaymentProcessor</literal> interface:
</para>
<programlisting role="JAVA"><![CDATA[public class DefaultPaymentProcessor
implements PaymentProcessor {
...
}]]></programlisting>
<para>
But in our staging environment, we don't really want to submit payments to the external system, so we override
that implementation of <literal>PaymentProcessor</literal> with a different bean:
</para>
<programlisting role="JAVA"><![CDATA[public @Alternative
class StagingPaymentProcessor
implements PaymentProcessor {
...
}]]></programlisting>
<para>or</para>
<programlisting role="JAVA"><![CDATA[public @Alternative
class StagingPaymentProcessor
extends DefaultPaymentProcessor {
...
}]]></programlisting>
<para>
We've already seen how we can enable this alternative by listing its class in the <literal>beans.xml</literal>
descriptor.
</para>
<para>
But suppose we have many alternatives in the staging environment. It would be much more convenient to be able
to enable them all at once. So let's make <literal>@Staging</literal> an <literal>@Alternative</literal> stereotype
and annotate the staging beans with this stereotype instead. You'll see how this level of indirection pays off.
First, we create the stereotype:
</para>
<programlisting role="JAVA"><![CDATA[@Alternative
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Staging {}]]></programlisting>
<para>
Then we replace the <literal>@Alternative</literal> annotation on our bean with <literal>@Staging</literal>:
</para>
<programlisting role="JAVA"><![CDATA[@Staging
public class StagingPaymentProcessor
implements PaymentProcessor {
...
}]]></programlisting>
<para>
Finally, we activate the <literal>@Staging</literal> stereotype in the <literal>beans.xml</literal> descriptor:
</para>
<programlisting role="XML"><![CDATA[<beans
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<stereotype>org.mycompany.myapp.Staging</stereotype>
</alternatives>
</beans>]]></programlisting>
<para>
Now, no matter how many staging beans we have, they will all be enabled at once.
</para>
</section>
<section>
<title>A minor problem with alternatives</title>
<para>
When we enable an alternative, does that mean the default implementation is disabled? Well, not exactly. If the
default implementation has a qualifier, for instance <literal>@LargeTransaction</literal>, and the alternative
does not, you could still inject the default implementation.
</para>
<programlisting role="JAVA"><![CDATA[@Inject @LargeTransaction PaymentProcessor paymentProcessor;]]></programlisting>
<para>
So we haven't completely replaced the default implementation in this deployment of the system. 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! We need something
extra.
</para>
<para>
CDI provides a special feature, called <emphasis>specialization</emphasis>, that helps the developer avoid
these traps. Specialization is a way of informing the system of your intent to completely replace and disable
an implementation of a bean.
</para>
</section>
<section>
<title>Using specialization</title>
<para>
When the goal is to replace one bean implementation with a second, to help prevent developer error, the first
bean may:
</para>
<itemizedlist>
<listitem>
<para>
directly extend the bean class of the second bean, or
</para>
</listitem>
<listitem>
<para>
directly override the producer method, in the case that the second bean is a producer method, and then
</para>
</listitem>
</itemizedlist>
<para>explicitly declare that it <emphasis>specializes</emphasis> the second bean:</para>
<programlisting role="JAVA"><![CDATA[@Alternative @Specializes
public class MockCreditCardPaymentProcessor
extends CreditCardPaymentProcessor {
...
}]]></programlisting>
<para>
When an enabled bean specializes another bean, the other bean is never instantiated or called by the container.
Even if the other bean defines a producer or observer method, the method will never be called.
</para>
<para>
So why does specialization work, and what does it have to do with inheritance?
</para>
<para>
Since we're informing the container that our alternative bean is meant to stand in as a replacement for the
default implementation, the alternative implementation automatically inherits all qualifiers of the default
implementation. Thus, in our example, <literal>MockCreditCardPaymentProcessor</literal> inherits the qualifiers
<literal>@Default</literal> and <literal>@CreditCard</literal>.
</para>
<para>
Furthermore, if the default implementation declares a bean EL name using <literal>@Named</literal>, the name
is inherited by the specialized alternative bean.
</para>
</section>
<!--
vim:et:ts=3:sw=3:tw=120
-->
</chapter>