Java通过注解或拦截器实现出入参驼峰与蛇式互转

一个努力中的公众号

长的好看的人都关注了

422387e529d6f00745b8f5a7a00eb9a9.png

    Java语言遵循的开发规范为使用驼峰式命名,如columnA,然而数据库表字段一般使用蛇式命名,如column_a,且前端一般也是使用数据库的字段格式进行交互。因此参数传递和返回就需要对驼峰和蛇式的参数进行转化。

一:注解式

最简单的一种方式就是采用注解直接进行驼峰转蛇形转换‍‍‍‍

引入Maven依赖com.fasterxml.jackson.core包‍‍

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.11.2</version>
</dependency>

在配置文件中声明

# application.properties
spring.jackson.property-naming-strategy=SNAKE_CASE

在实体类添加注解

JsonInclude注解可以过滤为null的数据

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TestDTO implements Serializable {
    private static final long serialVersionUID = 1L;


    private Integer id;


    private String userName;


    private Integer createTime;
}

上面注解就可以实现输入与输出都为蛇式,但是这种情况不能满足自定义开关,以及同时兼容驼峰与蛇式(因为第三方接口交互时有些要求采用驼峰,有的又要求蛇式)

所以还有另外一种方式

二:自定义拦截器统一处理‍‍‍‍‍‍‍‍‍‍

2.1 新建过滤器 

替换过滤器内request,防止参数丢失

SpringBoot也是通过获取request的输入流来获取参数,如果不替换过滤
器来到Controller请求参数就没了,这是因为 InputStream read方法
内部有一个,postion,标志当前流读取到的位置,每读取一次,位置就
会移动一次,如果读到最后,InputStream.read方法会返回-1,标志已
经读取完了,如果想再次读取,可以调用inputstream.reset方法,
position就会移动到上次调用mark的位置,mark默认是0,所以就能从头
再读了。但是呢是否能reset又是由markSupported决定的,为true能
reset,为false就不能reset,从源码可以看到,markSupported是为
false的,而且一调用reset就是直接异常所以这也就代表,InpuStream
只能被读取一次,后面就读取不到了。因此我们在过滤器的时候,已经将
InputStream读取过了一次,当来到Controller,SpringBoot读取
InputStream的时候自然是什么都读取不到了
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;


/**
 * @Description: 重写过滤器Filter
 * @Author: bigearchart
 * @CreateTime: 2023-02-22
 * @Version: 1.0
 */
public class HttpServletRequestReplacedFilter implements Filter{


  @Override
  public void destroy() {


  }


  /**
   * @description: 替换过滤器内request,防止参数丢失
   * @author: bigearchart
   * @date: 2023/2/22 14:50
   * @param: [request, response, chain]
   * @return: void
   **/
  @Override
  public void doFilter(ServletRequest request, ServletResponse response,
                       FilterChain chain) throws IOException, ServletException {
    ServletRequest requestWrapper = null;
    if (request instanceof HttpServletRequest) {
      requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request);
    }
    //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
    // 在chain.doFiler方法中传递新的request对象
    if (requestWrapper == null) {
      chain.doFilter(request, response);
    } else {
      chain.doFilter(requestWrapper, response);
    }
  }


  @Override
  public void init(FilterConfig arg0) throws ServletException {


  }


}

2.2 新建请求拦截器

从request中获取所有请求参数、根据需求将特定请求中参数进行将蛇形

命名转为驼峰

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fz.common.util.JudgeUtils;
import com.fz.common.util.sys.JsonObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;


/**
 * @Description: 获取所有请求参数执行蛇形转驼峰
 * @Author: bigearchart
 * @CreateTime: 2023-02-22
 * @Version: 1.0
 */
