Spring自定义消息转换器替换ResponseBody

-前言
在Spring和Hibernate整合使用中,碰到了一个让人很无语的问题,在使用ResponseBody注解后,Hibernate的延迟加载会让Spring的MappingJackson2HttpMessageConverter转换JSON数据的时候出现无限循环级联的错误,本文就是解决Spring整合Hibernate后转换Hibernate延迟加载对象为JSON数据格式问题,以自定义注解的方式替换Spring的ResponseBody并且保留ResponseBody的其他转换功能
-* 编写自定义的注解,设置需要的属性*

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface JSONFromat {String date_Fromat() default "yyyy:MM:dd:HH:ss:mm";//默认日期转换格式boolean filterCollection() default true;//默认开启对象和集合过滤String filterStr() default "";//默认的过滤字段字符串boolean filterObject() default true;//默认开启对象过滤String NotFilterStr() default "parentId,childers";//默认不过过滤的复合类型字段名称}

为了性能考虑 一半都是自动忽略实体中的关联对象属性的转换,
在进行下一步之前,需要先知道Spring的ResponseBody是怎么工作的,在Spring处理ResponseBody的流程是怎么样的,

-声明
以下的观点都是本人的浅显简介,我也是新人一枚,刚刚开始研究Spring,为了这个东西我看了两天的Spring源代码,各种百度,终于算是圆满的解决了这个问题,我看到很多人遇到这样的问题,但是很多网友回答和解决的方法都不是很实用,所以我觉得有必要分享一下我解决这个问题的方法,不可能都适用,但是至少我会很完整的告诉大家我是怎么做得,这样按照自己的要求更改实现代码就可以了,

-Spring绑定参数和方法返回的流程
Spring是基于请求/响应的,所有的一切功能不过都是为了处理这四个字,在请求之前做什么,响应之前做什么,请求相关的我没怎么研究,因为是返回值转换问题,所以主要说的就是响应的问题,但是会涉及到一点点请求,很浅薄勿喷,
请求:
一般现在都是使用RequestMapping注解的方式来定义方法的URl

    @RequestMapping(value="/login")@JSONFromatpublic Object Login(String username,String password,HttpServletRequest req,String language){return userService.Longin(username, password, req, language);}

在Spring中要用注解就必须要在xml中配置两个bean,分别是RequestMappingHandlerAdapter和RequestMappingHandlerMapping两个类,前者是注册注解处理器的后者是进行注解映射的,里面都封装了Spring默认的一些注解处理器和返回值处理器

    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();// Annotation-based argument resolutionresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();// Single-purpose return value typeshandlers.add(new ModelAndViewMethodReturnValueHandler());handlers.add(new ModelMethodProcessor());handlers.add(new ViewMethodReturnValueHandler());handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager));handlers.add(new HttpHeadersReturnValueHandler());handlers.add(new CallableMethodReturnValueHandler());handlers.add(new DeferredResultMethodReturnValueHandler());handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));// Annotation-based return value typeshandlers.add(new ModelAttributeMethodProcessor(false));handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager));

上面的代码是RequestMappingHandlerAdapter中Spring默认的注解处理器(resolvers)和返回值处理器(handlers)集合,RequestMappingHandlerAdapter和RequestMappingHandlerMapping都在org.springframework.web.servlet.mvc.method.annotation包下面, RequestMappingHandlerAdapter不仅仅是定义注解处理器和返回值处理器,还可以定义消息转换器(messageconverters)和视图模型转换器(modelAndViewResolvers)等等,大家可以自己去看一下代码,
需要注意的是如果是Spring3.0之前的版本,这个两个类名字不同的 ,具体的可以自己百度,但是不影响今天我要做的事情,因为Spring中有专门的标签来注册这两个类,

-* mvc:annotation-driven标签介绍*

<mvc:annotation-driven>

这个标签是专门用来开启Spring注解功能的,里面包含了以下标签,用来给RequestMappingHandlerAdapter添加自定义东西的

    <mvc:async-support></mvc:async-support><mvc:path-matching/><mvc:message-converters></mvc:message-converters><mvc:argument-resolvers></mvc:argument-resolvers><mvc:return-value-handlers></mvc:return-value-handlers>

因为只用到了几个,所以前面两个不知道是干嘛的,希望看过这篇博客的人可以回复一下具体用法注意事项等 谢谢,mvc:message-converters的作用是注册消息转换器,mvc:argument-resolvers是注册注解转换器 mvc:return-value-handlers 是注册返回值转换器的,今天要用的是mvc:return-value-handlers,例如:

<mvc:annotation-driven>
mvc:return-value-handlers><bean name="reover" class="com.hqhop.sys.controller.Reover"><constructor-arg ref="messageconverters"</constructor-arg>  </bean></mvc:return-value-handlers></mvc:annotation-driven><bean name="messageconverters" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>

上面的xml代码作用就是在注册号一个自定义返回值处理器constructor-arg标签使用SringIOC的构造注入,mvc:return-value-handlers的bean必须是实现了HandlerMethodReturnValueHandler接口的,至于为什么需要构造函数注入,后面再讲,
如果有不是很了解SpringIOC注入的,可以参考下面的文章:

http://blessht.iteye.com/blog/1162131

既然知道了怎么编写自定义注解,也知道了怎么注册这个自定义注解让Spring引用,下面就开始跑流程:
1、在方法上使用自定义注解,Spring执行完这个方法后,会跳转到
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite类

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler

这个类继承与HandlerMethodReturnValueHandler,这是所有返回值处理器共同的接口,在org.springframework.web.method.support包下面,有两个方法

boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

supportsReturnType方法是判断返回值类型是不是该处理器能处理的类型,如果返回true,那就证明这个返回值是这个处理器处理,可以参考ResponseBody的处理器RequestResponseBodyMethodProcessor的supportsReturnType方法:

return parameter.hasParameterAnnotation(RequestBody.class);

只有当方法上面出现ResponseBody注解的时候才调用这个方法处理器,
HandlerMethodReturnValueHandlerComposite类的handleReturnValue方法会调用这个处理器进行下一步处理,

@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws Exception {HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}

在handleReturnValue方法中HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);会调用getReturnValueHandler方法进行返回值处理器匹配

