2014-01-21 5 views
1

Я использую HK2 как часть API Джерси RESTful. Я работаю в многопользовательской системе, и в большинстве моих вызовов API арендатор является параметром пути. У меня также есть несколько объектов DAO, которые в настоящее время принимают на tenantId в своем конструкторе, такие как:HK2 и Impls с аргументами конструктора

public final class WidgetMapper { 
    public WidgetMapper(final int tenantId) { .. } 
    .. 
} 

Я хотел бы использовать HK2, чтобы обеспечить свои объекты DAO к другим слоям моего приложения. Каков правильный способ сделать это?

  1. Измените DAO для использования сеттера, а не аргумента конструктора. Только .. ick. tenantId является частью требуемого состояния DAO.

  2. Добавить слой абстракции. Создайте <interface>MapperFactory и MapperFactoryImpl, у которого есть конструктор no-arg и куча геттеров, например getWidgetMapper и getGizmoMapper. Только .. это кажется громоздким. Я бы предпочел не поддерживать эти дополнительные занятия.

  3. Есть ли какой-то волшебный способ для HK2 внедрить это значение int в конструктор WidgetMapper во время выполнения? Тогда я мог бы вставить tenantId в картограф, а картограф - в другие классы.

  4. ?? Другая магия HK2?

ответ

0

Измените объекты DAO использовать сеттер, а не аргумент конструктора. только .. ick. TenantId является частью требуемого состояния DAO.

Если ваши DAO являются одиночными, я не вижу, как это будет работать (или, по крайней мере, как это можно сделать чисто).

Каков правильный способ сделать это?

IMO, я думаю, что лучший подход должен иметь 1) одноэлементные объекты DAO 2) некоторый тип прокси, который был впрыскивается в DAO, когда они были инстанцированы по HK2, а затем при условии правильного арендатора идентификатора для текущего потока.

я могу думать о двух способах сделать это:

Вариант 1:

Я не пробовал, но я думаю, что вы могли бы, вероятно, впрыснуть UriInfo в ваше DAO,, либо через конструктор, либо в частное поле, либо в сеттер. Вы можете извлечь идентификатор арендатора для текущего запроса из экземпляра UriInfo.

Если бы я был вами, я бы создал абстрактный класс для своих DAO, которые получили UriInfo, введенные в личное поле. Я бы тогда предоставить защищенный метод возвращает текущий арендатор идентификатор из uriInfo.getPathParameters

public abstract class AbstractDao { 

    // jersey/hk2 provides a proxy that references the current thread-bound request 
    @Context 
    private UriInfo info; 

    protected int getTenantId() 
    { 
     // always returns the tenant id for the current request. TODO: add 
     // logic to handle calls that don't have a tenant id. 
     return Integer.valueOf(info.getPathParameters.getFirst("tenantId"); 
    } 
} 

Вариант 2:

?? Другая магия HK2?

Вы можете написать custom injection resolver.

Еще одна идея ...

Вариант 3:

Это один не напрямую ответить на ваш вопрос, так как он не использует HK2 впрыснуть арендатора ID в DAO, но я думаю, это стоит упомянуть.

Вы можете реализовать свой собственный ContainerRequestFilter, который получил идентификатор арендатора и предоставил его другим компонентам вашего приложения.

По умолчанию Джерси будет вызывать фильтр после того, как он разрешит метод ресурса, но до того, как метод будет фактически вызван. Вы можете получить UriInfo с ContainerRequestContext, получить идентификатор пути идентификатора арендатора, а затем передать этот параметр в свою собственную локальную переменную потока. Затем вы можете ссылаться на поток, локальный внутри вашего DAO. Опять же, я рекомендую добавить защищенный метод в базовый класс DAO для инкапсуляции этой логики.

в большинстве моих API вызовов, арендатор параметр пути,

По желанию, вы можете использовать NameBinding контролировать поведение, описанное выше.

Если вы хотите, вы можете реализовать вариант 3, используя обычный ServletFilter.

Примечание:

После того как я написал этот ответ, я понял, что я предположил, что вы были удобными простирающимся ResourceConfig, что вы знали, как получить экземпляр ServiceLocator, и что вы были comfortable with adding your own bindings. Если вы этого не сделаете, дайте мне знать, и я отредактирую свой ответ, чтобы предоставить более подробную информацию.

+0

Джон, спасибо за ответ. DAO не являются синглонами и не будут одиночными, если вы не можете предоставить убедительный аргумент в пользу этого. Вариант 1 не подходит, потому что уровень доступа к данным не должен знать или заботиться о том, чтобы он обслуживал веб-API. Вариант 3 является многопоточным эквивалентом всех уровней, разделяющих статическую переменную. Думаю, я мог бы это сделать. Вариант 2 мог бы работать. Я сейчас воткнулся. Если это то, с чем я иду, я буду отмечать как правильные и уверенные. Еще раз спасибо! –

+0

Звучит как вариант 2 - лучший. Вероятно, вам нужно будет обернуть идентификатор вашего арендатора в классе TenantIdentifier. Попробуйте сделать это, а затем напишите «Factory » и AbstractBinder, который связывает TenantIdentifier с фабрикой. После регистрации связующего, вы должны иметь возможность @Inject экземпляров TenantIdentifier. –

+0

Эрик, любая удача с вариантом 2? Любопытно, как вы решили эту проблему, потому что я делаю что-то подобное в своем проекте. –

1

Вам необходимо извлечь идентификатор арендатора из параметра пути в запросе, при условии, что это нормально для создания ваших DAO для каждого запроса, вы можете реализовать Factory.

public WidgetMapperFactory implements Factory<WidgetMapper> { 

    private final ContainerRequestContext containerRequestContext; 

    @Inject 
    public WidgetMapperFactory(ContainerRequestContext containerRequestContext) { 
     this.containerRequestContext = containerRequestContext; 
    } 

    public WidgetMapper provide() { 
     UriInfo uriInfo = containerRequestContext.getUriInfo(); 
     List<String> matchedUris = uriInfo.getMatchedURIs(); 
     int tenantId = 1; // Actually work it out from the matched URIs 
     return new WidgetMapper(tenantId); 
    } 

    public void dispose() { 
     // Do any clean up you need 
    } 

} 

Затем связать завод:

public MyResourceConfig extends ResourceConfig { 

    public MyResourceConfig() { 
     register(new AbstractBinder() { 
      @Override 
      protected void configure() { 
       bindFactory(WidgetMapperFactory.class).to(WidgetMapper.class).in(RequestScoped.class); 
      } 
     }); 
    } 

} 

Вы можете вводить WidgetMapper в Resource классе, и WidgetMapper не имеет каких-либо знаний он используется в веб-службы.

Смежные вопросы