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

聊聊mybatis-plus的sql加载顺序

本文主要研究一下如果mybatis mapper定义了多个同名方法会不会有问题

MybatisConfiguration

com/baomidou/mybatisplus/core/MybatisConfiguration.java

    /*** MybatisPlus 加载 SQL 顺序:* <p> 1、加载 XML中的 SQL </p>* <p> 2、加载 SqlProvider 中的 SQL </p>* <p> 3、XmlSql 与 SqlProvider不能包含相同的 SQL </p>* <p>调整后的 SQL优先级:XmlSql > sqlProvider > CurdSql </p>*/@Overridepublic void addMappedStatement(MappedStatement ms) {if (mappedStatements.containsKey(ms.getId())) {/** 说明已加载了xml中的节点; 忽略mapper中的 SqlProvider 数据*/logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file");return;}mappedStatements.put(ms.getId(), ms);}

MybatisSqlSessionFactoryBean

com/baomidou/mybatisplus/extension/spring/MybatisSqlSessionFactoryBean.java

    /*** Build a {@code SqlSessionFactory} instance.* <p>* The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a* {@code SqlSessionFactory} instance based on an Reader. Since 1.3.0, it can be specified a* {@link Configuration} instance directly(without config file).* </p>** @return SqlSessionFactory* @throws IOException if loading the config file failed*/protected SqlSessionFactory buildSqlSessionFactory() throws Exception {//......if (this.mapperLocations != null) {if (this.mapperLocations.length == 0) {LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");} else {for (Resource mapperLocation : this.mapperLocations) {if (mapperLocation == null) {continue;}try {XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());xmlMapperBuilder.parse();} catch (Exception e) {throw new IOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);} finally {ErrorContext.instance().reset();}LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");}}}}

MybatisSqlSessionFactoryBean的buildSqlSessionFactory方法会根据mapperLocations的配置取加载xml配置,即加载xml的mapper信息

XMLMapperBuilder

org/apache/ibatis/builder/xml/XMLMapperBuilder.java

  public void parse() {if (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}private void bindMapperForNamespace() {String namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {boundType = Resources.classForName(namespace);} catch (ClassNotFoundException e) {// ignore, bound type is not required}if (boundType != null && !configuration.hasMapper(boundType)) {// Spring may not know the real resource name so we set a flag// to prevent loading again this resource from the mapper interface// look at MapperAnnotationBuilder#loadXmlResourceconfiguration.addLoadedResource("namespace:" + namespace);configuration.addMapper(boundType);}}}

XMLMapperBuilder的parse方法会执行configurationElement,即加载xml的mapper方法,之后执行bindMapperForNamespace,加载对应java mapper的方法

MybatisMapperRegistry

com/baomidou/mybatisplus/core/MybatisMapperRegistry.java

    @Overridepublic <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {// TODO 如果之前注入 直接返回return;// TODO 这里就不抛异常了
//                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {// TODO 这里也换成 MybatisMapperProxyFactory 而不是 MapperProxyFactoryknownMappers.put(type, new MybatisMapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.// TODO 这里也换成 MybatisMapperAnnotationBuilder 而不是 MapperAnnotationBuilderMybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}

MybatisMapperRegistry通过MybatisMapperAnnotationBuilder进行parse

MybatisMapperAnnotationBuilder

com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java

    public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {loadXmlResource();configuration.addLoadedResource(resource);String mapperName = type.getName();assistant.setCurrentNamespace(mapperName);parseCache();parseCacheRef();IgnoreStrategy ignoreStrategy = InterceptorIgnoreHelper.initSqlParserInfoCache(type);for (Method method : type.getMethods()) {if (!canHaveStatement(method)) {continue;}if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()&& method.getAnnotation(ResultMap.class) == null) {parseResultMap(method);}try {// TODO 加入 注解过滤缓存InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, mapperName, method);parseStatement(method);} catch (IncompleteElementException e) {// TODO 使用 MybatisMethodResolver 而不是 MethodResolverconfiguration.addIncompleteMethod(new MybatisMethodResolver(this, method));}}// TODO 注入 CURD 动态 SQL , 放在在最后, because 可能会有人会用注解重写sqltry {// https://github.com/baomidou/mybatis-plus/issues/3038if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {parserInjector();}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new InjectorResolver(this));}}parsePendingMethods();}

这里通过反射获取对应java mapper的方法(这里的顺序是先接口本身定义的方法,然后是逐层继承的接口定义的方法),然后挨个执行parseStatement,接着执行parserInjector来处理内置的通过SqlMethod提供的内置方法

parseStatement

    private static final Set<Class<? extends Annotation>> statementAnnotationTypes = Stream.of(Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class,InsertProvider.class, DeleteProvider.class).collect(Collectors.toSet());void parseStatement(Method method) {final Class<?> parameterTypeClass = getParameterType(method);final LanguageDriver languageDriver = getLanguageDriver(method);getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options) x.getAnnotation()).orElse(null);final String mappedStatementId = type.getName() + StringPool.DOT + method.getName();final KeyGenerator keyGenerator;String keyProperty = null;String keyColumn = null;if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {// first check for SelectKey annotation - that overrides everything elseSelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class).map(x -> (SelectKey) x.getAnnotation()).orElse(null);if (selectKey != null) {keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);keyProperty = selectKey.keyProperty();} else if (options == null) {keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;} else {keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;keyProperty = options.keyProperty();keyColumn = options.keyColumn();}} else {keyGenerator = NoKeyGenerator.INSTANCE;}Integer fetchSize = null;Integer timeout = null;StatementType statementType = StatementType.PREPARED;ResultSetType resultSetType = configuration.getDefaultResultSetType();boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = !isSelect;boolean useCache = isSelect;if (options != null) {if (FlushCachePolicy.TRUE.equals(options.flushCache())) {flushCache = true;} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {flushCache = false;}useCache = options.useCache();fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348timeout = options.timeout() > -1 ? options.timeout() : null;statementType = options.statementType();if (options.resultSetType() != ResultSetType.DEFAULT) {resultSetType = options.resultSetType();}}String resultMapId = null;if (isSelect) {ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);if (resultMapAnnotation != null) {resultMapId = String.join(StringPool.COMMA, resultMapAnnotation.value());} else {resultMapId = generateResultMapName(method);}}assistant.addMappedStatement(mappedStatementId,sqlSource,statementType,sqlCommandType,fetchSize,timeout,// ParameterMapIDnull,parameterTypeClass,resultMapId,getReturnType(method),resultSetType,flushCache,useCache,// TODO gcode issue #577false,keyGenerator,keyProperty,keyColumn,statementAnnotation.getDatabaseId(),languageDriver,// ResultSetsoptions != null ? nullOrEmpty(options.resultSets()) : null);});}

parseStatement这里解析带有Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class, InsertProvider.class, DeleteProvider.class注解的方法,然后通过assistant.addMappedStatement注册到configuration的mappedStatements中,key为statementId(type.getName() + StringPool.DOT + method.getName())

parserInjector

com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java

    void parserInjector() {GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);}

com/baomidou/mybatisplus/core/injector/AbstractSqlInjector.java

    @Overridepublic void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);if (modelClass != null) {String className = mapperClass.toString();Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());if (!mapperRegistryCache.contains(className)) {TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);if (CollectionUtils.isNotEmpty(methodList)) {// 循环注入自定义方法methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));} else {logger.debug(mapperClass.toString() + ", No effective injection method was found.");}mapperRegistryCache.add(className);}}}

com/baomidou/mybatisplus/core/injector/AbstractMethod.java

    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {this.configuration = builderAssistant.getConfiguration();this.builderAssistant = builderAssistant;this.languageDriver = configuration.getDefaultScriptingLanguageInstance();/* 注入自定义方法 */injectMappedStatement(mapperClass, modelClass, tableInfo);}protected MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> parameterType, String id,SqlSource sqlSource, KeyGenerator keyGenerator,String keyProperty, String keyColumn) {return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.INSERT, parameterType, null,Integer.class, keyGenerator, keyProperty, keyColumn);}protected MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource,SqlCommandType sqlCommandType, Class<?> parameterType,String resultMap, Class<?> resultType, KeyGenerator keyGenerator,String keyProperty, String keyColumn) {String statementName = mapperClass.getName() + DOT + id;if (hasMappedStatement(statementName)) {logger.warn(LEFT_SQ_BRACKET + statementName + "] Has been loaded by XML or SqlProvider or Mybatis's Annotation, so ignoring this injection for [" + getClass() + RIGHT_SQ_BRACKET);return null;}/* 缓存逻辑处理 */boolean isSelect = sqlCommandType == SqlCommandType.SELECT;return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType,null, null, null, parameterType, resultMap, resultType,null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,configuration.getDatabaseId(), languageDriver, null);}

这里会通过statementName(mapperClass.getName() + DOT + id)j检测是否存在,如果不存在则添加

org/apache/ibatis/builder/MapperBuilderAssistant.java

  public MappedStatement addMappedStatement(String id,SqlSource sqlSource,StatementType statementType,SqlCommandType sqlCommandType,Integer fetchSize,Integer timeout,String parameterMap,Class<?> parameterType,String resultMap,Class<?> resultType,ResultSetType resultSetType,boolean flushCache,boolean useCache,boolean resultOrdered,KeyGenerator keyGenerator,String keyProperty,String keyColumn,String databaseId,LanguageDriver lang,String resultSets) {if (unresolvedCacheRef) {throw new IncompleteElementException("Cache-ref not yet resolved");}id = applyCurrentNamespace(id, false);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType).resource(resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired(valueOrDefault(flushCache, !isSelect)).useCache(valueOrDefault(useCache, isSelect)).cache(currentCache);ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);if (statementParameterMap != null) {statementBuilder.parameterMap(statementParameterMap);}MappedStatement statement = statementBuilder.build();configuration.addMappedStatement(statement);return statement;}public String applyCurrentNamespace(String base, boolean isReference) {if (base == null) {return null;}if (isReference) {// is it qualified with any namespace yet?if (base.contains(".")) {return base;}} else {// is it qualified with this namespace yet?if (base.startsWith(currentNamespace + ".")) {return base;}if (base.contains(".")) {throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);}}return currentNamespace + "." + base;}

添加的话,最后的id会拼接上当前的namespace

小结

如果mybatis mapper定义了多个同名方法,则启动时不会报错,但是会有error日志告知同名方法被忽略。整体加载顺序是xml的方法优先于java mapper定义的方法,优先于自定义的SqlMethod;而xml或者java mapper方法都是以最先出现的为准。

相关文章:

聊聊mybatis-plus的sql加载顺序

序 本文主要研究一下如果mybatis mapper定义了多个同名方法会不会有问题 MybatisConfiguration com/baomidou/mybatisplus/core/MybatisConfiguration.java /*** MybatisPlus 加载 SQL 顺序&#xff1a;* <p> 1、加载 XML中的 SQL </p>* <p> 2、加载 SqlP…...

基于jeecg-boot的flowable流程审批时增加下一个审批人设置

更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/nbcio-boot 前端代码&#xff1a;https://gitee.com/nbacheng/nbcio-vue.git 在线演示&#xff08;包括H5&#xff09; &#xff1a; http://122.227.135.243:9888 因为有时…...

HTML 与 CSS 有什么区别?

HTML&#xff08;超文本标记语言&#xff09;和 CSS&#xff08;层叠样式表&#xff09;是构建网页的两个核心技术。HTML负责定义网页的结构和内容&#xff0c;而CSS则用于控制网页的样式和布局。虽然它们在构建网页时密切相关&#xff0c;但它们在功能和用途上有明显的区别。 …...

服务器数据恢复-vmware ESXI虚拟机数据恢复案例

服务器数据恢复环境&#xff1a; 从物理机迁移一台虚拟机到ESXI&#xff0c;迁移后做了一个快照。该虚拟机上部署了一个SQLServer数据库&#xff0c;存放了5年左右的数据。ESXI上有数十台虚拟机&#xff0c;EXSI连接了一台EVA存储&#xff0c;所有的虚拟机都在EVA存储上。 服务…...

Rabbitmq的Shovel

Federation 具备的数据转发功能类似&#xff0c; Shovel 够可靠、持续地从一个 Broker 中的队列 ( 作为源端&#xff0c;即source)拉取数据并转发至另一个 Broker 中的交换器 ( 作为目的端&#xff0c;即 destination) 。作为源端的队列和作为目的端的交换器可以同时位于…...

华为手机实用功能介绍

一、内置app介绍 分四块介绍&#xff0c;包括出门款、规划款、工作款和生活款。 出门款&#xff1a;红色框框部分&#xff0c;照镜子化妆/看天气 规划款&#xff1a;黄色框框部分&#xff0c;日程表/计划表/番茄时间/计时 工作款&#xff1a;蓝色框框部分&#xff0c;便笺/录…...

算法题打卡day50-动态规划 | 123.买卖股票的最佳时机III、188.买卖股票的最佳时机IV

123. 买卖股票的最佳时机 III - 力扣&#xff08;LeetCode&#xff09; 状态&#xff1a;查看索引含义和初始化思路后AC。 增加了两次的限制&#xff0c;相应的就是需要考虑的状态改变&#xff0c;具体的索引含义在代码中&#xff1a; class Solution { public:int maxProfit(…...

jvm与锁

今天是《面霸的自我修养》的第二弹&#xff0c;内容是Java并发编程中关于Java内存模型&#xff08;Java Memory Model&#xff09;和锁的基础理论相关的问题。这两块内容的八股文倒是不多&#xff0c;但是难度较大&#xff0c;接下来我们就一起一探究竟吧。 数据来源&#xff…...

零基础安装pycuda

零基础安装pycuda 前言安装Visual Studio安装C/C环境添加环境变量 安装pycuda查看系统位数查看python版本下载whl文件 前言 最近开始学习基于python的cuda编程&#xff0c;记录一下pycuda的安装。 在安装pycuda之前&#xff0c;首先需要有NVIDIA的独立显卡并且要安装CUDA和CUD…...

Streamlit 讲解专栏(十一):数据可视化-图表绘制详解(中)

文章目录 1 前言2 绘制交互式散点图3 定制图表主题4 增强数据可视化的交互性与注释步骤1步骤二 5 结语 1 前言 在上一篇博文《 Streamlit 讲解专栏&#xff08;十&#xff09;&#xff1a;数据可视化-图表绘制详解&#xff08;上&#xff09;》中&#xff0c;我们学习了一些关…...

d3dx9_35.dll丢失怎么解决

今天&#xff0c;我将为大家介绍关于电脑d3dx9_35.dll丢失的4种详细修复方法。希望通过这次分享&#xff0c;能够帮助大家解决在日常工作和生活中遇到的一些问题。 首先&#xff0c;让我们来了解一下d3dx9_35.dll是什么&#xff1f; d3dx9_35.dll是一个非常重要的动态链接库文…...

Ansible自动化运维工具(二)

目录 &#xff08;6&#xff09;copy模块 &#xff08;7&#xff09;file模块 ​编辑​编辑&#xff08;8&#xff09;hostname模块 &#xff08;9&#xff09;ping模块 &#xff08;10&#xff09;yum 模块 &#xff08;11&#xff09;service/system模块 ​编辑 ​…...

uniapp中使用原生canvas标签绘制视频帧来模拟拍照,拍照后将图绘制在另外一个canvas上编辑画图,这样反复操作

uniapp中使用原生canvas标签绘制视频帧来模拟拍照&#xff0c;拍照后将图绘制在另外一个canvas上编辑画图&#xff0c;这样反复操作会导致ios系统上白屏&#xff0c;canvas2d上下文为null,经查阅找到相关资料 IOS 创建Canvas过多导致getContext(‘2d’) 返回null 总 Canvas 内存…...

机器视觉工程师们,学习是工作以外的事情

面试时&#xff0c;领导问你&#xff0c;很多技术问题&#xff0c;你永远的回答是&#xff0c;我可以学。 公司以为你来公司的目标就是学习&#xff0c;学完就跑。 那你进公司的目标到底是什么&#xff1f; 我认为你&#xff0c;你最好想好再回答。 对于每一家公司来说&#xf…...

数据驱动的生活:探索未来七天生活指数API的应用

前言 随着科技的不断发展&#xff0c;数据已经成为我们生活中不可或缺的一部分。从社交媒体上的点赞和分享&#xff0c;到电子邮件和搜索引擎的历史记录&#xff0c;数据正在以前所未有的速度积累。而这些数据的利用不仅仅停留在社交媒体或商业领域&#xff0c;它们还可以为我…...

【数据分享】2006-2021年我国城市级别的集中供热相关指标(免费获取\20多项指标)

《中国城市建设统计年鉴》中细致地统计了我国城市市政公用设施建设与发展情况&#xff0c;在之前的文章中&#xff0c;我们分享过基于2006-2021年《中国城市建设统计年鉴》整理的2006—2021年我国城市级别的市政设施水平相关指标、2006-2021年我国城市级别的各类建设用地面积数…...

2022年研究生数学建模竞赛优秀论文汇总

A题&#xff1a;移动场景超分辨定位问题 参考代码论文1 论文2 论文3 论文4 论文5 论文6 论文7B题&#xff1a; 方形件排样优化与订单组批问题探析 参考代码论文1 论文2 论文3 论文4 论文5 论文6 论文7C题&#xff1a; 汽车制造涂装-总装缓存调序区调度优化问题论文1 论文2 论文…...

阿里云申请免费SSL证书的两种验证方式及配置服务器Tomcat升级HTTPS协议

通用教程&#xff0c;其他服务商的免费 SSL 证书也差不多是这个流程。&#xff08;至少腾讯云的操作步骤和本文是一致&#xff0c;嘻嘻&#xff01;&#xff09; 申请 SSL 证书 首先在阿里云上创建并申请 SSL 证书&#xff0c;之后选择 DNS 验证的方式&#xff0c;一种是手动配…...

SQL Server 和 MySql 语法和关键字的区别

SQL Server 和 MySql 语法和关键字的区别 ——用于SQLServer到MySql的转换 mysql的ifnull()函数对应sql的isnull()函数;mysql的存储过程中变量的定义去掉;mysql的每句结束要用";"SQLServer存储过程的AS在MySql中需要用begin .....end替换字符串连接用concat()函数;…...

2023_Spark_实验三:基于IDEA开发Scala例子

一、创建一个空项目&#xff0c;作为整个项目的基本框架 二、创建SparkStudy模块&#xff0c;用于学习基本的Spark基础 三、创建项目结构 1、在SparkStudy模块下的pom.xml文件中加入对应的依赖&#xff0c;并等待依赖包下载完毕。 在pom.xml文件中加入对应的依赖 ​<!-- S…...

Macro average 和 Weighted average【把每个类别的指标聚合成一个总体指标】

Macro average 和 Weighted average 是评价分类模型性能时常用的两种汇总方法&#xff0c;它们本质上都是把每个类别的指标聚合成一个总体指标&#xff0c;但对数据分布的敏感度不同。针对你的作物病害zero-shot分类项目&#xff0c;它们的作用差异尤其重要。下面详细说明&…...

双风道半导体制冷风扇硬件设计与热管理优化

1. 项目概述桌面制冷风扇V1.0是一款面向便携式个人降温场景的嵌入式硬件系统&#xff0c;其核心设计目标是突破市面常见小型冷风扇的热管理瓶颈。当前主流产品普遍采用单风道结构&#xff0c;将半导体制冷片&#xff08;TEC&#xff09;的冷端与热端气流混合排出&#xff0c;导…...

**发散创新:基于领域驱动设计的API接口优雅重构实践**在现代微服务架构中,API设

发散创新&#xff1a;基于领域驱动设计的API接口优雅重构实践 在现代微服务架构中&#xff0c;API设计早已不是简单的HTTP路由映射&#xff0c;而是承载业务语义、体现系统结构的关键一环。本文将围绕领域驱动设计&#xff08;DDD&#xff09;思想与API设计原则的融合实践&…...

VSCode+Codex插件实战:不用命令行也能玩转Azure GPT-5-codex的3种方法

VSCodeCodex插件实战&#xff1a;不用命令行也能玩转Azure GPT-5-codex的3种方法 在开发者工具生态中&#xff0c;Visual Studio Code&#xff08;VSCode&#xff09;以其丰富的插件系统和高度可定制性&#xff0c;成为现代开发者的首选IDE。而对于那些更倾向于图形界面操作、希…...

Qwen3模型ComfyUI工作流搭建:可视化编排视觉生成任务

Qwen3模型ComfyUI工作流搭建&#xff1a;可视化编排视觉生成任务 你是不是也遇到过这样的场景&#xff1f;拿到一个功能强大的多模态模型&#xff0c;比如Qwen3&#xff0c;知道它能看图、能理解、能生成&#xff0c;但每次想实现一个稍微复杂点的流程&#xff0c;比如“先让模…...

科研翻译新范式:从AI辅助到代码自动化,打造地道英文论文的实践指南

1. 从“人肉翻译”到“人机协同”&#xff1a;我的科研翻译进化史 十年前&#xff0c;我刚读博那会儿&#xff0c;写一篇英文论文简直是扒层皮。那时候的流程&#xff0c;现在回想起来都头皮发麻&#xff1a;打开Word&#xff0c;左边放着中文稿&#xff0c;右边开着Google翻译…...

实战应用:用TranslateGemma处理外文资料,提升学习和工作效率

实战应用&#xff1a;用TranslateGemma处理外文资料&#xff0c;提升学习和工作效率 1. 为什么你需要一个本地翻译引擎 想象一下这个场景&#xff1a;你正在阅读一篇最新的英文技术论文&#xff0c;里面有几个关键段落你反复看了几遍&#xff0c;还是觉得理解得不够透彻。你打…...

Chord - Ink Shadow 开发实战:基于Node.js构建模型API服务

Chord - Ink & Shadow 开发实战&#xff1a;基于Node.js构建模型API服务 如果你手头有一个像Chord - Ink & Shadow这样强大的文本生成模型&#xff0c;但每次使用都得打开命令行&#xff0c;或者想把它集成到自己的应用里却无从下手&#xff0c;这篇文章就是为你准备的…...

亚洲美女-造相Z-Turbo镜像合规认证:通过ISO/IEC 27001信息安全管理初步评估要点

亚洲美女-造相Z-Turbo镜像合规认证&#xff1a;通过ISO/IEC 27001信息安全管理初步评估要点 1. 镜像概述与部署说明 亚洲美女-造相Z-Turbo是基于Z-Image-Turbo模型的LoRA版本&#xff0c;专门针对生成亚洲风格美女图片进行了优化训练。该镜像通过Xinference框架进行部署&…...

BGE-Large-Zh应用场景:政务政策文件语义比对与关键条款定位

BGE-Large-Zh应用场景&#xff1a;政务政策文件语义比对与关键条款定位 1. 项目简介 BGE-Large-Zh是基于FlagEmbedding库和BAAI/bge-large-zh-v1.5模型开发的本地语义向量化工具&#xff0c;专门针对中文语境优化设计。这个工具能够将中文文本转换为高维语义向量&#xff0c;…...