[摘录]Spring 3.1, Hibernate 4 and Jackson-Module-...

Those of you who use Spring MVC 3 with Hibernate have probably also used jackson-module-hibernate to handle json serialization from hibernate proxies. With the recent release of Hibernate 4 the old version of jackson-module-hibernate has become obsolete, and indeed, the people behind jackson-module-hibernate have created a new version. However, this new version is intended to work with Jackson 2. Sadly, Spring 3.1′s MappingJacksonHttpMessageConverter is hardcoded with Jackson 1.x.x in mind. So until there is a new Spring release that fixes this issue and you want to use Jackson 2 in your project, you can follow our solution:

1. Create a new HttpMessageConverter that uses Jackson 2.

In order to make  Spring’s MappingJacksonHttpMessageConverter work, all you have to do is change the package names for Jackson. The easiest way is to copy all the code from org.springframework.http.converter.json.MappingJacksonHttpConverter into a new class (for example MappingJackson2HttpMessageConverter). Replace the following imports:

import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.type.JavaType;

with

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.JavaType;

Another small tweak you’ll need to do is change the method:

protected JavaType getJavaType(Class<?> clazz) {
    return TypeFactory.type(clazz);
}

to

protected JavaType getJavaType(Class<?> clazz) {
    return TypeFactory.defaultInstance().constructType(clazz);
}

Here is the whole picture:

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
 
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.Assert;
 
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
 
public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConverter<Object> {
 
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
 
    private ObjectMapper objectMapper = new ObjectMapper();
 
    private boolean prefixJson = false;
 
    public MappingJackson2HttpMessageConverter() {
       super(new MediaType("application", "json", DEFAULT_CHARSET));
    }
 
    public void setObjectMapper(ObjectMapper objectMapper) {
        Assert.notNull(objectMapper, "ObjectMapper must not be null");
        this.objectMapper = objectMapper;
    }
 
    public ObjectMapper getObjectMapper() {
        return this.objectMapper;
    }
 
    public void setPrefixJson(boolean prefixJson) {
        this.prefixJson = prefixJson;
    }
 
    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        JavaType javaType = getJavaType(clazz);
        return (this.objectMapper.canDeserialize(javaType) && canRead(mediaType));
    }
 
    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return (this.objectMapper.canSerialize(clazz) && canWrite(mediaType));
    }
 
    @Override
    protected boolean supports(Class<?> clazz) {
        // should not be called, since we override canRead/Write instead
        throw new UnsupportedOperationException();
    }
 
    @Override
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
    throws IOException, HttpMessageNotReadableException {
 
        JavaType javaType = getJavaType(clazz);
        try {
            return this.objectMapper.readValue(inputMessage.getBody(), javaType);
        }
        catch (JsonProcessingException ex) {
            throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
        }
    }
 
    @Override
    protected void writeInternal(Object object, HttpOutputMessage outputMessage)
    throws IOException, HttpMessageNotWritableException {
 
        JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
        JsonGenerator jsonGenerator =
        this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
        try {
            if (this.prefixJson) {
                jsonGenerator.writeRaw("{} && ");
            }
            this.objectMapper.writeValue(jsonGenerator, object);
        }
        catch (JsonProcessingException ex) {
            throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
        }
    }
 
    protected JavaType getJavaType(Class<?> clazz) {
        return TypeFactory.defaultInstance().constructType(clazz);
    }
 
    protected JsonEncoding getJsonEncoding(MediaType contentType) {
        if (contentType != null && contentType.getCharSet() != null) {
            Charset charset = contentType.getCharSet();
            for (JsonEncoding encoding : JsonEncoding.values()) {
                if (charset.name().equals(encoding.getJavaName())) {
                return encoding;
                }
            }
        }
    return JsonEncoding.UTF8;
    }
}

2. Create a HibernateAwareObjectMapper

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
 
public class HibernateAwareObjectMapper extends ObjectMapper {
 
    public HibernateAwareObjectMapper() {
        Hibernate4Module hm = new Hibernate4Module();
        registerModule(hm);
    }
}

3. Register the new MappingJackson2HttpMessageConverter with Spring.

Here’s a sample bean definition:

<mvc:message-converters>
    <bean class="com.pastelstudios.json.MappingJackson2HttpMessageConverter">
        <property name="objectMapper">
            <bean class="com.pastelstudios.json.HibernateAwareObjectMapper" />
        </property>
    </bean>
</mvc:message-converters>

If you are using <mvc:annotation-driven /> then it should wrap <mvc:message-converters> like this:

<mvc:annotation-driven>
<mvc:message-converters>
<!-- bean definitions go here -->
</mvc:message-converters>
</mvc:annotation-driven>

That’s it! You can now serialize hibernate proxies like this:

@RequestMapping
 
public @ResponseBody Person getPerson(@RequestParam(“personId”) int personId) {
    Person person = personDao.load(personId);
    return person;
}

without getting LazyInitialization exceptions.

Jackson 2, as well as its extensions, like jackson-module-hibernate, can be downloaded from https://github.com/FasterXML/.In the time of the writing of this article, the latest Jackson 2 version is Jackson 2.0 RC2, but this should work with the full release, too.

转载于:https://my.oschina.net/foggy/blog/62270

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值