private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {if (logger.isTraceEnabled()) {logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +returnType.getGenericParameterType() + "]");}if(returnValueHandler.supportsReturnType(returnType)) {return returnValueHandler;}}return null;}

在getReturnValueHandler方法中出现了一个for循环,for循环的参数returnValueHandlers是HandlerMethodReturnValueHandlerComposite类的静态参数,里面存放了13个Spring默认的返回值处理器:

private final List<HandlerMethodReturnValueHandler> returnValueHandlers =new ArrayList<HandlerMethodReturnValueHandler>();

值是在调用这个类的时候在RequestMappingHandlerAdapter中获取的,
进行返回值处理器匹配后,获得返回值处理器方法对象:

HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);

先判断返回值对象是不是空的,如果是空的Spring结束返回值处理,不会报错,但是前台会包404找不到错误,应为Spring默认的是使用ModelAndView的形式返回的,意思就是,你返回一个“hello”字符串,Spring会自动去webapp下面找hello.jsp,找不到肯定报404;

Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");

如果不为空,继续下一步,

handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);

这里是调用匹配处理器的handleReturnValue方法,在介绍这个方法之前,我们先来看一下ResponseBody注解的处理器类RequestResponseBodyMethodProcessor,它在Spring的org.springframework.web.servlet.mvc.method.annotation包下面,

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor

它继承与AbstractMessageConverterMethodProcessor类,这是一个消息转换器调用类,类似于HandlerMethodReturnValueHandlerComposite返回值处理器调用类,还有一个就HandlerMethodArgumentResolverComposite注解处理器调用类。