@Slf4j
public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
  
  private String body = null;
  @Value ("${open_camel}")
  private boolean openCamel;


  /**
   * @description: 从request中获取所有请求参数将蛇形命名转为驼峰
   * @author: bigearchart
   * @date: 2023/2/22 14:47
   * @param: [request]
   * @return:
   **/
  public RequestReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
    super(request);
    JSONObject js = new JSONObject ();
    //获取请求路径
    String url = request.getRequestURI();
    //获取请求ip
    String ip = JudgeUtils.getIpAddrByRequest (request);
    //获取请求内容
    String bodyOld = getBodyStr(request);
  
    //如果开启 蛇形命名转驼峰-将请求内容按要求转换
    if(openCamel && (url.contains ("third/") || url.contains ("api/third")|| url.contains ("web/api"))){
      //如果请求是jsonObj
      try {
        JSONObject jsonObject = JSONObject.parseObject (bodyOld);
        //如果开启 蛇形命名转驼峰-将请求内容按要求转换
        if(openCamel){
          log.info ("================开启蛇形命名转驼峰");
          jsonObject = JsonObjectUtil.humpConvertJSONObject (JSONObject.parseObject (bodyOld));
        }
        if (jsonObject != null) {
          js =  jsonObject;
        }
        //重构入参
        body = js.toJSONString();
        //将ip塞入请求参数内
        js.put("ip", ip);
    
      } catch (Exception e) {
        //否则为array
        log.info ("=======array=========开启蛇形命名转驼峰");
        JSONArray jsonArray = JSONObject.parseArray (bodyOld);
        JSONArray valArray = new JSONArray ();
        JSONObject jsonObject ;
        //数组长度大于1才执行转换
        if (jsonArray != null && jsonArray.size () > 0) {
          for(int i = 0;i < jsonArray.size(); i++){
            valArray.set (i, JsonObjectUtil.humpConvertJSONObject (jsonArray.getJSONObject (i)));
          }
          //重构入参
          body = valArray.toJSONString();
        }else{
          body = bodyOld;
        }
        //将ip塞入请求参数内
        js.put("ip", ip);
      }
    }else {
      body = bodyOld;
    }
  }
  
  @Override
  public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(getInputStream()));
  }
  
  @Override
  public ServletInputStream getInputStream() throws IOException {
    
    final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes(Charset.forName("UTF-8")));
    
    return new ServletInputStream() {
      
      @Override
      public int read() throws IOException {
        return bais.read();
      }
      
      @Override
      public boolean isFinished() {
        return false;
      }
      
      @Override
      public boolean isReady() {
        return false;
      }
      
      @Override
      public void setReadListener(ReadListener readListener) {
      
      }
    };
  }
  /**
   * 获取post请求body参数
   * @param request HttpServletRequest
   * @return String
   */
  public static String getBodyStr(HttpServletRequest request) {
    StringBuilder sb = new StringBuilder();
    InputStream inputStream = null;
    BufferedReader reader = null;
    try {
      inputStream = request.getInputStream();
      reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
      String line = "";
      while ((line = reader.readLine()) != null) {
        sb.append(line);
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (inputStream != null) {
        try {
          inputStream.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (reader != null) {
        try {
          reader.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    return sb.toString();
  } 
}

2.3 新建驼峰蛇式互转工具类

下划线转驼峰或者驼峰转下划线

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @Description: 驼峰转换Util类
 * @Author: bigearchart
 * @CreateTime: 2023-02-22
 * @Version: 1.0
 */
public class JsonObjectUtil {
  
  private static Pattern humpPattern = Pattern.compile("[A-Z]");
  
  /**
   * @param json 下划线转驼峰
   * @Description 驼峰命名转换
   * @Throws
   * @Return com.alibaba.fastjson.JSONObject
   * @Date 2021-09-17 17:04:31
   * @Author bigearchart
   **/
  public static JSONObject humpConvertJSONObject (JSONObject json) {
    if (null == json) {
      return null;
    }
    //得到所有待处理的key
    Set<String> keys = json.keySet ();
    //key转数组
    String[] array = keys.toArray (new String[ 0 ]);
    //循环遍历处理所有key
    for (String key : array) {
      //得到key
      Object value = json.get (key);
      //根据_分割为数组
      String[] keyArray = key.toLowerCase ().split ("_");
      //定义接收处理好格式的Obj
      HashMap<String, Object> objMap;
      //如果是数组 进行数组转换
      if(json.get (key) instanceof JSONArray){
        JSONArray vArray = (JSONArray) json.get (key);
        //数组长度大于1才执行转换
        if (keyArray.length > 0) {
          JSONArray newArray = new JSONArray();
          for(int i = 0;i < vArray.size(); i++){
            objMap = objToCamel(vArray.getJSONObject (i));
//            vArray.remove (i);
            newArray.set (i, objMap);
            value = newArray;
          }
        }
      }else if(json.get (key) instanceof JSONObject){
        //否则执行obj转换
        objMap = objToCamel(json.getJSONObject (key));
        json.put (key, objMap);
        value = objMap;
      }
      //如果含有蛇形命名执行转换
      if (isUpperCase (key) && ! key.contains ("_")) {
        json.remove (key);
        json.put (key.toLowerCase (), value);
        continue;
      }
      //如果含有多个蛇形命名执行转换
      if (keyArray.length > 1) {
        StringBuilder sb = new StringBuilder ();
        for (int i = 0; i < keyArray.length; i++) {
          String ks = keyArray[ i ];
          if (! "".equals (ks)){
            if (i == 0) {
              sb.append (ks);
            } else {
              int c = ks.charAt (0);
              if (c >= 97 && c <= 122) {
                int v = c - 32;
                sb.append ((char) v);
                if (ks.length () > 1) {
                  sb.append (ks.substring (1));
                }
              } else {
                sb.append (ks);
              }
            }
          }
        }
        json.remove (key);
        json.put (sb.toString (), value);
      }
    }
    
    return json;
  }
  
  /**
   * @param objectList obj驼峰转下划线
   * @Description 驼峰转换
   * @Throws
   * @Return java.util.List<com.alibaba.fastjson.JSONObject>
   * @Date 2021-09-17 17:09:15
   * @Author bigearchart
   **/
  public static List<JSONObject> humpConvertListObject (List<JSONObject> objectList) {
    if (null == objectList || objectList.size () <= 0) {
      return null;
    }
    List<JSONObject> data = new ArrayList<> ();
    for (JSONObject object : objectList) {
      data.add (humpConvertJSONObject (object));
    }
    return data;
  }
  
  /**
   * @param array
   * @Description array驼峰转下划线
   * @Throws
   * @Return java.util.List<com.alibaba.fastjson.JSONObject>
   * @Date 2021-09-17 17:13:22
   * @Author bigearchart
   **/
  public static List<JSONObject> humpConvertJSONArray (JSONArray array) {
    if (null == array || array.size () <= 0) {
      return null;
    }
    List<JSONObject> data = new ArrayList<> ();
    for (int i = 0; i < array.size (); i++) {
      data.add (humpConvertJSONObject (array.getJSONObject (i)));
    }
    return data;
  }
  
  /**
   * @param str
   * @Description 判断字符串字母是否为大写
   * @Throws
   * @Return boolean
   * @Date 2021-09-17 17:03:50
   * @Author bigearchart
   **/
  public static boolean isUpperCase (String str) {
    for (int i = 0; i < str.length (); i++) {
      char c = str.charAt (i); if (c >= 97 && c <= 122) { return false; }
    } return true;
  }
  
  /**
   * @param json obj驼峰转下划线
   * @Description 驼峰转换
   * @Throws
   * @Return java.util.List<com.alibaba.fastjson.JSONObject>
   * @Date 2021-09-17 17:09:15
   * @Author bigearchart
   **/
  public static JSONObject humpConvertJson (JSONObject json) {
    if (null == json || json.size () <= 0) {
      return null;
    }
    //得到所有待处理的key
    Set<String> keys = json.keySet ();
    //key转数组
    String[] array = keys.toArray (new String[ 0 ]);
    //循环遍历处理所有key
    for (String key : array) {


      //根据_分割为数组
      String[] keyArray = key.toLowerCase ().split ("_");
      //定义接收处理好格式的Obj
      HashMap<String, Object> objMap;
      //如果是数组 进行数组转换
      if(json.get (key) instanceof JSONArray){
        JSONArray vArray = (JSONArray) json.get (key);
        //数组长度大于1才执行转换
        if (keyArray.length > 0) {
          for(int i = 0;i < vArray.size(); i++){
        
            Set<Map.Entry<String, Object>> objEntry = vArray.getJSONObject (i).entrySet ();
            objMap = new HashMap<> ();
            for (Map.Entry<String, Object> entry : objEntry) {
              String oKey = entry.getKey ();
              Object oVal = entry.getValue ();
              objMap.put (humpToLine2 (oKey), oVal);
            }
            
            vArray.remove (i);
            vArray.set (i, objMap);
          }
        
        }
      }else{
        //否则执行obj转换
        Set<Map.Entry<String, Object>> objEntry = json.getJSONObject (key).entrySet ();
        objMap = new HashMap<> ();
        for (Map.Entry<String, Object> entry : objEntry) {
          String oKey = entry.getKey ();
          Object oVal = entry.getValue ();
          objMap.put (humpToLine2 (oKey), oVal);
        }
        json.put (key, objMap);


      }
    }
    return json;
  }
  
  /**
   * @description: 驼峰转下划线,
   * @author: bigearchart
   * @date: 2023/2/22 16:41
   * @param: [str]
   * @return: java.lang.String
   **/
  public static String humpToLine2(String str){
      Matcher matcher = humpPattern.matcher (str);
      StringBuffer sb = new StringBuffer ();
      while (matcher.find ()) {
        matcher.appendReplacement (sb, "_" + matcher.group (0).toLowerCase ());
      }
      matcher.appendTail (sb);
      return sb.toString (); 
  }
  /**
   * 功能:下划线命名转驼峰命名
   * 将下划线替换为空格,将字符串根据空格分割成数组,再将每个单词首字母大写
   * @param s
   * @return
   */
  private static String under2camel(String s){
      String separator = "_";
      String under = "";
      //判断如果有蛇形命名才执行
      if (s.contains (separator)) {
        s = s.toLowerCase ().replace (separator, " ");
        String sarr[] = s.split (" ");
        for (int i = 0; i < sarr.length; i++) {
          if (i > 0) {
            String w = sarr[ i ].substring (0, 1).toUpperCase () + sarr[ i ].substring (1);
            under += w;
          } else {
            under = sarr[ i ];
          }
        }
      } else {
        under = s;
      }
    return under;
  }
  
  /**
   * @description: obj蛇形转驼峰
   * @author: bigearchart
   * @date: 2023/2/22 14:23
   * @param: []
   * @return: java.util.HashMap<java.lang.String,java.lang.Object>
   **/
  private static HashMap<String, Object> objToCamel(JSONObject obj) {
    Set<Map.Entry<String, Object>> objEntry = obj.entrySet();
    HashMap<String, Object> objMap = new HashMap<> ();
    for (Map.Entry<String, Object> entry : objEntry) {
      String oKey = entry.getKey ();
      Object oVal = entry.getValue ();
      objMap.put (under2camel (oKey), oVal);
    }
    return objMap;
  } 
}

2.2 拦截器注入启动类

/**
     * @description: 注册过滤器到启动类
     * @author: bigearchart
     * @date: 2023/2/22 14:54
     * @param: []
     * @return: org.springframework.boot.web.servlet.FilterRegistrationBean
     **/
    @Bean
    public FilterRegistrationBean httpServletRequestReplacedRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new HttpServletRequestReplacedFilter ());
        registration.addUrlPatterns("/*");
        registration.addInitParameter("paramName", "paramValue");
        registration.setName("httpServletRequestReplacedFilter");
        registration.setOrder(1);
        return registration;
    }

至此实现参数驼峰与蛇式的两种方式到此结束,通过接口测试工具调用即可验证。‍‍‍‍

如果对您有帮助 请点个关注,万分感谢

                                (微信技术交流群 请加图图微信)
b9d3a541bdb806c9a6106f23355eaa58.png

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的示例,演示了如何使用自定义注解拦截器实现拦截功能: 首先,定义一个自定义注解`NoNeedToken`,用于标记不需要进行拦截的方法或类: ```java import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface NoNeedToken { } ``` 然后,创建一个拦截器`TokenInterceptor`,实现`HandlerInterceptor`接口,并在`preHandle`方法中进行拦截逻辑的处理: ```java import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; // 判断方法上是否有NoNeedToken注解 if (handlerMethod.getMethodAnnotation(NoNeedToken.class) != null) { return true; // 不需要拦截,直接放行 } // 判断类上是否有NoNeedToken注解 if (handlerMethod.getBeanType().isAnnotationPresent(NoNeedToken.class)) { return true; // 不需要拦截,直接放行 } } // 需要拦截的逻辑处理 // ... return true; // 放行 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 拦截后的处理 // ... } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 请求完成后的处理 // ... } } ``` 最后,在Spring Boot的配置类中,将拦截器添加到拦截器链中: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { private final TokenInterceptor tokenInterceptor; @Autowired public WebConfig(TokenInterceptor tokenInterceptor) { this.tokenInterceptor = tokenInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor); } } ``` 这样,当请求进入拦截器,会根据方法或类上的`NoNeedToken`注解判断是否需要进行拦截。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值