Java使用modbus4j实现modbus tcp通讯

 更新时间:2023年12月27日 10:38:00   作者:左搜  
Modbus是由Modicon(现为施耐德电气公司的一个品牌)在1979年发明的,是全球第一个真正用于工业现场的总线协议,本文主要介绍了java如何使用modbus4j实现modbus tcp通讯,感兴趣的可以了解下

一、什么是modbus

Modbus是由Modicon(现为施耐德电气公司的一个品牌)在1979年发明的,是全球第一个真正用于工业现场的总线协议。

ModBus网络是一个工业通信系统,由带智能终端的可编程序控制器和计算机通过公用线路或局部专用线路连接而成。其系统结构既包括硬件、亦包括软件。它可应用于各种数据采集和过程监控。

ModBus网络只有一个主机,所有通信都由他发出。网络可支持247个之多的远程从属控制器,但实际所支持的从机数要由所用通信设备决定。采用这个系统,各PC可以和中心主机交换信息而不影响各PC执行本身的控制任务。

二、Java实现modbus协议通讯

Java编程中,使用modbus4j实现Java中的modbus协议通讯

modbus4j实现了Java与modbus协议的以下几种通讯方式:

  • modbus TCP/IP通讯
  • modubs UDP/IP通讯
  • modbus RTU/IP通讯

核心依赖:

modbus4j.jar

commons-lang3-3.0.jar

下载地址代码里有

Java读取工具类

package com.leftso.project.demo.modbus4j;

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;

/**
 * modbus通讯工具类,采用modbus4j实现
 * 
 * @author lxq
 * @dependencies modbus4j-3.0.3.jar
 * @website https://github.com/infiniteautomation/modbus4j
 */
public class Modbus4jUtils {
	/**
	 * 工厂。
	 */
	static ModbusFactory modbusFactory;
	static {
		if (modbusFactory == null) {
			modbusFactory = new ModbusFactory();
		}
	}

	/**
	 * 获取master
	 * 
	 * @return
	 * @throws ModbusInitException
	 */
	public static ModbusMaster getMaster() throws ModbusInitException {
		IpParameters params = new IpParameters();
		params.setHost("localhost");
		params.setPort(502);
		//
		// modbusFactory.createRtuMaster(wapper); //RTU 协议
		// modbusFactory.createUdpMaster(params);//UDP 协议
		// modbusFactory.createAsciiMaster(wrapper);//ASCII 协议
		ModbusMaster master = modbusFactory.createTcpMaster(params, false);// TCP 协议
		master.init();

		return master;
	}

