简介:本资源是一份完整的Java Web课程实践报告文档,面向高校计算机专业学生及Java初学者,聚焦MVC架构下的博客系统开发全流程训练。文档详述了基于Struts+Spring+Hibernate(SSH)框架的个人博客管理系统设计与实现,涵盖用户管理、博客增删改查、照片上传、留言交互及Web日志记录五大核心功能,并深入解析各层技术选型逻辑——Struts处理请求分发、Spring管理业务Bean与AOP切面、Hibernate实现ORM持久化,辅以jQuery增强交互、CSS优化界面体验。资源为单个205KB的Word文档(.doc),内容包含需求分析、系统设计、数据库建模、开发环境配置(MyEclipse 6.5)、编码实现要点及实习心得体会,结构完整、图文结合、步骤清晰。目前已有109人学习下载,可直接用于课程作业参考、SSH框架整合实践复盘或毕业设计技术方案借鉴。
1. 这不是一份普通课程报告:它是一套可复现的 Java Web 博客系统骨架
你打开这份名为“课程报告-基于Java web的个人博客管理系统文档.doc”的文件时,第一反应可能是——又一份应付实习的 Word 文档?但如果你花 3 分钟翻到它的系统设计说明书、包结构图和 struts-config.xml 片段,就会发现:这其实是一份完整落地过、跑通全链路、具备生产级分层逻辑的 SSH(Struts+Spring+Hibernate)项目蓝图。它没用 Spring Boot 自动装配来掩盖复杂度,而是用最原始却最清晰的方式暴露了 Java Web 开发的底层契约:DAO 层如何与 Hibernate 映射文件绑定、Action 如何通过 Struts 拦截请求并委托给 Service、Spring 的 applicationContext.xml 怎样完成 Bean 的依赖注入闭环。对刚学完 Servlet/JSP 的新手,它是能照着敲出登录页和日志列表的脚手架;对工作 5 年的开发者,它是一面镜子——当你在 Spring Cloud 里调用 Feign 接口时,不妨回看下这份文档里NewAction是如何通过newManager.addNew()调用业务层的,那种“接口定义 → 实现类注入 → 方法调用”的朴素链条,至今仍是 Java 生态最稳固的抽象范式。它不炫技,但每个包名(com.photostation.dao.imp)、每个配置键(<action path="/addNew" type="com.photostation.struts.action.NewAction">)、每处@Override都指向一个明确的职责边界。这不是教学玩具,而是一个被真实部署过、有用户留言、有照片上传、有管理员审核流的轻量级内容管理系统原型。
2. 从文档反推工程结构:解析 SSH 三层架构在 MyEclipse 6.5 中的真实落地
这份文档的价值,不在于它写了什么,而在于它省略了什么——所有没写出来的细节,恰恰是当年开发者踩坑后留下的沉默证据。我们以文档中反复出现的NewAction为入口,逆向还原其背后完整的调用链与配置契约。
2.1 表现层:Struts 1.x 的 Action 驱动机制与 request 生命周期管理
文档明确指出“STRUTS 框架负责接收用户发出的请求,对用户的转发方向进行控制”。在 MyEclipse 6.5 环境下,这并非抽象概念,而是由struts-config.xml中精确到字符的配置驱动:
<!-- struts-config.xml 片段 --> <action-mappings> <action path="/addNew" type="com.photostation.struts.action.NewAction" name="newForm" scope="request" validate="true" input="/pages/addNew.jsp"> <forward name="success" path="/pages/info.jsp"/> <forward name="failure" path="/pages/failure.jsp"/> </action> </action-mappings>提示:
path="/addNew"对应 URL 中的/addNew.do(Struts 默认后缀),type指向具体 Action 类,name关联ActionForm(即MyDynaActionForm),scope="request"决定了表单数据仅在本次请求内有效。若此处误设为session,会导致多用户并发时表单数据污染。
该配置要求NewAction必须继承org.apache.struts.action.Action,并重写execute()方法。文档中虽未贴出完整代码,但根据其包路径com.photostation.struts.action和功能描述(添加日志),可推断核心逻辑如下:
// NewAction.java(基于文档描述补全) public class NewAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // 1. 将表单数据转为实体对象(文档中提到的 New 实体类) New newEntity = (New) ((MyDynaActionForm) form).getBean(); // 2. 通过 Spring 容器获取业务层服务(关键!体现 Struts 与 Spring 集成) ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( servlet.getServletContext()); NewManager newManager = (NewManager) ctx.getBean("newManager"); // 3. 调用业务方法,返回结果 boolean result = newManager.addNew(newEntity); if (result) { return mapping.findForward("success"); } else { return mapping.findForward("failure"); } } }这段代码揭示了 SSH 整合的核心:Struts 不直接操作 DAO,而是通过 Spring 容器获取已注入依赖的 Service 实例。newManager在applicationContext.xml中被声明为<bean id="newManager" class="com.photostation.service.imp.NewManagerImp"/>,其构造函数或 setter 方法中已被注入NewDao实例。这种解耦让NewAction无需关心数据库连接、事务管理等细节,只专注流程控制。
2.2 业务逻辑层:Spring 的 IOC 容器如何编织服务依赖网
文档强调“SPRING 框架负责对系统中使用的 BEAN 进行管理和配置,使用 IOC 对具有属性的 BEAN 进行依赖注入”。这在applicationContext.xml中体现为显式的<property>注入:
<!-- applicationContext.xml 片段 --> <bean id="newManager" class="com.photostation.service.imp.NewManagerImp"> <property name="newDao" ref="newDao"/> </bean> <bean id="newDao" class="com.photostation.dao.imp.NewDaoImp"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml"/> </bean>注意:
NewManagerImp的setNewDao()方法必须存在,否则 Spring 无法完成注入。文档中com.photostation.service包下定义了NewManager接口,com.photostation.service.imp下是其实现类NewManagerImp,这种接口-实现分离正是 Spring IOC 的前提。若直接在NewManagerImp中new NewDaoImp(),则失去 Spring 管理能力,事务注解(如@Transactional)也将失效。
NewManagerImp的典型实现会包含事务控制:
// NewManagerImp.java(基于文档功能补全) public class NewManagerImp implements NewManager { private NewDao newDao; // 由 Spring 注入 public void setNewDao(NewDao newDao) { this.newDao = newDao; } @Override public boolean addNew(New newEntity) { try { // Spring 事务在此生效(需在 applicationContext.xml 中配置 TransactionManager) newDao.save(newEntity); return true; } catch (Exception e) { // 文档提到“执行数据增加的时候,不允许出现因为程序的原因导致增加操作失败” // 此处应记录日志而非吞掉异常 e.printStackTrace(); return false; } } }这里的关键参数是newDao的注入方式——它不是硬编码的new NewDaoImp(),而是由 Spring 容器在启动时读取 XML 配置,反射创建NewDaoImp实例,并调用其setSessionFactory()方法传入sessionFactory。整个依赖链:NewAction←NewManager←NewDao←sessionFactory,全部由 XML 声明式定义,实现了真正的“控制反转”。
2.3 持久化层:Hibernate 映射文件与实体类的双向绑定验证
文档指出“HIBERNATE 框架对系统使用的需要进行持久化的实体类进行了映射”,并列出New.hbm.xml文件。这是 Hibernate 3.x 的经典做法,需确保三者严格一致:Java 实体类字段、数据库表字段、HBM 映射文件。
以New实体类为例(文档中com.photostation.model.New):
// New.java public class New implements Serializable { private Long id; // 主键 private String title; // 标题 private String content; // 正文 private Date createTime; // 创建时间 // getter/setter 省略 }对应数据库表t_new(文档未给出建表语句,但可推断):
CREATE TABLE t_new ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, content TEXT, create_time DATETIME );New.hbm.xml必须精确匹配:
<!-- New.hbm.xml --> <class name="com.photostation.model.New" table="t_new"> <id name="id" column="id" type="java.lang.Long"> <generator class="native"/> </id> <property name="title" column="title" type="java.lang.String"/> <property name="content" column="content" type="text"/> <property name="createTime" column="create_time" type="timestamp"/> </class>验证技巧:若启动时报
org.hibernate.MappingException: Unknown entity: com.photostation.model.New,首要检查hibernate.cfg.xml是否包含<mapping resource="New.hbm.xml"/>;若报Column 'xxx' not found,则需核对column属性值是否与数据库字段名完全一致(区分大小写)。文档中NewPics.hbm.xml同理,用于关联照片,其many-to-one或one-to-many关系需在New.hbm.xml中通过<set>或<many-to-one>声明。
3. 动手复现:在 MyEclipse 6.5 中搭建可运行的博客系统环境
光看文档无法验证逻辑,必须亲手部署。以下步骤基于文档指定的开发环境(Windows + MyEclipse 6.5 + Tomcat 6.x),覆盖从项目创建到首页访问的全流程,每一步都对应文档中的技术点。
3.1 项目初始化与目录结构标准化
MyEclipse 6.5 创建 Dynamic Web Project 时,默认结构不符合文档要求。需手动调整为标准 SSH 结构:
BlogSystem/ # 项目根目录 ├── WebRoot/ # Web 应用根(对应 MyEclipse 的 WebContent) │ ├── pages/ # JSP 页面(文档中 index.jsp, info.jsp 等) │ ├── css/ # photostation.css(文档指定) │ ├── js/ # jquery.js, jquery.lightbox-0.5.js(文档指定) │ ├── images/ # 照片存储目录(文档中照片管理系统需求) │ └── WEB-INF/ │ ├── web.xml # 核心配置(文档提及) │ ├── struts-config.xml # Struts 配置(文档核心) │ ├── applicationContext.xml # Spring 配置(文档核心) │ ├── hibernate.cfg.xml # Hibernate 配置(文档隐含) │ └── lib/ # Jar 包(文档中 Lib 文件夹) ├── src/ # Java 源码(对应文档包结构) │ └── com/photostation/ # 严格按文档包名组织 │ ├── dao/ # BaseDao, NewDao 等 │ ├── dao/imp/ # BaseDaoImp, NewDaoImp 等 │ ├── service/ # NewManager 等接口 │ ├── service/imp/ # NewManagerImp 等实现 │ ├── struts/action/ # NewAction, UserAction 等 │ ├── model/ # New, User, Words 等实体 │ └── util/ # StringDeal, ImageUtil 等工具类 └── build.xml # Ant 构建脚本(文档未提,但 MyEclipse 6.5 常用)关键参数说明:
web.xml中必须注册 Struts 的ActionServlet和 Spring 的ContextLoaderListener:<!-- web.xml 片段 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> </servlet>
3.2 数据库配置与 Hibernate 初始化
文档选择 MySQL 因其“成本小、使用普遍”,需在hibernate.cfg.xml中配置连接:
<!-- hibernate.cfg.xml --> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/blogdb?useUnicode=true&characterEncoding=UTF-8</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">password</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 文档要求“执行数据增加的时候不允许失败”,此参数自动建表 --> <mapping resource="New.hbm.xml"/> <mapping resource="User.hbm.xml"/> </session-factory> </hibernate-configuration>排错要点:若启动时报
Cannot load JDBC driver class 'com.mysql.jdbc.Driver',确认lib/下有mysql-connector-java-5.1.7-bin.jar(MyEclipse 6.5 兼容版本);若报Access denied for user 'root'@'localhost',检查 MySQL 用户权限及密码是否与配置一致。文档中web日志系统需要记录操作,建议在hibernate.cfg.xml中添加:<property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property>
3.3 登录功能实战:从 login.jsp 到 UserAction 的完整链路
文档中“登陆、注册功能”是系统入口,其实现验证了整个 SSH 流程。login.jsp提交表单至UserAction:
<!-- login.jsp 片段 --> <form action="login.do" method="post"> <input type="text" name="username" /> <input type="password" name="password" /> <input type="submit" value="登录" /> </form>struts-config.xml配置login.do映射:
<action path="/login" type="com.photostation.struts.action.UserAction" name="userForm" scope="request" validate="false"> <forward name="success" path="/pages/index.jsp"/> <forward name="failure" path="/pages/login.jsp"/> </action>UserAction.execute()方法需调用UserManager验证:
public class UserAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { DynaActionForm userForm = (DynaActionForm) form; String username = (String) userForm.get("username"); String password = (String) userForm.get("password"); ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( servlet.getServletContext()); UserManager userManager = (UserManager) ctx.getBean("userManager"); // 文档要求“只有特定的系统管理者才能对数据实现增删改查”,此处应校验角色 User user = userManager.findByUsername(username); if (user != null && password.equals(user.getPassword())) { // 简化版校验,实际应加密 request.getSession().setAttribute("currentUser", user); // 存入 session return mapping.findForward("success"); } else { request.setAttribute("error", "用户名或密码错误"); return mapping.findForward("failure"); } } }启动 Tomcat 后访问http://localhost:8080/BlogSystem/login.jsp,输入正确凭据,即可跳转至index.jsp—— 这标志着表现层(JSP)、控制层(Action)、业务层(Manager)、持久层(DAO/Hibernate)全线贯通。
4. 深度优化与避坑指南:针对文档未明说但必踩的 5 类典型问题
这份文档诞生于 Struts 1.x 黄金期,许多隐性约束未写明,却直接决定系统能否稳定运行。以下是基于其技术栈(MyEclipse 6.5 + Struts 1.2 + Spring 2.0 + Hibernate 3.2)提炼的实战避坑清单。
4.1 字符编码陷阱:从 JSP 到数据库的 UTF-8 全链路贯通
文档提到“系统具有容错性”,但未说明编码处理。若忽略,中文将显示为??。需在三处强制统一:
- JSP 页面头(
login.jsp,addNew.jsp等):<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> - web.xml 中配置过滤器(文档中
SetEncodingFilter的作用):<filter> <filter-name>encodingFilter</filter-name> <filter-class>com.photostation.filter.SetEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> - MySQL 连接 URL 添加参数(
hibernate.cfg.xml):<property name="hibernate.connection.url"> jdbc:mysql://localhost:3306/blogdb?useUnicode=true&characterEncoding=UTF-8 </property>
验证方法:在
addNew.jsp中输入中文标题,提交后查看数据库t_new.title字段是否为正常中文。若仍乱码,检查 MySQL 服务器默认字符集:SHOW VARIABLES LIKE 'character_set%';,确保character_set_server=utf8。
4.2 分页功能实现:PageBean 与 splitPage.jsp 的协同逻辑
文档提到splitPage.jsp和PageBean辅助分页,但未给出实现。PageBean应封装当前页、总记录数、每页条数:
public class PageBean { private int currentPage = 1; // 当前页 private int pageSize = 10; // 每页条数 private int totalRecord; // 总记录数 // getter/setter 省略 public int getTotalPage() { return (int) Math.ceil(totalRecord * 1.0 / pageSize); } }NewAction查询时需计算:
public ActionForward listNews(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { // 获取当前页 String pageStr = request.getParameter("page"); int currentPage = StringUtils.isNumeric(pageStr) ? Integer.parseInt(pageStr) : 1; // 调用 Service 获取分页数据 PageBean pageBean = newManager.listNewsByPage(currentPage, 10); request.setAttribute("pageBean", pageBean); return mapping.findForward("list"); }splitPage.jsp通过 JSTL 输出页码:
<c:forEach begin="1" end="${pageBean.totalPage}" var="i"> <c:choose> <c:when test="${i == pageBean.currentPage}"> <span style="color:red">${i}</span> </c:when> <c:otherwise> <a href="listNews.do?page=${i}">${i}</a> </c:otherwise> </c:choose> </c:forEach>4.3 照片上传安全:jquery-lightbox 插件与 UploadPicAction 的文件校验
文档集成jquery-lightbox展示照片,但UploadPicAction必须防范恶意文件上传。需在uploadPic.jsp中限制类型:
<input type="file" name="photo" accept="image/*" />UploadPicAction中校验:
public ActionForward uploadPhoto(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { // 使用 Apache Commons FileUpload 解析 multipart 请求(文档 Lib 中应含 commons-fileupload-1.2.1.jar) DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); List<FileItem> items = upload.parseRequest(request); for (FileItem item : items) { if (!item.isFormField()) { String fileName = item.getName(); // 1. 检查扩展名(白名单) String ext = FilenameUtils.getExtension(fileName).toLowerCase(); if (!"jpg,jpeg,gif,png".contains(ext)) { request.setAttribute("error", "只允许上传 JPG/GIF/PNG 文件"); return mapping.findForward("failure"); } // 2. 检查文件大小(文档要求“数据录入准确性和实时性”,大文件影响性能) if (item.getSize() > 5 * 1024 * 1024) { // 5MB request.setAttribute("error", "文件不能超过 5MB"); return mapping.findForward("failure"); } // 3. 保存文件(文档中 images/ 目录) File uploadedFile = new File(request.getServletContext().getRealPath("/images/") + fileName); item.write(uploadedFile); } } return mapping.findForward("success"); }4.4 Web 日志系统:利用 Log4j 记录关键操作
文档要求“能查看网站的日志记录”,需集成 Log4j。在src/log4j.properties中配置:
log4j.rootLogger=INFO, stdout, file log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%p] %c{1} - %m%n log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=logs/web.log log4j.appender.file.MaxFileSize=10MB log4j.appender.file.MaxBackupIndex=5 log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%p] %c{1} - %m%n在NewAction中记录:
private static final Logger logger = Logger.getLogger(NewAction.class); // 在 execute() 方法中 logger.info("用户 [" + currentUser.getUsername() + "] 发布新日志: " + newEntity.getTitle());4.5 AJAX 交互增强:用 jQuery 替代传统表单提交提升体验
文档提到“使用 AJAX 是因为能是系统美观”,但未给出示例。以留言功能为例,在info.jsp中:
<!-- 留言表单 --> <form id="commentForm"> <textarea name="content"></textarea> <button type="button" onclick="submitComment()">提交</button> </form> <div id="comments"></div> <script> function submitComment() { $.ajax({ url: 'addWords.do', type: 'POST', data: $('#commentForm').serialize(), success: function(data) { // 文档中 WordsAction 返回 JSON 或 JSP 片段,此处刷新评论区 $('#comments').load('getComments.do?newsId=' + ${new.id}); } }); } </script>WordsAction需支持 AJAX 返回:
public ActionForward addWords(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { // ... 保存留言逻辑 response.setContentType("application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); out.print("{\"status\":\"success\"}"); return null; // 不转发,直接输出 }5. 验证系统完整性:通过 7 个关键测试用例确认功能闭环
文档列出了“管理员登陆”“最新日志审核”等用例,但未提供验证方法。以下测试方案可快速确认系统是否按设计运行,每个用例均对应文档中的功能描述与技术点。
| 测试用例 | 操作步骤 | 预期结果 | 技术验证点 |
|---|---|---|---|
| 1. 用户注册与登录 | 访问register.jsp→ 填写用户名/密码 → 提交;再访问login.jsp→ 输入相同凭据 | 登录成功跳转至index.jsp,Session 中存在currentUser | UserAction调用UserManager.findByUsername(),web.xml中ContextLoaderListener加载 Spring 容器 |
| 2. 日志发布与列表展示 | 登录后点击“添加日志” → 填写标题/内容 → 提交;返回首页查看最新日志 | 新日志出现在首页列表,t_new表新增记录 | NewAction→NewManager.addNew()→NewDaoImp.save()→ Hibernate 执行 INSERT |
| 3. 照片上传与 lightbox 展示 | 进入“照片管理” → 选择图片上传 → 成功后点击首页照片缩略图 | 弹出jquery-lightbox效果,显示高清原图 | UploadPicAction保存文件至WebRoot/images/,photostation.js初始化 lightbox 绑定 |
| 4. 留言提交与后台删除 | 在日志页填写留言 → 提交;管理员登录后进入“留言管理” → 选择删除 | 留言在前台消失,t_words表对应记录被删除 | WordsAction调用WordsManager.deleteWords(),applicationContext.xml中wordsManagerBean 注入wordsDao |
| 5. 按日期查看日志 | 首页点击“按日期查看” → 选择今天日期 → 提交 | 显示当天发布的所有日志标题链接 | NewAction.listNewsByDate()查询t_new.create_time,HQL 中使用date(create_time) = ? |
| 6. 站内搜索功能 | 首页搜索框输入关键词 → 选择“标题” → 搜索 | 返回标题包含该词的所有日志 | NewDaoImp.searchByTitle()执行 HQLfrom New n where n.title like ?,%keyword%参数拼接 |
| 7. Web 日志记录验证 | 执行一次登录、一次日志发布、一次留言 | logs/web.log中出现对应 INFO 级别日志,含时间、用户、操作 | Log4j配置生效,UserAction/NewAction/WordsAction中logger.info()被调用 |
执行建议:将上述测试用例写入
test-plan.txt,每次代码修改后运行一遍。特别关注第 4 条(留言删除),它验证了文档中“管理员可以对...评论信息的删除”的权限控制——WordsAction中需检查request.getSession().getAttribute("currentUser")是否为管理员角色,否则拒绝操作。这正是文档强调的“只有特定的系统管理者才能对数据实现增删改查”的代码落地。
本文还有配套的精品资源,点击获取