【问题标题】:Weld: Generic factory for many service-interfaces extending common super-interfaceWeld:用于扩展通用超级接口的许多服务接口的通用工厂
【发布时间】:2018-01-29 06:27:11
【问题描述】:

如何为数百个服务接口创建一个通用工厂?

我有一个通用的通用超级接口,我的所有服务接口都扩展了它:BaseDao<T>

有数百个(生成的)接口子类化我的BaseDao,例如CustomerDao extends BaseDao<Customer>。当然,我不想为每个子类实现一个工厂。特别是,因为已经有一个DaoFactory,我需要将它“粘合”到我的焊接环境中。

因此,我实现了这个:

@ApplicationScoped
public class InjectingDaoFactory {

    @SuppressWarnings("rawtypes") // We *MUST* *NOT* declare a wild-card -- Weld does not accept it => omit the type argument completely.
    @Produces
    public BaseDao getDao(final InjectionPoint injectionPoint) {
        final Type type = injectionPoint.getType();
        // ... some checks and helpful exceptions ...

        final Class<?> c = (Class<?>) type;
        // ... more checks and helpful exceptions ...

        @SuppressWarnings("unchecked")
        final Class<BaseDao<?>> clazz = (Class<BaseDao<?>>) c;
        final BaseDao<?> dao = DaoFactory.getDao(clazz);
        return dao;
    }
}

在需要这样一个 DAO 的代码中,我现在尝试了这个:

@Inject
private CustomerDao customerDao;

但是我得到了错误org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type CustomerDao with qualifiers @Default -- Weld 不明白我的InjectingDaoFactory 能够提供正确的子类来满足对CustomerDao 的依赖。

请注意,我(当然)没有机会调试我工厂的代码。也许我需要使用InjectionPoint.getMember() 而不是InjectionPoint.getType()——这不是我的问题,现在。我的问题是 Weld 根本不理解我工厂对扩展 BaseDao 的子接口的责任。

那么,我需要做些什么才能让 Weld 了解一个工厂可以提供我的BaseDao common DAO 接口的许多子接口的所有实现?

【问题讨论】:

  • 我只是尝试使用javax.enterprise.inject.spi.Extension,但是一旦存在实现此接口的类,就不会注入任何内容,并且找不到以前找到的依赖项 :-( 我没有甚至(还)在META-INF/services/ 中注册我的扩展——只是存在一个实现这个接口的类,甚至没有任何方法导致所有依赖项的WELD-001408: Unsatisfied dependencies for type ... with qualifiers ...(我删除了很多用于测试——它似乎影响了所有) .
  • 我自己已经发现,javax.enterprise.inject.spi.Extension 的实现 必须 not 与服务实现位于同一个项目 (JAR) 中.否则找不到服务。在我将扩展程序移到另一个项目后,它又可以工作了。

标签: java dependency-injection interface factory weld


【解决方案1】:

根据this documentation,我创建了以下扩展,它似乎工作正常:

public class InjectingDaoExtension implements Extension {

    public InjectingDaoExtension() {
    }

    private final Set<Class<? extends BaseDao>> injectedDaoInterfaces = new HashSet<>();

    public <T> void processInjectionTarget(@Observes ProcessInjectionTarget<T> pit, BeanManager beanManager) {
        final InjectionTarget<T> it = pit.getInjectionTarget();

        for (InjectionPoint injectionPoint : it.getInjectionPoints()) {
            Field field = null;
            try {
                Member member = injectionPoint.getMember();
                field = member.getDeclaringClass().getDeclaredField(member.getName());
            } catch (Exception e) {
                // ignore
            }
            if (field != null) {
                Class<?> type = field.getType();
                if (BaseDao.class.isAssignableFrom(type)) {
                    if (! type.isInterface()) {
                        pit.addDefinitionError(new IllegalStateException(String.format("%s is not an interface! Cannot inject: %s", type, field)));
                    }
                    @SuppressWarnings("unchecked")
                    Class<? extends BaseDao> c = (Class<? extends BaseDao>) type;
                    injectedDaoInterfaces.add(c);
                } else {
                    field = null;
                }
            }
        }
    }

    public void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager beanManager) {
        for (Class<? extends BaseDao> daoInterface : injectedDaoInterfaces) {
            abd.addBean(createBean(daoInterface, beanManager));
        }
    }

    protected <D extends BaseDao> Bean<D> createBean(final Class<D> daoInterface, final BeanManager beanManager) {

        return new Bean<D>() {

            private InjectionTarget<D> injectionTarget;

            public synchronized InjectionTarget<D> getInjectionTargetOrNull() {
                return injectionTarget;
            }

            public synchronized InjectionTarget<D> getInjectionTarget() {
                if (injectionTarget == null) {
                    D handler = DaoFactory.getDao(daoInterface);
                    @SuppressWarnings("unchecked")
                    Class<D> handlerClass = (Class<D>) handler.getClass();
                    final AnnotatedType<D> at = beanManager.createAnnotatedType(handlerClass);
                    injectionTarget = beanManager.createInjectionTarget(at);
                }
                return injectionTarget;
            }

            @Override
            public Class<?> getBeanClass() {
                return daoInterface;
            }

            @Override
            public Set<InjectionPoint> getInjectionPoints() {
                // The underlying DaoFactory is not yet initialised, when this method is first called!
                // Hence we do not use getInjectionTarget(), but getInjectionTargetOrNull(). Maybe this
                // causes problems with injections inside the DAOs, but so far, they don't use injection
                // and it does not matter. Additionally, they are RequestScoped and therefore the injection
                // later *may* work fine. Cannot and do not need to test this now. Marco :-)
                InjectionTarget<D> it = getInjectionTargetOrNull();
                return it == null ? Collections.emptySet() : it.getInjectionPoints(); 
            }

            @Override
            public String getName() {
                return getBeanClass().getSimpleName();
            }

            @Override
            public Set<Annotation> getQualifiers() {
                Set<Annotation> qualifiers = new HashSet<Annotation>();
                qualifiers.add(new AnnotationLiteral<Default>() {});
                qualifiers.add(new AnnotationLiteral<Any>() {});
                return qualifiers;
            }

            @Override
            public Class<? extends Annotation> getScope() {
                return RequestScoped.class;
            }

            @Override
            public Set<Class<? extends Annotation>> getStereotypes() {
                return Collections.emptySet();
            }

            @Override
            public Set<Type> getTypes() {
                Set<Type> types = new HashSet<>();
                types.add(daoInterface); // TODO add more types?!
                return types;
            }

            @Override
            public D create(CreationalContext<D> creationalContext) {
                D handler = DaoFactory.getDao(daoInterface);
                InjectionTarget<D> it = getInjectionTarget();
                it.inject(handler, creationalContext);
                it.postConstruct(handler);
                return handler;
            }

            @Override
            public void destroy(D instance, CreationalContext<D> creationalContext) {
                InjectionTarget<D> it = getInjectionTarget();
                it.preDestroy(instance);
                it.dispose(instance);
                creationalContext.release();
            }

            @Override
            public boolean isAlternative() {
                return false;
            }

            @Override
            public boolean isNullable() {
                return false;
            }
        };
    }
}

思路是先收集BaseDao所有需要注入的子接口。然后,它为每个人提供工厂。

重要提示: 正如 cmets 中已经说明的,有必要将此扩展放在一个单独的 JAR 中,该 JAR 不提供任何服务。一旦我将实现Extension 的类与服务实现放在同一个JAR 中(例如通过@RequestScoped 发布),就再也找不到该服务了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-09
    • 2020-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多