千家信息网

如何解决Controller层返回值的公共包装类的问题

发表于:2024-11-28 作者:千家信息网编辑
千家信息网最后更新 2024年11月28日,本篇文章为大家展示了如何解决Controller层返回值的公共包装类的问题,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。场景:在微服务中,一般返回数据都会有个
千家信息网最后更新 2024年11月28日如何解决Controller层返回值的公共包装类的问题

本篇文章为大家展示了如何解决Controller层返回值的公共包装类的问题,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

场景:在微服务中,一般返回数据都会有个返回码返回信息返回消息体,但是每次返回时候调用或者是封装,太过麻烦,有没有什么办法不用每次都封装呢?

答案是有的。

返回值对象 ResponseData
package com.study.auth.comm; import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.annotation.JSONField;import com.alibaba.fastjson.serializer.SerializerFeature; import java.io.Serializable; /** * @Package: com.study.auth.comm * @Description: <返回数据> * @Author: MILLA * @CreateDate: 2018/4/8 9:10 * @UpdateUser: MILLA * @UpdateDate: 2018/4/8 9:10 * @Version: 1.0 */public final class ResponseData implements Serializable {    private static final long serialVersionUID = 7824278330465676943L;     private static final String SUCCESS_CODE = "1000";     private static final String SUCCESS_MSG = "success";    /**     * 响应编码     */    @JSONField(serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 1)    private String code;     /**     * 响应提示     */    @JSONField(serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 2)    private String msg;     /**     * 返回的数据     */    @JSONField(serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 10)    private T data;     public static ResponseData success() {        return initData(SUCCESS_CODE, SUCCESS_MSG, null);    }     public static ResponseData error(String code) {        String msg = PropertiesReaderUtil.getProperty(code, null);        return initData(code, msg, null);    }     public static ResponseData error(String code, String msg) {        return initData(code, msg, null);    }     public static  ResponseData success(T t) {        return initData(SUCCESS_CODE, SUCCESS_MSG, t);    }     public static  ResponseData errorData(String code, T data) {        String msg = PropertiesReaderUtil.getProperty(code, null);        return initData(code, msg, data);    }     public static  ResponseData errorData(String code, String msg, T data) {        return initData(code, msg, data);    }     private static  ResponseData initData(String code, String msg, T t) {        ResponseData data = new ResponseData(SUCCESS_CODE);        if (!isBlank(msg)) {            data.setMsg(msg);        }        if (!isBlank(code)) {            data.setCode(code);        }        if (t != null) {            data.setData(t);        }        return data;    }     private static boolean isBlank(CharSequence cs) {        int strLen;        if (cs != null && (strLen = cs.length()) != 0) {            for (int i = 0; i < strLen; ++i) {                if (!Character.isWhitespace(cs.charAt(i))) {                    return false;                }            }            return true;        } else {            return true;        }    }     public ResponseData() {    }     public ResponseData(String code) {        this.code = code;    }     public String getCode() {        return code;    }     public void setCode(String code) {        this.code = code;    }     public String getMsg() {        return msg;    }     public void setMsg(String msg) {        this.msg = msg;    }     public T getData() {        return data;    }     public void setData(T data) {        this.data = data;    }     @Override    public String toString() {        return JSON.toJSONString(this);    }}

如上图的包装,还是太繁琐了。

装饰者模式使用-增强类InitializingAdviceDecorator

通过实现InitializingBean和装饰者模式对Controller层的返回值进行包装,大致思路:

通过RequestMappingHandlerAdapter获取所有的返回值处理对象HandlerMethodReturnValueHandler创建一个新的集合存储上一步获取的集合(因为上一步的结果是unmodifiableList类型的)遍历该集合找到HandlerMethodReturnValueHandler对象,将这个位置的handler替换程自定义的handler将新获到的集合重新设置到RequestMappingHandlerAdapter的setReturnValueHandlers方法中
package com.study.auth.config; import com.study.auth.comm.ResponseData;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.core.MethodParameter;import org.springframework.web.context.request.NativeWebRequest;import org.springframework.web.method.support.HandlerMethodReturnValueHandler;import org.springframework.web.method.support.ModelAndViewContainer;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import java.util.ArrayList;import java.util.List; /** * @Package: com.study.auth.config.core * @Description: <增强controller层返回值> * @Author: milla * @CreateDate: 2020/09/04 14:42 * @UpdateUser: milla * @UpdateDate: 2020/09/04 14:42 * @UpdateRemark: <> * @Version: 1.0 */@Configurationpublic class InitializingAdviceDecorator implements InitializingBean {    @Autowired    private RequestMappingHandlerAdapter adapter;     @Override    public void afterPropertiesSet() {        //获取所有的handler对象        List returnValueHandlers = adapter.getReturnValueHandlers();        //因为上面返回的是unmodifiableList,所以需要新建list处理        List handlers = new ArrayList(returnValueHandlers);        this.decorateHandlers(handlers);        //将增强的返回值回写回去        adapter.setReturnValueHandlers(handlers);    }      /**     * 使用自定义的返回值控制类     *     * @param handlers     */    private void decorateHandlers(List handlers) {        for (HandlerMethodReturnValueHandler handler : handlers) {            if (handler instanceof RequestResponseBodyMethodProcessor) {                //找到返回值的handler并将起包装成自定义的handler                ControllerReturnValueHandler decorator = new ControllerReturnValueHandler((RequestResponseBodyMethodProcessor) handler);                int index = handlers.indexOf(handler);                handlers.set(index, decorator);                break;            }        }    }     /**     * 自定义返回值的Handler     * 采用装饰者模式     */    private class ControllerReturnValueHandler implements HandlerMethodReturnValueHandler {        //持有一个被装饰者对象        private HandlerMethodReturnValueHandler handler;         ControllerReturnValueHandler(RequestResponseBodyMethodProcessor handler) {            this.handler = handler;        }         @Override        public boolean supportsReturnType(MethodParameter returnType) {            return true;        }         /**         * 增强被装饰者的功能         *         * @param returnValue  返回值         * @param returnType   返回类型         * @param mavContainer view         * @param webRequest   请求对象         * @throws Exception 抛出异常         */        @Override        public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {            //如果已经封装了结构体就直接放行            if (returnValue instanceof ResponseData) {                handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);                return;            }            //正常返回success            ResponseData success = ResponseData.success(returnValue);            handler.handleReturnValue(success, returnType, mavContainer, webRequest);        }    }}
配置文件读取类PropertiesReaderUtil
package com.study.auth.comm; import org.slf4j.Logger;import org.slf4j.LoggerFactory; import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.util.Properties; /** * @Package: com.milla.navicat.comm * @Description: <读取配置properties工具类> * @Author: MILLA * @CreateDate: 2018/8/10 10:30 * @UpdateUser: MILLA * @UpdateDate: 2018/8/10 10:30 * @UpdateRemark: <> * @Version: 1.0 */public final class PropertiesReaderUtil {    private static final String ENCODING = "UTF-8";    private static final Logger logger = LoggerFactory.getLogger(PropertiesReaderUtil.class);    private static Properties propsZH;    private static Properties propsCN;    private static String name = null;     static {        //加载英文        //loadProps(false);        //加载中文        loadProps(true);    }     /**     * 第一种,通过类加载器进行获取properties文件流     * 第二种,通过类进行获取properties文件流     * in = PropertyUtil.class.getResourceAsStream("/properties/message_ZH.properties");     * in = PropertiesReaderUtil.class.getClassLoader().getResourceAsStream("properties/message_ZH.properties");     */    synchronized static private void loadProps(boolean isZh) {        logger.debug("start loading properties");        InputStream in = null;        if (isZh) {            propsZH = new Properties();            name = "properties/message_ZH.properties";            in = PropertiesReaderUtil.class.getClassLoader().getResourceAsStream(name);        } else {            propsCN = new Properties();            name = "properties/message_EN.properties";            in = PropertiesReaderUtil.class.getClassLoader().getResourceAsStream(name);        }        try {            if (isZh) {                propsZH.load(new InputStreamReader(in, ENCODING));            } else {                propsCN.load(new InputStreamReader(in, ENCODING));            }        } catch (Exception e) {            logger.debug("loading properties error :{}", e);        } finally {            try {                if (null != in) {                    in.close();                }            } catch (IOException e) {                logger.debug("closing properties io error :{}", e);            }        }    }     public static String getProperty(String key) {        return getPropertyZH(key);    }     public static String getProperty(String key, String defaultValue) {        return getPropertyZH(key, defaultValue);    }     public static String getPropertyZH(String key) {        if (null == propsZH) {            loadProps(true);        }        return propsZH.getProperty(key);    }     public static String getPropertyZH(String key, String defaultValue) {        if (null == propsZH) {            loadProps(true);        }        return propsZH.getProperty(key, defaultValue);    }     public static String getPropertyCN(String key) {        if (null == propsCN) {            loadProps(false);        }        return propsCN.getProperty(key);    }     public static String getPropertyCN(String key, String defaultValue) {        if (null == propsCN) {            loadProps(false);        }        return propsCN.getProperty(key, defaultValue);    }}
配置文件message_ZH.properties

路径为:properties/message_ZH.properties

也可添加国家化英文或者是其他语言配置

1001=用户未登录
#====非业务返回码=========
1100=服务器内部错误
1101=空指针异常
1102=数据类型转换异常
1103=IO异常
1104=该方法找不到异常
1105=数组越界异常
1106=请求体缺失异常
1107=类型匹配异常
1108=请求参数缺失异常
1109=请求方法不支持异常
1110=请求头类型不支持异常
1111=参数解析异常
1112=必要参数不能为空
#=======================

统一异常捕捉类RestfulExceptionHandler

此时,基本能保证增强Controller层的返回值了,如果有需要的话,可能通过@RestControllerAdvice注解,针对抛出的异常使用返回值对象进行包装

package com.study.auth.exception; import com.alibaba.fastjson.JSONException;import com.study.auth.comm.PropertiesReaderUtil;import com.study.auth.comm.ResponseData;import com.study.auth.constant.CommonConstant;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.TypeMismatchException;import org.springframework.boot.json.JsonParseException;import org.springframework.http.converter.HttpMessageNotReadableException;import org.springframework.web.HttpMediaTypeNotSupportedException;import org.springframework.web.HttpRequestMethodNotSupportedException;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.MissingServletRequestParameterException;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseStatus;import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.security.auth.login.AccountException;import java.io.IOException;import java.sql.SQLException; /** * @Package: com.study.auth.exception * @Description: <所有异常拦截类> * @Author: milla * @CreateDate: 2020/09/04 15:35 * @UpdateUser: milla * @UpdateDate: 2020/09/04 15:35 * @UpdateRemark: <> * @Version: 1.0 */@Slf4j@RestControllerAdvicepublic class RestfulExceptionHandler {    private ResponseData responseData(String code, Exception e) {        log.error("异常代码:{},异常描述:{},异常堆栈:", code, PropertiesReaderUtil.getProperty(code), e);        return ResponseData.error(code);    }     private ResponseData responseData(String code, String message, Exception e) {        log.error("异常代码:{},异常描述:{},异常堆栈:", code, message, e);        return ResponseData.error(code, message);    }     /**     * 运行时异常     *     * @param e 异常     * @return     */    @ExceptionHandler(Exception.class)    public ResponseData runtimeExceptionHandler(Exception e) {        return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e);    }     /**     * 处理SQLSyntaxErrorException     *     * @param e 异常     * @return     */    @ExceptionHandler(SQLException.class)    public ResponseData sqlException(SQLException e) {        return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e);    }      /**     * 处理CustomerMessageException     *     * @param e 异常     * @return     */    @ExceptionHandler(CustomMessageException.class)    public ResponseData customerMessageException(CustomMessageException e) {        return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e);    }     /**     * 处理AccountException     *     * @param e 异常     * @return     */    @ExceptionHandler(AccountException.class)    public ResponseData accountException(AccountException e) {        return responseData(e.getMessage(), e);    }      //---------------------------------------jdk/spring自带的异常----------------------------------     /**     * 处理IllegalArgumentException     *     * @param e 异常     * @return     */    @ExceptionHandler(IllegalArgumentException.class)    public ResponseData illegalArgumentException(IllegalArgumentException e) {        return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e);    }     /**     * 空指针异常     *     * @param e 异常     * @return     */    @ResponseStatus    @ExceptionHandler(NullPointerException.class)    public ResponseData nullPointerExceptionHandler(NullPointerException e) {        return responseData(CommonConstant.EX_NULL_POINTER_EXCEPTION, e);    }     /**     * 类型转换异常     *     * @param e 异常     * @return     */    @ExceptionHandler(ClassCastException.class)    public ResponseData classCastExceptionHandler(ClassCastException e) {        return responseData(CommonConstant.EX_CLASS_CAST_EXCEPTION, e);    }     /**     * IO异常     *     * @param e 异常     * @return     */    @ExceptionHandler(IOException.class)    public ResponseData iOExceptionHandler(IOException e) {        return responseData(CommonConstant.EX_IO_EXCEPTION, e);    }     /**     * 未知方法异常     *     * @param e 异常     * @return     */    @ExceptionHandler(NoSuchMethodException.class)    public ResponseData noSuchMethodExceptionHandler(NoSuchMethodException e) {        return responseData(CommonConstant.EX_NO_SUCH_METHOD_EXCEPTION, e);    }     /**     * 数组越界异常     *     * @param e 异常     * @return     */    @ExceptionHandler(IndexOutOfBoundsException.class)    public ResponseData indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException e) {        return responseData(CommonConstant.EX_INDEX_OUT_OF_BOUNDS_EXCEPTION, e);    }     /**     * 请求body缺失异常     *     * @param e 异常     * @return     */    @ExceptionHandler({HttpMessageNotReadableException.class})    public ResponseData requestNotReadable(HttpMessageNotReadableException e) {        return responseData(CommonConstant.EX_HTTP_MESSAGE_NOT_READABLE_EXCEPTION, e);    }     /**     * 类型匹配异常     *     * @param e 异常     * @return     */    @ExceptionHandler({TypeMismatchException.class})    public ResponseData requestTypeMismatch(TypeMismatchException e) {        return responseData(CommonConstant.EX_HTTP_MESSAGE_NOT_READABLE_EXCEPTION, e);    }     /**     * 方法不支持异常     *     * @param e 异常     * @return     */    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})    public ResponseData methodNotSupported(HttpRequestMethodNotSupportedException e) {        return responseData(CommonConstant.EX_HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION, e);    }     /**     * 请求头不支持异常     *     * @param e 异常     * @return     */    @ExceptionHandler({HttpMediaTypeNotSupportedException.class})    public ResponseData mediaTypeNotAcceptable(HttpMediaTypeNotSupportedException e) {        return responseData(CommonConstant.EX_HTTP_MEDIA_TYPE_NOT_ACCEPTABLE_EXCEPTION, e);    }     /**     * 参数解析异常     *     * @param e 异常     * @return     */    @ExceptionHandler(JSONException.class)    public ResponseData runtimeExceptionHandler(JSONException e) {        return responseData(CommonConstant.PARAMS_PARSE_EXCEPTION, e);    }     /**     * 参数解析异常     *     * @param e 异常     * @return     */    @ExceptionHandler(JsonParseException.class)    public ResponseData runtimeExceptionHandler(JsonParseException e) {        return responseData(CommonConstant.PARAMS_PARSE_EXCEPTION, e);    }     /**     * 请求参数缺失异常     *     * @param e 异常     * @return     */     @ExceptionHandler({MissingServletRequestParameterException.class})    public ResponseData requestMissingServletRequest(MissingServletRequestParameterException e) {        return responseData(CommonConstant.EX_MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION, e);    }     /**     * 参数不能为空     *     * @param e 异常     * @return     */    @ExceptionHandler(MethodArgumentNotValidException.class)    public ResponseData exceptionHandler(MethodArgumentNotValidException e) {        return responseData(CommonConstant.PARAMS_IS_NULL, e);    }}
常量类 CommonConstant
package com.study.auth.constant; /** * @Package: com.study.auth.constant * @Description: <公共常量类> * @Author: milla * @CreateDate: 2020/09/04 15:37 * @UpdateUser: milla * @UpdateDate: 2020/09/04 15:37 * @UpdateRemark: <> * @Version: 1.0 */public final class CommonConstant {    /**     * 当前用户名称     */    public static final String C_CURRENT_ACCOUNT = "current_account";     /**     * 用户未登录     */    public static final String EX_NO_TOKEN_EXCEPTION = "1001";     //--------------------------------非业务返回码---------------------------------------    /**     * 运行时异常     */    public static final String EX_RUN_TIME_EXCEPTION = "1100";    /**     * 空指针异常     */    public static final String EX_NULL_POINTER_EXCEPTION = "1101";    /**     * 数据转换异常     */    public static final String EX_CLASS_CAST_EXCEPTION = "1102";    /**     * IO异常     */    public static final String EX_IO_EXCEPTION = "1103";    /**     * 找不到该方法异常     */    public static final String EX_NO_SUCH_METHOD_EXCEPTION = "1104";    /**     * 数组越界异常     */    public static final String EX_INDEX_OUT_OF_BOUNDS_EXCEPTION = "1105";    /**     * 请求体缺失异常     */    public static final String EX_HTTP_MESSAGE_NOT_READABLE_EXCEPTION = "1106";    /**     * TYPE匹配异常     */    public static final String EX_TYPE_MISMATCH_EXCEPTION = "1107";    /**     * 请求参数丢失     */    public static final String EX_MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION = "1108";    /**     * 请求方法类型不支持异常     */    public static final String EX_HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION = "1109";    /**     * MEDIA 类型不支持异常     */    public static final String EX_HTTP_MEDIA_TYPE_NOT_ACCEPTABLE_EXCEPTION = "1110";    /**     * 参数解析异常     */    public static final String PARAMS_PARSE_EXCEPTION = "1111";    /**     * 参数不能为空     */    public static final String PARAMS_IS_NULL = "1112";    //-----------------------------------------------------------------------------------}
自定义异常类 CustomMessageException
package com.study.auth.exception; /** * @Package: com.study.auth.exception * @Description: <自定义异常类> * @Author: MILLA * @CreateDate: 2019/8/15 18:39 * @UpdateUser: MILLA * @UpdateDate: 2019/8/15 18:39 * @UpdateRemark: <> * @Version: 1.0 */public class CustomMessageException extends RuntimeException {     public CustomMessageException() {        super();    }     public CustomMessageException(String message) {        super(message);    }     public CustomMessageException(String message, Throwable cause) {        super(message, cause);    }     public CustomMessageException(Throwable cause) {        super(cause);    }     protected CustomMessageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {        super(message, cause, enableSuppression, writableStackTrace);    }}
所需依赖

因为使用了阿里的fastJson工具类还需要进入该类的依赖

      com.alibaba      fastjson      1.2.58  

至此,可以愉快的使用该返回值的增强类了,在为服务中,还以将该代码重构到comm中,供多个服务共同使用,避免重复早轮子

上述内容就是如何解决Controller层返回值的公共包装类的问题,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注行业资讯频道。

0