-前言
在spring和hibernate整合使用中,碰到了一个让人很无语的问题,在使用ResponseBody注解后,Hibernate的延迟加载会让Spring的MappingJackson2HttpMessageConverter转换JSON数据的时候出现无限循环级联的错误,本文就是解决Spring整合Hibernate后转换Hibernate延迟加载对象为JSON数据格式问题,以自定义注解的方式替换Spring的ResponseBody而且保留ResponseBody的其余转换功能
-* 编写自定义的注解,设置须要的属性*java
@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的流程是怎么样的,mysql
-声明
如下的观点都是本人的浅显简介,我也是新人一枚,刚刚开始研究Spring,为了这个东西我看了两天的Spring源代码,各类百度,终于算是圆满的解决了这个问题,我看到不少人遇到这样的问题,可是不少网友回答和解决的方法都不是很实用,因此我以为有必要分享一下我解决这个问题的方法,不可能都适用,可是至少我会很完整的告诉你们我是怎么作得,这样按照本身的要求更改实现代码就能够了,web
-Spring绑定参数和方法返回的流程
Spring是基于请求/响应的,全部的一切功能不过都是为了处理这四个字,在请求以前作什么,响应以前作什么,请求相关的我没怎么研究,由于是返回值转换问题,因此主要说的就是响应的问题,可是会涉及到一点点请求,很浅薄勿喷,
请求:
通常如今都是使用RequestMapping注解的方式来定义方法的URlspring
@RequestMapping(value="/login") @JSONFromat public Object Login(String username,String password,HttpServletRequest req,String language){ return userService.Longin(username, password, req, language); }
在Spring中要用注解就必需要在xml中配置两个bean,分别是RequestMappingHandlerAdapter和RequestMappingHandlerMapping两个类,前者是注册注解处理器的后者是进行注解映射的,里面都封装了Spring默认的一些注解处理器和返回值处理器sql
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); // Annotation-based argument resolution resolvers.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 types handlers.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 types handlers.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标签介绍*json
<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,例如:session
<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注入的,能够参考下面的文章:mvc
既然知道了怎么编写自定义注解,也知道了怎么注册这个自定义注解让Spring引用,下面就开始跑流程:
一、在方法上使用自定义注解,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方法会调用这个处理器进行下一步处理,
@Override public 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 AbstractMessageConverterMethodArgumentResolver implements 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反射:
一、延迟加载的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++) {// 循环属性调用方法和封装JSON temp = 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); }else str1 = 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=""; }else str1=user.getEmp().getEmpName(); } } } else { str1 = ""; } t.put(field, str1); //System.out.println(field + " +|" + t.get(field)); } } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.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 所表明的方法,其方法的参数是 parameters return 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中调用
@Override public 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类型返回1 if(classz.isPrimitive()||classz.isAssignableFrom(String.class)){ return 1; } //集合类型返回2 Class<?> classp=Collection.class; if(classp.isAssignableFrom(classz)){ return 2; } //数组类型返回3 if(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注解就进行处理
@Override public boolean supportsReturnType(MethodParameter returnType) { // TODO Auto-generated method stub boolean b= ((AnnotationUtils.findAnnotation(returnType.getContainingClass(), JSONFromat.class) != null) || (returnType.getMethodAnnotation(JSONFromat.class) != null)); jsonFromat=returnType.getMethodAnnotation(JSONFromat.class);// return b; }
这些是继承与父类的方法,重写掉父类的方法,
@Override public boolean supportsParameter(MethodParameter parameter) { // TODO Auto-generated method stub return parameter.hasParameterAnnotation(JSONFromat.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { // TODO Auto-generated method stub Object 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