当前位置: 首页 > news >正文

(更新至v0.108)Termux从零到一:下载、安装与国内源配置全攻略

1. Termux是什么?为什么你需要它?

如果你是一名Android用户,同时又对Linux命令行感兴趣,那么Termux绝对是你的菜。简单来说,Termux是一个强大的终端模拟器,它能在你的Android设备上运行一个完整的Linux环境。想象一下,你可以直接在手机上运行Python脚本、管理Git仓库、甚至搭建一个轻量级的服务器,是不是很酷?

我最初接触Termux是因为需要在手机上快速测试一些Python代码。当时在外出差,手边没有电脑,Termux完美解决了我的燃眉之急。后来发现它的功能远不止于此——从网络工具到开发环境,从系统管理到娱乐应用,几乎无所不能。特别是对于开发者来说,Termux相当于把一台Linux服务器装进了口袋。

目前最新版本是v0.108(截至2021年2月发布),支持Android 7.0及以上系统。不过很多用户反映直接从官网下载速度极慢,这也是为什么我们需要寻找替代下载源和配置国内镜像的原因。

2. 如何下载Termux?各版本安装包获取指南

2.1 官方下载渠道与问题

理论上,最正规的下载方式是访问Termux官网或通过F-Droid应用商店获取。但实测下来,这两个渠道在国内的下载速度简直让人崩溃。我曾经尝试从官网下载v0.108版本,50MB的APK文件花了近2小时,期间还断连了3次。

如果你坚持要从官方渠道下载,可以尝试使用下载加速工具。不过根据我的经验,还是推荐使用国内网盘资源,速度会快很多。

2.2 国内网盘资源汇总

经过多次测试和对比,我整理了几个稳定可用的Termux版本下载链接:

  • v0.108(最新推荐版本)

    • 蓝奏云:https://wwi.lanzoui.com/i7SYAnw87sd 密码:1o1x
    • 需要Android 7.0及以上系统
  • v0.88(兼容性较好的旧版)

    • 坚果云:https://www.jianguoyun.com/p/DWlJPH0QmMKCCBjTitYC
    • 百度云:https://pan.baidu.com/s/1OOOz66H55rovtjKTC4F6GA 提取码:f310
  • v0.94(中间版本)

    • 百度云:https://pan.baidu.com/s/1yZz103QEJTSLZw35b1TGDA 提取码:a5g3
    • 蓝奏云:https://wwe.lanzous.com/iTCrGeoxmtc 密码:9ur7

个人建议优先选择v0.108,功能最全且bug最少。如果设备较旧无法支持,再考虑v0.88或v0.94。

2.3 安装注意事项

下载APK后,安装过程与普通应用无异。但有几个关键点需要注意:

  1. 安装前请确保已开启"允许安装未知来源应用"的权限
  2. 首次启动Termux时,它会自动下载约100MB的基础组件,建议在WiFi环境下进行
  3. 如果遇到存储权限问题,运行termux-setup-storage命令授予存储访问权限

3. 配置国内镜像源:告别蜗牛速度

3.1 为什么要换源?

默认情况下,Termux使用的是官方软件源,服务器在国外。这就导致了一个很现实的问题:安装或更新软件时速度极慢,还经常出现连接超时。我曾经尝试用官方源安装Python,结果一个简单的安装过程花了近半小时。

更换为国内镜像源后,同样的操作通常只需要几秒钟。以清华大学开源软件镜像站为例,下载速度可以从几KB/s提升到几MB/s,体验完全是天壤之别。

3.2 详细换源步骤

打开Termux,依次执行以下命令:

