聂秋秋 5 yıl önce
ebeveyn
işleme
6912aa72d0

+ 555 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/IOUtils.java

@@ -0,0 +1,555 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.core.toolkit;
+
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URLConnection;
+import java.nio.channels.Selector;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+/**
+ * IOUtils Copy org.apache.commons.io.IOUtils
+ *
+ * @author Caratacus
+ * @since 2016-11-23
+ * @deprecated 3.3.0
+ */
+@Deprecated
+public class IOUtils {
+
+    private static final Log logger = LogFactory.getLog(IOUtils.class);
+
+    private IOUtils() {
+    }
+
+    /**
+     * Closes a URLConnection.
+     *
+     * @param conn the connection to close.
+     * @since 2.4
+     */
+    public static void close(final URLConnection conn) {
+        if (conn instanceof HttpURLConnection) {
+            ((HttpURLConnection) conn).disconnect();
+        }
+    }
+
+    /**
+     * Closes an <code>Reader</code> unconditionally.
+     * <p>
+     * Equivalent to {@link Reader#close()}, except any exceptions will be ignored. This is typically used in finally
+     * blocks.
+     * <p>
+     * Example code:
+     * <p>
+     * <pre>
+     * char[] data = new char[1024];
+     * Reader in = null;
+     * try {
+     * 	in = new FileReader(&quot;foo.txt&quot;);
+     * 	in.read(data);
+     * 	in.close(); // close errors are handled
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(in);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param input the Reader to close, may be null or already closed
+     */
+    public static void closeQuietly(final Reader input) {
+        closeQuietly((Closeable) input);
+    }
+
+    /**
+     * Closes an <code>Writer</code> unconditionally.
+     * <p>
+     * Equivalent to {@link Writer#close()}, except any exceptions will be ignored. This is typically used in finally
+     * blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * Writer out = null;
+     * try {
+     * 	out = new StringWriter();
+     * 	out.write(&quot;Hello World&quot;);
+     * 	out.close(); // close errors are handled
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(out);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param output the Writer to close, may be null or already closed
+     */
+    public static void closeQuietly(final Writer output) {
+        closeQuietly((Closeable) output);
+    }
+
+    /**
+     * Closes an <code>InputStream</code> unconditionally.
+     * <p>
+     * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored. This is typically used in
+     * finally blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * byte[] data = new byte[1024];
+     * InputStream in = null;
+     * try {
+     * 	in = new FileInputStream(&quot;foo.txt&quot;);
+     * 	in.read(data);
+     * 	in.close(); // close errors are handled
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(in);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param input the InputStream to close, may be null or already closed
+     */
+    public static void closeQuietly(final InputStream input) {
+        closeQuietly((Closeable) input);
+    }
+
+    /**
+     * Closes an <code>OutputStream</code> unconditionally.
+     * <p>
+     * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored. This is typically used in
+     * finally blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * byte[] data = &quot;Hello, World&quot;.getBytes();
+     *
+     * OutputStream out = null;
+     * try {
+     * 	out = new FileOutputStream(&quot;foo.txt&quot;);
+     * 	out.write(data);
+     * 	out.close(); // close errors are handled
+     * } catch (IOException e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(out);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param output the OutputStream to close, may be null or already closed
+     */
+    public static void closeQuietly(final OutputStream output) {
+        closeQuietly((Closeable) output);
+    }
+
+    /**
+     * Closes a <code>Closeable</code> unconditionally.
+     * <p>
+     * Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. This is typically used in finally
+     * blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * Closeable closeable = null;
+     * try {
+     * 	closeable = new FileReader(&quot;foo.txt&quot;);
+     * 	// process closeable
+     * 	closeable.close();
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(closeable);
+     * }
+     * </pre>
+     * </p>
+     * <p>
+     * Closing all streams:
+     * </p>
+     * <p>
+     * <pre>
+     * try {
+     * 	return IOUtils.copy(inputStream, outputStream);
+     * } finally {
+     * 	IOUtils.closeQuietly(inputStream);
+     * 	IOUtils.closeQuietly(outputStream);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param closeable the objects to close, may be null or already closed
+     * @since 2.0
+     */
+    public static void closeQuietly(final Closeable closeable) {
+        try {
+            if (closeable != null) {
+                closeable.close();
+            }
+        } catch (final IOException ioe) {
+            logger.error("error close io", ioe);
+        }
+    }
+
+    /**
+     * Closes a <code>Closeable</code> unconditionally.
+     * <p>
+     * Equivalent to {@link Closeable#close()}, except any exceptions will be ignored.
+     * </p>
+     * <p>
+     * This is typically used in finally blocks to ensure that the closeable is closed even if an Exception was thrown
+     * before the normal close statement was reached. <br>
+     * <b>It should not be used to replace the close statement(s) which should be present for the non-exceptional
+     * case.</b> <br>
+     * It is only intended to simplify tidying up where normal processing has already failed and reporting close failure
+     * as well is not necessary or useful.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * Closeable closeable = null;
+     * try {
+     *     closeable = new FileReader(&quot;foo.txt&quot;);
+     *     // processing using the closeable; may throw an Exception
+     *     closeable.close(); // Normal close - exceptions not ignored
+     * } catch (Exception e) {
+     *     // error handling
+     * } finally {
+     *     <b>IOUtils.closeQuietly(closeable); // In case normal close was skipped due to Exception</b>
+     * }
+     * </pre>
+     * </p>
+     * <p>
+     * Closing all streams: <br>
+     * </p>
+     * <p>
+     * <pre>
+     * try {
+     * 	return IOUtils.copy(inputStream, outputStream);
+     * } finally {
+     * 	IOUtils.closeQuietly(inputStream, outputStream);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param closeables the objects to close, may be null or already closed
+     * @see #closeQuietly(Closeable)
+     * @since 2.5
+     */
+    public static void closeQuietly(final Closeable... closeables) {
+        if (closeables == null) {
+            return;
+        }
+        for (final Closeable closeable : closeables) {
+            closeQuietly(closeable);
+        }
+    }
+
+    /**
+     * Closes a <code>Socket</code> unconditionally.
+     * <p>
+     * Equivalent to {@link Socket#close()}, except any exceptions will be ignored. This is typically used in finally
+     * blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * Socket socket = null;
+     * try {
+     * 	socket = new Socket(&quot;https://www.foo.com/&quot;, 443);
+     * 	// process socket
+     * 	socket.close();
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(socket);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param sock the Socket to close, may be null or already closed
+     * @since 2.0
+     */
+    public static void closeQuietly(final Socket sock) {
+        if (sock != null) {
+            try {
+                sock.close();
+            } catch (final IOException ioe) {
+                logger.error("error close io", ioe);
+            }
+        }
+    }
+
+    /**
+     * Closes a <code>Selector</code> unconditionally.
+     * <p>
+     * Equivalent to {@link Selector#close()}, except any exceptions will be ignored. This is typically used in finally
+     * blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * Selector selector = null;
+     * try {
+     * 	selector = Selector.open();
+     * 	// process socket
+     *
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(selector);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param selector the Selector to close, may be null or already closed
+     * @since 2.2
+     */
+    public static void closeQuietly(final Selector selector) {
+        if (selector != null) {
+            try {
+                selector.close();
+            } catch (final IOException ioe) {
+                logger.error("error close io", ioe);
+            }
+        }
+    }
+
+    /**
+     * Closes a <code>ServerSocket</code> unconditionally.
+     * <p>
+     * Equivalent to {@link ServerSocket#close()}, except any exceptions will be ignored. This is typically used in
+     * finally blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * ServerSocket socket = null;
+     * try {
+     * 	socket = new ServerSocket();
+     * 	// process socket
+     * 	socket.close();
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(socket);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param sock the ServerSocket to close, may be null or already closed
+     * @since 2.2
+     */
+    public static void closeQuietly(final ServerSocket sock) {
+        if (sock != null) {
+            try {
+                sock.close();
+            } catch (final IOException ioe) {
+                logger.error("error close io", ioe);
+            }
+        }
+    }
+
+    /**
+     * Closes a <code>Connection</code> unconditionally.
+     * <p>
+     * Equivalent to {@link Connection#close()}, except any exceptions will be ignored. This is typically used in
+     * finally blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * Connection conn = null;
+     * try {
+     * 	conn = new Connection();
+     * 	// process close
+     * 	conn.close();
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(conn);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param conn the Connection to close, may be null or already closed
+     * @since 2.2
+     */
+    public static void closeQuietly(final Connection conn) {
+        if (conn != null) {
+            try {
+                conn.close();
+            } catch (Exception e) {
+                logger.error("error close conn", e);
+            }
+        }
+    }
+
+    /**
+     * Closes a <code>AutoCloseable</code> unconditionally.
+     * <p>
+     * Equivalent to {@link ResultSet#close()}, except any exceptions will be ignored. This is typically used in finally
+     * blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * AutoCloseable statement = null;
+     * try {
+     * 	statement = new Connection();
+     * 	// process close
+     * 	statement.close();
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(conn);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param resultSet the Connection to close, may be null or already closed
+     * @since 2.2
+     */
+    public static void closeQuietly(final ResultSet resultSet) {
+        if (resultSet != null) {
+            try {
+                resultSet.close();
+            } catch (Exception e) {
+                logger.error("error close resultSet", e);
+            }
+        }
+    }
+
+    /**
+     * Closes a <code>AutoCloseable</code> unconditionally.
+     * <p>
+     * Equivalent to {@link Statement#close()}, except any exceptions will be ignored. This is typically used in finally
+     * blocks.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * AutoCloseable statement = null;
+     * try {
+     * 	statement = new Connection();
+     * 	// process close
+     * 	statement.close();
+     * } catch (Exception e) {
+     * 	// error handling
+     * } finally {
+     * 	IOUtils.closeQuietly(conn);
+     * }
+     * </pre>
+     * </p>
+     *
+     * @param statement the Connection to close, may be null or already closed
+     * @since 2.2
+     */
+    public static void closeQuietly(final Statement statement) {
+        if (statement != null) {
+            try {
+                statement.close();
+            } catch (Exception e) {
+                logger.error("error close statement", e);
+            }
+        }
+    }
+
+    /**
+     * Closes a <code>AutoCloseable</code> unconditionally.
+     * <p>
+     * Equivalent to {@link AutoCloseable#close()}, except any exceptions will be ignored.
+     * </p>
+     * <p>
+     * This is typically used in finally blocks to ensure that the closeable is closed even if an Exception was thrown
+     * before the normal close statement was reached. <br>
+     * <b>It should not be used to replace the close statement(s) which should be present for the non-exceptional
+     * case.</b> <br>
+     * It is only intended to simplify tidying up where normal processing has already failed and reporting close failure
+     * as well is not necessary or useful.
+     * </p>
+     * <p>
+     * Example code:
+     * </p>
+     * <p>
+     * <pre>
+     * AutoCloseable closeable = null;
+     * try {
+     *     closeable = new AutoCloseable();
+     *     // processing using the closeable; may throw an Exception
+     *     closeable.close(); // Normal close - exceptions not ignored
+     * } catch (Exception e) {
+     *     // error handling
+     * } finally {
+     *     <b>IOUtils.closeQuietly(closeable); // In case normal close was skipped due to Exception</b>
+     * }
+     * </pre>
+     * </p>
+     * <p>
+     * Closing all streams: <br>
+     * </p>
+     *
+     * @param statements the objects to close, may be null or already closed
+     * @see #closeQuietly(Statement)
+     * @since 2.5
+     */
+    public static void closeQuietly(final Statement... statements) {
+        if (statements == null) {
+            return;
+        }
+        for (final Statement statement : statements) {
+            closeQuietly(statement);
+        }
+    }
+
+}

