网站重新解析站酷网电脑版
网站重新解析,站酷网电脑版,建个大型网站要多少钱,微企点网站建设国产AI绘画新体验#xff1a;Neeshck-Z-lmage_LYX_v2快速上手与效果实测
想体验一款真正“开箱即用”的国产AI绘画工具吗#xff1f;如果你厌倦了复杂的云端服务、繁琐的模型切换和令人头疼的显存问题#xff0c;那么今天介绍的这款工具#xff0c;可能会让你眼前一亮。
…国产AI绘画新体验Neeshck-Z-lmage_LYX_v2快速上手与效果实测想体验一款真正“开箱即用”的国产AI绘画工具吗如果你厌倦了复杂的云端服务、繁琐的模型切换和令人头疼的显存问题那么今天介绍的这款工具可能会让你眼前一亮。Neeshck-Z-lmage_LYX_v2一个基于Z-Image底座模型开发的轻量化绘画工具。它最大的特点就是“简单”——纯本地部署无需网络依赖通过一个简洁的网页界面就能动态切换不同风格的LoRA模型实时调节各种绘画参数。无论是想快速生成一张概念图还是想深入研究不同LoRA权重对画面的影响它都能提供一个高效、直观的解决方案。这篇文章我将带你从零开始快速上手这个工具并通过一系列实际测试看看它的生成效果到底如何。1. 工具核心它到底解决了什么痛点在深入操作之前我们先来理解一下这个工具设计的初衷。市面上很多AI绘画工具尤其是涉及LoRA低秩适应模型时通常会遇到几个让人头疼的问题切换模型太麻烦每次想换一个画风或主题的LoRA可能需要修改配置文件、重启程序流程繁琐。参数调节不直观像“引导强度”、“LoRA强度”这些参数到底调多少合适只能靠猜和反复试错。硬件门槛高一些大型模型对显卡显存要求苛刻普通用户的电脑可能根本跑不起来。Neeshck-Z-lmage_LYX_v2正是针对这些问题设计的。它把Z-Image这个优秀的国产文生图模型包装成了一个轻量级的应用。其核心价值在于一键切换LoRA工具会自动扫描你放在指定文件夹里的所有LoRA模型文件在网页上形成一个下拉菜单点一下就能换像换滤镜一样简单。参数实时调节所有关键参数都做成了滑动条一边调一边就能看到数值变化生成下一张图时立即生效。显存优化友好工具内部采用了torch.bfloat16精度加载模型并启用了显存卸载技术。这意味着即使你的显卡显存不大比如只有6GB或8GB也有很大机会能流畅运行而不是直接报“CUDA out of memory”错误。错误信息明确如果生成过程中出了问题它不会只给你一个模糊的提示而是会输出相对详细的错误信息和堆栈追踪这对于排查问题非常有帮助。简单来说它把一个技术性很强的AI模型变成了一个普通人也能轻松操作的“绘画软件”。2. 十分钟快速上手从启动到生成第一张图理论说再多不如动手试一下。这个工具的部署和启动过程极其简单我们一步步来。2.1 环境与启动假设你已经通过CSDN星图镜像广场或其他方式获取并运行了Neeshck-Z-lmage_LYX_v2的镜像。整个过程通常是一键式的。启动成功后你会在控制台或日志里看到类似下面的输出Streamlit 应用已启动。 网络地址http://localhost:8501或者You can now view your Streamlit app in your browser. Local URL: http://localhost:8501这时你只需要打开电脑上的浏览器Chrome、Edge等都可以在地址栏输入这个地址通常是http://你的服务器IP:8501就能看到工具的界面了。2.2 认识操作界面工具的界面非常清晰主要分为三个区域提示词输入区页面最上方的一个大文本框这里就是你“告诉”AI你想画什么的地方。参数调节区位于页面中部有几个关键的滑动条和选择框。结果展示区页面下方用于显示AI生成好的图片。整个布局逻辑清晰完全符合“输入指令 - 调节参数 - 查看结果”的操作流。2.3 生成你的第一幅作品现在让我们来创作第一张图。这个过程就像用手机APP修图一样直观。第一步输入画面描述在「输入画面描述」框里用中文或英文写下你想画的内容。越具体越好。 例如我们可以输入一只站在樱花树下的橘猫午后阳光花瓣飘落动漫风格细节丰富4K画质。第二步调节绘画参数在「绘画与 LoRA 参数」区域你会看到几个选项推理步数可以理解为AI“思考”的细致程度。步数越多比如调到30-50画面细节可能# 1. 概述本文我们来分享 MyBatis 的日志模块对应logging包。如下图所示logging包在 《精尽 MyBatis 源码解析 —— 项目结构一览》 中简单介绍了这个模块如下无论在开发测试环境中还是在线上生产环境中日志在整个系统中的地位都是非常重要的。良好的日志功能可以帮助开发人员和测试人员快速定位 Bug 代码也可以帮助运维人员快速定位性能瓶颈等问题。目前的 Java 世界中存在很多优秀的日志框架例如 Log4j、 Log4j2、Slf4j 等。MyBatis 作为一个设计优良的框架除了提供详细的日志输出信息还要能够集成多种日志框架其日志模块的一个主要功能就是集成第三方日志框架。本文涉及的类如下图所示类图左边的 Log 和 LogFactory 是 MyBatis 定义的日志接口和工厂。右边的是第三方日志框架的适配器。例如Slf4jImpl是 Slf4j 的适配器Log4jImpl是 Log4j 的适配器。下面让我们开始遨游把。2. LogFactoryorg.apache.ibatis.logging.LogFactoryLog 工厂类。2.1 构造方法// 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 尝试初始化 logConstructor 属性 tryImplementation(LogFactory::useSlf4jLogging); tryImplementation(LogFactory::useCommonsLogging); tryImplementation(LogFactory::useLog4J2Logging); tryImplementation(LogFactory::useLog4JLogging); tryImplementation(LogFactory::useJdkLogging); tryImplementation(LogFactory::useNoLogging); }MARKER静态属性标记用于打印日志时标记该日志来自 MyBatis 。目前这个属性是没有用的艿艿暂时也没想明白具体用途。有知道的胖友麻烦告知下。logConstructor静态属性使用的 Log 的构造方法。在1处的静态代码块中会进行初始化。#tryImplementation(Runnable runnable)方法尝试初始化logConstructor属性。代码如下// LogFactory.java private static void tryImplementation(Runnable runnable) { // 若 logConstructor 为空则执行 runnable if (logConstructor null) { try { runnable.run(); } catch (Throwable t) { // ignore } } }通过这样的方式保证只有第一个成功初始化的日志框架会被使用。#useSlf4jLogging()方法尝试使用 Slf4j 。代码如下// LogFactory.java public static synchronized void useSlf4jLogging() { setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class); }在该方法内部会调用#setImplementation(Class? extends Log implClass)方法尝试初始化logConstructor。代码如下// 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()); // 打印 Log 的级别判断日志框架是否可用 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); } }通过这样的方式可以判断日志框架是否可用。其它#useXXXLogging()方法也是类似的逻辑。代码如下// LogFactory.java 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); }2.2 getLog#getLog(...)方法获得 Log 对象。代码如下// LogFactory.java public static Log getLog(Class? aClass) { return getLog(aClass.getName()); } 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); } }通过logConstructor属性创建 Log 对象。2.3 小结LogFactory 负责管理 MyBatis 所使用的日志框架并且保证同一时间只能使用一种日志框架。3. Logorg.apache.ibatis.logging.LogMyBatis 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); }定义了日志的接口。但是这个接口不是直接定义而是参考了 Slf4j 的 Logger 接口。3.1 Slf4jImplorg.apache.ibatis.logging.slf4j.Slf4jImpl实现 Log 接口Slf4j 实现类。代码如下// Slf4jImpl.java public class Slf4jImpl implements Log { private Log log; public Slf4jImpl(String clazz) { // 使用 Slf4j 的 LoggerFactory 创建 org.slf4j.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 e) { // fail-back to Slf4jLoggerImpl } catch (NoSuchMethodException e) { // fail-back to Slf4jLoggerImpl } } // 否则创建 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); } }在构造方法中我们可以看到根据不同的情况创建Slf4jLocationAwareLoggerImpl或Slf4jLoggerImpl对象。为什么呢因为 Slf4j 有org.slf4j.impl.LocationAwareLogger这个接口需要特殊处理。当然这个不是我们关注的重点我们只要知道Slf4jImpl 对这两个实现类做了适配统一封装成满足 Log 接口的实现。org.apache.ibatis.logging.slf4j.Slf4jLocationAwareLoggerImpl实现 Log 接口对org.slf4j.impl.LocationAwareLogger的封装适配。代码如下// 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; 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); } }org.apache.ibatis.logging.slf4j.Slf4jLoggerImpl实现 Log 接口对org.slf4j.Logger的封装适配。代码如下// Slf4jLoggerImpl.java class Slf4jLoggerImpl implements Log { private final Logger logger; 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); } }3.2 其它实现类其它实现类和 Slf4jImpl 的思路是一致的都是对第三方日志框架的适配。所以这里就不详细解析。感兴趣的胖友可以自己去看。4. 日志打印在 MyBatis 中日志打印的用途非常广泛。例如在 ParameterHandler 中打印参数。代码如下// DefaultParameterHandler.java Override public void setParameters(PreparedStatement ps) throws SQLException { ErrorContext.instance().activity(setting parameters).object(mappedStatement.getParameterMap().getId()); // 遍历 ParameterMapping 数组 ListParameterMapping parameterMappings boundSql.getParameterMappings(); if (parameterMappings ! null) { for (int i 0; i parameterMappings.size(); i) { // 获得 ParameterMapping 对象 ParameterMapping parameterMapping parameterMappings.get(i); if (parameterMapping.getMode() ! ParameterMode.OUT) { // 获得值 Object value; String propertyName parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value boundSql.getAdditionalParameter(propertyName); } else if (parameterObject null) { value null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value parameterObject; } else { MetaObject metaObject configuration.newMetaObject(parameterObject); value metaObject.getValue(propertyName); } // 获得 typeHandler、jdbcType 属性 TypeHandler typeHandler parameterMapping.getTypeHandler(); JdbcType jdbcType parameterMapping.getJdbcType(); if (value null jdbcType null) { jdbcType configuration.getJdbcTypeForNull(); } // 设置 ? 占位符的参数 try { typeHandler.setParameter(ps, i 1, value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException(Could not set parameters for mapping: parameterMapping . Cause: e, e); } // 日志打印 if (log.isDebugEnabled()) { // 如果是 DEBUG 级别打印日志 log.debug(Setting parameter: parameterMapping.getProperty() Value: value); } } } } }通过log.isDebugEnabled()判断如果是 DEBUG 级别则进行日志的打印。5. ConnectionLogger在logging包中MyBatis 内置了多种对 JDBC 接口的代理类通过打印相关日志方便开发人员进行调试。ConnectionLogger 就是其中之一负责打印 Connection 相关日志。5.1 BaseJdbcLogger在 ConnectionLogger 的父类org.apache.ibatis.logging.jdbc.BaseJdbcLogger是一个抽象类为其它 Logger 抽象了公共的代码。代码如下// BaseJdbcLogger.java public abstract class BaseJdbcLogger { /** * SET 方法名集合 */ protected static final SetString SET_METHODS new HashSet(); /** * EXECUTE 方法名集合 */ protected static final SetString EXECUTE_METHODS new HashSet(); /** * 记录 PreparedStatement 的占位符参数集合 */ private final MapObject, Object columnMap new HashMap(); /** * 记录 PreparedStatement 的占位符参数列表 */ private final ListObject columnNames new ArrayList(); /** * 记录 PreparedStatement 的占位符参数列表 */ private final ListObject columnValues new ArrayList(); /** * Log 对象 */ protected final Log statementLog; /** * 查询超时时间 */ protected final int queryStack; /** * 计数用于判断是否 debug 开启 */ private int queryCount 0; static { // SET 方法名集合 SET_METHODS.add(setString); SET_METHODS.add(setNString); 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(setNull); // EXECUTE 方法名集合 EXECUTE_METHODS.add(execute); EXECUTE_METHODS.add(executeUpdate); EXECUTE_METHODS.add(executeQuery); EXECUTE_METHODS.add(addBatch); } protected BaseJdbcLogger(Log log, int queryStack) { this.statementLog log; // 一般情况下queryStack 0 // 在 BaseExecutor 中queryStack 用于判断是否嵌套查询 if (queryStack 0) { this.queryStack 1; } else { this.queryStack queryStack; } } // ... 省略一些方法 }代码比较简单胖友自己瞅瞅。5.1.1 增强方法BaseJdbcLogger 定义了#setColumn(Object key, Object value)和#getValue(Object key)方法用于设置和获得参数。代码如下// BaseJdbcLogger.java protected void setColumn(Object key, Object value) { columnMap.put(key, value); columnNames.add(key); columnValues.add(value); } protected Object getValue(Object key) { return columnMap.get(key); }这两个方法用于记录 PreparedStatement 的占位符参数集合。BaseJdbcLogger 定义了#removeValue(Object key)和#clearColumn()方法用于移除参数。代码如下// BaseJdbcLogger.java protected void removeValue(Object key) { columnMap.remove(key); columnNames.remove(key); columnValues.remove(key); } protected void clearColumn() { columnMap.clear(); columnNames.clear(); columnValues.clear(); }BaseJdbcLogger 定义了#debug(String text, boolean forceDebug)方法用于打印日志。代码如下// BaseJdbcLogger.java protected void debug(String text, boolean forceDebug) { // 如果强制调试则直接输出 if (statementLog.isDebugEnabled()) { statementLog.debug(text); } // 如果非强制调试并且 queryCount 未超过 queryStack 则输出 if (!statementLog.isDebugEnabled() !forceDebug queryCount queryStack) { statementLog.debug(text); } }一般情况下forceDebug falsequeryStack 1所以queryCount queryStack满足会打印日志。也就是说在非 DEBUG 级别下也会打印日志。但是只会打印一次因为queryCount会自增。BaseJdbcLogger 定义了#isDebugEnabled()方法用于判断是否开启 Debug 级别。代码如下// BaseJdbcLogger.java protected boolean isDebugEnabled() { return statementLog.isDebugEnabled(); }5.1.2 其它方法BaseJdbcLogger 定义了#reset()方法用于重置queryCount计数。代码如下// BaseJdbcLogger.java protected void reset() { // 重置 queryCount 计数 queryCount 0; // 清空 columnMap、columnNames、columnValues clearColumn(); }BaseJdbcLogger 定义了#record(String method)方法用于记录方法调用。代码如下// BaseJdbcLogger.java protected void record(String method) { // 如果为 SET 方法则 queryCount 计数不变 // 如果为 EXECUTE 方法则 queryCount 计数 1 if (EXECUTE_METHODS.contains(method)) { queryCount; } }通过这样的方式保证#debug(String text, boolean forceDebug)方法在非 DEBUG 级别下只会打印一次日志。5.2 ConnectionLoggerorg.apache.ibatis.logging.jdbc.ConnectionLogger继承 BaseJdbcLogger 抽象类实现对java.sql.Connection的代理打印日志。5.2.1 构造方法// ConnectionLogger.java /** * Connection 对象的代理 */ private final Connection connection; private ConnectionLogger(Connection conn, Log statementLog, int queryStack) { super(statementLog, queryStack); this.connection conn; }5.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); }通过动态代理的方式创建 Connection 的代理对象。5.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); } // 执行方法 Object result method.invoke(connection, params); // 如果是调用 prepareStatement、prepareCall、createStatement 方法进行打印日志并创建对应的代理对象 if (prepareStatement.equals(method.getName()) || prepareCall.equals(method.getName())) { if (isDebugEnabled()) { debug( Preparing: removeBreakingWhitespace((String) params[0]), true); // 打印日志 } // 创建 PreparedStatement 或 CallableStatement 的代理对象 return PreparedStatementLogger.newInstance((Statement) result, statementLog, queryStack); } else if (createStatement.equals(method.getName())) { if (isDebugEnabled()) { debug( Creating: removeBreakingWhitespace((String) params[0]), true); // 打印日志 } // 创建 Statement 的代理对象 return StatementLogger.newInstance((Statement) result, statementLog, queryStack); } else { // 其它方法直接返回 return result; } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }对于#prepareStatement(...)、#prepareCall(...)、#createStatement(...)方法除了打印日志还会创建对应的代理对象。这样就能打印这些对象执行方法的日志。5.2.4 removeBreakingWhitespace#removeBreakingWhitespace(String original)静态方法移除换行。代码如下// 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(); }5.3 其它实际上logging包下还有 PreparedStatementLogger、StatementLogger、ResultSetLogger 等等这里就不详细解析。感兴趣的胖友可以自己去看。6. 总结总的来说logging包代码实现上还是相对简单的。