# 备份原始源列表# 1. 概述 本文,我们来分享 MyBatis 的日志模块,对应 `logging` 包。如下图所示:[![`logging` 包](http://static.iocoder.cn/images/MyBatis/2020_01_07/01.png)](http://static.iocoder.cn/images/MyBatis/2020_01_07/01.png)`logging` 包 在 [《精尽 MyBatis 源码解析 —— 项目结构一览》](http://svip.iocoder.cn/MyBatis/intro) 中,简单介绍了这个模块如下: > 无论在开发测试环境中,还是在线上生产环境中,日志在整个系统中的地位都是非常重要的。良好的日志功能可以帮助开发人员和测试人员快速定位 Bug 代码,也可以帮助运维人员快速定位性能瓶颈等问题。目前的 Java 世界中存在很多优秀的日志框架,例如 Log4j、 Log4j2、Slf4j 等。 > > MyBatis 作为一个设计优良的框架,除了提供详细的日志输出信息,还要能够集成多种日志框架,其日志模块的一个主要功能就是**集成第三方日志框架**。 本文涉及的类如下图所示:<img src="http://static.iocoder.cn/images/MyBatis/2020_01_07/02.png" alt="类图" style="zoom:80%;" /> 下面,我们逐个类来解析。 # 2. LogFactory `org.apache.ibatis.logging.LogFactory` ,Log 工厂类。 ## 2.1 构造方法 ```java // LogFactory.java /** * Marker to be used by logging implementations that support markers */ public static final String MARKER = "MYBATIS"; /** * 使用的 Log 的构造方法 */ private static Constructor<? extends Log> logConstructor; static { // <1> 逐个尝试,判断使用哪个 Log 的实现类,即初始化 logConstructor 属性 tryImplementation(LogFactory::useSlf4jLogging); tryImplementation(LogFactory::useCommonsLogging); tryImplementation(LogFactory::useLog4J2Logging); tryImplementation(LogFactory::useLog4JLogging); tryImplementation(LogFactory::useJdkLogging); tryImplementation(LogFactory::useNoLogging); }

<1>处,基于静态代码块,初始化logConstructor静态属性。而初始化的方式,是调用#tryImplementation(Runnable runnable)方法,逐个尝试,判断使用哪个 Log 的实现类。代码如下:

// LogFactory.java private static void tryImplementation(Runnable runnable) { // 若 logConstructor 为空,则执行 runnable if (logConstructor == null) { try { runnable.run(); } catch (Throwable t) { // ignore } } }

logConstructor为空时,执行runnable的方法。那么,我们就可以理解LogFactory提供的#useSlf4jLogging()等的静态方法,代码如下:

// LogFactory.java public static synchronized void useSlf4jLogging() { setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class); } public static synchronized void useCommonsLogging() { setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class); } public static synchronized void useLog4JLogging() { setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class); } public static synchronized void useLog4J2Logging() { setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class); } public static synchronized void useJdkLogging() { setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class); } public static synchronized void useStdOutLogging() { setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class); } public static synchronized void useNoLogging() { setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class); }
  • 每个方法,都调用了#setImplementation(Class<? extends Log> implClass)方法。代码如下:

    // LogFactory.java private static void setImplementation(Class<? extends Log> implClass) { try { // 获得参数为 String 的构造方法 Constructor<? extends Log> candidate = implClass.getConstructor(String.class); // 创建 Log 对象 Log log = candidate.newInstance(LogFactory.class.getName()); if (log.isDebugEnabled()) { log.debug("Logging initialized using '" + implClass + "' adapter."); } // 创建成功,意味着可以使用,设置为 logConstructor logConstructor = candidate; } catch (Throwable t) { throw new LogException("Error setting Log implementation. Cause: " + t, t); } }
    • 通过反射的方式,创建指定implClass类的对象,成功后,意味着可以使用,设置为logConstructor

2.2 getLog

#getLog(...)静态方法,获得 Log 对象。代码如下:

// LogFactory.java public static Log getLog(String logger) { try { return logConstructor.newInstance(logger); } catch (Throwable t) { throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t); } } public static Log getLog(Class<?> clazz) { return getLog(clazz.getName()); }
  • 基于logConstructor构造方法,创建 Log 对象。

3. Log

org.apache.ibatis.logging.Log,MyBatis Log 接口。代码如下:

// Log.java public interface Log { boolean isDebugEnabled(); boolean isTraceEnabled(); void error(String s, Throwable e); void error(String s); void debug(String s); void trace(String s); void warn(String s); }
  • 定义了 Log 的接口。

3.1 StdOutImpl

org.apache.ibatis.logging.stdout.StdOutImpl,实现 Log 接口,StdOut 实现类。代码如下:

