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

Spring Boot 配置多数据源并手动配置事务

Spring Boot 配置多数据源并手动配置事务

  • 一、为什么多数据源需要手动配置?
  • 二、配置多数据源
    • 1. 数据源配置类 (DataSourceConfig)
    • 2. 主数据库 MyBatis 配置类 (PrimaryDbMyBatisConfig)
    • 3. 从数据库 MyBatis 配置类 (SecondaryDbMyBatisConfig)
    • 4. application.yml 配置
    • 5. 使用 @Transactional 指定事务管理器
  • 三、问题说明:will not be managed by Spring
  • 四、总结


在现代企业级应用中,往往会涉及到多个数据库的使用。例如,一个应用可能需要同时操作不同的业务数据库,或分离读写操作。

Spring Boot 提供了对多数据源的支持,但在某些情况下,自动配置可能无法满足复杂的需求。特别是在事务管理方面,当需要在多个数据源间进行操作时,默认的事务管理方式可能不适用,或者无法满足更高的灵活性需求。

因此,手动配置多个数据源并支持事务管理是一个常见的解决方案。

一、为什么多数据源需要手动配置?

  1. 多数据源隔离:

    在微服务架构或者不同模块之间可能会使用不同的数据库,每个数据库有独立的数据源和事务管理。如果使用自动配置的方式,Spring Boot会自动配置一个数据源和一个事务管理器,这可能会导致不同数据源的事务冲突或共享问题。因此,需要手动配置多个数据源和多个事务管理器

  2. 跨数据源的事务管理:

    Spring 默认的事务管理仅适用于单一数据源,如果在一个方法中涉及到多个数据源,Spring并不会自动处理这些数据源之间的事务关系。这时,我们需要显式地配置事务管理器,以便手动控制事务的提交与回滚。

  3. 灵活的事务控制:

    有时我们可能需要在不同的数据源之间手动控制事务,比如先操作主数据源,再操作从数据源,或者在同一个方法中对多个数据源进行操作。这时,手动配置事务管理器可以为我们提供更高的灵活性和控制能力。

二、配置多数据源

1. 数据源配置类 (DataSourceConfig)

