【问题标题】:Custom Jackson ObjectMapper in Jersey 2 with Spring带有 Spring 的 Jersey 2 中的自定义 Jackson ObjectMapper
【发布时间】:2014-03-15 10:02:09
【问题描述】:

我在将 Jersey 从 1.x 迁移到 2.x 时遇到了一些问题。我的应用程序使用 Jersey 来提供 REST Web 服务,通过 Jackson 和 Spring 4 以 JSON 格式提供数据来处理依赖注入。

在 Jersey 1.x 中,我曾经将 JsonDeserializer 编写为 Spring 管理的组件,因此我可以在反序列化期间访问我的服务以从我的域对象的持久层加载,但在 2.x 中我遇到了注入问题反序列化程序中的服务工作。我遵循的方法受到这篇博文的启发:http://www.runningasroot.com/blog/2012/05/02/autowiring-jackson-deserializers-in-spring/

这是我的 pom.xml 的依赖部分:

<dependencies>
    <!-- Spring -->
    <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>
    <!-- Jersey -->
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-spring3</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-multipart</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <!-- Commons Codec -->
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>${commons-codec.version}</version>
    </dependency>

    <!-- cut -->

<dependencies>

Jersey 版本是 2.7,Spring 4.0.2.RELEASE。

这是我的 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <module-name>myApp/module-name>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>it.mgt.myApp.config.ApplicationConfig</param-value>
    </context-param>

    <servlet>
        <servlet-name>jersey-serlvet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>it.mgt.myApp.config.JerseyConfig</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jersey-serlvet</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>

</web-app>

这是我的Spring配置类:

@Configuration
@ComponentScan({"it.mgt.myApp"})
@PropertySource("classpath:myApp.properties")
public class ApplicationConfig {

    // Cut

}

这是我的 Jersey 资源配置 类:

public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        packages("it.mgt.myApp");

        register(MultiPartFeature.class);

        register(RequestContextFilter.class);

        register(ObjectMapperContextResolver.class);
        register(JacksonFeature.class);

        register(CorsRequestFilter.class);
        register(SignatureProcessingFilter.class);
        register(AuthorizationFeature.class);
        register(CorsResponseFilter.class);
        register(new UserBinder());
    }
}

这是我的 ObjectMapperContextResolver 类:

@Component
@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {

    @Autowired
    private SpringObjectMapper objectMapper;

    public ObjectMapperContextResolver() {
        super();
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return objectMapper;
    }

}

我认为@Provider注解对于资源配置类中的registrationg来说是多余的。

这是我的 SpringObjectMapper 类:

@Component
public class SpringObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 1413033425692174337L;

    @Autowired
    ApplicationContext applicationContext;

    public SpringObjectMapper() {
        this.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
        this.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, true);
    }

    @Override
    @Autowired
    public void setHandlerInstantiator(HandlerInstantiator hi) {
        super.setHandlerInstantiator(hi);
    }

}

这是我的 SpringBeanHandlerInstantiator 类:

@Component
public class SpringBeanHandlerInstantiator extends HandlerInstantiator {

    private ApplicationContext applicationContext;

    @Autowired
    public SpringBeanHandlerInstantiator(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public JsonDeserializer<?> deserializerInstance(DeserializationConfig dc, Annotated antd, Class<? extends JsonDeserializer<?>> type) {
        try {
            return (JsonDeserializer<?>) applicationContext.getBean(type);
        } catch (Exception e) {
        }

        return null;
    }

    @Override
    public KeyDeserializer keyDeserializerInstance(DeserializationConfig dc, Annotated antd, Class<? extends KeyDeserializer> type) {
        try {
            return (KeyDeserializer) applicationContext.getBean(type);
        } catch (Exception e) {
        }

        return null;
    }

    @Override
    public JsonSerializer<?> serializerInstance(SerializationConfig sc, Annotated antd, Class<? extends JsonSerializer<?>> type) {
        try {
            return (JsonSerializer<?>) applicationContext.getBean(type);
        } catch (Exception e) {
        }

        return null;
    }

    @Override
    public TypeResolverBuilder<?> typeResolverBuilderInstance(MapperConfig<?> mc, Annotated antd, Class<? extends TypeResolverBuilder<?>> type) {
        try {
            return (TypeResolverBuilder<?>) applicationContext.getBean(type);
        } catch (Exception e) {
        }

        return null;
    }

    @Override
    public TypeIdResolver typeIdResolverInstance(MapperConfig<?> mc, Annotated antd, Class<? extends TypeIdResolver> type) {
        try {
            return (TypeIdResolver) applicationContext.getBean(type);
        } catch (Exception e) {
        }

        return null;
    }

}

这是我的域实体类,序列化器和反序列化器是静态内部类:

@JsonSerialize(using = User.Serializer.class)
@JsonDeserialize(using = User.Deserializer.class)
public class User {

    @Component
    public static class Serializer extends JsonSerializer<User> {

        @Override
        public void serialize(User obj, JsonGenerator jg, SerializerProvider sp) throws IOException, JsonProcessingException {
            // Cut
        }

    }

    @Component
    public static class Deserializer extends JsonDeserializer<User> {

        @Autowired
        SomeService someService;

        @Override
        public User deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
            User user = new User();

            // Cut
            // Use someService here
        }

    }

    // Cut

}

我试图在 ObjectMapperContextResolver.getContext(Class type) 中设置一个制动点,但它从未受到打击,我怀疑这是问题的根源,但经过两天的尝试和研究球衣文档后,我的想法已经用完了。

任何人都可以指出如何正确实现这一目标?

【问题讨论】:

  • 在 Jersey 和 Spring 中配置 Jackson 的很好的说明。

标签: spring jersey jackson jersey-2.0


【解决方案1】:

经过进一步尝试,结果发现 ObjectMapperContextResolver 上的 @Component 导致 Jersey 2.x 不使用提供程序,即使它已在 Jersey 配置类中显式注册。这与需要 @Component 的 Jersey 1.x 行为相反。

删除它就可以了,这看起来很奇怪。 ObjectMapperContextResolver 中的@Autowired SpringObjectMapper 仍然是由 Jersey 注入的。

从球衣文档中我无法判断这是设计使然还是错误。

【讨论】:

  • 我相信您遇到了this 错误(已在 Jersey 2.15 中修复)。您可以使用 @Named 注释而不是 @Component (它们大部分是等效的)
猜你喜欢
  • 2013-09-23
  • 2011-07-12
  • 2017-11-07
  • 2019-09-23
  • 2015-09-26
  • 1970-01-01
  • 1970-01-01
  • 2018-11-15
  • 2014-02-23
相关资源
最近更新 更多