CDI生成与服务定位器模式

时间:2014-11-25 20:20:27

标签: cdi service-locator

请仔细阅读我的情景并告诉我哪种方法更适合我的情况:

  1. CDI with producer method
  2. service locator pattern
  3. 情景:

    • 有一个界面,例如ConfigurationManager。
    • 它有三种完全不同的实现方式。它们中的每一个都位于与必要库相连的独立EAR中。我们有MemoryConfigurationManager,FileConfigurationManager和JpaConfigurationManager。
    • 有一个应用程序(EAR)。它需要使用ConfigurationManager的一个实例。

    也许以后,开发人员将创建接口的第4个实现并将其安装到应用服务器中并重新配置主App,以便应用程序使用最新的接口实现。主App总是只使用一个接口实现,管理员可以重新配置使用哪个实现。

    这里我不能使用简单的CDI,因为实现在不同的EAR中,并且应用程序也处于分离的耳朵中,因此它们具有不同的类加载器。简单的@Inject在EAR之间无效。

    所以我有两种方式:

    • 如果我想使用CDI而不是我需要使用@Produces方法。它提供了一种注入不是bean的对象的方法。
    • 我可以使用服务定位器模式。这种查找工作很快,因为它有自己的缓存。

    当然,在CDI @Produces方法的后面,我也可以使用服务定位器模式。不同实现的服务URL来自外部配置文件。如果某人创建了ConfigurationManager的新实现并将其安装到Java EE容器中,那么他需要将新服务URL放入配置文件中。

    那么哪种方法更好? (1)或(2)?或者还有另一种明确的方法来实现此功能? : - )

    我更喜欢(2)。
    我认为CDI +制作人会带来更多不必要的课程。

2 个答案:

答案 0 :(得分:1)

我会更少考虑如何获取服务代理,在这种情况下,您的两个方案都将工作相同(您已经发现,甚至可以使用@Produces注释您的ServiceLocator)。

考虑使用Service的类将如何获取代理实例。您想阅读属性并在每个构造函数中找到服务吗?你有一个工厂每个实施一种方法吗? SPI服务加载?您打算如何使用该服务测试课程?

在所有这些情况下,依赖注入都有很大的优势,因为它分离了关注点并定义了客户端和实现之间的明确依赖关系。我的最佳实践:每当你有选择时,去(C)DI。如果将ServiceLocator / PropertyReader方法与@Produces注释结合使用,您甚至可以编写更多类,但是您可以获得更多回报。

答案 1 :(得分:0)

所以我为CDI创建了一些类。它看起来不错,效果很好。

带有类型的限定符类:

@Qualifier
@Retention(RUNTIME)
@Target( {TYPE, METHOD, FIELD, PARAMETER} )
public @interface DaoQualifier
{
    public enum DatasourceType { FILE, JPA, MEMORY }

    public DatasourceType type();
}

工厂类,它返回正确的接口实例:

@ApplicationScoped
public class ConfigurationReaderDaoFactory implements Serializable
{
    private static final long serialVersionUID = -7097322271912690164L;
    private static final Logger LOGGER = Logger.getLogger( ConfigurationReaderDaoFactory.class.getName() );
    private final String LOG_MESSAGE = "getting ConfigurationReaderDao ejb remote reference for %s datasource";

    @Produces
    @DaoQualifier(type = DatasourceType.MEMORY)
    public ConfigurationReaderDao getMemoryDao() throws NamingException
    {
        LOGGER.finer( String.format(LOG_MESSAGE,  DatasourceType.MEMORY) );

        String jndi = ""java:global/earName/ejbName/MemoryConfigurationReaderDao!a.b.c.ConfigurationReaderDao";";
        return (ConfigurationReaderDao) InitialContextGenerator.getInitialContext().lookup(jndi);
    }

    @Produces
    @DaoQualifier(type = DatasourceType.JPA)
    public ConfigurationReaderDao getJpaDao() throws NamingException
    {
        LOGGER.finer( String.format(LOG_MESSAGE,  DatasourceType.JPA) );

        String jndi = ""java:global/earName/ejbName/JpaConfigurationReaderDao!a.b.c.ConfigurationReaderDao";";
        return (ConfigurationReaderDao) InitialContextGenerator.getInitialContext().lookup(jndi);
    }
    ...
}

使用@Inject注释很容易使用它:

public class ConfigurationReaderService
{
    @Inject
    @DaoQualifier(type = DatasourceType.MEMORY)
    private ConfigurationReaderDao configurationReaderDaoService;

    public String getStringSystemParam(final String key)
    {
        String value = null;

        if (key != null)
        {
            value = configurationReaderDaoService.getStringSystemParam(key);
        }
    }
}

我喜欢这个解决方案,因为它易于阅读和理解。

我只剩下三个问题:

(1)我需要弄清楚的最后一件小事是:在我当前的实现中,type参数是硬编码的:

@Inject
@DaoQualifier(type = DatasourceType.MEMORY)
private ConfigurationReaderDao configurationReaderDaoService;

type参数来自应用程序配置,由管理员管理。所以不知怎的,我需要动态地注入接口的正确实现,而它依赖于一个简单的String变量值。

我想我需要以某种方式使用@Any注释。

(2)我试图在@RequestScoped类的开头使用ConfigurationReaderDaoFactory注释,但它没有用。我得到了一个WELD-001303:没有用于范围类型javax.enterprise.context.RequestScoped异常的活动上下文。

(3)为什么使用CDI注入比在我的情况下直接使用ConfigurationReaderDaoFactory类更好?如果我直接使用我的工厂类,我不需要DaoQualifier类,我不需要弄清楚动态注入过程。较少的类和代码总是更好: - )