	/**
	 * 读取[01 Coil Status 0x]类型 开关数据
	 * 
	 * @param slaveId
	 *            slaveId
	 * @param offset
	 *            位置
	 * @return 读取值
	 * @throws ModbusTransportException
	 *             异常
	 * @throws ErrorResponseException
	 *             异常
	 * @throws ModbusInitException
	 *             异常
	 */
	public static Boolean readCoilStatus(int slaveId, int offset)
			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
		// 01 Coil Status
		BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
		Boolean value = getMaster().getValue(loc);
		return value;
	}

	/**
	 * 读取[02 Input Status 1x]类型 开关数据
	 * 
	 * @param slaveId
	 * @param offset
	 * @return
	 * @throws ModbusTransportException
	 * @throws ErrorResponseException
	 * @throws ModbusInitException
	 */
	public static Boolean readInputStatus(int slaveId, int offset)
			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
		// 02 Input Status
		BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
		Boolean value = getMaster().getValue(loc);
		return value;
	}

	/**
	 * 读取[03 Holding Register类型 2x]模拟量数据
	 * 
	 * @param slaveId
	 *            slave Id
	 * @param offset
	 *            位置
	 * @param dataType
	 *            数据类型,来自com.serotonin.modbus4j.code.DataType
	 * @return
	 * @throws ModbusTransportException
	 *             异常
	 * @throws ErrorResponseException
	 *             异常
	 * @throws ModbusInitException
	 *             异常
	 */
	public static Number readHoldingRegister(int slaveId, int offset, int dataType)
			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
		// 03 Holding Register类型数据读取
		BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
		Number value = getMaster().getValue(loc);
		return value;
	}

	/**
	 * 读取[04 Input Registers 3x]类型 模拟量数据
	 * 
	 * @param slaveId
	 *            slaveId
	 * @param offset
	 *            位置
	 * @param dataType
	 *            数据类型,来自com.serotonin.modbus4j.code.DataType
	 * @return 返回结果
	 * @throws ModbusTransportException
	 *             异常
	 * @throws ErrorResponseException
	 *             异常
	 * @throws ModbusInitException
	 *             异常
	 */
	public static Number readInputRegisters(int slaveId, int offset, int dataType)
			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
		// 04 Input Registers类型数据读取
		BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
		Number value = getMaster().getValue(loc);
		return value;
	}

	/**
	 * 批量读取使用方法
	 * 
	 * @throws ModbusTransportException
	 * @throws ErrorResponseException
	 * @throws ModbusInitException
	 */
	public static void batchRead() throws ModbusTransportException, ErrorResponseException, ModbusInitException {

		BatchRead<Integer> batch = new BatchRead<Integer>();

		batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
		batch.addLocator(1, BaseLocator.inputStatus(1, 0));

		ModbusMaster master = getMaster();

		batch.setContiguousRequests(false);
		BatchResults<Integer> results = master.send(batch);
		System.out.println(results.getValue(0));
		System.out.println(results.getValue(1));
	}

	/**
	 * 测试
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			// 01测试
			Boolean v011 = readCoilStatus(1, 0);
			Boolean v012 = readCoilStatus(1, 1);
			Boolean v013 = readCoilStatus(1, 6);
			System.out.println("v011:" + v011);
			System.out.println("v012:" + v012);
			System.out.println("v013:" + v013);
			// 02测试
			Boolean v021 = readInputStatus(1, 0);
			Boolean v022 = readInputStatus(1, 1);
			Boolean v023 = readInputStatus(1, 2);
			System.out.println("v021:" + v021);
			System.out.println("v022:" + v022);
			System.out.println("v023:" + v023);

			// 03测试
			Number v031 = readHoldingRegister(1, 1, DataType.FOUR_BYTE_FLOAT);// 注意,float
			Number v032 = readHoldingRegister(1, 3, DataType.FOUR_BYTE_FLOAT);// 同上
			System.out.println("v031:" + v031);
			System.out.println("v032:" + v032);

			// 04测试
			Number v041 = readInputRegisters(1, 1, DataType.FOUR_BYTE_FLOAT);//
			Number v042 = readInputRegisters(1, 3, DataType.FOUR_BYTE_FLOAT);//
			System.out.println("v041:" + v041);
			System.out.println("v042:" + v042);
			// 批量读取
			batchRead();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

三、测试

使用ModbusSlave模拟modbus协议

slave中模拟数据如下

运行工具类的main方法:

11:14:54.547 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 01 00 00 00 01 
11:14:54.550 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:54.598 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 01 01 01 
11:14:54.600 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 01 00 01 00 01 
11:14:54.600 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:54.650 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 01 01 00 
11:14:54.652 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 01 00 06 00 01 
11:14:54.652 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:54.703 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 01 01 01 
v011:true
v012:false
v013:true
11:14:54.704 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 02 00 00 00 01 
11:14:54.704 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:54.755 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 02 01 01 
11:14:54.757 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 02 00 01 00 01 
11:14:54.757 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:54.807 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 02 01 00 
11:14:54.810 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 02 00 02 00 01 
11:14:54.810 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:54.860 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 02 01 01 
v021:true
v022:false
v023:true
11:14:54.866 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 03 00 01 00 02 
11:14:54.866 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:54.915 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 07 01 03 04 40 20 00 00 
11:14:54.917 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 03 00 03 00 02 
11:14:54.917 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:54.967 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 07 01 03 04 41 28 00 00 
v031:2.5
v032:10.5
11:14:54.971 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 04 00 01 00 02 
11:14:54.971 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:55.020 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 07 01 04 04 3F C0 00 00 
11:14:55.021 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 04 00 03 00 02 
11:14:55.021 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:55.072 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 07 01 04 04 40 40 00 00 
v041:1.5
v042:3.0
11:14:55.074 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 02 00 00 00 01 
11:14:55.074 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:55.123 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 02 01 01 
11:14:55.125 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 01 00 00 00 06 01 03 00 01 00 02 
11:14:55.125 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502
11:14:55.179 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 01 00 00 00 07 01 03 04 40 20 00 00 
2.5
true

观察输出结果与 slave上的模拟数据一致

四、Java通过modbus4j对数据的写入

Modbus4jWriteUtils.java

package com.leftso.project.demo.modbus4j;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.ModbusResponse;
import com.serotonin.modbus4j.msg.WriteCoilRequest;
import com.serotonin.modbus4j.msg.WriteCoilResponse;
import com.serotonin.modbus4j.msg.WriteCoilsRequest;
import com.serotonin.modbus4j.msg.WriteCoilsResponse;
import com.serotonin.modbus4j.msg.WriteRegisterRequest;
import com.serotonin.modbus4j.msg.WriteRegisterResponse;
import com.serotonin.modbus4j.msg.WriteRegistersRequest;

/**
 * modbus4j写入数据
 * 
 * @author xq
 *
 */