这个配置类主要负责配置 primary_db 和 secondary_db 两个数据源,并为它们设置基本的连接属性。

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {/*** 配置主数据库(primary_db)数据源* * 这个方法使用 `@ConfigurationProperties` 注解,从 `application.yml` 配置文件中读取 * `spring.datasource.primary_db` 下的配置,自动配置数据源的连接信息(如 URL、用户名、密码等)。* * @return 配置的主数据库数据源对象*/@Bean(name = "primaryDataSource")@ConfigurationProperties(prefix = "spring.datasource.primary_db")public DataSource primaryDataSource() {return DataSourceBuilder.create().build(); // 创建并返回数据源对象}/*** 配置从数据库(secondary_db)数据源* * 该方法使用 `@ConfigurationProperties` 注解,从 `application.yml` 配置文件中读取 * `spring.datasource.secondary_db` 下的配置,自动配置数据源的连接信息。* * @return 配置的从数据库数据源对象*/@Bean(name = "secondaryDataSource")@ConfigurationProperties(prefix = "spring.datasource.secondary_db")public DataSource secondaryDataSource() {return DataSourceBuilder.create().build(); // 创建并返回数据源对象}}
  • primaryDataSource()secondaryDataSource() 方法用于创建和配置两个数据源。通过 @ConfigurationProperties 注解,Spring 会自动读取配置文件中的数据源信息,并将其与 DataSource 配置绑定。DataSourceBuilder.create().build() 是 Spring Boot 提供的便捷方式来创建数据源实例。

  • @EnableTransactionManagement 启用了事务管理功能,允许你在需要的地方使用 @Transactional 注解来声明事务。

2. 主数据库 MyBatis 配置类 (PrimaryDbMyBatisConfig)

这个配置类负责配置与主数据库(primary_db)相关的 MyBatis 组件。它包含了 MyBatis 的 SqlSessionFactory 和事务管理器配置。

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.cx.mail.mapper.primary_db", sqlSessionFactoryRef = "primaryDbSqlSessionFactory")
public class PrimaryDbMyBatisConfig {@Autowired@Qualifier("primaryDataSource")private DataSource primaryDataSource;/*** 配置主数据库的 MyBatis SqlSessionFactory* * 创建并配置 MyBatis 的 `SqlSessionFactory`,它是 MyBatis 用来与数据库交互的核心组件。* 在这个配置中,我们指定了使用的 `primaryDataSource` 数据源。* * @return 配置好的 SqlSessionFactory 对象* @throws Exception 可能抛出异常,例如创建 `SqlSessionFactory` 时出错*/@Beanpublic SqlSessionFactory primaryDbSqlSessionFactory() throws Exception {MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();factory.setDataSource(primaryDataSource); // 设置使用的主数据库数据源// 配置全局设置,主要是 MetaObjectHandler,用于自动填充字段GlobalConfig globalConfig = GlobalConfigUtils.defaults();globalConfig.setMetaObjectHandler(new EntityAutoFillHandler()); // 自动填充创建时间和更新时间factory.setGlobalConfig(globalConfig);// 配置 MyBatis Plus 插件MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 添加分页插件factory.setPlugins(interceptor);// 配置 MyBatis 基本设置MybatisConfiguration configuration = new MybatisConfiguration();configuration.setMapUnderscoreToCamelCase(true); // 将数据库字段下划线命名转换为 Java 类的驼峰命名法configuration.setLogImpl(StdOutImpl.class); // 配置 SQL 打印factory.setConfiguration(configuration);// 配置 MyBatis Mapper XML 文件的位置factory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/primary_db/*.xml"));return factory.getObject(); // 返回创建的 SqlSessionFactory}/*** 配置主数据库的事务管理器* * 该方法创建一个 `DataSourceTransactionManager`,它是 Spring 提供的事务管理器,用来处理主数据库的事务。* * @param dataSource 主数据库的数据源* @return 配置好的事务管理器*/@Primary@Bean(name = "primaryDbTransactionManager")public DataSourceTransactionManager primaryDbTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource); // 返回主数据库的事务管理器}
}
  • primaryDbSqlSessionFactory() 方法配置了主数据库的 SqlSessionFactory,并指定了与该数据库交互时的 MyBatis 配置。

  • primaryDbTransactionManager() 方法创建了主数据库的事务管理器(DataSourceTransactionManager),使得我们可以通过 @Transactional 注解来管理与主数据库相关的事务。

3. 从数据库 MyBatis 配置类 (SecondaryDbMyBatisConfig)

这个配置类与主数据库配置类似,但是它专门配置与从数据库(secondary_db)相关的 MyBatis 组件。

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.cx.mail.mapper.secondary_db", sqlSessionFactoryRef = "secondaryDbSqlSessionFactory")
public class SecondaryDbMyBatisConfig {@Autowired@Qualifier("secondaryDataSource")private DataSource secondaryDataSource;/*** 配置从数据库的 MyBatis SqlSessionFactory* * 创建并配置从数据库的 `SqlSessionFactory`,它将用于从数据库的 MyBatis 操作。* * @return 配置好的 SqlSessionFactory 对象* @throws Exception 可能抛出异常,例如创建 `SqlSessionFactory` 时出错*/@Bean(name = "secondaryDbSqlSessionFactory")public SqlSessionFactory secondaryDbSqlSessionFactory() throws Exception {MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();factory.setDataSource(secondaryDataSource); // 设置使用的从数据库数据源// 配置全局设置,主要是 MetaObjectHandler,用于自动填充字段GlobalConfig globalConfig = GlobalConfigUtils.defaults();globalConfig.setMetaObjectHandler(new EntityAutoFillHandler());factory.setGlobalConfig(globalConfig);// 配置 MyBatis Plus 插件MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 添加分页插件factory.setPlugins(interceptor);// 配置 MyBatis 基本设置MybatisConfiguration configuration = new MybatisConfiguration();configuration.setMapUnderscoreToCamelCase(true); // 设置驼峰命名法configuration.setLogImpl(StdOutImpl.class); // 配置 SQL 打印factory.setConfiguration(configuration);// 设置 Mapper XML 文件的位置factory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/secondary_db/*.xml"));return factory.getObject(); // 返回创建的 SqlSessionFactory}/*** 配置从数据库的事务管理器* * 该方法创建一个 `DataSourceTransactionManager`,它是 Spring 提供的事务管理器,用来处理从数据库的事务。* * @param dataSource 从数据库的数据源* @return 配置好的事务管理器*/@Bean(name = "secondaryDbTransactionManager")public DataSourceTransactionManager secondaryDbTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource); // 返回从数据库的事务管理器}
}
  • secondaryDbSqlSessionFactory()secondaryDbTransactionManager() 方法与主数据库的配置类类似,但它们专门用于配置和管理从数据库的 MyBatis 和事务。

4. application.yml 配置

# 本地库
spring:# 数据源配置 mysqldatasource:primary_db:url: jdbc:mysql://localhost:3306/primary_dbusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driversecondary_db:url: jdbc:mysql://localhost:3306/secondary_dbusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
  • 配置了 primary_dbsecondary_db 两个数据源的连接信息(如 URL、用户名、密码等),这些配置将被数据源配置类读取。

5. 使用 @Transactional 指定事务管理器

在多个数据源配置好之后,需要手动管理事务,确保每个数据源的操作在不同的事务上下文中进行。

使用 @Transactional 指定事务管理器,应该加在需要使用该事务管理器进行事务管理的 服务层方法或类 上。通过 @Transactional 注解来指定使用哪个事务管理器。这样,Spring 会使用指定的事务管理器来管理该方法的事务。

@Service
@Transactional(transactionManager = "primaryDbTransactionManager")  // 显式指定事务管理器
public class AdminServiceImpl implements AdminService {@Autowiredprivate FakeDomainMapper fakeDomainMapper;@Autowiredprivate FakeUserService fakeUserService;/*** 新增域名*/@Overridepublic void addFakeDomain(String domain, String domainDescription) {String loginUsername = FakeSecurityUtil.getUsername();if (!fakeUserService.isFakeUserAdmin(loginUsername)) {throw new FakeServiceException("FORBIDDEN");}FakeDomainEntity fakeDomainEntity = new FakeDomainEntity();fakeDomainEntity.setFakeDomain(domain);if (domainDescription != null) {fakeDomainEntity.setFakeDescription(domainDescription);}fakeDomainEntity.setFakeCreated(new Date());fakeDomainEntity.setFakeModified(new Date());// 设置默认的虚拟配额String fakeDefaultSettings = "default_user_quota:2048;";fakeDomainEntity.setFakeSettings(fakeDefaultSettings);fakeDomainMapper.insertFakeDomain(fakeDomainEntity);}
}
  • @Transactional(transactionManager = "primaryDbTransactionManager") 使得 addFakeDomain 方法使用 primaryDbTransactionManager 来管理事务,这样该方法的事务操作会应用到 primaryDataSource 数据源上。

三、问题说明:will not be managed by Spring

在使用 @Transactional 注解时,如果有多个事务管理器(例如多数据源配置),需要特别注意 指定事务管理器

否则,Spring 无法自动决定使用哪个事务管理器来管理当前方法的事务,可能导致事务 无法被 Spring 管理,从而出现错误或者无法保证事务的一致性。

如果没有指定正确的事务管理器,Spring 可能会抛出类似如下的错误:

在这里插入图片描述

这个问题通常出现在以下两种情况下:

  1. 多个数据源
    应用程序配置了多个数据源,每个数据源有自己的事务管理器。Spring 可能不知道应该使用哪个事务管理器来管理当前方法的事务。

  2. 未指定事务管理器
    在有多个事务管理器的环境下,@Transactional 默认使用主数据源的事务管理器。如果你没有显式指定事务管理器,可能会导致事务无法被正确管理,或者使用错误的事务管理器。


四、总结

通过上述配置,系统能够实现同时操作两个不同的数据源,每个数据源有独立的事务管理、MyBatis 配置等,确保了在进行跨库操作时能够稳定运行。每个模块的配置都进行了明确的注释,方便维护和修改。

  • 在有 多个数据源多个事务管理器 的情况下,必须显式指定 transactionManager,否则事务将不会被 Spring 管理,可能导致错误。

  • 使用 @Transactional(transactionManager = "primaryDbTransactionManager") 来明确指定事务管理器。

  • 默认情况下,如果没有显式指定事务管理器,Spring 只会使用主数据源的事务管理器,其他数据源的事务管理器将无法生效。

  • 通过正确的事务管理器配置,可以确保每个方法或服务层类的事务控制是正确的,从而避免由于事务管理不当导致的数据库操作异常或数据不一致问题。

相关文章:

Spring Boot 配置多数据源并手动配置事务

Spring Boot 配置多数据源并手动配置事务 一、为什么多数据源需要手动配置?二、配置多数据源1. 数据源配置类 (DataSourceConfig)2. 主数据库 MyBatis 配置类 (PrimaryDbMyBatisConfig)3. 从数据库 MyBatis 配置类 (SecondaryDbMyBatisConfig)4. application.yml 配…...

YashanDB 23.2 YAC 共享集群部署和使用自带YMP迁移工具进行数据迁移,效果很city

1. 环境准备 本文以经典架构(2 台服务器,1 共享存储且包含 3 个及以上 LUN)为例,搭建双实例单库的共享集群环境。 主机名 IP 版本 CPU 内存 硬盘 用途 yac1 192.168.50.60 Kylin-Server-V10-SP3 4C 8G 100G YAC 集群…...

【数学】矩阵的逆与伪逆 EEGLAB

文章目录 前言matlab代码作用EEGLAB 中的代码总结参考文献 前言 在 EEGLAB 的使用中,运行程序时出现了矩阵接近奇异值,或者缩放错误。结果可能不准确。RCOND 1.873732e-20 的 bug,调查 EEGLAB 后发现是 raw 数据的问题。 matlab代码 A_1 …...

狐猬编程 C++ L3 第7课 字符串入门 元音字母

给你一个所有字符都是字母的字符串, 请输出其中元音字母的个数。(提示: 二十六个字母中的五个元音字母是 a, e, i, o, u; 所有字符有大小写区别。) 输入格式 仅一行, 包…...

APP UI自动化测试的思路小结

在移动互联网飞速发展的今天,APP质量直接影响用户体验。为了保障UI功能的稳定性和一致性,APP UI自动化测试已经成为各大企业必不可少的一环。那么如何设计一套高效的测试方案?本篇为你总结关键思路! 如何从零构建UI自动化测试&am…...

2412d,d的7月会议

原文 总结 卡斯滕 Carsten说,Decard一直在大量试验WebAssembly.他们一直在把d运行时挖出来,直到它工作.他们在浏览器中运行了一些库函数,并试了不同虚机. 他们在移动方面遇见了很多问题,因为不同芯片按不同方式工作.他们想让他们的整个SDK在WASM上运行,但可能需要一年时间才…...

ANOMALY BERT 解读

出处: ICLR workshop 2023 代码:Jhryu30/AnomalyBERT 可视化效果: 一 提出动机 动机:无监督 TSAD 领域内,“训练集” 也缺失:真值标签(GT);换句话说,一个…...

定时/延时任务-Netty时间轮源码分析

文章目录 1. 概要2. 参数3. 构造器4. 回收5. 启动时间轮 - start6. 停止时间轮 - stop7. 添加任务8. 工作线程 - Worker8.1 线程参数8.2 核心逻辑-run8.3 指针跳动到下一个tick8.4 处理要取消的任务8.5 把新增的任务加入时间轮8.6 执行过期任务 9. HashedWheelTimeout9.1 属性9…...

React的一些主要优点是?

React 一些主要的优点: 组件化架构: React 通过组件化的方式构建 UI,允许开发者将复杂的应用拆分成可重用的小部分。这使得代码更加模块化和可维护。 虚拟 DOM: React 使用虚拟 DOM 来提高性能。它通过在内存中维护一个与应用状态…...

RabbitMQ 基本使用方法详解

RabbitMQ 基本使用方法 在你的代码中,涉及到了 RabbitMQ 的基本使用,包括队列定义、交换机的配置、消息的发送与接收等内容。下面我将详细总结 RabbitMQ 的基本使用方法,重点解释如何在 Spring Boot 项目中与 RabbitMQ 集成。 1. 引入依赖 …...

[leetcode100] 101. 对称二叉树

https://leetcode.cn/problems/symmetric-tree/description/?envTypestudy-plan-v2&envIdtop-100-liked 心血来潮,突然感觉很久没做leetcode,刷一题。 看到“简单”,哦吼,应该很快吧。 结果真是《简单》 题目描述 给你一个…...

Vue.createApp的对象参数

目录 template 属性 data 属性 methods 属性 疑问 function 函数的两种写法 methods 属性中 this 的指向 总结 Vue 实例是通过 Vue.createApp() 创建的,该函数需要接收一个对象作为参数,该对象可添加 template、data、methods 等属性。 template …...

短信验证码burp姿势

首先声明,本文仅仅作为学习使用,因个人原因导致的后果,皆有个人承担,本人没有任何责任。 在之前的burp学习中,我们学习了图片验证码的突破,但是现实中还有很多短信验证码,在此我介绍几种短信验…...

ubuntu WPS安装

需要进入国外官网下载 [OFFICIAL] WPS Office-Free Office Download for PC & Mobile, AI-Powered Office Suite 安装 sudo dpkg -i wps-office_11.1.0.11723.XA_amd64.deb 提示缺失字体操作 下载字体包 链接: https://pan.baidu.com/s/1EVzb3F8Ry_dJ_hj0A4MksQ 提取…...

中粮凤凰里共有产权看房记

中粮凤凰里看房是希望而来,失望而归。主要是对如下失望,下述仅个人看房感受: 1. 户型不喜欢:三房的厨房和餐厅位置很奇葩 2. 样板间在25楼:湖景一言难尽和有工厂噪声 3. 精装修的交房质量:阳台的推拉门用料很草率 …...

学习笔记068——Hibernate框架介绍以及使用方法

文章目录 一、如何使用二、具体操作1、创建 Maven 工程,pom.xml2、hibernate.cfg.xml3、创建实体类4、创建实体关系映射文件5、实体关系映射文件注册到 Hibernate 的配置文件中。6、使用 Hibernate API 完成数据操作。7、pom.xml 中需要配置 resource 三、Hibernate…...

Maven 安装配置(详细教程)

文章目录 一、Maven 简介二、下载 Maven三、配置 Maven3.1 配置环境变量3.2 Maven 配置3.3 IDEA 配置 四、结语 一、Maven 简介 Maven 是一个基于项目对象模型(POM)的项目管理和自动化构建工具。它主要服务于 Java 平台,但也支持其他编程语言…...

虚幻开发中的MYPROJECTFORPLUG_API

百度网盘-免费云盘丨文件共享软件丨超大容量丨存储安全 在虚幻引擎5(Unreal Engine 5)中,以及许多其他使用C的项目中,__declspec(dllexport) 和 __declspec(dllimport) 是用来处理动态链接库(DLL)的宏定义…...

顺序栈及其实现过程

目录 一、概念 二、顺序栈 2.1顺序栈的结构模型 2.2顺序栈的实现 2.2.1创建 2.2.2判断栈是否为空 2.2.3判断栈是否为空 2.2.4入栈 2.2.5出栈 2.2.6查看栈顶 2.2.7清空栈 2.2.8释放栈 一、概念 栈是限制在某一端进行插入、删除操作的线性表,俗称堆栈&…...

内圆弧转子泵绘制工具开发

接着上期的Gerotor 泵的话题继续。最近有小伙伴找我开发一个内圆弧摆线泵的计算绘制工具,也就是把上次计算绘制的过程做成一个桌面应用工具,这样用起来会更方便、效率更高。那究竟是什么样的工具呢?一起来看看: 前面不是已经有了上…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

GitHub 趋势日报 (2025年06月08日)

📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践

前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...