Jersey-spring3 экземпляр Spring -установленный bean (null!)
Прежде всего хочу отметить, что это по большому счету самая большая проблема с точки зрения времени, которое я когда-либо занимался в своей карьере. (В течение двух дней прямо сейчас с существенным успехом 0). Каждое "работа вокруг" или "решение", которое я попытался, не сработало, поэтому я заблокирован и довольно отчаянно нуждаюсь в некоторой помощи.
Проблема в двух словах заключается в том, что Jersey/HK2, кажется, всегда создает экземпляр Spring -managed beans ПОСЛЕ того, как они были созданы экземпляром Spring, который говорит мне, что jersey-spring3 не выполняет свою работу или, по крайней мере, не с моей текущей настройкой (или с любой из ~ 50 перестановок настроек, которые я пробовал до сих пор.)
Обратите внимание, что когда я использую пустой конструктор, эти поля ресурсов равны нулю во время выполнения.
Я не понимаю, почему моя текущая настройка не работает, поскольку я в основном копирует этот онлайн-пример
Любая помощь более чем оценена!
Конфигурация
- - - - - pom.xml - - - - -
<!-- ... -->
<dependencies>
<!-- Spring Dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument-tomcat</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>${spring.version}</version>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-support</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-dao</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- / Spring Dependencies -->
<!-- API dependencies -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!-- / API dependencies -->
<!-- ... -->
</dependencies>
<!-- ... -->
<properties>
<!-- ... -->
<spring.version>3.0.5.RELEASE</spring.version>
<jersey.version>2.4.1</jersey.version>
<gson.version>2.2.4</gson.version>
<!-- ... -->
</properties>
<!-- ... -->
- - - - - web.xml - - - - -
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/beans.xml</param-value>
</context-param>
<!-- ... -->
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>fubar.rest.FubarJerseyApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>
- - - - - beans.xml(Конфигурация контекста) - - - - -
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- ... -->
<!-- beans-outbound-api has configuration for spring-jersey3 to work properly -->
<import resource="beans-api.xml" />
</beans>
- - - - - beans -api.xml - - - - -
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Services -->
<bean id="locationServiceV1" class="fubar.rest.v1.services.location.impl.LocationServiceV1" />
<bean id="locationServiceV2" class="fubar.rest.v2.services.location.impl.LocationServiceV2" />
<bean id="viewServiceV1" class="fubar.rest.v1.services.view.impl.ViewServiceV1" />
<bean id="viewServiceV2" class="fubar.rest.v2.services.view.impl.ViewServiceV2" />
<!-- Resources -->
<bean class="fubar.rest.resources.location.impl.LocationResource">
<constructor-arg index="0" ref="locationServiceV1" />
<constructor-arg index="1" ref="locationServiceV2" />
</bean>
<bean class="fubar.rest.resources.view.impl.ViewResource">
<constructor-arg index="0" ref="viewServiceV1" />
<constructor-arg index="1" ref="viewServiceV2" />
</bean>
</beans>
Код
- - - - - Ресурс (JAX-RS) - - - - -
@Path(RESTLocationResourceV1.PathFields.PATH_ROOT)
@Produces({V1_JSON, APPLICATION_JSON})
public class LocationResource
extends ResourceBase<LocationResource, ILocationServiceV1, ILocationServiceV2> {
private static final Logger logger = Logger.getLogger(LocationResource.class);
@Inject
public LocationResource(final LocationServiceV1 v1Loc, final LocationServiceV2 v2Loc) {
super(v1Loc, v2Loc);
logger.info(format(Messages.INF_INSTANTIATED, "LocationResource"));
}
@GET
@Path(PathFields.SUBPATH_LIST)
public LocationListV1 listV1(@HeaderParam(HEADER_API_KEY) String apiKey)
throws ApplicationException {
// Implementation
}
@GET
@Path(PathFields.SUBPATH_SEARCH)
public LocationListV1 searchV1(@HeaderParam(HEADER_API_KEY) String apiKey,
@QueryParam(QueryFields.QUERY) String likeText) throws ApplicationException {
// Implementation
}
}
- - - - - Сервис (Spring Bean) - - - - -
public class LocationServiceV1 extends ServiceBaseV1<LocationBean, LocationV1, LocationListV1>
implements
ILocationServiceV1 {
@Autowired
private LocationDao daoLoc;
public LocationServiceV1() {
super(new LocationBeanToJsonTranslatorV1());
}
@Override
public LocationListV1 listV1() throws ApplicationException {
// Implementation
}
@Override
public LocationListV1 searchV1(String likeText) throws ApplicationException {
// Implementation
}
}
(по сути, то же самое для версии 2)
- - - - - Заявка (Джерси) - - - - -
public class FubarJerseyApplication extends ResourceConfig {
private static final class Messages {
static final String INF_STARTING_APPLICATION = "Starting %s!";
}
private static final Logger logger = Logger.getLogger(FubarJerseyApplication.class);
public FubarJerseyApplication() {
packages("fubar.rest");
logger.info(format(Messages.INF_STARTING_APPLICATION, this.getClass().getName()));
}
}
Вызов (клиент)
curl http://my-ip-address/fubar/api/location/list
(500 Внутренняя ошибка сервера)
Ошибка (сервер)
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object
available for injection at Injectee(requiredType=LocationServiceV1,parent=
LocationResource,qualifiers={}),position=0,optional=false,self=false,
unqualified=null,344016971)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)
at org.jvnet.hk2.internal.ClazzCreator.resolve(ClazzCreator.java:208)
at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:225)
at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:329)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:456)
at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:158)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2350)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:612)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:597)
at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:173)
at org.glassfish.jersey.server.model.MethodHandler$ClassBasedMethodHandler.getInstance(MethodHandler.java:185)
at org.glassfish.jersey.server.internal.routing.PushMethodHandlerRouter.apply(PushMethodHandlerRouter.java:103)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:128)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:131)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:131)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:131)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:131)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:110)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:65)
at org.glassfish.jersey.process.internal.Stages.process(Stages.java:197)
at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:250)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:318)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:236)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:983)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:361)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:372)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:335)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:218)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at fubar.server.springframework.SessionFilter.doFilter(SessionFilter.java:44)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at fubar.server.springframework.loader.ContextLoaderHttpInterceptor$LoaderState.filter(ContextLoaderHttpInterceptor.java:75)
at fubar.server.springframework.loader.ContextLoaderHttpInterceptor$StartedState.filter(ContextLoaderHttpInterceptor.java:120)
at fubar.server.springframework.loader.ContextLoaderHttpInterceptor.doFilter(ContextLoaderHttpInterceptor.java:62)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.jk.server.JkCoyoteHandler.invoke(JkCoyoteHandler.java:190)
at org.apache.jk.common.HandlerRequest.invoke(HandlerRequest.java:311)
at org.apache.jk.common.ChannelSocket.invoke(ChannelSocket.java:776)
at org.apache.jk.common.ChannelSocket.processConnection(ChannelSocket.java:705)
at org.apache.jk.common.ChannelSocket$SocketConnection.runIt(ChannelSocket.java:898)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:690)
at java.lang.Thread.run(Thread.java:662)
Журнал API
Dec 10, 2013 13:36:28 INFO [main] fubar.rest.FubarJerseyApplication
- Starting fubar.rest.FubarJerseyApplication!
Dec 10, 2013 13:38:06 INFO [pool-1-thread-1] resources.location.impl.LocationResource
- LocationResource has been instantiated
Dec 10, 2013 13:38:06 INFO [pool-1-thread-1] resources.view.impl.ViewResource
- ViewResource has been instantiated
Обновить - нашел это:
Журнал Catalina
Dec 10, 2013 1:36:42 PM org.glassfish.jersey.server.ApplicationHandler initialize
INFO: Initiating Jersey application, version Jersey: 2.4.1 2013-11-08 12:08:47...
Dec 10, 2013 1:36:43 PM org.glassfish.jersey.server.spring.SpringComponentProvider initialize
SEVERE: Spring context lookup failed, skipping spring component provider initialization.
Dec 10, 2013 1:38:00 PM com.sun.xml.bind.v2.runtime.reflect.opt.Injector inject
... поэтому ApplicationContext не найден в SpringComponentProvider # initialize.
Ответы
Ответ 1
У нас есть пользовательский асинхронный ContextLoader, поэтому промежуточное решение потребовало размещения полного взлома в Jersey-Spring3, чтобы дождаться, когда приложение будет инициализировано, прежде чем инициализирует поставщик пользовательских компонентов.
P.S. Для любой бедной души, которая вынуждена делать что-то подобное, убедитесь, что параметры META-INF/содержат конфигурацию SpringComponentProvider.
(2014-04-18) Разработка для @Scott
Обратите внимание, что это ужасный взлом, и я бы попытался сделать это как последнее средство, когда все другие попытки потерпели неудачу, как в моем случае. Также я проконсультирую группу рассылки Джерси о вашей проблеме, прежде чем пытаться что-то вроде этого.
Это сказало... вот что я сделал для решения моей проблемы:
=== >
/**
* Hack class for RN-8979.
*
* @author ryan
*
*/
public class ContextLoadWaiter {
private static final Logger logger = Logger.getLogger(ContextLoadWaiter.class);
public void doWait() {
try {
while (ContextLoaderHttpInterceptor.isNotStarted()) {
logger.info("Waiting for ContextLoader to start...");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
logger.error("SpringComponentProvider was interrupted!");
}
}
}
Обратите внимание, что это относится к * нашей * кодовой базе, поскольку ContextLoaderHttpInterceptor
- это http-сервлет, где isNotStarted
возвращает true
, если еще не загружен наш пользовательский ContextLoader
(который является асинхронным).
Пользовательский асинхронный ContextLoader
был когда-то вставлен кем-то по какой-то причине в соответствии с тем, что позволяет пользовательскому интерфейсу отображать страницу загрузки при загрузке сервера. (Вероятно, это не правильный способ добавить этот "пользовательский интерфейс", но код был там, и пользовательский интерфейс зависел от него, поэтому мне пришлось иметь дело с ним...)
Поскольку эта часть не будет применяться непосредственно к вам, главное - отладить SpringComponentProvider
(от здесь) и посмотреть на значение ClassPathXmlApplicationContext
. Если это null
, как и в нашем случае, тогда вам нужно выяснить, почему это null
и ждать на любом ContextLoader
, который вы используете для загрузки, прежде чем инициализировать этот компонент.
- Разместил эту взломанную строку в
SpringComponentProvider
-
== >
...
private final ContextLoadWaiter waiter = new ContextLoadWaiter();
...
@Override
public void initialize(ServiceLocator locator) {
waiter.doWait(); // Wait on our asynchronous context loader.
this.locator = locator;
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Context lookup started");
}
...
-
Создал этот файл: META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider
с содержимым, являющимся полным классом пути к SpringComponentProvider, например. com.company.server.nbi.rest.internal.jspring.SpringComponentProvider
-
Добавлен специальный пакет Jersey-spring3 в качестве пакета для сканирования в приложении; см. ниже...
== >
/**
* Application configuration.
*
* @author ryan
*
*/
public class MyJerseyApplication extends ResourceConfig {
private static final class Messages {
static final String INF_STARTING_APPLICATION = "Starting %s!";
}
private static final Logger logger = Logger.getLogger(MyJerseyApplication.class);
public MyJerseyApplication() {
packages(
/* Internal providers */
"com.company.server.nbi.rest.providers",
/* Internal filters */
"com.company.server.nbi.rest.filters",
/* Spring injection support */
"com.company.server.nbi.rest.internal.jspring", // HERE!!!
/* Json providers */
"com.fasterxml.jackson.jaxrs.json",
/* Jackson exception mappers */
"com.fasterxml.jackson.jaxrs.base");
/* Resources */
register(ResourceA.class);
register(ResourceB.class);
register(ResourceC.class);
/* Miscellaneous features */
register(MultiPartFeature.class);
register(LoggingFilter.class);
logger.info(format(Messages.INF_STARTING_APPLICATION, this.getClass().getName()));
}
}
Это "это". Определенно, это не решение, которым можно гордиться, но если вы находитесь в режиме отчаяния, как я, это, вероятно, не помешает ему сделать снимок.
Ответ 2
Какая загрузка сначала? Spring или Джерси? Возможно, ваш Spring контекст не инициализируется, когда SpringComponentProvider вызывает WebApplicationContextUtils.getWebApplicationContext(sc);
. Попробуйте использовать Spring ContextLoaderListener, чтобы Spring выполнил свою инициализацию сразу после развертывания приложения.
Я столкнулся с множеством тех же проблем, которые вы испытываете в библиотеке jersey-spring3. У меня возникли проблемы с поиском моего приложения Spring ApplicationContext (похоже, что вы застряли), и он взорвал инъекционные сеттеры, которые в качестве аргумента использовали общий параметр.
Если вы столкнетесь с проблемой контекста приложения, я не думаю, что у вас все равно будет работать. Вы определили ViewResource и LocationResource beans в XML. Из того, что я могу сказать, Джерси получит экземпляр ресурса только из Spring, если класс ресурса аннотируется с @Component. Взгляните на org.glassfish.jersey.server.spring.SpringComponentProvider, в частности component.isAnnotationPresent(Component.class)
:
// detect JAX-RS classes that are also Spring @Components.
// register these with HK2 ServiceLocator to manage their lifecycle using Spring.
@Override
public boolean bind(Class<?> component, Set<Class<?>> providerContracts) {
if (ctx == null) {
return false;
}
if(component.isAnnotationPresent(Component.class)) {
DynamicConfiguration c = Injections.getConfiguration(locator);
String[] beanNames = ctx.getBeanNamesForType(component);
if(beanNames == null || beanNames.length != 1) {
LOGGER.severe(LocalizationMessages.NONE_OR_MULTIPLE_BEANS_AVAILABLE(component));
return false;
}
String beanName = beanNames[0];
ServiceBindingBuilder bb = Injections.newFactoryBinder(new SpringComponentProvider.SpringManagedBeanFactory(ctx, locator, beanName));
bb.to(component);
Injections.addBinding(bb, c);
c.commit();
LOGGER.config(LocalizationMessages.BEAN_REGISTERED(beanName));
return true;
}
return false;
}
Не связанная с этим проблема заключалась в том, что мы также хотели перенести все наши аннотации JAX-RS на интерфейсы. Всякий раз, когда я его пробовал, я получил "Не удалось найти подходящий конструктор для com.foo.ResourceInterface".
В конце концов, я решил все свои проблемы, не используя jersey-spring3 и перевернув свой собственный Jersey на разъем Spring. Вот что я сделал:
- Конфигурировал все мои ресурсы как обычные Spring beans. Вы можете использовать XML, если хотите.
- В моем приложении я добавил привязки к контейнеру HK2 для использования factory всякий раз, когда ему нужен экземпляр одного из ресурсов. Мой класс factory просто возвращает управляемый экземпляр Spring ресурса.
- Перед тем, как factory возвращает Spring -установленный bean, я использую службу Jersey/HK2 ServiceLocator для инъекций вещей, которые предоставляет Джерси. Например, все, что было аннотировано с помощью @Context.
My javax.ws.rs.Application выглядит следующим образом:
public class RestConfig extends ResourceConfig {
private static final Log log = LogFactory.getLog(RestConfig.class);
@Inject
public RestConfig(ServiceLocator locator) {
super();
// specific to my app. get your spring beans however you like
Collection<Object> beans = BeanLocator.beansByAnnotation(RestResource.class);
DynamicConfiguration c = Injections.getConfiguration(locator);
for (Object bean : beans)
{
// tell jersey to use a factory for any interface that the bean implements. since your resources don't implement interfaces,
// you'll want to do something a bit different here.
for (Class<?> currentInterface : bean.getClass().getInterfaces())
{
if (log.isTraceEnabled())
log.trace("binding " + currentInterface.getSimpleName() + " to Spring managed bean");
ServiceBindingBuilder<Object> bb = Injections.newFactoryBinder(new StaticLookupFactory(locator, bean));
bb.to(currentInterface);
Injections.addBinding(bb, c);
}
}
// commit the changes to the HK2 container (don't skip this step!)
c.commit();
property("jersey.config.disableMoxyJson.server", true);
packages("com.foo.web.rest");
register(MoxyXmlFeature.class);
}
// a "factory" where the provide() method returns the spring managed bean
// that was passed to the constructor.
private static class StaticLookupFactory implements Factory<Object> {
private ServiceLocator locator;
private Object bean;
StaticLookupFactory(ServiceLocator locator, Object bean)
{
this.locator = locator;
this.bean = bean;
}
@Override
public Object provide() {
// inject this annotated with @Context, @Inject, etc
locator.inject(bean);
return bean;
}
@Override
public void dispose(Object instance) {
}
}
}
BeanLocator
- это класс утилиты, который я написал, что позволяет легко захватывать экземпляры bean, используя статические методы, когда автоустановка недоступна. Например, при работе за пределами Spring beans. Не слишком много происходит там:
public static Collection<Object> beansByAnnotation(Class<? extends Annotation> annotation)
{
return applicationContext.getBeansWithAnnotation(annotation).values();
}
RestResource
также относится к нашему приложению. Это настраиваемый стереотип, который работает как @Component, @Service и т.д.:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RestResource {
String value() default "";
}
Обратите внимание, что Джерси позволяет регистрировать пользовательские реализации org.glassfish.jersey.server.spring.ComponentProvider для управления жизненным циклом ресурсов самостоятельно. Я попробовал, но не смог понять, как это реализовать, независимо от того, что я сделал.
Еще одно примечание... вызов locator.inject(bean)
, который активирует механизм впрыскивания в Джерси, будет обрабатывать все, что помечено с помощью @Inject. Используйте @Autowired в своих классах или настройте beans с XML, чтобы избежать как Spring, так и попытки Джерси разрешить значения для вещей, аннотированных с помощью @Inject.
Ответ 3
Это сообщение, которое является ключевым для понимания проблемы. Это означает, что Spring неправильно инициализируется:
SEVERE: Spring context lookup failed, skipping spring component provider initialization.
(На стороне примечания: поскольку Spring не инициализируется, единственная реализация JSR-330 для попытки и разрешения @Inject
- это HK2 - вот почему вы видите другую проблему).
Во всяком случае, проблема в том, что ваш контейнер не выполняет проверку аннотаций, которые делают все магии jersey-spring3
.
Это поведение является частью спецификации Servlet 3.0 (JSR-33, раздел 1.6.2), поэтому вам следует дважды проверить, поддерживает ли ваш контейнер этот файл.
В случае Tomcat - если вы не используете Tomcat 7.0.29 или новее, вам действительно нужно убедиться, что версия Servlet указана в вашем web.xml.
http://tomcat.apache.org/tomcat-7.0-doc/changelog.html#Tomcat_7.0.29_ (markt)
Недавно я столкнулся с этой проблемой, и это заставило меня замочить, и исправление web.xml было проще, чем обновление с Ubuntu/Precise!
Надеюсь, это поможет!