Jersey;:通过SpringServlet注册注入的ExceptionMapper实例

时间:2012-11-30 08:07:21

标签: spring jersey

泽西岛:1.12 春天:3.11 JDK:1.6.0_35 Tomcat:6..0.33

我正在尝试为在SpringServlet的上下文中由Spring IoC容器实例化的Jersey servlet注册ExceptionMapper实例,并且正在运行“在组件类X的范围必须是单例”异常期间Jersey servlet初始化。我现在无法发布实际的代码,因此我将提供实现的基本知识,看看是否有人明显跳出某人,或者我是否可以指出我的某些文档错过。

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();
    }
}

applicationContext.xml中的bean定义如下,默认为singleton范围(添加显式范围说明符似乎不会改变行为):

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

web.xml中的Spring上下文加载器侦听器和SpringServlet配置本质上是样板文件,当从ApplicationContext.xml中注释掉MyExceptionMapper的bean定义时,servlet将使用其他注入属性加载,初始化和正常运行。但是当applicationContext.xml中存在bean定义时,我会得到以下结果的日志消息:

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拾取,并且在单独的包层次结构中,结果不会改变。

对此的任何帮助或指导将不胜感激。

6 个答案:

答案 0 :(得分:4)

我的ExceptionMapper属于我正在进行组件扫描的结构,而我的简单注释为@Component用于Spring注册表,@Provider用于Jersey。

我的组件扫描看起来像:

<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();
    }
}

答案 1 :(得分:4)

在spring配置中显式添加范围对我来说很有用:

<bean id="exceptionMapper" class="my.pkg.MyExceptionMapper" scope="singleton" />

答案 2 :(得分:1)

我有同样的问题,但使用MessageReaderWriterProvider类:

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

解决方案是在web.xml中为Jersey的SpringServlet的init-param标记添加此类的包:

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

请注意,您不应在Spring上下文中定义此bean。

答案 3 :(得分:1)

在Spring 3.2.3和Jersey 2.3.1集成方面遇到同样的问题。

对我有用的是两个组合:

  1. 使用@Provider注释jersey @Service类或在app-context xml中明确单例化

    @Service //implies a singleton-scope
    @Provider
    public class UnhandledExceptionMapper implements ExceptionMapper<Throwable> {
    
        @Override
        public Response toResponse(Throwable exception) { //...
    
  2. 将包含提供程序类的包添加到jersey包中,我更喜欢在MyApplication类中执行此操作:

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

答案 4 :(得分:0)

在我看来,必须将ExceptionMapper配置为Spring的“singleton”。如果你想使用注释配置,你必须注意,因为球衣也有一个“@Singleton”注释(具有由spring解释的javax.inject.Singleton的不同含义) 为了避免任何混淆,我更喜欢使用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();
    }
}

答案 5 :(得分:0)

我还发现某些时候,ExceptionMapper不起作用,而且它并不总是有效或不起作用。

所以我调试了球衣的源代码。

class: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);

... 如果mapper为null,那么coustom ExceptionMapper将无效。

class:org.glassfish.jersey.internal.ExceptionMapperFactory methed:ExceptionMapperFactory

异常映射器:(有两个异常映射一个异常:java.lang.Exception)

org.glassfish.jersey.server.mvc.internal.ErrorTemplateExceptionMapper @ 6473fc2,class java.lang.Exception

...

com.baidu.ssp.web.ws.exception.BaseExceptionMapper @ 7a84639c,class java.lang.Exception

它是因为在MvcFeature中:

@覆盖 public boolean configure(final FeatureContext context){     final config 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