Джерси;: Регистрация внедренных экземпляров ExceptionMapper через SpringServlet

Джерси: 1.12 Пружина: 3.11 JDK: 1.6.0_35 Tomcat: 6..0.33

Я пытаюсь зарегистрировать экземпляры ExceptionMapper для сервлета Джерси, которые создаются контейнером Spring IoC в контексте SpringServlet, и я сталкиваюсь со старым исключением «Область класса компонента X должна быть одноэлементным» во время сервлета Джерси инициализация. В настоящее время я не в состоянии опубликовать фактический код, поэтому я собираюсь дать основы реализации, чтобы увидеть, не бросается ли кому-то что-то очевидное, или если я могу указать на какую-то документацию, которая у меня есть. пропущенный.

Экземпляр ExceptionMapper выглядит следующим образом:

// ... package definitions omitted ...

@Provider
@Singleton
public class MyExceptionMapper
    implements ExceptionMapper<MyException>
{
    // ... injected attributes omitted ...

    @Override
    public Response toResponse(MyException exception)
    {
        // ... specific handling logic omitted ...

       return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
    }
}

Определение bean-компонента в applicationContext.xml выглядит следующим образом, по умолчанию используется одноэлементная область (и добавление явного спецификатора области, похоже, не меняет поведение):

<bean id="myExceptionMapper" class="my.package.MyExceptionMapper" />

Слушатель загрузчика контекста Spring и конфигурация SpringServlet в web.xml по сути являются шаблонными, и сервлет будет загружаться, инициализироваться и правильно работать с другими атрибутами ввода, когда определение bean-компонента для MyExceptionMapper закомментировано из applicationContext.xml. Но когда определение bean-компонента присутствует в applicationContext.xml, я получаю сообщения журнала о следующем:

SpringProviderFactory - Registering Spring bean, myExpectionMapper, of type my.package.MyExceptionMapper as a provider class
SpringProviderFactory - Registering Spring bean, myServiceController, of type my.package.MyServiceController as a root resource class
... other root resource classes loading ...
SpringServlet - Exception occurred when initialization
java.lang.RuntimeException: The scope of the component class my.package.MyExceptionMapper must be a singleton
  at som.sun.jersey.core.spi.component.ioc.IoCProviderFactory.wrap(...)

Я попытался поместить MyExceptionMapper в иерархию отсканированных пакетов, чтобы SpringServlet собирал их во время инициализации, и в отдельную иерархию пакетов, и результаты не изменились.

Любая помощь или руководство по этому поводу будут очень признательны.


person J. Gregory Wright    schedule 30.11.2012    source источник
comment
Вы дважды проверили, что используете javax.inject.Singleton, а аннотации javax.inject включены в Spring?   -  person Nacho Coloma    schedule 10.01.2013


Ответы (6)


Наступила та же проблема с интеграцией Spring 3.2.3 и Jersey 2.3.1.

