Я использую Jersey 2 и Spring, и я пытаюсь инициализировать свое приложение Jersey (т. Е. Класс, производный от ResourceConfig) с параметрами из контекста Spring.
Предыстория: у меня есть одно приложение Jersey, которое я создаю (то есть одна WAR), и я развертываю его в кластере серверов с разными конфигурациями Spring на разных серверах, чтобы включить или отключить разные части сервера, например на некоторых серверах включены ресурсы /search
и т. д. Это было очень просто в Джерси 1.0: я просто добавил,
<context:component-scan base-package="com.mycompany.resources.search"/>
В конфигурации Spring, чтобы Jersey сканировал этот конкретный пакет и включал в нем поставщиков ресурсов JAX-RS.
Теперь в Джерси 2.0 Spring <context:component-scan ... />
не работает, поэтому ресурсы должны быть программно зарегистрированы в классе запуска, производном от ResourceConfig
:
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("com.mycompany.resources.search");
}
}
Пока все хорошо, но мне нужно условно просканировать этот пакет, и я не могу понять, как получить любую конфигурацию Spring в классе MyApplication
. Я думал, что инъекция конструктора может сработать:
public class MyApplication extends ResourceConfig {
@Autowired
public MyApplication(@Qualifier("my-config") MyConfiguration myConfiguration) {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
Однако HK2 жалуется, что не может найти конструктор по умолчанию для использования ... так что это указывает мне, что DI участвует в создании этого класса, но что DI не использует Spring.
Точно так же использование жизненного цикла bean-компонента Spring не работает:
public class MyApplication extends ResourceConfig implements InitializingBean {
@Autowired
private MyConfiguration myConfiguration;
public MyApplication() {
}
@Override
public void afterPropertiesSet() throws Exception {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
(Метод afterPropertiesSet
не вызывается.)
Итак, я застрял: есть ли способ настроить объект приложения Jersey ResourceConfig
с помощью Spring?
ОБНОВЛЕНИЕ:
Я принял ответ @ JohnR ниже, но я также включу свое возможное решение, которое, на мой взгляд, немного чище. Ответ @ JohnR заключался в том, чтобы объект был инициализирован дважды: сначала Spring, а затем Jersey / HK2. Когда Spring инициализирует объект, вы кэшируете зависимости в статическом члене, а затем, когда Jersey / HK2 инициализирует его позже, вы можете получить зависимости.
Я закончил тем, что сделал это:
public class MyApplication extends ResourceConfig {
public MyApplication() {
ApplicationContext rootCtx = ContextLoader.getCurrentWebApplicationContext();
MyConfiguration myConfiguration = rootCtx.getBean(MyConfiguration.class);
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.whatever");
}
}
}
Вместо того, чтобы инициализировать объект дважды, мы позволяем Jersey / HK2 инициализировать его, но затем получаем зависимости из Spring.
Оба решения уязвимы для времени: они оба предполагают, что Spring инициализируется до Jersey / HK2.
2 ответа
Итак, я застрял: есть ли способ настроить объект приложения Jersey ResourceConfig с помощью Spring?
Я не думаю, что вы можете настроить Jersey для получения ResourceConfig из Spring как управляемого компонента Spring. Это немного взломано, но вы можете сделать что-то вроде этого. Обратите внимание, что у вас будет два экземпляра вашего ResourceConfig: один управляется Spring, а другой - Jersey:
public class MyApplication extends ResourceConfig {
// static, available to all instances
private static MyConfiguration myConfiguration;
public MyApplication() {
// when Spring creates the first instance of MyApplication, myConfiguration
// will be null because the setter wasn't called yet
if (myConfiguration != null)
{
// second instance created by jersey... Spring will have autowired
// the first instance, and myConfiguration is static
if (myConfiguration.isEnabled())
packages("com.mycompany.resources.search");
}
}
@Autowired
public void setMyConfiguration(MyConfiguration config)
{
// instance level setter saves to a static variable to make it available for
// future instances (i.e. the one created by jersey)
MyApplication.myConfiguration = config;
}
}
Опять же, это довольно хакерский прием. Вы захотите убедиться, что Spring инициализирован до Jersey, и внимательно посмотрите на любые проблемы с потоками, которые могут возникнуть во время инициализации.
Продолжая мой предыдущий комментарий:
Попытка расширить ResourceConfig опасна, если вы не знаете, что делаете. Джерси становится непредсказуемым, и если вы попытаетесь создать подкласс из абстрактного класса, Джерси выйдет из строя.
Вместо этого спецификация JAX-RS предоставляет нам очень полезный интерфейс под названием Feature : он позволяет вам регистрировать любые классы, которые вы хотите, как если бы вы настраивали свое собственное приложение. Кроме того, вам не нужно использовать неудобный AbstractBinder, вы просто указываете, с какими контрактами вы регистрируете свои классы.
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
// Don't use @Component here, we need to inject the Spring context manually.
public class MySpringFeature implements Feature {
@Context
private ServletContext servletContext;
private ApplicationContext applicationContext;
@Autowired
private MySecurityDAO mySecurityDAO;
@Autowired
private MySpringResponseFilter myResponseFilter;
@Override
public boolean configure(FeatureContext context) {
if(this.servletContext == null) {
return false; // ERROR!
}
this.applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if(this.applicationContext == null) {
return false; // ERROR!
}
// This is where the magic happens!
AutowireCapableBeanFactory bf = applicationContext.getAutowireCapableBeanFactory();
bf.autowireBean(this);
// From here you can get all the beans you need
// Now we take a Spring bean instance,
// and register it with its appropriate JAX-RS contract
context.register(myResponseFilter, ContainerResponseFilter.class);
// Or, we could do this instead:
SomeSecurityFilter mySecurityFilter = new SomeSecurityFilter();
mySecurityFilter.setSecurityDAO(mySecurityDAO);
context.register(mySegurityFilter, ContainerRequestFilter.class);
// Or even this:
SomeOtherSpringBean someOtherBean = applicationContext.getBean(SomeOtherSpringBean.class);
context.register(someOtherBean, SomeOtherJerseyContract.class);
// Success!
return true;
}
}
И в вашем ResourceConfig:
public class MyApplication extends ResourceConfig() {
public MyApplication() {
register(MySpringFeature.class);
}
}
Та-да !
Похожие вопросы
Связанные вопросы
Новые вопросы
spring
Spring Framework - это среда с открытым исходным кодом для разработки приложений на платформе Java. В ее основе лежит широкая поддержка компонентно-ориентированных архитектур, и в настоящее время в ней имеется более двадцати высокоинтегрированных модулей.