Weld and Client Proxy Creation

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

https://github.com/weld/core/blob/be7382b01c4a56c54f92873c1c2ebf0445714bfe/impl/src/main/java/org/jboss/weld/bootstrap/WeldStartup.java#L335

That causes the create method to be called on the ProxyInstantiator.Factory class with access to the configuration subsystem:

https://github.com/weld/core/blob/151e1fedcc16d6d2dfec3ecdf1c095f75fdd995d/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyInstantiator.java#L128-L129

The create method begins by assuming that the ProxyInstantiator that will be used is the DefaultProxyInstantiator:

https://github.com/weld/core/blob/151e1fedcc16d6d2dfec3ecdf1c095f75fdd995d/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyInstantiator.java#L90

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

https://github.com/weld/core/blob/151e1fedcc16d6d2dfec3ecdf1c095f75fdd995d/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyInstantiator.java#L91-L103

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:

https://github.com/weld/core/blob/151e1fedcc16d6d2dfec3ecdf1c095f75fdd995d/impl/src/main/java/org/jboss/weld/bean/proxy/UnsafeProxyInstantiator.java#L47-L49

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:

https://github.com/weld/core/blob/151e1fedcc16d6d2dfec3ecdf1c095f75fdd995d/impl/src/main/java/org/jboss/weld/bean/proxy/ReflectionFactoryProxyInstantiator.java#L52-L60

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

https://github.com/weld/core/blob/be7382b01c4a56c54f92873c1c2ebf0445714bfe/impl/src/main/java/org/jboss/weld/bean/proxy/DefaultProxyInstantiator.java#L42-L44

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!

Author: Laird Nelson

Devoted husband and father; working on Helidon at the intersection of Java, Jakarta EE, architecture, Kubernetes and microservices at Oracle; open source guy; Hammond B3 player and Bainbridge Islander.