Подтвердить что ты не робот

Используя Джерси 2.0, как вы регистрируете экземпляр привязки для каждого запроса?

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

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 factory и должны придерживаться только одного "единицы работы", который является запросом в моем случае. Я знаю, что есть обходные пути (просто введите factory или привяжите к threadlocal), но я нашел предыдущее решение элегантным и хотел бы его воссоздать, если это возможно.

EDIT:
После того, как я немного разобрался с хакадоками HK2, я обнаружил, что что-то подобное может быть достигнуто следующим образом:

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); не влияет на поведение. Я что-то упустил, или это намеченное решение?

4b9b3361

Ответ 1

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

Для конкретного примера см. ниже 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 - a 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);
        }
    }
}