-前言
在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