使用Spring在Jersey 2中定制Jackson ObjectMapper [英] Custom Jackson ObjectMapper in Jersey 2 with Spring

查看:139
本文介绍了使用Spring在Jersey 2中定制Jackson ObjectMapper的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm having some issues migrating Jersey from 1.x to 2.x. My application uses Jersey to provide REST web services, with data served in JSON via Jackson and Spring 4 to handle the dependency injection.

在Jersey 1.xi用于编写JsonDeserializer作为Spring管理的组件,所以我可以在反序列化期间访问我的服务以从persistance层加载我的域对象,但是在2.x中我遇到了在反序列化器中注入服务的问题。我所采用的方法受到了这篇博文的启发: http://www.runningasroot.com/blog/2012/05/02/autowiring-jackson-deserializers-in-spring/

In Jersey 1.x i used to write JsonDeserializer as components managed by Spring, so i could access my services to load from the persistance layer my domain object during the deserialization, but in 2.x i'm having issues getting the injection of services in the deserializers to work. The approach i follewd was inspired by this blog post: 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。

Jersey version is 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

}

这是我的泽西资源配置类:

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

This is my ObjectMapperContextResolver class:

@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。

I think @Provider annotation is redundant with the registrationg in the resource config class.

这是我的 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;
    }

}

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

This is my domain entity class, serializers and deserializers are static inner classes:

@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)中放置一个brakpoint,但它从不受到打击,我怀疑这是问题的根源,但经过两天的尝试和研究球衣文档后,我的想法已经不多了。

I tried to put a brakpoint in ObjectMapperContextResolver.getContext(Class type) but it never get hit, i suspect that's the root of the problem, but after two days of attempts and studying jersey docs i'm running out of ideas.

任何人都可以指出我关于如何正确实现这一点?

Anybody can point me out on how to achive this properly?

推荐答案

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

After further attempts, it turned out the @Component on the ObjectMapperContextResolver was causing Jersey 2.x to not use the provider even if it was explicitly registered in Jersey configuration class. This is the opposite of Jersey 1.x behaviour, where the @Component was needed.

删除它可以解决这个问题。 ObjectMapperContextResolver中的@Autowired SpringObjectMapper仍然由Jersey注入。

Removing it did the trick, as odd it may seems. The @Autowired SpringObjectMapper in ObjectMapperContextResolver was still injected by Jersey.

从jersey docs我无法判断这是设计还是错误。

From jersey docs i wasn't able to tell if this is by design or if this is a bug.

这篇关于使用Spring在Jersey 2中定制Jackson ObjectMapper的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