若依框架实战5分钟搞定SpringBoot多模块开发含完整代码示例你是否曾面对一个日渐臃肿的SpringBoot单体项目感到头疼业务代码、工具类、配置信息混杂在一起每次修改都如履薄冰生怕牵一发而动全身。对于Java开发者而言尤其是那些已经熟悉SpringBoot基础正准备将技术栈应用于更复杂、更规范的企业级项目的朋友如何优雅地组织代码结构实现清晰的职责分离是一个绕不开的课题。今天我们就来深入探讨如何利用国内广受欢迎的开源后台管理系统框架——若依RuoYi在短短几分钟内搭建起一个标准、可扩展的多模块项目骨架并亲手实现一个独立的API接口模块。这不仅仅是添加一个文件夹那么简单而是关乎项目长期可维护性、团队协作效率以及技术债务控制的核心实践。1. 理解若依多模块架构的设计哲学在动手敲代码之前我们有必要先厘清多模块开发的核心价值。若依框架本身就是一个经典的多模块Maven项目典范它并非简单地将不同功能的代码物理隔离而是贯彻了“高内聚、低耦合”的架构思想。传统的单体应用通常将所有层Controller, Service, Mapper, Domain, Config堆叠在一个模块里。当项目规模增长这种结构会导致编译时间漫长任何微小改动都需要重新编译整个项目。依赖管理混乱所有组件共享同一套依赖容易引入不必要的Jar包。部署不灵活无法针对特定功能模块进行独立部署或伸缩。团队协作冲突多人修改同一模块的不同部分合并代码时冲突频发。而若依倡导的多模块架构则通过清晰的模块边界解决了这些问题。其典型结构如下表所示模块名称职责说明关键依赖ruoyi-admin主启动模块。包含SpringBoot启动类、全局配置、过滤器、拦截器等。依赖其他所有业务模块。ruoyi-system系统核心模块。通常包含用户、角色、菜单、部门等基础权限管理功能。依赖ruoyi-common。ruoyi-common通用工具模块。封装了工具类、常量、通用返回对象如AjaxResult、异常处理等。基础工具依赖如Hutool。ruoyi-framework框架支撑模块。包含安全配置Shiro/Spring Security、数据源配置、MyBatis配置等框架级组件。依赖ruoyi-common。你的业务模块(如ruoyi-mall)自定义业务模块。存放你特定业务领域的Controller, Service, Mapper, Domain等。依赖ruoyi-common 可选依赖ruoyi-system。提示理解这张表是成功的第一步。添加新模块的本质就是在若依这个成熟的生态中创建一个与ruoyi-system平级、遵循相同规范的新“业务子系统”。这种设计的精妙之处在于ruoyi-admin像一个容器它聚合了所有业务模块的能力并对外提供统一的服务入口。而各个业务模块则可以独立开发、测试甚至理论上可以被其他项目复用。当你需要开发一套全新的API比如一个电商商品系统或内容管理系统为其创建一个独立模块是最佳选择。2. 五分钟实战创建并集成一个独立API模块假设我们要为一个简单的“横幅广告Banner”管理功能创建一套独立的API。我们将这个新模块命名为module-banner。请跟随以下步骤在IDEA或Eclipse中操作。2.1 第一步在父工程中创建新模块首先确保你已经从若依官网 (https://ruoyi.vip) 下载了RuoYi-Vue前后端分离版本或RuoYi单体版本的源码。这里我们以更主流的RuoYi-Vue后端项目为例。在项目根目录即包含ruoyi-admin,ruoyi-system,ruoyi-common等文件夹的目录上右键。选择New-Module。使用Maven作为构建工具不要选择任何 Archetype项目模板直接下一步。填写模块坐标GroupId:com.ruoyi(建议与父工程保持一致)ArtifactId:ruoyi-banner(命名清晰体现功能)点击完成。此时你的项目结构中会多出一个ruoyi-banner目录。2.2 第二步配置新模块的pom.xml创建完成后IDE会自动生成一个基础的pom.xml。我们需要对其进行关键配置使其融入若依家族。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent !-- 指向若依的父pom继承版本号等统一管理 -- groupIdcom.ruoyi/groupId artifactIdruoyi/artifactId version4.7.5/version !-- 请替换为你的实际版本号 -- /parent artifactIdruoyi-banner/artifactId description横幅广告管理模块/description dependencies !-- 核心依赖若依通用工具包包含AjaxResult、常量、工具类等 -- dependency groupIdcom.ruoyi/groupId artifactIdruoyi-common/artifactId /dependency !-- Spring Web支持通常已通过ruoyi-common间接引入显式声明更清晰 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis支持用于数据库操作 -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId /dependency !-- 数据库驱动根据你的数据库选择 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency /dependencies /project2.3 第三步修改主启动模块的配置新模块不会自动被主应用扫描到我们需要在ruoyi-admin模块中“声明”它。1. 修改ruoyi-admin的pom.xml在dependencies部分添加对新模块的依赖。!-- 在ruoyi-admin的pom.xml中添加 -- dependency groupIdcom.ruoyi/groupId artifactIdruoyi-banner/artifactId version${ruoyi.version}/version !-- 版本号由父pom统一管理 -- /dependency2. 修改ruoyi-admin的启动类RuoYiApplication.java扩大组件扫描的范围使其能扫描到新模块下的Spring组件如Controller,Service,Mapper。package com.ruoyi; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; SpringBootApplication(exclude { DataSourceAutoConfiguration.class }) // 关键使用ComponentScan显式指定扫描包路径包含新模块的包 // “com.ruoyi.*” 会扫描原有所有模块 // “com.ruoyi.banner.*” 扫描我们新建的模块 // 注意若依原配置可能是 ComponentScan({ com.ruoyi.* })需要修改 org.springframework.context.annotation.ComponentScan(basePackages { com.ruoyi.*, com.ruoyi.banner.*}) public class RuoYiApplication { public static void main(String[] args) { SpringApplication.run(RuoYiApplication.class, args); System.out.println(若依启动成功); } }3. 修改MyBatis配置可选但重要在ruoyi-admin的配置文件application.yml中找到MyBatis的配置项将新模块的mapper和domain包路径加入扫描。# MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain,com.ruoyi.banner.domain # 配置mapper的扫描找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 注意如果你的新模块mapper.xml放在自己模块的resources下需要确保路径能被扫描到。 # 通常若依的配置已包含 classpath*:mapper/**/*Mapper.xml这能扫描所有jar包和模块下的对应路径。注意关于ComponentScan的配置不同若依版本可能有差异。有些版本可能通过MapperScan注解在单独的配置类中管理。如果启动后找不到新模块的Bean请检查项目中ApplicationConfig或类似配置类中的扫描路径是否包含了新模块的包。3. 在新模块中实现完整的API接口现在我们开始在ruoyi-banner模块中编写一个完整的、返回横幅列表的API。我们将严格按照若依的代码分层规范domain-mapper-service-controller。3.1 创建领域模型Domain在src/main/java/com/ruoyi/banner/domain/路径下创建Banner.java。package com.ruoyi.banner.domain; import com.fasterxml.jackson.annotation.JsonFormat; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.util.Date; /** * 横幅广告对象 banner */ public class Banner { private static final long serialVersionUID 1L; /** 主键ID */ private Long id; /** 图片URL */ private String imageUrl; /** 跳转链接 */ private String redirectUrl; /** 显示排序值越小越靠前 */ private Integer sortOrder; /** 状态0正常 1停用 */ private String status; /** 创建时间 */ JsonFormat(pattern yyyy-MM-dd HH:mm:ss) private Date createTime; /** 更新时间 */ JsonFormat(pattern yyyy-MM-dd HH:mm:ss) private Date updateTime; /** 备注 */ private String remark; // 以下省略了所有Getter和Setter方法... // 使用IDE的Generate功能或Lombok的Data注解快速生成 Override public String toString() { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) .append(id, getId()) .append(imageUrl, getImageUrl()) .append(sortOrder, getSortOrder()) .append(status, getStatus()) .append(createTime, getCreateTime()) .append(updateTime, getUpdateTime()) .append(remark, getRemark()) .toString(); } }3.2 创建数据访问层Mapper首先创建Mapper接口src/main/java/com/ruoyi/banner/mapper/BannerMapper.javapackage com.ruoyi.banner.mapper; import com.ruoyi.banner.domain.Banner; import java.util.List; /** * 横幅广告Mapper接口 */ public interface BannerMapper { /** * 查询横幅广告列表 * * param banner 查询条件可扩展 * return 横幅广告集合 */ public ListBanner selectBannerList(Banner banner); /** * 根据ID查询横幅广告 * param id 横幅广告ID * return 横幅广告对象 */ public Banner selectBannerById(Long id); }接着在src/main/resources/mapper/banner/路径下创建对应的XML映射文件BannerMapper.xml。?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.ruoyi.banner.mapper.BannerMapper resultMap typeBanner idBannerResult result propertyid columnid/ result propertyimageUrl columnimage_url/ result propertyredirectUrl columnredirect_url/ result propertysortOrder columnsort_order/ result propertystatus columnstatus/ result propertycreateTime columncreate_time/ result propertyupdateTime columnupdate_time/ result propertyremark columnremark/ /resultMap sql idselectBannerVo select id, image_url, redirect_url, sort_order, status, create_time, update_time, remark from sys_banner /sql select idselectBannerList parameterTypeBanner resultMapBannerResult include refidselectBannerVo/ where if testimageUrl ! null and imageUrl ! AND image_url like concat(%, #{imageUrl}, %) /if if teststatus ! null and status ! AND status #{status} /if /where order by sort_order asc, create_time desc /select select idselectBannerById parameterTypeLong resultMapBannerResult include refidselectBannerVo/ where id #{id} /select /mapper3.3 创建业务逻辑层Service创建服务接口src/main/java/com/ruoyi/banner/service/IBannerService.javapackage com.ruoyi.banner.service; import com.ruoyi.banner.domain.Banner; import java.util.List; /** * 横幅广告 服务层 */ public interface IBannerService { /** * 查询横幅广告列表 * param banner 查询条件 * return 结果 */ public ListBanner selectBannerList(Banner banner); }创建服务实现类src/main/java/com/ruoyi/banner/service/impl/BannerServiceImpl.javapackage com.ruoyi.banner.service.impl; import com.ruoyi.banner.domain.Banner; import com.ruoyi.banner.mapper.BannerMapper; import com.ruoyi.banner.service.IBannerService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * 横幅广告 服务层实现 */ Service // 关键注解声明这是一个Spring管理的Bean public class BannerServiceImpl implements IBannerService { Autowired private BannerMapper bannerMapper; Override public ListBanner selectBannerList(Banner banner) { // 这里可以添加业务逻辑比如数据校验、缓存处理等 return bannerMapper.selectBannerList(banner); } }3.4 创建Web控制层Controller最后创建控制器对外提供APIsrc/main/java/com/ruoyi/banner/controller/BannerController.javapackage com.ruoyi.banner.controller; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.banner.domain.Banner; import com.ruoyi.banner.service.IBannerService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * 横幅广告信息 */ RestController RequestMapping(/banner) // 定义API路径前缀为 /banner public class BannerController extends BaseController { // 继承若依提供的BaseController方便使用分页等方法 Autowired private IBannerService bannerService; /** * 获取横幅广告列表 * 访问路径GET /banner/list */ GetMapping(/list) public TableDataInfo list(Banner banner) { startPage(); // 来自BaseController自动处理分页参数 ListBanner list bannerService.selectBannerList(banner); return getDataTable(list); // 返回标准的分页表格数据格式 } /** * 获取所有启用的横幅广告无分页用于前端轮播等 * 访问路径GET /banner/listEnabled */ GetMapping(/listEnabled) public AjaxResult listEnabled() { Banner query new Banner(); query.setStatus(0); // 状态为0-正常 ListBanner list bannerService.selectBannerList(query); return AjaxResult.success(list); } }4. 配置、测试与深度避坑指南代码写完了但要让其正确运行还需要最后几步配置和测试。4.1 配置数据源与SQL脚本确保你的数据库如MySQL中有一张对应的sys_banner表。你可以在ruoyi-admin模块的sql目录下找到项目初始化脚本参照其风格创建自己的建表语句。-- 示例建表语句请根据实际需求调整 DROP TABLE IF EXISTS sys_banner; CREATE TABLE sys_banner ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键, image_url varchar(500) NOT NULL COMMENT 图片地址, redirect_url varchar(500) DEFAULT COMMENT 跳转地址, sort_order int(4) DEFAULT 0 COMMENT 显示顺序, status char(1) DEFAULT 0 COMMENT 状态0正常 1停用, create_time datetime DEFAULT NULL COMMENT 创建时间, update_time datetime DEFAULT NULL COMMENT 更新时间, remark varchar(500) DEFAULT NULL COMMENT 备注, PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4 COMMENT横幅广告表; -- 插入测试数据 INSERT INTO sys_banner VALUES (1, /profile/upload/banner1.jpg, https://example.com/link1, 1, 0, now(), now(), 首页主Banner); INSERT INTO sys_banner VALUES (2, /profile/upload/banner2.jpg, https://example.com/link2, 2, 0, now(), now(), 活动推广);4.2 处理安全拦截Shiro/Security若依集成了权限框架。默认情况下所有API都需要认证。对于你想公开访问的API如上述横幅列表需要在权限配置中将其设置为匿名访问。找到ruoyi-framework模块下的安全配置类例如ShiroConfig.java在配置过滤链的地方添加规则// 在方法 filterChainDefinitionMap.put(...) 的配置中添加 // 对于Shiro filterChainDefinitionMap.put(/banner/listEnabled, anon); // 仅开放 listEnabled 接口 // 或者开放整个 /banner 路径慎用 // filterChainDefinitionMap.put(/banner/**, anon); // 对于Spring Security版本通常在 WebSecurityConfig 中配置 // http.authorizeRequests().antMatchers(/banner/listEnabled).permitAll()...4.3 启动测试与接口验证启动项目运行ruoyi-admin模块下的RuoYiApplication主类。查看日志确保启动日志中没有关于BannerController、BannerServiceImpl等类扫描或注入的错误。接口测试使用 Postman、Swagger若依已集成或浏览器进行测试。访问http://localhost:8080/banner/list?pageNum1pageSize10测试分页列表。访问http://localhost:8080/banner/listEnabled测试公开接口。4.4 高级避坑与最佳实践在实际操作中你可能会遇到一些“坑”。这里分享几个关键点Bean名称冲突这是最经典的错误。绝对不要在多个模块中创建完全同包名同类名的Bean。例如在ruoyi-system和ruoyi-banner中都有一个com.ruoyi.common.utils.StringUtils。Spring启动时会因找到多个相同类型的Bean而报BeanDefinitionOverrideException。解决方案就是确保每个模块的包路径是独立的通常以模块名作为根包的一部分如com.ruoyi.banner.*。配置扫描路径如前所述确保主启动类的ComponentScan或MapperScan注解包含了新模块的包路径。如果新模块的Bean没被扫描到服务就无法注入。资源文件路径Mapper XML文件的位置。若依默认配置classpath*:mapper/**/*Mapper.xml会扫描所有Jar包和模块src/main/resources/mapper/下的XML。确保你的BannerMapper.xml放在ruoyi-banner/src/main/resources/mapper/banner/目录下且文件名与接口名对应。依赖传递在父工程的dependencyManagement中统一管理版本号。子模块声明依赖时如果版本号由父POM管理可以省略version标签。模块间依赖如果你的ruoyi-banner模块需要调用ruoyi-system模块的某个服务如用户服务只需在ruoyi-banner的pom.xml中添加对ruoyi-system的依赖即可。这体现了模块化的优势——清晰的依赖关系。完成以上所有步骤你就成功地在若依框架中孵化了一个全新的、结构清晰的业务模块。整个过程从创建模块到接口测试熟练后完全可以在五分钟内完成核心搭建。这种模式不仅让当前的功能管理井然有序更为未来添加会员模块、订单模块、支付模块铺平了道路。每个模块像乐高积木一样可以独立开发、测试和迭代最终由主应用组装成一个强大的系统。下次当你面对一个新的功能需求时第一反应不再是往哪个现有的Controller里塞方法而是思考“这个功能是否值得我为其创建一个新的模块”