CDI object not proxyable with injected constructor
Asked Answered
M

5

16

When trying to inject arguments into the constructor of a CDI bean (ApplicationScoped), I'm encountering the following issue:

Caused by: org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001435: Normal scoped bean class xx.Config is not proxyable because it has no no-args constructor - Managed Bean [class xx.Config] with qualifiers [@Default @Named @Any].
    at org.jboss.weld.bean.proxy.DefaultProxyInstantiator.validateNoargConstructor(DefaultProxyInstantiator.java:50)
    at org.jboss.weld.util.Proxies.getUnproxyableClassException(Proxies.java:217)
    at org.jboss.weld.util.Proxies.getUnproxyableTypeException(Proxies.java:178)

However, I do have an injectable constructor on the class:

@Inject
public Config(ConfigLocator configLocator) {
    defaultConfigPath = configLocator.getPath();
    doStuff();
}

With a default constructor, variable injection and a postconstruct method, this all works fine, but I'd prefer the constructor injection in this case.

Any thoughts what is going wrong here?

Microsurgery answered 3/10, 2017 at 11:36 Comment(2)
Adding a private default constructor gives me WELD-001436 . Adding a protected default constructor works, but then brings up an exception that RequestScope is not active while initializing the ConfigLocator.Microsurgery
Ok !! I have doneKassandra
B
7

We resolved similar problem splitting class into interface and implementation. In your case something like this:

public interface Config
{
  // API here
}

@ApplicationScoped @Priority(0)
public class ConfigImpl implements Config
{
  @Inject
  public ConfigImpl(ConfigLocator configLocator) { ... }

  // API implementation here
}
Branen answered 28/11, 2017 at 20:37 Comment(0)
K
5

This example might help you :

@ApplicationScoped
public class Config {

    private String defaultConfigPath;  

    @Inject
    public Config(ConfigLocator configLocator) {
       this.defaultConfigPath = configLocator.getPath();
       doStuff();
    }

    // create a no-args constructor which is required for any scoped bean.
    public Config() {
    }

}

You need to have a public non-args constructor in the @ApplicationScoped bean.

Note : The bean for this class will created only once and is maintained for the entire life of an application. This bean will shared among all the managed beans. @ApplicationScoped beans are singleton in nature.

The mentioned issue:

Caused by: org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001435: Normal scoped bean class xx.Config is not proxyable because it has no no-args constructor - Managed Bean [class xx.Config] with qualifiers [@Default @Named @Any].

The possible reason for this is that a non-dependent scoped bean is required to provide a public no-args constructor for CDI, so that it can pick up the desired proxy bean at runtime.

Kassandra answered 14/7, 2019 at 8:1 Comment(1)
Note: Weld still seems happy if the no-args constructor (or the @Inject constructor) is protected. This way, developers cannot accidentally construct their own instance (or at least not within code residing in another package).Tramway
S
4

The non-private, no-arg constructor is needed for the implementation to create a proxy to your managed bean You don't lose the functionality of your injected constructor based on the presence of the non-private, no-arg constructor.

The container uses proxies to allow things like interception, decoration, and to retrieve the right contextual instance when the bean is dereferenced. It's also needed to allow circular injection between beans.

Sandwich answered 4/10, 2017 at 1:15 Comment(2)
How do you then explain the difference beteen the field injected case, where everything runs smoothly, and the constructor injection case, where I end up with a RequestScope undefined error?Microsurgery
Sorry, I don't know what your contrasting it with exactly. But presumably weld decides either a proxy is not required (the case for simple beans that only inject simple beans, all of which are not intercepted or decorated and who knows what other esoteric stuff) or is able to create one (if you added a no-arg public ctor at the same time you changed to field injection). The net is that the no-arg ctor is used to create proxies and proxies are often needed to provide the addl services of CDI (like fetching the appropriate normal scoped instance from the appropriate context)Sandwich
A
0

I think Vladimir is pointing you to the right direction.

I'm just learning CDI (and Weld) therefore my answer might not be 100% accurate in all aspects, but it seems like you need to pick a type at injection point that has a no arg constructor.

I hit the same error today with the next type hierarchy:

  • there is a provided IServerInterceptor interface,
  • there is a custom implementation AuthenticationInterceptor that has one constructor that takes dependencies (fun fact: this knows nothing about (C)DI),
  • there is an InjectableAuthenticationInterceptor which has only one injected constructor.

I have another bean where I would like to inject an instance of AuthenticationInterceptor. It works, when I define the field as type IServerInterceptor (as it is an interface and weld can create a proxy for it(?)), but it stops working when I define the field as AuthenticationInterceptor.

With some code:

// IServerInterceptor.java
public interface IServerInterceptor {
}

// AuthenticationInterceptor.java
public class AuthenticationInterceptor extends InterceptorAdapter {
    private final Predicate<String> validAccessTokenString;
    private final Function<String, AccessToken> toAccessTokenModel;
    private final LoginManager<AccessToken> loginManager;

    public AuthenticationInterceptor(Predicate<String> validAccessTokenString, Function<String, AccessToken> toAccessTokenModel, LoginManager<AccessToken> loginManager) {
        this.validAccessTokenString = validAccessTokenString;
        this.toAccessTokenModel = toAccessTokenModel;
        this.loginManager = loginManager;
    }
    // ...
}


// InjectableAuthenticationInterceptor.java
@ApplicationScoped
public class InjectableAuthenticationInterceptor extends AuthenticationInterceptor {
    @Inject
    public InjectableAuthenticationInterceptor(LoginManager<AccessToken> loginManager) {
        super(isWelformedAccessToken(), toAccessToken(), loginManager);
    }
}

Now,

@Inject private IServerInterceptor authenticationInterceptor;

works great, but

@Inject private AuthenticationInterceptor authenticationInterceptor;

does not.

Ancel answered 31/1, 2018 at 5:51 Comment(0)
R
0

Splitting in interface and implementation won't solve the problem completely. The Problem is that the interface will not be ApplicationScoped, because an ApplicationScoped Bean needs a Default "no args" Construcutor, to be proxyable. Like this it will always create a new instance of the implementation. So the implementation behaves like a @Dependent annotated bean.

If you want to solve it this way, you need to use a method with @PostConstruct to handle the inject of the argument and only use a non arg constructor.

Rodge answered 2/5, 2019 at 11:20 Comment(3)
Please support your statement with facts. In particular you imply that CDI disregards @ApplicationScoped annotation over implementation.Branen
docs.jboss.org/cdi/spec/1.0/html/injectionelresolution.html 5.4.1. Unproxyable bean types Certain legal bean types cannot be proxied by the container: classes which don't have a non-private constructor with no parameters, classes which are declared final or have final methods, primitive types, and array types. If an injection point whose declared type cannot be proxied by the container resolves to a bean with a normal scope, the container automatically detects the problem and treats it as a deployment problem.Rodge
"If an injection point whose declared type cannot be proxied..." In suggested solution declared type in injection point is interface. CDI is capable to proxy interfaces.Branen

© 2022 - 2024 — McMap. All rights reserved.