public class Modbus4jWriteUtils {
	static Log log = LogFactory.getLog(Modbus4jWriteUtils.class);
	/**
	 * 工厂。
	 */
	static ModbusFactory modbusFactory;
	static {
		if (modbusFactory == null) {
			modbusFactory = new ModbusFactory();
		}
	}

	/**
	 * 获取tcpMaster
	 * 
	 * @return
	 * @throws ModbusInitException
	 */
	public static ModbusMaster getMaster() throws ModbusInitException {
		IpParameters params = new IpParameters();
		params.setHost("localhost");
		params.setPort(502);

		ModbusMaster tcpMaster = modbusFactory.createTcpMaster(params, false);
		tcpMaster.init();

		return tcpMaster;
	}

	/**
	 * 写 [01 Coil Status(0x)]写一个 function ID = 5
	 * 
	 * @param slaveId
	 *            slave的ID
	 * @param writeOffset
	 *            位置
	 * @param writeValue
	 *            值
	 * @return 是否写入成功
	 * @throws ModbusTransportException
	 * @throws ModbusInitException
	 */
	public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue)
			throws ModbusTransportException, ModbusInitException {
		// 获取master
		ModbusMaster tcpMaster = getMaster();
		// 创建请求
		WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
		// 发送请求并获取响应对象
		WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request);
		if (response.isException()) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * 写[01 Coil Status(0x)] 写多个 function ID = 15
	 * 
	 * @param slaveId
	 *            slaveId
	 * @param startOffset
	 *            开始位置
	 * @param bdata
	 *            写入的数据
	 * @return 是否写入成功
	 * @throws ModbusTransportException
	 * @throws ModbusInitException
	 */
	public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata)
			throws ModbusTransportException, ModbusInitException {
		// 获取master
		ModbusMaster tcpMaster = getMaster();
		// 创建请求
		WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
		// 发送请求并获取响应对象
		WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);
		if (response.isException()) {
			return false;
		} else {
			return true;
		}

	}

	/***
	 * 写[03 Holding Register(4x)] 写一个 function ID = 6
	 * 
	 * @param slaveId
	 * @param writeOffset
	 * @param writeValue
	 * @return
	 * @throws ModbusTransportException
	 * @throws ModbusInitException
	 */
	public static boolean writeRegister(int slaveId, int writeOffset, short writeValue)
			throws ModbusTransportException, ModbusInitException {
		// 获取master
		ModbusMaster tcpMaster = getMaster();
		// 创建请求对象
		WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
		WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);
		if (response.isException()) {
			log.error(response.getExceptionMessage());
			return false;
		} else {
			return true;
		}

	}

	/**
	 * 
	 * 写入[03 Holding Register(4x)]写多个 function ID=16
	 * 
	 * @param slaveId
	 *            modbus的slaveID
	 * @param startOffset
	 *            起始位置偏移量值
	 * @param sdata
	 *            写入的数据
	 * @return 返回是否写入成功
	 * @throws ModbusTransportException
	 * @throws ModbusInitException
	 */
	public static boolean writeRegisters(int slaveId, int startOffset, short[] sdata)
			throws ModbusTransportException, ModbusInitException {
		// 获取master
		ModbusMaster tcpMaster = getMaster();
		// 创建请求对象
		WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);
		// 发送请求并获取响应对象
		ModbusResponse response = tcpMaster.send(request);
		if (response.isException()) {
			log.error(response.getExceptionMessage());
			return false;
		} else {
			return true;
		}
	}

	/**
	 * 写入数字类型的模拟量(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long)
	 * 
	 * @param slaveId
	 * @param offset
	 * @param value
	 *            写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long
	 * @param registerCount
	 *            ,com.serotonin.modbus4j.code.DataType
	 * @throws ModbusTransportException
	 * @throws ErrorResponseException
	 * @throws ModbusInitException
	 */
	public static void writeHoldingRegister(int slaveId, int offset, Number value, int dataType)
			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
		// 获取master
		ModbusMaster tcpMaster = getMaster();
		// 类型
		BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
		tcpMaster.setValue(locator, value);
	}

	public static void main(String[] args) {
		try {
			//@formatter:off
			// 测试01
//			boolean t01 = writeCoil(1, 0, true);
//			System.out.println("T01:" + t01);

			// 测试02
//			boolean t02 = writeCoils(1, 0, new boolean[] { true, false, true });
//			System.out.println("T02:" + t02);

			// 测试03
//			short v = -3;
//			boolean t03 = writeRegister(1, 0, v);
//			System.out.println("T03:" + t03);
			// 测试04
//			boolean t04 = writeRegisters(1, 0, new short[] { -3, 3, 9 });
//			System.out.println("t04:" + t04);
			//写模拟量
			writeHoldingRegister(1,0, 10.1f, DataType.FOUR_BYTE_FLOAT);
			
			//@formatter:on
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

以上就是Java使用modbus4j实现modbus tcp通讯的详细内容,更多关于Java modbus tcp通讯的资料请关注脚本之家其它相关文章!

相关文章

  • SpringBoot如何优雅地处理全局异常详解

    SpringBoot如何优雅地处理全局异常详解

    这篇文章主要给大家介绍了关于SpringBoot如何优雅地处理全局异常的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用SpringBoot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • 利用SpringBoot实现高效的文件分块上传方案

    利用SpringBoot实现高效的文件分块上传方案

    在互联网应用中,大文件上传是一个常见而棘手的挑战,传统的单文件上传方式在面对大文件时经常面临超时、内存溢出等问题,本文将深入探讨如何利用Spring Boot实现高效的分块上传方案,解决大文件传输痛点,需要的朋友可以参考下
    2025-06-06
  • 如何用IDEA+Docker一键部署项目SpringBoot项目

    如何用IDEA+Docker一键部署项目SpringBoot项目

    IDEA作为主流的Java开发环境,通过集成Docker插件,能够极大地简化从代码编写到容器镜像构建、上传及部署的整个流程,这篇文章主要介绍了如何用IDEA+Docker一键部署项目SpringBoot项目的相关资料,需要的朋友可以参考下
    2025-12-12
  • 使用SpringBoot生成war包的流程步骤

    使用SpringBoot生成war包的流程步骤

    一般情况下,在SpringBoot项目开发完成进行服务器部署时,都是打成JAR包进行部署运行的,但是在有些情况下也需要将其打成War包使用Tomcat进行部署,本篇文章就简单介绍一下SpringBoot如何打成War包,需要的朋友可以参考下
    2024-10-10
  • Java中class和Class的区别示例详解

    Java中class和Class的区别示例详解

    class 是java的关键字,在声明java类时使用,Class是java JDK提供的一个类,完整路径为java.lang.Class,下面这篇文章主要给大家介绍了关于Java中class和Class区别的相关资料,需要的朋友可以参考下
    2022-04-04
  • Spring负载均衡LoadBalancer使用详解

    Spring负载均衡LoadBalancer使用详解

    这篇文章主要介绍了Spring负载均衡LoadBalancer使用详解,Spring Cloud LoadBalancer是Spring Cloud官方自己提供的客户端负载均衡器, 用来替代Ribbon,Spring官方提供了两种客户端都可以使用loadbalancer,需要的朋友可以参考下
    2023-11-11
  • Java多线程之Semaphore实现信号灯

    Java多线程之Semaphore实现信号灯

    这篇文章主要给大家分享的是Java多线程之Semaphore实现信号灯的练习,emaphore是计数信号量。Semaphore管理一系列许可证。每个acquire方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;下面一起进入文章学习Semaphore的具体内容
    2021-10-10
  • 完整java开发中JDBC连接数据库代码和步骤

    完整java开发中JDBC连接数据库代码和步骤

    这篇文章主要介绍了完整java开发中JDBC连接数据库代码和步骤,需要的朋友可以参考下
    2015-09-09
  • javacv依赖太大导致jar包也大的解决办法

    javacv依赖太大导致jar包也大的解决办法

    随着项目的复杂度和依赖关系的增加,打包后的JAR包可能会变得很大,这篇文章主要介绍了javacv依赖太大导致jar包也大的解决办法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-09-09
  • springboot整合mybatis plus与druid详情

    springboot整合mybatis plus与druid详情

    这篇文章主要介绍了springboot整合mybatis plus与druid详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的下伙伴可以参考一下
    2022-09-09

最新评论