CDI:由于多重继承和泛型抽象导致的属性注入问题

Posted

技术标签:

【中文标题】CDI:由于多重继承和泛型抽象导致的属性注入问题【英文标题】:CDI: Property injection issue due to multiple inheritance and Generics abstraction 【发布时间】:2012-01-24 22:10:14 【问题描述】:

我们使用 CDI 进行依赖注入。我们需要实现一个通用类,LazyAccountDataModel,它将被所有数据表 bean 使用/注入(如下面的 DataTableBean 中)。通用的 LazyAccountDataModel 需要根据注入它的数据表 bean 来处理特定类型的外观。我尝试使用泛型作为一种方法来确定在 LazyAccountDataModel 中使用哪个外观,如下所示,但它引发了以下异常:

@Named
@RequestScoped
public class DataTableBean

    @Inject
    private LazyAccountDataModel<IAccount, IAccountFacade> lazyModel;


@Named
@RequestScoped
public class LazyAccountDataModel<DO extends IDomainObject, FACADE extends
IPersistableFacade<DO>> extends LazyDataModel<DO>

    @EJB (@Named doesn't work here due to WELD bug GLASSFISH-16186 which is not-optimal)
    private FACADE facade;

    private List<DO> datasource;

    @Override
    public List<DO> load(int first, int pageSize, String sortField, SortOrder 
        sortOrder, Map<String, String> filters)
    
    setRowCount((int) facade.findTotalCount());

    // do more work on specific facade derivation (IAccountFacade in this case)

    return datasource;
    


public interface IAccountFacade extends IPersistableFacade<IAccount>

    public void logIn(String userName);


public interface IPersistableFacade<DO extends IDomainObject> extends IFacade<DO>

    void create(DO domainData);

    List<DO> getAll();

    long findTotalCount();

异常堆栈跟踪

SEVERE: Exception while loading the app : WELD-001408 Unsatisfied dependencies for type [LazyAccountDataModel<IAccount, IAccountFacade>] with qualifiers [@Default] at injection point [[field] @Inject private view.dashDOard.DataTableBean.lazyModel]
    org.jDOss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [LazyAccountDataModel<IAccount, IAccountFacade>] with qualifiers [@Default] at injection point [[field] @Inject private view.dashboard.DataTableBean.lazyModel]
        at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:270)
        at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:106)
        at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:129)
        at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:351)
        at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:336)
        at org.jboss.weld.bootstrap.WeldDOotstrap.validateBeans(WeldDOotstrap.java:396)
        at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:190)
        at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)
        at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:306)
        at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:462)
        at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:240)
        at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:382)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:355)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:370)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1064)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:96)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1244)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1232)
        at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:459)
        at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:209)
        at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:168)
        at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:238)
        at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
        at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:725)
        at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1019)
        at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
        at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
        at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
        at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
        at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
        at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
        at java.lang.Thread.run(Thread.java:662)

我们的问题与以下帖子非常相似:CDI producer method for data model。 谁能提供关于我们如何注入 LazyAccountDataModel 并指定需要使用哪个外观的指针?

一种可能的解决方案是将特定外观类型与 LazyAccountDataModel 一起注入,然后显式设置外观类型。但是,这不是一个干净的解决方案:

@Named
@RequestScoped
public class DataTableBean

    @EJB
    private IAccountFacade facade;

    @Inject
    private LazyAccountDataModel<IAccount> lazyModel;

@PostConstruct
public void init()

    // would rather this stayed decoupled/handled by IoC framework
    lazyModel.setfacade(facade);



@Named
@RequestScoped
public class LazyAccountDataModel<DO extends IDomainObject> extends LazyDataModel<DO>

    private IPersistableFacade<DO> facade;

    private List<DO> datasource;

    @Override
    public List<DO> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters)
    
            setRowCount((int) facade.findTotalCount());

         // do more work on specific facade derivation (IAccountFacade in this     case)
        return datasource;
    

    public void setfacade(IPersistableBusinessObjectfacade<DO> facade)
    
        this.facade = facade;
    

另一种解决方案可能是使用上下文生成器,例如:http://blog.frankel.ch/further-into-cdi 但这似乎不适用于 2 深的抽象层。通过在上下文 XML 中指定嵌套属性,这个用例可以很容易地在 Spring 中实现。任何人都可以提供有关如何在 CDI 中完成此操作的任何意见吗?任何帮助将不胜感激。

谢谢。

【问题讨论】:

【参考方案1】:

根据 Pete Muir(如果您使用 Weld)的说法,这是一个错误。更多信息请访问CDI events and generics

【讨论】:

感谢您抽出宝贵时间阅读我们的问题并做出回应。【参考方案2】:

乍一看,你为什么要使用@Named 限定符?如果你在一个类上使用过它,那么你需要在注入点有,否则它不会解决依赖关系。

第二个是根据我的经验,我可以说 CDI 不喜欢泛型,并且不确定你能否让它像那样工作。但是,如果您要执行以下操作,那么它可能会起作用: 将此作为接口:

LazyAccountDataModel<DO extends IDomainObject, FACADE extends IPersistableFacade<DO>>

并创建一个实现的类:

LazyAccountDataModel<IAccount, IAccountFacade>

然后你可以像你的例子一样注入。 我怀疑这部分是否会起作用:

@EJB (@Named doesn't work here due to WELD bug GLASSFISH-16186 which is not-optimal)
private FACADE facade;

【讨论】:

以上是关于CDI:由于多重继承和泛型抽象导致的属性注入问题的主要内容,如果未能解决你的问题,请参考以下文章

按自己的想法去理解事件和泛型(C#)

Protobuf 继承和泛型

抽象类的多重继承导致定义不明确

Visual Studio 2008 表单继承和泛型加载失败

JavaSE习题 继承接口和泛型

来自两个抽象类的多重继承 (Qt)