Что сработало для меня, так это комбинация двух:

  1. Аннотирование класса трикотажа @Provider с помощью @Service или явное создание синглтона в контексте приложения xml

    @Service //implies a singleton-scope
    @Provider
    public class UnhandledExceptionMapper implements ExceptionMapper<Throwable> {
    
        @Override
        public Response toResponse(Throwable exception) { //...
    
  2. Добавляя пакет, содержащий класс провайдера, в пакеты трикотажа, я предпочитаю делать это в классе MyApplication:

    public class MyApplication extends ResourceConfig {
    public MyApplication() {
        packages("com.mycompany.api.controller", "com.mycompany.api.jersey");
                //....
        }
    }
    
person mSolujic    schedule 09.10.2013

Мои ExceptionMapper находятся в структуре, в которой я выполняю сканирование компонентов, а мои просто аннотированы @Component для реестра Spring и @Provider для Джерси.

Мое сканирование компонентов выглядит так:

<context:component-scan base-package="com.company.web"/>

и ExceptionMapper очень похож на ваш:

package com.mycompany.web.provider;

import com.mycompany.exception.SpecialException;
import com.mycompany.exception.Error;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
@Component
public class SpecialExceptionMapper implements ExceptionMapper<SpecialException> {

    @Autowired
    private MessageSource messageSource;

    @Override
    public Response toResponse(SpecialException exception) {

        Error error = new Error();


        error.errorMessage = messageSource.getMessage(exception.getResourceBundleKey(), exception.getArgs());

        return Response.status(Response.Status.BAD_REQUEST).
                entity(error).
                type(MediaType.APPLICATION_JSON).
                build();
    }
}
person Patrick    schedule 18.02.2013

Явное добавление области действия в конфигурацию spring помогло мне:

<bean id="exceptionMapper" class="my.pkg.MyExceptionMapper" scope="singleton" />
person Tim Van Laer    schedule 15.04.2013
comment
Хорошо, это помогло, но что это значит? Разве Spring по умолчанию не считает все bean-компоненты, определенные в XML, одноэлементными? Почему мы должны явно указывать это? - person saratbeesa; 18.04.2014

У меня была такая же проблема, но с классом MessageReaderWriterProvider:

package my.provider.package;
...
@Provider
@Consumes({MediaType.APPLICATION_JSON, "text/json"})
@Produces({MediaType.APPLICATION_JSON, "text/json"})
public class ReRwProvider extends AbstractMessageReaderWriterProvider<Object> {
...
}

Решение состояло в том, чтобы добавить пакет этого класса в тег init-param для SpringServlet Джерси в файле web.xml:

<init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>my.provider.package</param-value>
</init-param>

Обратите внимание, что вы не должны определять этот компонент в контексте Spring.

person catac    schedule 18.02.2013

Мне кажется, что ExceptionMapper должен быть настроен как «одиночка» с Spring. Если вы хотите использовать конфигурацию аннотации, вы должны обратить внимание, потому что на джерси также есть аннотация «@Singleton» (с другим значением javax.inject.Singleton, интерпретируемым Spring). Чтобы избежать путаницы, я предпочитаю использовать Spring @Service аннотация, которая подразумевает одноэлементную область... поэтому:

import org.springframework.stereotype.Service;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Service //implies a singleton-scope
@Provider
public class UnhandledExceptionMapper implements ExceptionMapper<Throwable> {

    @Override
    public Response toResponse(Throwable exception) {
        return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorBean(false, exception.getMessage(), null))
                .build();
    }
}
person Fabio Bonfante    schedule 08.07.2013

Я также обнаружил, что когда-то пользовательский ExceptionMapper не работает, и он не всегда работает или не работает.

поэтому я отлаживаю исходный код трикотажа.

класс: org.glassfish.jersey.server.ServerRuntime, methed:mapException

 ...
 final long timestamp = tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING);
 **ExceptionMapper mapper = runtime.exceptionMappers.findMapping(throwable);**
 if (mapper != null) {
      request.getRequestEventBuilder().setExceptionMapper(mapper);

... если маппер нулевой, пользовательский ExceptionMapper не будет работать.

класс: org.glassfish.jersey.internal.ExceptionMapperFactory метод: ExceptionMapperFactory

сопоставитель исключений: (есть два исключения, отображающие одно и то же исключение: java.lang.Exception)

org.glassfish.jersey.server.mvc.internal.ErrorTemplateExceptionMapper@6473fc2, класс java.lang.Exception

...

com.baidu.ssp.web.ws.exception.BaseExceptionMapper@7a84639c, класс java.lang.Exception

это потому, что в MvcFeature:

@Override public boolean configure (конечный контекст FeatureContext) { final Configuration config = context.getConfiguration();

if (!config.isRegistered(ErrorTemplateExceptionMapper.class)) {
    context.register(ErrorTemplateExceptionMapper.class);
    context.register(new MvcBinder());

    return true;
}

return false;

} ErrorTemplateExceptionMapper также добавляется в ExceptionMapper.

поэтому я меняю свой собственный тип универсальности MapperException: ExceptionMapper на ExceptionMapper

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

person xfei6868    schedule 26.11.2014