
简介面向Java毕业设计或课程设计场景这是一套基于SSM框架与Vue前后端分离的广告管理系统项目。系统按业务角色划分为管理员、广告客户、广告商、广告制作商四大端覆盖广告投放渠道管理、广告需求与提案流转、设计预览与调整、签署管理及结算等完整业务流程适合作为计算机类毕业设计选题参考或项目学习模板。资源包共540个文件压缩后约83.42MB包含Java后端源码、Vue前端页面、SQL数据库脚本、XML与yml配置以及svg、jpg、png等UI素材并附带环境安装与启动的bat脚本和项目工程配置文件便于本地还原与二次开发。另含演示视频可快速理解系统运行效果。资源已有150人浏览学习对需要快速搭建广告管理类项目或借鉴SSMVue整合思路的开发者具有一定参考价值。1. 基于SSM框架的广告管理系统一个能直接答辩的毕业设计到底长什么样不少人解压这类资源包后的第一反应是盯着文件列表发呆十几个文件夹、一个 .sql 脚本不知道从哪看起。基于SSM框架的广告管理系统是典型的完整毕业设计项目——广告主、广告位、广告订单、审核、权限、曝光与点击统计功能齐全到能答辩代码量又适合逐行读懂。它适合两类人一类是要交毕业设计或课程设计、想找可靠骨架做二次开发的学生另一类是刚学完JavaWeb、想搞明白Spring、SpringMVC、MyBatis三层到底怎么串起来的新手。下面按从解压到跑通、再到答辩改造的路径把架构、配置、核心流程和坑一次讲透。2. 为什么毕业设计选 SSM 而不是 Spring Boot技术栈拆解与选型逻辑2.1 Spring、SpringMVC、MyBatis 在广告系统里各自管什么先解决最基础的问题SSM 到底是哪三样它们在广告系统里各干各的活。很多人把这三个框架当成三座孤岛去学其实在项目里它们是一条流水线上的三个岗位边界非常清楚。Spring 管对象。广告服务类、用户服务类、数据源、事务管理器这些对象的创建和依赖注入都由 Spring 的 IoC 容器接管。你在源码里基本找不到 new AdService() 这种写法常见的是在 Controller 里写一个 Autowired 字段把 Service 依赖拿进来。好处是业务代码不关心对象怎么创建以后换实现类只改配置或注解不动调用方这就是控制反转带来的直接效果。SpringMVC 管请求。浏览器发出的 HTTP 请求先到 DispatcherServlet它根据 URL 匹配到某个 Controller 方法Controller 调 ServiceService 调 Mapper最后通过视图解析器把结果渲染成 JSP 页面。这个系统里你在浏览器看到的登录页、广告位列表、新增表单、审核按钮的每一次响应都是这一层在分发和处理。MyBatis 管数据库。它负责把 Mapper 接口和 XML 里的 SQL 绑定起来。比如“查询某个广告位下面所有状态正常的广告”这种 SQL写在 XML 里MyBatis 把结果集自动映射成 Java 对象反过来Java 对象的属性也能通过 #{...} 写到 SQL 里。复杂查询都集中在这里这也是后面做统计报表时最能发挥的地方。把三者串成一条链路看JSP 表单提交 → SpringMVC 的 Controller 接收 → Spring 管理的 Service 做业务判断 → MyBatis 的 Mapper 执行 SQL → MySQL 落库并返回结果。三个框架各管一段谁也不越界。答辩时被问到“SSM 怎么协作”把这条链路讲清楚比背十遍概念都管用。2.2 三层结构与典型包划分一眼看穿这套代码的骨架导入项目之后第一件事不是读代码而是看 src/main 下的包结构。常见划分是 controller、service、mapper、entity 四个包加上 resources 下的 mapper XML 和 Spring 配置文件。这套划分就是标准的三层架构表现层、业务层、持久层。| 包/目录 | 职责 | 在广告系统里的典型类 | | controller | 接收请求、做参数校验、决定返回哪个视图 | AdController、UserController | | service | 业务规则、事务控制、状态流转 | AdService、OrderService | | mapper/dao | 定义数据库操作方法 | AdMapper、OrderMapper | | entity/pojo | 对应数据库表的 Java 对象 | Advertisement、AdPosition、User | | resources/mapper | 存实际的 SQL 语句 | AdMapper.xml、UserMapper.xml | | resources/*.xml | Spring、MyBatis 的配置 | applicationContext.xml、spring-mvc.xml |这个表看着简单但它是整个项目的索引。一旦你能把任意一个类快速放进表格对应的格子里读代码的速度会明显提升。比如看到 AdController第一反应就应该是“表现层只负责接收和转发不写业务逻辑”看到 XML 里的 SQL就明白持久层的活在这个文件里。至于为什么这类毕设选 SSM 而不是更时髦的 Spring Boot我说句实话不是 SSM 比 Boot 好而是教程和资源包里沉淀下来的旧项目大量是 SSM。SSM 的配置是显式的每个 Bean 注册、每个映射、每个拦截器都写在 XML 里明明白白特别适合答辩现场讲“原理”Spring Boot 把配置自动化了反而容易让新手面对“DispatcherServlet 从哪来的”这类问题答不上来。如果你的目标是快速出活Boot 更省事如果目标是弄懂 JavaWeb 的底层协作方式这套项目的学习密度更高。提示判断一个项目是 SSM 还是 SSHStruts2 Hibernate不用看标题直接翻 pom.xml 里有没有 mybatis 相关依赖、resources 下有没有大量 *Mapper.xml 就够了。2.3 拿到手先读哪几个文件理解一套陌生代码的阅读顺序陌生项目最怕从头到尾读两小时过去还停在第一个类。我一直在用的做法是按“入口到链路”的顺序读具体五步pom.xml 看依赖版本和项目年代web.xml 看总入口DispatcherServlet 映射路径、字符编码过滤器、Spring 配置文件加载位置都在这spring-mvc.xml 看组件扫描范围、视图解析器前缀后缀、静态资源放行规则jdbc.properties 看数据库连接这是跑通前必改的文件最后选一条业务主链读代码比如广告审核从 Controller 进 Service 再进 Mapper 的 XML把一条链路读通。这个顺序比按文件夹从上往下刷快得多。尤其是答辩前临时拿到一个陌生项目“先看入口再顺链路”基本是标准动作。读完一条链路后你会发现其他模块只是换了业务名词骨架完全一致剩下的都是体力活。3. 从 .rar 到第一个页面环境配置与导入运行全流程3.1 JDK、Tomcat、MySQL 版本怎么定这类项目绝大多数按 JDK 1.8 编写运行时环境也按 1.8 配最稳。有人图新鲜装了 JDK 17 甚至 21老项目里如果用了 CGLIB 代理或旧版 Tomcat可能蹦出一堆看似莫名其妙、实则版本不兼容的报错。所以我的习惯是用一条经验值表格直接锁死| 组件 | 常见搭配 | 关键注意点 | | JDK | 1.8 | 兼容性最好答辩环境普遍可用 | | Tomcat | 8.5 或 9.0 | 不要上 10 以上包名已改成 jakarta老代码会编译不过 | | MySQL | 5.7 或 8.0 | 8.0 的连接串必须带时区和公钥参数 | | IDE | 近年稳定版即可 | 安装时勾选 Maven 支持 |这个组合能覆盖市面上九成 SSM 毕业设计。如果打开 pom.xml 发现 Spring 版本还在 3.x 时代连 JDK 1.7 写法都有不用慌把 spring 相关依赖统一升到 4.3 或 5.x 通常不用改代码主要是命名空间版本号不同。参数说明放在连接串这里MySQL 8.0 最典型的问题是驱动加载和时区。连接串写 jdbc:mysql://localhost:3306/ad_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue。useSSLfalse 是关闭安全套接字减少握手报错serverTimezone 是 8.0 必须指定的时区allowPublicKeyRetrievaltrue 专门解决 8.0 的 RSA 公钥获取失败问题。这三项缺哪个启动阶段都会给出让人摸不着头脑的报错。3.2 导入 IDE 与数据库脚本执行第一步是解压。把 .rar 解压到一个不含中文和空格的路径比如 D:\workspace\ad-system。这一步看着没技术含量但确实是很多人的第一个坑路径带中文时Tomcat 有时候能启动页面上的静态资源和文件上传功能却会诡异报错排查半天才发现是路径编码问题。解压后确认是不是 Maven 项目。最常见的做法是 IDE 里 File - Open 直接选 pom.xml让 IDE 以 Maven 项目方式导入然后等依赖下载。如果你习惯用命令行验证依赖可以先跑两个命令# 在项目根目录执行依次验证 Maven 环境和依赖解析 mvn -v mvn clean compile -DskipTeststruemvn -v 确认本机 Maven 版本和 Java 版本mvn clean compile 清理 target 目录后做一次编译验证。这一步通过说明依赖基本完整如果报红或失败优先检查 Maven 仓库镜像是否配置以及网络是否把某些 jar 卡在半路。数据库脚本一般叫 ad_system.sql 或 ad_manage.sql 之类包含建库、建表和初始数据。在终端里用一行命令导入# 把脚本文件导入 MySQLroot 密码按实际修改 mysql -uroot -p ad_system.sql导入前先看一眼脚本头部有没有 CREATE DATABASE 语句。如果有要确认本机不存在同名库如果没有就先手动建库再导入。我更常用的方式是进入 MySQL 后执行 SOURCE 命令因为 SOURCE 的编码行为比部分图形客户端更可控不容易出现中文乱码-- 手动建库并导入脚本 CREATE DATABASE IF NOT EXISTS ad_system DEFAULT CHARACTER SET utf8mb4; USE ad_system; SOURCE D:/workspace/ad-system/ad_system.sql;注意这里的库名要和后面 jdbc.properties 里 jdbc.url 的路径保持一致。这一步的失误会在启动时以“Unknown database”报错的形式告诉你属于能定位但很浪费时间的低级错误。3.3 改 jdbc.properties 并启动 Tomcat第一次跑起来的完整动作数据库恢复完成后找到 src/main/resources 下的 jdbc.properties这是系统连数据库的钥匙。典型内容如下jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/ad_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456实际要改的只有两处jdbc.url 里的库名 ad_system 和 jdbc.password。如果你用的 MySQL 是 8.x驱动类建议换成 com.mysql.cj.jdbc.Driver这是 MySQL 8 的新驱动全名老驱动虽然能用但控制台会一直打 deprecated 警告。jdbc.username 不要硬套 root以你本机 MySQL 的实际账号为准。改完配置就可以启动 Tomcat。IDE 里新增 Tomcat ServerDeployment 中把项目以 war exploded 方式部署Application context 填 /ad这样访问路径就是 http://localhost:8080/ad。需要注意两点一是 Server 页的 JRE 下拉框要选 1.8否则用默认高版本 JDK 编译会有兼容风险二是本机 8080 端口若被其他服务占用就把 HTTP port 改成 8081别浪费时间找幽灵进程。注意启动过程如果报 “Error during artifact deployment”通常是 Tomcat 版本和项目编译级别不匹配或部署配置里没带上 Maven 依赖。这类问题九成出在配置而不是代码。3.4 启动成功后的验证清单别急着高兴看到 Tomcat 日志输出“Server startup in xxx ms”只说明进程起来了不代表系统可用。建议按下面这张清单逐项走一遍| 验证项 | 操作 | 预期结果 | | 登录页 | 访问 http://localhost:8080/ad/login | 正常渲染验证码可显示 | | 登录 | 输入初始账号 | 跳转主页无 500 报错 | | 广告位列表 | 打开对应菜单 | 表格出现初始数据 | | 新增一条广告位 | 提交表单 | 刷新后出现新记录且中文不乱码 | | 越权访问 | 不登录直接访问管理页 | 被拦截器重定向回登录页 |初始账号在哪看一般在数据库脚本的插入语句里或者项目文档里。如果你拿到手没有文档就去 SQL 脚本里搜 INSERT INTO t_user 或 sys_user密码通常以 MD5 形式存储md5(123456) 这类值是常见默认。这一套走通系统才算真正落到你手里。4. 广告管理系统的核心流程是怎么串起来的4.1 三张主表怎么设计广告位置、广告、点击日志的关系先看数据库核心表。广告系统最核心的数据模型是“广告位-广告-点击日志”三条线。广告位是售卖的资源比如首页轮播图、侧边栏横幅定义好尺寸和价格广告是广告主投放到某个位上的具体内容点击日志是每次曝光点击的行为记录用来做统计。-- 广告位表定义可投放的资源 CREATE TABLE ad_position ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT 广告位名称, width INT COMMENT 宽度px, height INT COMMENT 高度px, price DECIMAL(10,2) COMMENT 每日投放价格, status TINYINT DEFAULT 1 COMMENT 状态1启用 0停用 ); -- 广告表广告主提交的投放内容 CREATE TABLE ad ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL COMMENT 广告标题, position_id INT COMMENT 投放的广告位, advertiser_id INT COMMENT 归属广告主, start_date DATE, end_date DATE, status TINYINT DEFAULT 0 COMMENT 0待审核 1通过 2驳回 3下线 );这段 DDL 里有两个值得注意的设计细节所有关联都用 xxx_id 的整型外键持有而不是直接存名称字符串目的是保持数据一致性和后续联表查询的能力状态字段全用 TINYINT 数字不用字符串因为字符串状态下“待审核”和“已下线”很容易因拼写不一致产生脏数据。对应的实体类通常长这样public class Ad { private Integer id; private String title; private Integer positionId; private Integer advertiserId; private Date startDate; private Date endDate; private Integer status; // 用于联表后直接承载关联对象避免二次查询 private AdPosition position; }这个实体类里的 AdPosition 字段是联表查询的关键。当 Mapper 的 XML 用了 join 查询时resultMap 可以把结果映射进这个嵌套对象前端展示时直接 ad.position.name 就能拿到广告位名称不用再单独查一次。4.2 广告审核的状态流转一个能被写进论文的细节广告主提交广告后不能直接上线要经过管理员审核。这个流程看起来只是改一个状态值但实现时有个容易翻车的细节并发重复审核。两个管理员同时打开同一条待审核广告一个点了通过另一个点了驳回。如果没有约束后提交的人会覆盖先提交的结果最终状态和操作日志对不上。解决方式是在 UPDATE 语句里带上当前状态作为条件// 审核广告只有状态等于 fromStatus 时才允许流转到 toStatus public boolean audit(Integer adId, Integer fromStatus, Integer toStatus) { int rows adMapper.updateStatus(adId, fromStatus, toStatus); return rows 1; }update idupdateStatus UPDATE ad SET status #{toStatus} WHERE id #{adId} AND status #{fromStatus} /update这里的原理是“乐观锁”先查出来看到的状态是待审核执行更新时再确认一遍还是待审核才动手。数据库层面 update 会锁行第二个事务进来时匹配不到原来的状态影响行数为 0于是 audit 返回 false业务层就可以提示“该广告状态已变化请刷新后重试”。这是很小的一个细节但在答辩现场很容易成为加分点因为很多学生写的审核就是直接 set status 2压根不管并发。业务层还要区分角色广告主只能提交和查看自己的广告管理员才能审批。这在 SSM 项目里通常通过拦截器或过滤器判断 session 里的角色字段后面第 6 章会落到具体代码。4.3 统计曝光与点击MyBatis 聚合查询的两种写法广告系统的交付指标是数据某个广告位曝光多少、被点击多少次、点击率是多少。统计功能在 SSM 里一般用 MyBatis 的聚合查询实现写法上有两个典型场景。场景一是按广告位统计在投广告数量用 GROUP BY COUNTselect idcountByPosition resultTypemap SELECT p.name AS positionName, COUNT(a.id) AS adCount FROM ad_position p LEFT JOIN ad a ON a.position_id p.id AND a.status 1 GROUP BY p.id, p.name /select这里有两个容易踩的点一是 JOIN 条件里那个 a.status 1如果把它写到 WHERE 里LEFT JOIN 会退化成 INNER JOIN统计出的无广告位数量会不准二是 resultType 用 map让 MyBatis 直接把查询结果转成 ListMapString, Object适合报表这种字段不固定的场景。场景二是按天统计点击趋势输出折线图数据SELECT DATE(click_time) AS day, COUNT(*) AS clicks FROM ad_click_log WHERE ad_id #{adId} AND click_time #{start} GROUP BY DATE(click_time) ORDER BY day这段 SQL 的逻辑是从点击日志里按天分组统计某条广告在给定时间范围内的每日点击量。使用 DATE() 函数把 datetime 截断到天再 GROUP BY 这个函数结果。性能上的注意点是 click_time 列必须建索引否则广告点击量大时这个查询会在日志表上全表扫描页面报表会明显卡顿。统计结果从 Service 层返回后一般封装成 List交给前端拼接表格或图表。这是最常见做法虽然不算高级但足够覆盖毕业设计的需求。5. 避坑SSM 项目导入后最常见的 5 个翻车现场这一章要把跑这套项目时遇过、也帮人排查过的几类高频问题列出来每条按现象、原因、解决的顺序写。这些问题集中在导入和启动阶段占掉了毕设求助里的大部分提问提前记住能省下好几个通宵。5.1 一启动就 ClassNotFoundException / NoClassDefFoundError现象Tomcat 启动日志抛 ClassNotFoundException或者页面跑到一半抛 NoClassDefFoundError关键类看起来明明在项目里编译也不报错。原因绝大多数是 Maven 依赖没有完整下载或 IDE 的 Artifacts 配置里没有把模块依赖打包进部署目录。后者更隐蔽因为 IDE 编码时能找到类但运行环境里没有这个 jar属于“编译通过、运行失败”的典型代表。解决先用 mvn clean compile 确认依赖本身有没有问题。如果编译通过但运行还报检查 Project Structure - Artifacts在输出目录的 lib 里看有没有项目自己的依赖 jar没有的话在 Artifacts 配置里把 module 依赖手动添加进去。再不行就把整个 target 删除后 Rebuild Project让构建工具重新拷贝 lib 目录。5.2 登录页能打开提交后却 404现象首页和登录页渲染正常点击登录按钮后地址栏跳转但页面 404。原因两种常见情况。一是 web.xml 里 DispatcherServlet 的 url-pattern 配置成 /*把本该交给容器处理的 JSP 请求也拦截了导致视图解析不到二是表单的 action 路径没写项目上下文路径比如写成 /login而实际部署上下文是 /ad路径一错自然 404。解决打开浏览器开发者工具看 Network 里的实际请求 URL判断是 404 还是 405这一步比瞎猜快得多。然后回头查 web.xml 的 url-patternSSM 项目最常见的两种写法是.do 或 /极少用 /再核对表单 action 是否包含上下文路径。改完后重启 Tomcat清浏览器缓存再看结果。5.3 MySQL 8 连不上报 Public Key Retrieval is not allowed现象项目启动时数据库连接池报错提示 Public Key Retrieval is not allowed日志末尾常跟着 caching_sha2_password 的字样看起来像是密码错误但密码确实是对的。原因MySQL 8 默认的认证插件是 caching_sha2_password首次连接需要从服务端获取 RSA 公钥做加密握手而驱动默认关闭了这个获取动作于是连接被拒。解决在 jdbc.url 连接串里追加 allowPublicKeyRetrievaltrueuseSSLfalse问题立刻消失。根本上也可以把 MySQL 用户认证插件改回 mysql_native_password但不推荐改库改连接串更通用——换一台电脑部署时不用动数据库。改完记得重启 Tomcat连接池只在启动时初始化一次改了配置不重启等于白改。5.4 新增数据后页面上中文全变成问号现象从初始脚本带进去的中文正常但自己新增的中文数据刷新后显示成 ??或者反过来初始数据乱码、新增正常。原因乱码是编码链路断了一环。常见断点有三个MySQL 数据库或表是 latin1 字符集jdbc.url 没带 characterEncodingutf8JSP 页面头部缺少 charsetUTF-8 的声明。只要有一环不对中文就保不住。解决按从易到难的顺序查。先看 JSP 头部有没有 % page contentTypetext/html;charsetUTF-8 %再看 jdbc.url 是否带 characterEncodingutf8最后进 MySQL 执行 SHOW CREATE TABLE 确认表字符集是 utf8mb4。已经产生 ?? 的旧数据建议直接清掉重新导脚本别指望能转换回来。三层一起保证前后端中文就不会再出幺蛾子。5.5 点击登录没反应控制台报 Invalid bound statement (not found)现象启动不报错登录按钮点了没反应或直接白屏后台日志出现类似 Invalid bound statement (not found): xxxMapper.login 的提示。原因MyBatis 把 Mapper 接口和 XML 绑定时要求接口的全限定名与 XML 的 namespace 完全一致方法名与 XML 里 statement 的 id 一致任何一个对不上运行时就找不到绑定。另一条常见原因是 XML 文件没有被构建工具复制到 classes 目录因为默认构建配置只会拷贝 resources 下已声明的文件类型。解决打开报错里提示的 Mapper XML检查 namespace 是否等于接口全限定名再检查 resources 目录下 XML 的位置是否与接口包路径对应。文件都在但还报错就在 pom.xml 的 build 节点里加一段 resource 配置把 src/main/java 下的 **/*.xml 一起打进输出目录然后 Rebuild Project。这个配置在 SSM 项目里属于“不加必翻车”的经典项。6. 进阶毕业设计想加分给这套系统加两个改造点6.1 统计页接上 ECharts从聚合查询到可视化图表论文里展示度最高的功能通常是图表。常见做法是后端加一个返回 JSON 的接口前端用 ECharts 渲染。改造量不大收益直观Controller 方法上加 ResponseBody返回 ListSpring 自动序列化成 JSONResponseBody RequestMapping(/stat/trend) public ListMapString, Object trend(Integer adId, String start) { return adMapper.selectClickTrend(adId, start); }前端引入 echarts.min.js用 ajax 拿数据填充图表$.get(/ad/stat/trend, {adId: 1}, function (rows) { var chart echarts.init(document.getElementById(trendChart)); chart.setOption({ xAxis: {type: category, data: rows.map(r r.day)}, yAxis: {type: value}, series: [{type: line, data: rows.map(r r.clicks)}] }); });这个改造的完整链路是 Controller 返回 JSON、前端异步渲染论文里能顺势写一节“前后端数据交互设计”。注意 ajax 路径要和部署上下文一致改完用开发者工具看 Network 里 JSON 是否真的返回。6.2 加一个操作日志拦截器小改动体现系统意识答辩老师常问“系统有没有权限控制、有没有操作留痕”。与其口头解释不如加一个 HandlerInterceptor在请求进 Controller 前记一笔public class OperLogInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) { User user (User) req.getSession().getAttribute(loginUser); String log (user ! null ? user.getUserName() : anonymous) req.getMethod() req.getRequestURI(); logService.write(log); return true; } }在 spring-mvc.xml 里注册只拦截管理路径。这个点能引出两类答辩话术操作留痕便于追溯以及拦截器与过滤器的区别后者几乎是必考题。我刚接触这类改造时栽过的坑是 ajax 请求被拦截器挡掉拿不到 JSON最后把拦截范围缩小到 /admin/** 就解决了。我也养成了一个习惯答辩前把数据库脚本删掉重跑一遍模拟评审现场从零部署顺带验证文档步骤没漏。这套系统顺着前面的路径走通后离这一步就不远了希望帮到你。本文还有配套的精品资源点击获取