public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolverimplements HandlerMethodReturnValueHandler {

AbstractMessageConverterMethodProcessor类实现了一个基础的返回值处理接口和继承了一个消息转换转换器类,这个是很关键的一个类,因为mvc:return-value-handlers标签的bean必须是实现了HandlerMethodReturnValueHandler 的类,

那么如果只是实现这个接口问题就来了,到现在为止RequestResponseBodyMethodProcessor 都只是一个返回值处理器,他只是处理返回值进行加工,我们不想直接直接在加工完成后直接使用输出流输出结果,这样只能是输出字符串,那要是我加工后还是一个对象呢?比如Spring的分页Page,我前台必须去得到Page对象,如果直接使用输出流输出Page.toString,那么结果就是前台毫无用处,没有任何数据,
在这里你也可以自己写类型转换方法,那样你的自定义返回值注解的步骤已经完成了,直接调用handleReturnValue获取输出流就是了,但是这样的局限性很大,而且不符合我们今天的主题,处理Hibernate延迟加载对象转换JSON的问题,

我们的目的只是为了把延迟加载的数据读出来,转换成JSON格式,其他的我们不想管,而且也不能管,因为你不知道自定义的类型转换器转换的String用输出流输出前天会出现什么样的意外错误,

那么问题来了,既然只是转换数据,而不自己输出,那我们肯定要想办法调用Spring原有的消息转换器啊,让他们来输出,在这里我们就需要一个类来接手我们的任务,参考ResponseBody的返回值处理器:我们需要一个消息转换器调用类,就类似Spring调用返回值处理器调用类HandlerMethodReturnValueHandlerComposite,AbstractMessageConverterMethodProcessor就是这个类
AbstractMessageConverterMethodProcessor是属于消息转换器相关的了,对于我们今天的任务已经无关重要了,因为我们不需要自定义消息转换器:接下来进行JSON转换

-JSON数据转换

百度一下Spring整合Hibernate转换延迟加载对象为JSON数据时就可以看到大量的回答,但是都不是很全、很符合要求,我想我的应该是很完整的了,虽然也是人云亦云的使用的Java反射:

1、延迟加载的Hibernate对象存在形式 :
使用Hibernate就是为了以对象的方式来操作关系数据库,所以取出来的肯定也全部是对象,一般使用Hibernate都会使用延迟加载模式以提高性能,Hibernate延迟加载返回的是一个代理对象,其类型肯定都是限定名$$@内存地址之类的,所以第一步肯定是写一个方法判断是不是延迟加载对象:

    public boolean isPoxry(Object obj) {Class classz =obj.getClass();//得到对象的class,String path = classz.toString();如果是代理对象,肯定会有_$$_这个东西,至少目前看到的都是if (path .contains("_$$_")) {return true;};return false;}

判断完类型,接下类就是根据返回值进行不同的处理:
转换集合类型的

public JSONArray formatByCollection(Collection collection) {JSONObject jo;JSONArray ja = new JSONArray();for (Object obj : collection) {jo = formatByObject(obj);ja.add(jo);}return ja;}

转换对象,先看看是不是代理对象,如果是调用HIbernate的方法进行强制初始化,注意/;
注:一般的session事物管理都是在Service层,当离开Service层后Session就会关闭,如果session关闭这里的强制初始化就会报错,需要在项目的web.xml中配置

    <filter>  <filter-name>openSession</filter-name>  <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>  <init-param>  <param-name>singleSession</param-name>  <param-value>false</param-value>  </init-param>  </filter>

进行session绑定,讲session绑定当前请求线程上,防止session关闭,有意的百度

public JSONObject formatByObject(Object obj) {Class c = null;if (!isPoxry(obj)) {c = obj.getClass();} else {// 调用Hibernate的方法强制初始化带你对象:意思是去数据库查询一次这个对象,// Session不能关闭,配合Spring的OpenEntityManagerInViewFilter使用;Hibernate.initialize(obj);c = getPoxryType(obj).getClass();}return formatJSON(obj, getMethodNames(c), null);}

c = getPoxryType(obj).getClass();这段代码的getPoxryType是为了获取代理对象的真正类型的class
代码如下:

/*** 获取代理对象的实际类型实例 代理对象的Class:com.hqhop.sys.entity.Emp_$$_v151dsf* 被代理对象Class:com.hqhop.sys.entity.Emp* @author * @param c* @return 被代理对象的实例*/public Object getPoxryType(Object obj) {Class src = obj.getClass();String str = src.toString();int size = str.length();String str2 = str.substring(5, size);String[] type = str2.split("_");Class classz = (type[0]).getClass();return getObject(classz);}

获取代理对象的实际类型是为了获取类的属性来调用get方法:
getMethodNames()方法就是获取有get方法的属性字段

/*** 过滤没有Get方法的属性和符合自定义过滤字段的* * @author * @param c*            当前类的Class* @param filter*            过滤的属性* @return 有Get方法的不符合过滤的属性*/public List<Field> getMethodNames(Class c) {Method[] methods = c.getDeclaredMethods();Field[] fields = c.getDeclaredFields();if (fields == null || methods == null) {return null;}List<Field> fList = new ArrayList<>();List<String> meList = new ArrayList<>();for (Method m : methods) {if (m.getName().startsWith("get") && m.getName() != null) {meList.add(m.getName());}}for (Field f : fields) {int type = getFieldType(f);String name = f.getName();if (filterCollection&&type==1&&!notFilterStr.contains(name)) {continue;}else if (filterObject&&!notFilterStr.contains(name)&&type!=1&&type!=0&&type!=4) {continue;} else if (!"".equals(filterStr)&&filterStr.contains(name)) {continue;} else {String field = "get"+ name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());// 属性名首字母大写if (meList.contains(field)) {// 如果有有相同名称的get方法就添加fList.add(f);}}}return fList;}

因为我的项目需要过滤一些字段和不过滤一下字段,所以有大量的if判断,具体参照自定义的注解属性
getFieldType方法是为了获取这个字段的具体属性,方便过滤,一般情况下,我们只需对象本身的属性值,那些关联对象关联集合的值,为了节省性能,所以直接默认是不读取集合和对象,有些例外

/*** 判断类型0是基本类型String和Date,1集合,3是普通对象4是我们不需要过滤的对象* * @author* @param field*            字段* @return 类型代码*/public int getFieldType(Field field) {Class<?> type = field.getType();Class<?> classz=Collection.class;if (!type.getName().contains("java.lang.String") && !type.isPrimitive()) {if(type.isAssignableFrom(classz)){return 1;}else {if (type.getName().contains("java.util")||type.getName().contains("java.lang")|| type.getName().contains("java.sql")) {return 0;}if(type.getName().contains("com.hqhop.sys.entity.User")){return 4;}return 3;}}return 0;}

到现在为止,我们获取了我们需要获取的属性的get方法名集合,有了具体的调用方法,下面就要编写核心来转换JSON格式,

public JSONObject formatJSON(Object obj, List<Field> fields) {JSONObject t = new JSONObject();try {Class c = obj.getClass();t.put("id", invokeMethod(obj, "getId", null, null));// 获取ID//这一步是获取父类的ID属性值,因为我们的实体类都有一个父类,里面都只用一个字段,就是ID ,所以需要获取父类属性值,其实还是自己的ID 具体点百度怎么获取Java反射父类属性值List temp;for (int i = 0; i < fields.size(); i++) {// 循环属性调用方法和封装JSONtemp = new ArrayList<>();String field = fields.get(i).getName();// 获取属性名称int type = getFieldType(fields.get(i));String str = field.replaceFirst(field.substring(0, 1), field.substring(0, 1).toUpperCase());// 属性名首字母大写String methodName = "get" + str;Method m1 = c.getDeclaredMethod(methodName);// 通过属性名获取方法Object str1 = m1.invoke(obj, null);// 反射调用方法返回值setFilterCollection(true);setFilterObject(true);if (str1 != null) {switch (type) {case 0:str1 = str1.toString();break;case 1:temp.addAll((List) str1);// 将Set集合转换为List集合if (str1 != null && temp.size() != 0) {str1 = formatByCollection(temp);// 递归方法,但是不再更深的拆包,返回的是当前集合中所有对象转换的JSON数据格式}break;case 3:if (field.equals("parentId")) {str1 = invokeMethod(str1, "getId", null, null);}elsestr1 = formatByObject(str1);break;case 4:if(isPoxry(str1)){Hibernate.initialize(str1);User user=(User)str1;str1=user.getEmp().getEmpName();}else{User user=(User)str1;if(user.getEmp()==null){str1="";}elsestr1=user.getEmp().getEmpName();}}} else {str1 = "";}t.put(field, str1);//System.out.println(field + "       +|" + t.get(field));}} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}return t;}

之所以Spring默认的Jackson转换JSON无限循环是应为关联对象你关联我,我关联他,他又关联我照成的的,要解决这个问题同时为了性能考虑,我们就必须限定其只能进行一次拆包,这类的控制是用setFilterCollection(true);setFilterObject(true);
这方法,在获取对象的属性get方法的时候判断其是不是对象和集合,如果是自己过滤掉这个字段,所以第二次拆包绝对不会出现继续拆包的状况,

到这里 所有的方法都写完了,贴上身下的相关方法
获取父类相关属性方法的方法

/*** 循环向上转型, 获取对象的 DeclaredMethod* * @param object*            : 子类对象* @param methodName*            : 父类中的方法名* @param parameterTypes*            : 父类中的方法参数类型* @return 父类中的方法对象*/public static Method getDeclaredMethod(Object object, String methodName,Class<?>... parameterTypes) {Method method = null;for (Class clazz = object.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {try {method = clazz.getDeclaredMethod(methodName, parameterTypes);return method;} catch (Exception e) {// 这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。// 如果这里的异常打印或者往外抛,则就不会执行clazz =// clazz.getSuperclass(),最后就不会进入到父类中了}}return null;}/*** 直接调用对象方法, 而忽略修饰符(private, protected, default)* * @param object*            : 子类对象* @param methodName*            : 父类中的方法名* @param parameterTypes*            : 父类中的方法参数类型* @param parameters*            : 父类中的方法参数* @return 父类中方法的执行结果*/public Object invokeMethod(Object object, String methodName,Class<?>[] parameterTypes, Object[] parameters) {// 根据 对象、方法名和对应的方法参数 通过反射 调用上面的方法获取 Method 对象Method method;if (isPoxry(object)) {Hibernate.initialize(object);method = getDeclaredMethod(object, methodName, parameterTypes);} else {method = getDeclaredMethod(object, methodName, parameterTypes);}// 抑制Java对方法进行检查,主要是针对私有方法而言try {if (null != method) {// 调用object 的 method 所代表的方法,其方法的参数是 parametersreturn method.invoke(object, parameters);}} catch (IllegalArgumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return "";}/*** 获取对象的 DeclaredField* * @param object*            : 子类对象* @param fieldName*            : 父类中的属性名* @return 父类中的属性对象*/public static Field getDeclaredField(Object object, String fieldName) {Field field = null;Class clazz = object.getClass();for (; clazz != Object.class; clazz = clazz.getSuperclass()) {try {field = clazz.getDeclaredField(fieldName);return field;} catch (Exception e) {e.printStackTrace();}}return null;}

JSON转化写完了,可以转换了,剩下的就是在handleReturnValue中调用

    @Overridepublic void handleReturnValue(Object returnValue,MethodParameter returnType, ModelAndViewContainer mavContainer,NativeWebRequest webRequest) throws Exception {setJsonAttr();HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);ServletServerHttpResponse OutputStream = new ServletServerHttpResponse(servletResponse);mavContainer.setRequestHandled(true);Object value;int type=getObjectType(returnValue);if((type==1||type==5)&&!isPoxry(returnValue)){value=returnValue;}else{value=fromat(type,returnValue);}writeWithMessageConverters(value, returnType, webRequest);}
/*** 判断对象类型* @author 王升龙* @param obj* @return*/public int getObjectType(Object obj){Class<?> classz=obj.getClass();//基本数据类型和String类型返回1if(classz.isPrimitive()||classz.isAssignableFrom(String.class)){return 1;}//集合类型返回2Class<?> classp=Collection.class;if(classp.isAssignableFrom(classz)){return 2;}//数组类型返回3if(classz.isArray()){return 3;}//判断分页类型Class<?> classPage=Page.class;if(classPage.isAssignableFrom(classz)){return 4;}//如果本来就是JSON格式if(classz.isAssignableFrom(JSONObject.class)||classz.isAssignableFrom(JSONArray.class)){return 5;}return 0;}
public Object fromat(int type,Object obj){if(type==4){return formatByPage((Page)obj);}if(type==2){return formatByCollection((Collection)obj);}if(type==3){//调用数组转换方法}return formatByObject(obj);}

这个方法是在消息转换器调用类调用的时候依据的方法,如果为true就调用这个处理的的handleReturnValue方法,否则进行下一个查找,都找不到包404,这里的依据是方法上有我自己定义的JSONFromat注解就进行处理

@Overridepublic boolean supportsReturnType(MethodParameter returnType) {// TODO Auto-generated method stubboolean b= ((AnnotationUtils.findAnnotation(returnType.getContainingClass(), JSONFromat.class) != null) ||(returnType.getMethodAnnotation(JSONFromat.class) != null));jsonFromat=returnType.getMethodAnnotation(JSONFromat.class);//return b;}

这些是继承与父类的方法,重写掉父类的方法,

@Overridepublic boolean supportsParameter(MethodParameter parameter) {// TODO Auto-generated method stubreturn parameter.hasParameterAnnotation(JSONFromat.class);}@Overridepublic Object resolveArgument(MethodParameter parameter,ModelAndViewContainer mavContainer, NativeWebRequest webRequest,WebDataBinderFactory binderFactory) throws Exception {// TODO Auto-generated method stubObject argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());String name = Conventions.getVariableNameForParameter(parameter);WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name);if (argument != null) {validate(binder, parameter);}mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());return argument;}private void validate(WebDataBinder binder, MethodParameter parameter) throws Exception, MethodArgumentNotValidException {Annotation[] annotations = parameter.getParameterAnnotations();for (Annotation annot : annotations) {if (annot.annotationType().getSimpleName().startsWith("Valid")) {Object hints = AnnotationUtils.getValue(annot);binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});BindingResult bindingResult = binder.getBindingResult();if (bindingResult.hasErrors()) {if (isBindExceptionRequired(binder, parameter)) {throw new MethodArgumentNotValidException(parameter, bindingResult);}}break;}}}/*** Whether to raise a {@link MethodArgumentNotValidException} on validation errors.* @param binder the data binder used to perform data binding* @param parameter the method argument* @return {@code true} if the next method argument is not of type {@link Errors}.*/private boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {int i = parameter.getParameterIndex();Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));return !hasBindingResult;}

注意以上的JSON转换都需要使用JSON-lib支持,所以请导入JSON-lib的包,可以百度也可以使用Maven

Spring自定义消息转换器替换ResponseBody相关推荐

  1. spring 自定义消息转换器

    消息转换器,顾名思义就是对返回的消息,进行转换.下面常见的例子如下: Spring MVC框架中,将HTTP请求信息转换为一个对象(@RequestBody注解),将对象输出为HTTP响应信息(@Re ...

  2. spring boot处理请求返回值的格式(自定义消息转换器)

    springboot 将对象转化成json对象返回给前端,是通过多个消息转换器配合完成的 但是有些时候,默认的转化格式未必符合我们的要求,这个时候就需要进行自定义消息转换器 只需要在@Configur ...

  3. SpringBoot___自定义消息转换器、MVC配置

    2019独角兽企业重金招聘Python工程师标准>>> 1. 自动配置的消息转换器   在SptingBoot的源码中的spring-boot-autoconfig的Jar包下,我们 ...

  4. spring 类型转换器_Spring中的类型转换

    spring 类型转换器 以下是一些需要类型转换的简单情况: 情况1. 为了帮助简化bean配置,Spring支持属性值与文本值之间的转换. 每个属性编辑器仅设计用于某些类型的属性. 为了使用它们,我 ...

  5. 在 springmvc.xml 中配置消息转换器处理 ResponseBody 中文乱码

    在 springmvc.xml 中配置消息转换器处理 ResponseBody 中文乱码 <mvc:annotation-driven><!-- 消息转换器 --><mv ...

  6. SpringMvc自定义消息转换器

    SpringMvc配置文件代码 对于定义的消息转换器 必须通过 mvc:annotation-driven进行注册 消息转换器  会对请求的mini类型进行匹配 如果无法匹配 不会进行消息转换 < ...

  7. SpringBoot添加自定义消息转换器

    首先我们需要明白一个概念:springboot中很多配置都是使用了条件注解进行判断一个配置或者引入的类是否在容器中存在,如果存在会如何,如果不存在会如何. 也就是说,有些配置会在springboot中 ...

  8. springboot自定义消息转换器HttpMessageConverter

    在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制就是利用HttpMessageCo ...

  9. Spring MVC控制器用@ResponseBody声明返回json数据报406的问题

    本打算今天早点下班,结果下午测试调试程序发现一个问题纠结到晚上才解决,现在写一篇博客来总结下. 是这样的,本人在Spring mvc控制层用到了@ResponseBody标注,以便返回的数据为json ...

  10. Spring MVC 3.2+ @ResponseBody 导致的中文乱码处理

    2019独角兽企业重金招聘Python工程师标准>>> 问题原因是spring mvc中竟然使用了ISO-编码 这个问题看了好几个,有的配置AnnotationMethodHandl ...

最新文章

  1. nvJPEG Codec库
  2. 最小二乘法、迭代优化、坐标轴下降法(Coordinate Descent, CD)、坐标下降和梯度下降对比、梯度下降法/最速下降法
  3. 怎么html中加样式,简明教程 在HTML中添加样式表的方法
  4. 问题解决:无法获得锁 /var/lib/dpkg/lock
  5. (视频+图文)机器学习入门系列-第4章 朴素贝叶斯
  6. python导入不在同一路径的函数_Python小课堂|模块
  7. python操作注册表能干啥_转 python操作注册表模块_winreg
  8. [当当网,你意欲何为]之二:无奈,配送之痛
  9. webform窗体怎么实现session唯一标识_微信小程序用户登录和登录态维护的实现_javascript技巧...
  10. 万稞pw80线切割编程软件_零基础如何快速学习UG数控编程?
  11. 逆变H桥IGBT单管驱动+保护
  12. 决策树的算法流程图(ID3/C4.5/CART)
  13. android加密技术框架,Android平台框架层hook技术的安全性研究
  14. lanhelper1.83 注册
  15. [bowen干货]-redis常用五种数据类型命令和场景描述
  16. linux关机会自动重启,linux——如何在linux下让系统定时自动重启(关机)
  17. 微信缓存dat怎么转图片_PC微信dat如何转图片?方式方法
  18. 一个简体/繁体字在线转换工具源码
  19. 51单片机 DHT11+LCD12864温湿度显示 + Proteus仿真
  20. java总是permgen out_java.lang.OutOfMemoryError: PermGen space及其解决方法

热门文章

  1. 机器学习(MACHINE LEARNING)MATLAB实现层次分析法案例【AHP】
  2. 机器学习(聚类七)——层次聚类的优化算法
  3. 咸鱼K210体验笔记—颜色识别
  4. 文件夹批量重命名编号的快速方法
  5. Drilldown饼状图
  6. 改图宝,快速修改图片大小
  7. 人的命,三分天注定,七分靠打拼,有梦就会红,爱拼才会赢
  8. 使用PowerShell查看Windows 补丁记录并写入数据库
  9. “舌战群儒”的技术分析
  10. C++输出透明背景字体