2013-07-01 3 views
22

... если экземпляр необходимо создать вручную, возможно, сторонним фабричным классом? Ранее (Jersey 1.x), вы могли бы сделать что-то вроде этого:Использование Джерси 2.0, как вы регистрируете экземпляр привязки для запроса?

public class MyInjectableProvider extends PerRequestTypeInjectableProvider<Context, MyInjectable> { 
    public MyInjectableProvider() { 
     super(MyInjectable.class); 
    } 

    @Override 
    public Injectable<MyInjectable> getInjectable(ComponentContext ic, Context context) { 
     MyInjectable myInjectableInstance = //... 

     return new Injectable<MyInjectable>() { 
      @Override 
      public MyInjectable getValue() { 
       return myInjectableInstance; 
      } 
     }; 
    } 
} 

анонимный локальный класс может получить доступ к экземпляру, чтобы вернуться в какой-то области. Это полезно, если вы не работаете с классами, у которых есть конструкторы по умолчанию, но они должны быть построены по запросу.

Джерси 2.0 переключился на HK2 как на платформу внедрения зависимостей, но, увы, страница миграции (https://jersey.java.net/documentation/latest/migration.html) не является примером такого типа привязки, а документация HK2 не содержит примеров с использованием AbstractBinder.

Чтобы разработать немного больше, я пытаюсь предоставить ресурсы-локальные, контейнер-агностические экземпляры JPA EntityManager для моих ресурсов. Они должны быть получены из одного заводского класса Singleton и должны придерживаться только одного «единицы работы», который является запросом в моем случае. Я знаю, что есть обходные пути (просто введите завод или привяжите к threadlocal), но я нашел предыдущее решение элегантным и хотел бы его воссоздать, если это возможно.

EDIT:
После рытья через ¯hk2 Javadocs для немного, я обнаружил, что нечто подобное может быть достигнуто следующим образом:

public class MyInjectableProvider extends AbstractBinder 
     implements Factory<MyInjectable> { 
    @Override 
    protected void configure() { 
     bindFactory(this).to(MyInjectable.class); 
    } 

    @Override 
    public MyInjectable provide() { 
     return getMyInjectable(); 
    } 

    @Override 
    public void dispose(MyInjectable instance) {} 
} 

А зарегистрировать его ...

public class MyResourceConfig extends ResourceConfig { 
    public MyResourceConfig() { 
     register(new MyInjectableProvider()); 
    } 
} 

Это «кажется, работает», но это также кажется немного неясным. Например, dispose() никогда не вызывается. Кроме того, эта привязка, по-видимому, неявно ведет себя как RequestScoped. Изменение конфигурации на bindFactory(this).to(MyInjectable.class).in(RequestScoped.class);, по-видимому, не влияет на поведение. Я что-то упустил, или это намеченное решение?

+0

Я столкнулся с теми же проблемами. Ваш «EDIT» был очень полезен. Спасибо. Перед прочтением последнего абзаца вашего сообщения я также обнаружил, что 'dispose' никогда не вызывается после запроса. Вам удалось понять, почему? – aioobe

+0

Здравствуйте, какой каркас DI вы используете для своего проекта? Потому что я могу предоставить вам пару примеров того, как мы делаем вещи с узором, а также мы сделали со сваркой. С помощью guice используйте guice-persist, где EntityManager является областью запроса. С Weld мы создали поставщика области запросов. Мы добавляем наши репозитории в ресурсы JAX. Каждый репозиторий вводит EM для разрешения операций с базой данных. –

+0

Область по умолчанию - область запроса. Конечно, объявление этого явно не меняет этого. Вы можете, например, объявить его singleton с помощью «Singleton.class» или что вам не нравится с этим решением? –

ответ

2

Вместо Factory<T>.dispose(T), зарегистрировавшись с помощью инъекций CloseableService, вы можете делать большую часть того, что хотите. Требуется адаптер CloseableFactory. CloseableServicecloses() все зарегистрированные ресурсы после выхода из области запроса.

Для конкретного примера см. Ниже ConnectionFactory.

import org.glassfish.hk2.api.Factory; 
import org.glassfish.jersey.server.CloseableService; 

import javax.inject.Inject; 
import javax.ws.rs.InternalServerErrorException; 
import java.sql.Connection; 
import java.sql.SQLException; 

import static com.google.common.base.Preconditions.checkNotNull; 

public class ConnectionFactory implements Factory<Connection> { 
    private final CloseableService closeableService; 

    @Inject 
    public ConnectionFactory(CloseableService closeableService) { 
     this.closeableService = checkNotNull(closeableService); 
    } 

    public Connection provide() { 
     final Connection connection; 
     try { 
      connection = acquireConnection(); 
     } catch (SQLException e) { 
      throw new InternalServerErrorException(e); 
     } 
     try { 
      closeableService.add(new CloseableConnection(connection)); 
     } catch (Throwable t) { 
      closeQuietly(connection); 
      throw runtime(t); 
     } 
     return connection; 
    } 

    public void dispose(Connection connection) { 
     closeQuietly(connection); 
    } 

    private static RuntimeException runtime(Throwable t) { 
     throw ConnectionFactory.<RuntimeException>unchecked(t); 
    } 

    private static <T extends Throwable> T unchecked(Throwable t) throws T { 
     throw (T) t; 
    } 

    private static void closeQuietly(Connection connection) { 
     try { 
      connection.close(); 
     } catch (SQLException ignore) {} 
    } 
} 

Ниже приводится менее общий вариант CloseableFactory - в CloseableConnection.

import java.io.Closeable; 
import java.io.IOException; 
import java.sql.Connection; 
import java.sql.SQLException; 

import static com.google.common.base.Preconditions.checkNotNull; 

public final class CloseableConnection implements Closeable { 
    private final Connection connection; 

    public CloseableConnection(Connection connection) { 
     this.connection = checkNotNull(connection); 
    } 

    public void close() throws IOException { 
     try { 
      connection.close(); 
     } catch (SQLException e) { 
      throw new IOException(e); 
     } 
    } 
}