// StdOutImpl.java public class StdOutImpl implements Log { public StdOutImpl(String clazz) { // Do Nothing } @Override public boolean isDebugEnabled() { return true; } @Override public boolean isTraceEnabled() { return true; } @Override public void error(String s, Throwable e) { System.err.println(s); e.printStackTrace(System.err); } @Override public void error(String s) { System.err.println(s); } @Override public void debug(String s) { System.out.println(s); } @Override public void trace(String s) { System.out.println(s); } @Override public void warn(String s) { System.out.println(s); } }
  • 基于System.outSystem.err实现。

3.2 NoLoggingImpl

org.apache.ibatis.logging.nologging.NoLoggingImpl,实现 Log 接口,空实现类。代码如下:

// NoLoggingImpl.java public class NoLoggingImpl implements Log { public NoLoggingImpl(String clazz) { // Do Nothing } @Override public boolean isDebugEnabled() { return false; } @Override public boolean isTraceEnabled() { return false; } @Override public void error(String s, Throwable e) { // Do Nothing } @Override public void error(String s) { // Do Nothing } @Override public void debug(String s) { // Do Nothing } @Override public void trace(String s) { // Do Nothing } @Override public void warn(String s) { // Do Nothing } }
  • 每个方法都是空实现。

3.3 Slf4jImpl

org.apache.ibatis.logging.slf4j.Slf4jImpl,实现 Log 接口,Slf4j 实现类。代码如下:

// Slf4jImpl.java public class Slf4jImpl implements Log { private Log log; public Slf4jImpl(String clazz) { // 使用 SLF LoggerFactory 获得 SLF Logger 对象 Logger logger = LoggerFactory.getLogger(clazz); // 如果是 LocationAwareLogger ,则创建 Slf4jLocationAwareLoggerImpl 对象 if (logger instanceof LocationAwareLogger) { try { // check for slf4j >= 1.6 method signature logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class); log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger); return; } catch (SecurityException | NoSuchMethodException e) { // fail-back to Slf4jLoggerImpl } } // Logger is not LocationAwareLogger or slf4j version < 1.6 log = new Slf4jLoggerImpl(logger); } @Override public boolean isDebugEnabled() { return log.isDebugEnabled(); } @Override public boolean isTraceEnabled() { return log.isTraceEnabled(); } @Override public void error(String s, Throwable e) { log.error(s, e); } @Override public void error(String s) { log.error(s); } @Override public void debug(String s) { log.debug(s); } @Override public void trace(String s) { log.trace(s); } @Override public void warn(String s) { log.warn(s); } }
  • 在构造方法中,我们可以看到,会根据不同的情况,创建Slf4jLocationAwareLoggerImplSlf4jLoggerImpl对象。那么这两个类的实现代码如下:

    // Slf4jLocationAwareLoggerImpl.java class Slf4jLocationAwareLoggerImpl implements Log { private static final Marker MARKER = MarkerFactory.getMarker(LogFactory.MARKER); private static final String FQCN = Slf4jImpl.class.getName(); private final LocationAwareLogger logger; public Slf4jLocationAwareLoggerImpl(LocationAwareLogger logger) { this.logger = logger; } @Override public boolean isDebugEnabled() { return logger.isDebugEnabled(); } @Override public boolean isTraceEnabled() { return logger.isTraceEnabled(); } @Override public void error(String s, Throwable e) { logger.log(MARKER, FQCN, LocationAwareLogger.ERROR_INT, s, null, e); } @Override public void error(String s) { logger.log(MARKER, FQCN, LocationAwareLogger.ERROR_INT, s, null, null); } @Override public void debug(String s) { logger.log(MARKER, FQCN, LocationAwareLogger.DEBUG_INT, s, null, null); } @Override public void trace(String s) { logger.log(MARKER, FQCN, LocationAwareLogger.TRACE_INT, s, null, null); } @Override public void warn(String s) { logger.log(MARKER, FQCN, LocationAwareLogger.WARN_INT, s, null, null); } } // Slf4jLoggerImpl.java class Slf4jLoggerImpl implements Log { private final Logger logger; public Slf4jLoggerImpl(Logger logger) { this.logger = logger; } @Override public boolean isDebugEnabled() { return logger.isDebugEnabled(); } @Override public boolean isTraceEnabled() { return logger.isTraceEnabled(); } @Override public void error(String s, Throwable e) { logger.error(s, e); } @Override public void error(String s) { logger.error(s); } @Override public void debug(String s) { logger.debug(s); } @Override public void trace(String s) { logger.trace(s); } @Override public void warn(String s) { logger.warn(s); } }
    • 都是基于org.slf4j.Logger进行实现。