+ 31 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/ReflectionKit.java

@@ -54,6 +54,37 @@ public final class ReflectionKit {
         PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class);
     }
 
+    /**
+     * <p>
+     * 反射 method 方法名,例如 getId
+     * </p>
+     *
+     * @param field
+     * @param str   属性字符串内容
+     * @deprecated 3.3.0 {@link #guessGetterName(Field, String)}
+     */
+    @Deprecated
+    public static String getMethodCapitalize(Field field, final String str) {
+        Class<?> fieldType = field.getType();
+        // fix #176
+        return StringUtils.concatCapitalize(boolean.class.equals(fieldType) ? "is" : "get", str);
+    }
+
+    /**
+     * <p>
+     * 反射 method 方法名,例如 setVersion
+     * </p>
+     *
+     * @param field Field
+     * @param str   String JavaBean类的version属性名
+     * @return version属性的setter方法名称,e.g. setVersion
+     * @deprecated 3.0.8
+     */
+    @Deprecated
+    public static String setMethodCapitalize(Field field, final String str) {
+        return StringUtils.concatCapitalize("set", str);
+    }
+
     /**
      * <p>
      * 获取 public get方法的值

+ 238 - 1
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/StringUtils.java

@@ -18,7 +18,12 @@ package com.baomidou.mybatisplus.core.toolkit;
 import com.baomidou.mybatisplus.core.toolkit.sql.StringEscape;
 import com.baomidou.mybatisplus.core.toolkit.support.BiIntFunction;
 
+import java.nio.charset.StandardCharsets;
+import java.sql.Blob;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -73,6 +78,25 @@ public final class StringUtils {
         return String.format(target, params);
     }
 
+
+    /**
+     * Blob 转为 String 格式
+     *
+     * @param blob Blob 对象
+     * @return 转换后的
+     */
+    public static String blob2String(Blob blob) {
+        if (null != blob) {
+            try {
+                byte[] returnValue = blob.getBytes(1, (int) blob.length());
+                return new String(returnValue, StandardCharsets.UTF_8);
+            } catch (Exception e) {
+                throw ExceptionUtils.mpe("Blob Convert To String Error!");
+            }
+        }
+        return null;
+    }
+
     /**
      * 判断字符串是否为空
      *
@@ -204,6 +228,23 @@ public final class StringUtils {
         return sb.toString();
     }
 
+    /**
+     * 解析 getMethodName -> propertyName
+     *
+     * @param getMethodName 需要解析的
+     * @return 返回解析后的字段名称
+     */
+    public static String resolveFieldName(String getMethodName) {
+        if (getMethodName.startsWith("get")) {
+            getMethodName = getMethodName.substring(3);
+        } else if (getMethodName.startsWith(IS)) {
+            getMethodName = getMethodName.substring(2);
+        }
+        // 小写第一个字母
+        return StringUtils.firstToLowerCase(getMethodName);
+    }
+
+
     /**
      * 字符串下划线转驼峰格式
      *
@@ -243,6 +284,16 @@ public final class StringUtils {
         return param.substring(0, 1).toLowerCase() + param.substring(1);
     }
 
+    /**
+     * 判断字符串是否为纯大写字母
+     *
+     * @param str 要匹配的字符串
+     * @return
+     */
+    public static boolean isUpperCase(String str) {
+        return matches("^[A-Z]+$", str);
+    }
+
     /**
      * 正则表达式匹配
      *
@@ -367,6 +418,16 @@ public final class StringUtils {
         return concatStr + Character.toTitleCase(firstChar) + str.substring(1);
     }
 
+    /**
+     * 字符串第一个字母大写
+     *
+     * @param str 被处理的字符串
+     * @return 首字母大写后的字符串
+     */
+    public static String capitalize(final String str) {
+        return concatCapitalize(null, str);
+    }
+
     /**
      * 判断对象是否为空
      *
@@ -453,6 +514,35 @@ public final class StringUtils {
         return endsWith(str, suffix, false);
     }
 
+
+    /**
+     * Case insensitive check if a String ends with a specified suffix.
+     * <p>
+     * <code>null</code>s are handled without exceptions. Two <code>null</code>
+     * references are considered to be equal. The comparison is case
+     * insensitive.
+     * </p>
+     * <p>
+     * <pre>
+     * StringUtils.endsWithIgnoreCase(null, null)      = true
+     * StringUtils.endsWithIgnoreCase(null, "abcdef")  = false
+     * StringUtils.endsWithIgnoreCase("def", null)     = false
+     * StringUtils.endsWithIgnoreCase("def", "abcdef") = true
+     * StringUtils.endsWithIgnoreCase("def", "ABCDEF") = false
+     * </pre>
+     * </p>
+     *
+     * @param str    the String to check, may be null
+     * @param suffix the suffix to find, may be null
+     * @return <code>true</code> if the String ends with the suffix, case
+     * insensitive, or both <code>null</code>
+     * @see String#endsWith(String)
+     * @since 2.4
+     */
+    public static boolean endsWithIgnoreCase(String str, String suffix) {
+        return endsWith(str, suffix, true);
+    }
+
     /**
      * Check if a String ends with a specified suffix (optionally case
      * insensitive).
@@ -476,6 +566,140 @@ public final class StringUtils {
         return str.regionMatches(ignoreCase, strOffset, suffix, 0, suffix.length());
     }
 
+    /**
+     * Splits the provided text into an array, separators specified. This is an
+     * alternative to using StringTokenizer.
+     * <p>
+     * The separator is not included in the returned String array. Adjacent
+     * separators are treated as one separator. For more control over the split
+     * use the StrTokenizer class.
+     * </p>
+     * <p>
+     * A {@code null} input String returns {@code null}. A {@code null}
+     * separatorChars splits on whitespace.
+     * </p>
+     * <p>
+     * <pre>
+     * StringUtils.split(null, *)         = null
+     * StringUtils.split("", *)           = []
+     * StringUtils.split("abc def", null) = ["abc", "def"]
+     * StringUtils.split("abc def", " ")  = ["abc", "def"]
+     * StringUtils.split("abc  def", " ") = ["abc", "def"]
+     * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
+     * </pre>
+     * </p>
+     *
+     * @param str            the String to parse, may be null
+     * @param separatorChars the characters used as the delimiters, {@code null} splits on
+     *                       whitespace
+     * @return an array of parsed Strings, {@code null} if null String input
+     */
+    public static String[] split(final String str, final String separatorChars) {
+        List<String> strings = splitWorker(str, separatorChars, -1, false);
+        return strings.toArray(new String[0]);
+    }
+
+    /**
+     * Performs the logic for the {@code split} and
+     * {@code splitPreserveAllTokens} methods that return a maximum array
+     * length.
+     *
+     * @param str               the String to parse, may be {@code null}
+     * @param separatorChars    the separate character
+     * @param max               the maximum number of elements to include in the array. A zero
+     *                          or negative value implies no limit.
+     * @param preserveAllTokens if {@code true}, adjacent separators are treated as empty
+     *                          token separators; if {@code false}, adjacent separators are
+     *                          treated as one separator.
+     * @return an array of parsed Strings, {@code null} if null String input
+     */
+    public static List<String> splitWorker(final String str, final String separatorChars, final int max,
+                                           final boolean preserveAllTokens) {
+        // Performance tuned for 2.0 (JDK1.4)
+        // Direct code is quicker than StringTokenizer.
+        // Also, StringTokenizer uses isSpace() not isWhitespace()
+
+        if (str == null) {
+            return null;
+        }
+        final int len = str.length();
+        if (len == 0) {
+            return Collections.emptyList();
+        }
+        final List<String> list = new ArrayList<>();
+        int sizePlus1 = 1;
+        int i = 0, start = 0;
+        boolean match = false;
+        boolean lastMatch = false;
+        if (separatorChars == null) {
+            // Null separator means use whitespace
+            while (i < len) {
+                if (Character.isWhitespace(str.charAt(i))) {
+                    if (match || preserveAllTokens) {
+                        lastMatch = true;
+                        if (sizePlus1++ == max) {
+                            i = len;
+                            lastMatch = false;
+                        }
+                        list.add(str.substring(start, i));
+                        match = false;
+                    }
+                    start = ++i;
+                    continue;
+                }
+                lastMatch = false;
+                match = true;
+                i++;
+            }
+        } else if (separatorChars.length() == 1) {
+            // Optimise 1 character case
+            final char sep = separatorChars.charAt(0);
+            while (i < len) {
+                if (str.charAt(i) == sep) {
+                    if (match || preserveAllTokens) {
+                        lastMatch = true;
+                        if (sizePlus1++ == max) {
+                            i = len;
+                            lastMatch = false;
+                        }
+                        list.add(str.substring(start, i));
+                        match = false;
+                    }
+                    start = ++i;
+                    continue;
+                }
+                lastMatch = false;
+                match = true;
+                i++;
+            }
+        } else {
+            // standard case
+            while (i < len) {
+                if (separatorChars.indexOf(str.charAt(i)) >= 0) {
+                    if (match || preserveAllTokens) {
+                        lastMatch = true;
+                        if (sizePlus1++ == max) {
+                            i = len;
+                            lastMatch = false;
+                        }
+                        list.add(str.substring(start, i));
+                        match = false;
+                    }
+                    start = ++i;
+                    continue;
+                }
+                lastMatch = false;
+                match = true;
+                i++;
+            }
+        }
+        if (match || preserveAllTokens && lastMatch) {
+            list.add(str.substring(start, i));
+        }
+        return list;
+    }
+
+
     /**
      * 是否为CharSequence类型
      *
@@ -505,6 +729,18 @@ public final class StringUtils {
         return propertyName;
     }
 
+    /**
+     * 是否为Boolean类型(包含普通类型)
+     *
+     * @param propertyCls ignore
+     * @return ignore
+     * @deprecated 3.3.0 {@link ClassUtils#isBoolean(Class)}
+     */
+    @Deprecated
+    public static boolean isBoolean(Class<?> propertyCls) {
+        return propertyCls != null && (boolean.class.isAssignableFrom(propertyCls) || Boolean.class.isAssignableFrom(propertyCls));
+    }
+
     /**
      * 第一个首字母小写,之后字符大小写的不变
      * <p>StringUtils.firstCharToLower( "UserService" )     = userService</p>
@@ -569,7 +805,8 @@ public final class StringUtils {
             boolean previousIsWhitespace = Character.isWhitespace(previousChar);
             boolean lastOneIsNotUnderscore = (buf.length() > 0) && (buf.charAt(buf.length() - 1) != '_');
             boolean isNotUnderscore = c != '_';
-            if (lastOneIsNotUnderscore && (isUpperCaseAndPreviousIsLowerCase || previousIsWhitespace)) {
+            if (lastOneIsNotUnderscore && (isUpperCaseAndPreviousIsLowerCase || previousIsWhitespace
+                || (betweenUpperCases && containsLowerCase && isUpperCaseAndPreviousIsUpperCase))) {
                 buf.append(StringPool.UNDERSCORE);
             } else if ((Character.isDigit(previousChar) && Character.isLetter(c))) {
                 buf.append('_');