(Taking a break from my blog post series in progress to write down stuff that I stepped through today on how Weld creates client proxies.)
The CDI specification says that if you have a managed bean in a normal scope (think: class with something like @ApplicationScoped
or @RequestScoped
on it), it must have a non-private, zero-argument constructor (and must not be final
) so that a CDI implementation can proxy it.
You may have noticed when using Weld’s implementation of CDI SE that this seems not to be required. I can do this:
@ApplicationScoped // normal scope
public class B {
private B() {} // hmm, seems to violate specification
@Override public String toString() { return "B"; }
}
…and can inject an instance of that wherever I like:
@Dependent
public class A {
@Inject
public A(final B b) { // hmm; how does CDI/Weld make b?
super();
System.out.println(b); // you'll see "B" on the console
}
}
Here is how that works.
When Weld’s CDI SE implementation starts up, it looks for a configuration item that indicates relaxed construction. This can be supplied in a few different ways, but the easiest way to supply it is by setting the org.jboss.weld.construction.relaxed
System property to a textual representation of a boolean value (i.e. “true
” or “false
“). In Weld’s implementation of CDI SE, if you do nothing, the value of this configuration item is effectively true
. In Weld’s implementation of CDI as found in application servers, the value of this configuration item is effectively false
. This is worth noting.
First, the easy path: if for whatever reason relaxed construction is not enabled, then we stop here. My example above will fail and Weld will correctly tell you that it has no way to create a B
instance because B
is “unproxyable [sic]” according to the rules laid out by the specification.
Let’s assume that relaxed construction is enabled. Weld begins by looking for a ProxyInstantiator
implementation:
That causes the create
method to be called on the ProxyInstantiator.Factory
class with access to the configuration subsystem:
The create
method begins by assuming that the ProxyInstantiator
that will be used is the DefaultProxyInstantiator
:
Then, if relaxed construction is enabled (which it is in this example), Weld will try two other hard-coded implementations in order, using the first “valid” one (we’ll see what that means shortly):
The first of these implementations is the UnsafeProxyInstantiator
, whose instantiation strategy is to use the sun.misc.Unsafe
class (redirected in modern JDKs to the jdk.internal.misc.Unsafe
class) to create an instance of a class without using constructors at all:
This is worth noting because you might be logging proxy instantiation inside your zero-argument constructor, but if this strategy is selected for whatever reason, your constructor won’t be called. I’ve personally been burned by this and have now seen others burned by it as well.
If that UnsafeProxyInstantiator
class is not available or can’t be used for any reason, then a second non-standard ProxyInstantiator
implementation is tried instead, which uses sun.reflect.ReflectionFactory
under the covers (which in modern JDKs is sort of redirected to jdk.internal.reflect.ReflectionFactory
). This class will happily use a private
zero-argument constructor:
(In this case of course your private
zero-argument constructor will be called so any logging you do in there will show up.)
(You can see how it does this here:)
Finally, if neither of these non-standard instantiation strategies works, then the already-constructed DefaultProxyInstantiator
is used instead, which does what you think it does, and adheres to the standard:
That is how the proxy object itself is created. Note that this does not create the actual underlying instance. For that, a private
constructor is just fine (in Weld’s CDI implementations, anyway).
Note also that the underlying instance is not created until a business method on the proxy is invoked. Note as well that any method defined by java.lang.Object
, other than toString()
, is not considered a business method.
Hopefully this helps someone!