3.4 JakartaCommonsLoggingImpl

org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl,实现 Log 接口,Jakarta Commons Logging 实现类。代码如下:

// JakartaCommonsLoggingImpl.java public class JakartaCommonsLoggingImpl implements Log { private final Log log; public JakartaCommonsLoggingImpl(String clazz) { // 获得 Log 对象 log = LogFactory.getLog(clazz); } @Override public boolean isDebugEnabled() { return log.isDebugEnabled(); } @Override public boolean isTraceEnabled() { return log.isTraceEnabled(); } @Override public void error(String s, Throwable e) { log.error(s, e); } @Override public void error(String s) { log.error(s); } @Override public void debug(String s) { log.debug(s); } @Override public void trace(String s) { log.trace(s); } @Override public void warn(String s) { log.warn(s); } }
  • 基于org.apache.commons.logging.Log实现。

3.5 Log4jImpl

org.apache.ibatis.logging.log4j.Log4jImpl,实现 Log 接口,Log4j 实现类。代码如下:

// Log4jImpl.java public class Log4jImpl implements Log { private static final String FQCN = Log4jImpl.class.getName(); private final Logger log; public Log4jImpl(String clazz) { log = Logger.getLogger(clazz); } @Override public boolean isDebugEnabled() { return log.isDebugEnabled(); } @Override public boolean isTraceEnabled() { return log.isTraceEnabled(); } @Override public void error(String s, Throwable e) { log.log(FQCN, Level.ERROR, s, e); } @Override public void error(String s) { log.log(FQCN, Level.ERROR, s, null); } @Override public void debug(String s) { log.log(FQCN, Level.DEBUG, s, null); } @Override public void trace(String s) { log.log(FQCN, Level.TRACE, s, null); } @Override public void warn(String s) { log.log(FQCN, Level.WARN, s, null); } }
  • 基于org.apache.log4j.Logger实现。

3.6 Log4j2Impl

org.apache.ibatis.logging.log4j2.Log4j2Impl,实现 Log 接口,Log4j2 实现类。代码如下:

// Log4j2Impl.java public class Log4j2Impl implements Log { private static final String FQCN = Log4j2Impl.class.getName(); private final Logger log; public Log4j2Impl(String clazz) { log = LogManager.getLogger(clazz); } @Override public boolean isDebugEnabled() { return log.isDebugEnabled(); } @Override public boolean isTraceEnabled() { return log.isTraceEnabled(); } @Override public void error(String s, Throwable e) { log.log(Level.ERROR, s, e); } @Override public void error(String s) { log.log(Level.ERROR, s); } @Override public void debug(String s) { log.log(Level.DEBUG, s); } @Override public void trace(String s) { log.log(Level.TRACE, s); } @Override public void warn(String s) { log.log(Level.WARN, s); } }
  • 基于org.apache.logging.log4j.Logger实现。

3.7 Jdk14LoggingImpl

org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl,实现 Log 接口,Jdk14Logging 实现类。代码如下:

// Jdk14LoggingImpl.java public class Jdk14LoggingImpl implements Log { private final Logger log; public Jdk14LoggingImpl(String clazz) { log = Logger.getLogger(clazz); } @Override public boolean isDebugEnabled() { return log.isLoggable(Level.FINE); } @Override public boolean isTraceEnabled() { return log.isLoggable(Level.FINER); } @Override public void error(String s, Throwable e) { log.log(Level.SEVERE, s, e); } @Override public void error(String s) { log.log(Level.SEVERE, s); } @Override public void debug(String s) { log.log(Level.FINE, s); } @Override public void trace(String s) { log.log(Level.FINER, s); } @Override public void warn(String s) { log.log(Level.WARNING, s); } }
  • 基于java.util.logging.Logger实现。

4. BaseJdbcLogger

logging包中,BaseJdbcLogger和其子类,实现将 JDBC 的操作,记录成日志。整体类图如下:类图

4.1 BaseJdbcLogger

org.apache.ibatis.logging.jdbc.BaseJdbcLogger,实现 Log 接口,BaseJdbcLogger 抽象类。代码如下:

// BaseJdbcLogger.java public abstract class BaseJdbcLogger implements Log { /** * 常见的 SET 方法的前缀 */ protected static final String SET_METHOD_PREFIX = "set"; /** * 常见的 GET 方法的前缀 */ private static final String GET_METHOD_PREFIX = "get"; /** * 常见的 IS 方法的前缀 */ private static final String IS_METHOD_PREFIX = "is"; /** * Set 方法的方法名的集合 */ private final Set<String> setMethods = new HashSet<>(); /** * Get 方法的方法名的集合 */ private final Set<String> getMethods = new HashSet<>(); /** * Is 方法的方法名的集合 */ private final Set<String> isMethods = new HashSet<>(); /** * Log 对象 */ protected final Log statementLog; /** * 查询的层级 */ protected final int queryStack; /* * Default constructor */ public BaseJdbcLogger(Log log, int queryStack) { this.statementLog = log; if (queryStack == 0) { this.queryStack = 1; } else { this.queryStack = queryStack; } // 初始化 setMethods 属性 Method[] methods = PreparedStatement.class.getMethods(); for (Method method : methods) { String methodName = method.getName(); if (methodName.startsWith(SET_METHOD_PREFIX)) { setMethods.add(methodName); } } // 初始化 getMethods 属性 methods = ResultSet.class.getMethods(); for (Method method : methods) { String methodName = method.getName(); if (methodName.startsWith(GET_METHOD_PREFIX)) { getMethods.add(methodName); } } // 初始化 isMethods 属性 methods = ResultSet.class.getMethods(); for (Method method : methods) { String methodName = method.getName(); if (methodName.startsWith(IS_METHOD_PREFIX)) { isMethods.add(methodName); } } } // ... 省略 isDebugEnabled 和 isTraceEnabled 方法 protected void debug(String text, boolean input) { if (statementLog.isDebugEnabled()) { statementLog.debug(prefix(input) + text); } } protected void trace(String text, boolean input) { if (statementLog.isTraceEnabled()) { statementLog.trace(prefix(input) + text); } } private String prefix(boolean isInput) { char[] buffer = new char[queryStack * 2 + 2]; Arrays.fill(buffer, '='); buffer[queryStack * 2 + 1] = ' '; if (isInput) { buffer[queryStack * 2] = '>'; } else { buffer[0] = '<'; } return new String(buffer); } // ... 省略 error 相关方法 /** * 判断是否为 Set 方法 * * @param methodName 方法名 * @return 是否 */ protected boolean isSet(String methodName) { return setMethods.contains(methodName); } /** * 判断是否为 Get 方法 * * @param methodName 方法名 * @return 是否 */ protected boolean isGet(String methodName) { return getMethods.contains(methodName); } /** * 判断是否为 Is 方法 * * @param methodName 方法名 * @return 是否 */ protected boolean isIs(String methodName) { return isMethods.contains(methodName); } /** * @return 参数值的 String 表现 */ protected String getParameterValueString(Object parameter) { if (parameter == null) { return "null"; } else { if (parameter instanceof Array) { try { return ArrayUtil.toString(((Array) parameter).getArray()); } catch (SQLException e) { return parameter.toString(); } } return parameter.toString(); } } /** * 打印参数 * * @param args 参数数组 */ protected void logParameter(Object[] args) { if (args != null && args.length > 0) { for (int i = 0; i < args.length; i++) { String parameterValueString = getParameterValueString(args[i]); debug(" Parameters: " + parameterValueString + (i == args.length - 1 ? "" : ","), true); } } } }
  • 代码比较简单,胖友自己看看注释。

4.2 ConnectionLogger

org.apache.ibatis.logging.jdbc.ConnectionLogger,继承 BaseJdbcLogger 抽象类,Connection 日志增强。通过动态代理的方式,增强java.sql.Connection和其下的java.sql.PreparedStatement的日志打印。

4.2.1 构造方法

// ConnectionLogger.java /** * Connection 的代理 */ private final Connection connection; private ConnectionLogger(Connection conn, Log statementLog, int queryStack) { super(statementLog, queryStack); this.connection = conn; }

4.2.2 newInstance

#newInstance(Connection conn, Log statementLog, int queryStack)静态方法,创建 Connection 的代理对象。代码如下:

// ConnectionLogger.java public static Connection newInstance(Connection conn, Log statementLog, int queryStack) { InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack); ClassLoader cl = Connection.class.getClassLoader(); return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler); }

4.2.3 invoke

// ConnectionLogger.java @Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { try { // 如果调用的是 Object 的方法,直接忽略 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, params); } // 调用 Connection 的方法 if ("prepareStatement".equals(method.getName())) { // 如果是 prepareStatement 方法,则打印日志,并创建 PreparedStatementLogger 代理对象 if (isDebugEnabled()) { debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true); } // 创建 PreparedStatement PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params); // 创建 PreparedStatement 的代理对象 stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack); return stmt; // 如果是 prepareCall 方法,则打印日志,并创建 PreparedStatementLogger 代理对象 } else if ("prepareCall".equals(method.getName())) { if (isDebugEnabled()) { debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true); } PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params); stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack); return stmt; // 如果是 createStatement 方法,则创建 StatementLogger 代理对象 } else if ("createStatement".equals(method.getName())) { Statement stmt = (Statement) method.invoke(connection, params); stmt = StatementLogger.newInstance(stmt, statementLog, queryStack); return stmt; } else { // 其它方法,则直接调用 return method.invoke(connection, params); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }
  • 根据不同的方法,进行不同的日志打印处理。其中,#removeBreakingWhitespace(String original)方法,去除 SQL 语句中多余的空白。代码如下:

    // ConnectionLogger.java public static String removeBreakingWhitespace(String original) { StringTokenizer whitespaceStripper = new StringTokenizer(original); StringBuilder builder = new StringBuilder(); while (whitespaceStripper.hasMoreTokens()) { builder.append(whitespaceStripper.nextToken()); builder.append(" "); } return builder.toString(); }

4.3 PreparedStatementLogger

org.apache.ibatis.logging.jdbc.PreparedStatementLogger,继承 BaseJdbcLogger 抽象类,PreparedStatement 日志增强。通过动态代理的方式,增强java.sql.PreparedStatement的日志打印。

4.3.1 构造方法

// PreparedStatementLogger.java /** * PreparedStatement 对象 */ private final PreparedStatement statement; private PreparedStatementLogger(PreparedStatement stmt, Log statementLog, int queryStack) { super(statementLog, queryStack); this.statement = stmt; }

4.3.2 newInstance

#newInstance(PreparedStatement stmt, Log statementLog, int queryStack)静态方法,创建 PreparedStatement 的代理对象。代码如下:

// PreparedStatementLogger.java public static PreparedStatement newInstance(PreparedStatement stmt, Log statementLog, int queryStack) { InvocationHandler handler = new PreparedStatementLogger(stmt, statementLog, queryStack); ClassLoader cl = PreparedStatement.class.getClassLoader(); return (PreparedStatement) Proxy.newProxyInstance(cl, new Class[]{PreparedStatement.class, CallableStatement.class}, handler); }

4.3.3 invoke

