【问题标题】:CDI object not proxyable with injected constructorCDI 对象不能被注入的构造函数代理
【发布时间】:2017-10-03 11:36:02
【问题描述】:

当尝试将参数注入 CDI bean (ApplicationScoped) 的构造函数时,我遇到了以下问题:

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)

但是,我确实在类上有一个可注入的构造函数:

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

使用默认构造函数、变量注入和 postconstruct 方法,这一切都可以正常工作,但在这种情况下我更喜欢构造函数注入。

你有什么想法吗?

【问题讨论】:

  • 添加一个私有的默认构造函数给了我 WELD-001436 。添加受保护的默认构造函数是可行的,但随后会引发一个异常,即 RequestScope 在初始化 ConfigLocator 时不活动。
  • 好的!我已经完成了

标签: java dependency-injection cdi weld


【解决方案1】:

我们解决了将类拆分为接口和实现的类似问题。 在你的情况下是这样的:

public interface Config
{
  // API here
}

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

  // API implementation here
}

【讨论】:

    【解决方案2】:

    这个例子可能会对你有所帮助:

    @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() {
        }
    
    }
    

    您需要在 @ApplicationScoped bean 中有一个公共的非参数构造函数。

    注意:这个类的 bean 将只创建一次,并在应用程序的整个生命周期中维护。该 bean 将在所有托管 bean 之间共享。 @ApplicationScoped bean 本质上是单例的。

    提到的问题:

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

    造成这种情况的可能原因是需要一个非依赖的作用域 bean 为 CDI 提供一个公共的无参数构造函数,以便它可以选择 在运行时启动所需的代理 bean。

    【讨论】:

    • 注意:如果无参数构造函数(或 @Inject 构造函数)是 protected,Weld 似乎仍然很高兴。这样,开发人员就不会意外地构建自己的实例(或至少不会在驻留在另一个包中的代码中)。
    【解决方案3】:

    实现需要非私有、无参数构造函数来创建托管 bean 的代理您不会因为存在非私有、无参数构造函数而失去注入构造函数的功能.

    容器使用代理来允许拦截、装饰以及在取消引用 bean 时检索正确的上下文实例。还需要允许 bean 之间的循环注入。

    【讨论】:

    • 你如何解释字段注入情况(一切运行顺利)和构造函数注入情况(最终出现 RequestScope 未定义错误)之间的区别?
    • 对不起,我不知道你的对比是什么。但是大概weld决定不需要代理(仅注入简单bean的简单bean的情况,所有这些都没有被拦截或装饰,谁知道还有什么其他深奥的东西)或者能够创建一个(如果你添加了一个no -arg public ctor 在您更改为字段注入的同时)。事实是,无参数 ctor 用于创建代理,并且通常需要代理来提供 CDI 的 addl 服务(例如从适当的上下文中获取适当的正常作用域实例)
    【解决方案4】:

    我认为 Vladimir 为您指明了正确的方向。

    我只是在学习 CDI(和 Weld),因此我的答案可能并非在所有方面都 100% 准确,但您似乎需要在注入点选择一个没有 arg 构造函数的类型。

    我今天在下一个类型层次结构中遇到了同样的错误:

    • 提供了IServerInterceptor接口,
    • 有一个自定义实现 AuthenticationInterceptor 有一个构造函数,它接受依赖项(有趣的事实:这对 (C)DI 一无所知)
    • 有一个InjectableAuthenticationInterceptor,它只有一个注入的构造函数。

    我有另一个 bean,我想在其中注入 AuthenticationInterceptor 的实例。当我将字段定义为IServerInterceptor 类型时它可以工作(因为它是一个接口并且weld 可以为其创建代理(?)),但是当我将字段定义为AuthenticationInterceptor 时它停止工作。

    一些代码:

    // 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);
        }
    }
    

    现在,

    @Inject private IServerInterceptor authenticationInterceptor;
    

    效果很好,但是

    @Inject private AuthenticationInterceptor authenticationInterceptor;
    

    没有。

    【讨论】:

      【解决方案5】:

      interface 中的Splitting 和实现不会完全解决问题。 问题是interface 不会是ApplicationScoped,因为ApplicationScoped Bean 需要默认"no args" Construcutor,才能被代理。 像这样它总是会创建一个新的实现实例。 所以实现的行为就像一个 @Dependent 带注释的 bean。

      如果你想这样解决,你需要使用@PostConstruct的方法来处理argument的注入,并且只使用non arg constructor

      【讨论】:

      • 请用事实支持你的陈述。特别是,您暗示 CDI 在实现上忽略了 @ApplicationScoped 注释。
      • docs.jboss.org/cdi/spec/1.0/html/injectionelresolution.html 5.4.1。不可代理的 bean 类型 容器不能代理某些合法的 bean 类型:没有非私有构造函数且没有参数的类、声明为 final 或具有 final 方法的类、原始类型和数组类型。如果声明类型不能被容器代理的注入点解析为正常范围的bean,容器会自动检测到问题并将其视为部署问题。
      • “如果一个注入点的声明的类型不能被代理......”在建议的解决方案中,注入点中声明的类型是接口。 CDI 能够代理接口。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-02
      • 1970-01-01
      • 2021-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多