// PreparedStatementLogger.java @Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { try { // 如果调用的是 Object 的方法,直接忽略 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, params); } // 执行 SQL 相关方法 if (EXECUTE_METHODS.contains(method.getName())) { // 打印日志 if (isDebugEnabled()) { debug("Parameters: " + getParameterValueString(), true); } // 清空 column* 相关的属性 clearColumnInfo(); // 执行 SQL 方法 if ("executeQuery".equals(method.getName())) { ResultSet rs = (ResultSet) method.invoke(statement, params); return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack); } else { return method.invoke(statement, params); } // 设置参数 } else if (SET_METHODS.contains(method.getName())) { if ("setNull".equals(method.getName())) { setColumn(params[0], null); } else { setColumn(params[0], params[1]); } return method.invoke(statement, params); // 如果是 get 相关方法 } else if ("getResultSet".equals(method.getName())) { ResultSet rs = (ResultSet) method.invoke(statement, params); return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack); // 如果是 getUpdateCount 方法 } else if ("getUpdateCount".equals(method.getName())) { int updateCount = (Integer) method.invoke(statement, params); if (updateCount != -1) { debug(" Updates: " + updateCount, false); } return updateCount; } else { return method.invoke(statement, params); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }
  • 根据不同的方法,进行不同的日志打印处理。其中,EXECUTE_METHODSSET_METHODS是静态属性,代码如下:

    // PreparedStatementLogger.java private static final Set<String> SET_METHODS; private static final Set<String> EXECUTE_METHODS; static { SET_METHODS = new HashSet<>(); SET_METHODS.add("setString"); SET_METHODS.add("setNull"); SET_METHODS.add("setInt"); SET_METHODS.add("setByte"); SET_METHODS.add("setShort"); SET_METHODS.add("setLong"); SET_METHODS.add("setDouble"); SET_METHODS.add("setFloat"); SET_METHODS.add("setTimestamp"); SET_METHODS.add("setDate"); SET_METHODS.add("setTime"); SET_METHODS.add("setArray"); SET_METHODS.add("setBigDecimal"); SET_METHODS.add("setAsciiStream"); SET_METHODS.add("setBinaryStream"); SET_METHODS.add("setBlob"); SET_METHODS.add("setBoolean"); SET_METHODS.add("setBytes"); SET_METHODS.add("setCharacterStream"); SET_METHODS.add("setNCharacterStream"); SET_METHODS.add("setClob"); SET_METHODS.add("setNClob"); SET_METHODS.add("setObject"); SET_METHODS.add("setUnicodeStream"); } static { EXECUTE_METHODS = new HashSet<>(); EXECUTE_METHODS.add("execute"); EXECUTE_METHODS.add("executeUpdate"); EXECUTE_METHODS.add("executeQuery"); EXECUTE_METHODS.add("addBatch"); }
  • `

http://www.jsqmd.com/news/655311/

相关文章:

  • 告别风扇噪音!FanControl高级配置与AMD显卡兼容性完全指南
  • 选购展览设计公司,北京蜂蚁展览设计是否值得考虑 - myqiye
  • Bilibili视频下载终极指南:如何免费高效批量获取B站高清资源?
  • Windows上的安卓应用革命:APK-Installer如何重新定义跨平台体验
  • 网络新手避坑指南:eNSP模拟ACL和NAT时,这几个配置细节错了全网不通
  • 精细结构常数与黄金比例八次幂差值 \Delta 的数值关联探索
  • UG FANUC四轴后处理输出程序,出现转速或刀号相关报警如何排查?
  • 关投强做软文推广效果好不好:核心资源、交付标准、价格体系与服务保障深度解析 - 发稿平台推荐
  • Python glob模块实战:从基础通配符到递归遍历的完整指南
  • LabVIEW模拟电路故障诊断
  • 告别软件延时!用RT-Thread的HWTIMER为你的STM32项目释放CPU(以TIM2为例)
  • 解密softmax:从数学原理到PyTorch实战
  • 别再傻傻分不清了!华为交换机上三种ARP代理的保姆级配置与场景拆解
  • 像素剧本圣殿部署教程:云服务器(阿里云/AWS)GPU实例镜像部署指南
  • 嵌入式Linux安全漏洞管理与技术债务优化实践
  • Python移动开发新范式:python-for-android技术实现深度解析
  • 阿里通义Z-Image-Turbo WebUI零基础教程:5分钟生成第一张AI图片
  • 当 AI Agent 进入生产环境:我们为什么需要 ClawVault 这样的安全 vault?
  • 如何安全使用R3nzSkin实现英雄联盟内存换肤的完整指南
  • 手把手教你用Clang/LLVM为你的C++项目开启CFI防护(含性能开销实测)
  • 如何用秒传脚本实现百度网盘文件永久分享
  • 实测6家储能电池模组PACK倍速链生产线厂家,谁更靠谱? - 丁华林智能制造
  • 一文看懂OpenClaw:基础概念详解 + 部署实操教程
  • 别再羡慕AR效果了!手把手教你用Android Camera API打造一个“透视”桌面(附完整源码)
  • Hive SQL进阶:从explode到posexplode,搞定‘多列同时炸裂‘的完整避坑指南
  • IndexTTS2终极指南:如何用一句指令生成情感丰富的语音?
  • 高效图片去重利器:AntiDupl.NET智能重复图片清理完整指南
  • 新手必看:千问3.5-2B视觉模型5分钟快速上手指南
  • 终极免费开源字体方案:Bebas Neue如何彻底改变你的标题设计体验
  • SpringBoot整合MyBatis:从“Consider defining a bean”报错剖析@MapperScan与@Mapper的配置陷阱