
简介在Java Web开发领域MVC架构模式是构建企业级应用的核心思想它将应用分为模型、视图和控制器三层以实现关注点分离和代码复用。其工作原理是通过前端控制器拦截用户请求分发给相应的处理器执行业务逻辑并最终渲染视图返回响应。这种架构的技术价值在于提升了代码的可维护性、可测试性和团队协作效率广泛应用于电商、内容管理、社交平台等各类Web系统。本文以宠物网站项目为具体场景深入剖析如何将经典的Spring框架与Struts2进行整合解决依赖注入与请求控制器的协同问题并详细演示了使用JSP结合JSTL进行动态数据展示以及通过配置Druid连接池优化MySQL数据库访问性能的工程实践为理解和掌握传统Java EE技术栈的落地提供了完整案例。1. 项目概述与核心价值最近几年身边想自己动手做个宠物网站的朋友越来越多了。有的是为了毕业设计有的是想创业做个宠物社区还有的纯粹是技术爱好者想练手。但不管出于什么目的大家普遍会遇到一个难题从零开始怎么把JSP、Spring、Struts2这些听起来就头大的技术和MySQL数据库串起来做出一个功能完整、能跑起来的宠物网站网上的资料要么太零散要么就是只讲理论不给代码看得人云里雾里。我这次分享的就是一个完整的“基于JAVA的宠物网站”项目实战复盘。这不仅仅是一个毕业设计的参考模板更是一套从需求分析、技术选型、数据库设计、前后端开发到最终部署上线的全流程解决方案。我会把JSP做页面、Spring管业务、Struts2处理请求、MySQL存数据这“四件套”是怎么协同工作的掰开揉碎了讲清楚。更重要的是我会分享在开发过程中踩过的那些坑以及如何避开它们。比如为什么在Spring和Struts2并存的项目里配置文件容易打架JSP页面里EL表达式突然不显示了怎么办数据库连接池配置多少才合适这些实战中才会遇到的问题我都会给出经过验证的解决方案。无论你是正在为毕业论文发愁的在校生还是想系统学习Java Web开发的中级开发者这个项目都能给你提供一个清晰的、可复现的路径。你拿到的不只是一堆源代码而是一个理解经典Java EE技术栈如何落地的活案例。2. 技术栈深度解析与选型考量2.1 为什么是JSPSpringStruts2MySQL这个组合看到这个技术栈有些朋友可能会问现在不都流行Spring Boot全家桶吗为什么还要用相对“老”的Struts2和JSP这里有几个关键的考量点。首先对于学术研究和经典架构学习而言这个组合具有不可替代的教学价值。Spring Boot确实极大地简化了配置但它也封装了很多底层细节。而采用Spring核心IOC/AOP Struts2MVC控制器 JSP视图层的分离式架构能让你清晰地看到Web请求的完整生命周期一个HTTP请求如何被Struts2的Filter拦截如何匹配ActionAction又如何调用Spring管理的ServiceService再操作DAO层访问MySQL最后数据如何通过JSP渲染成HTML返回给浏览器。这个过程就像解剖一只麻雀把MVC的每一层肌肉和骨骼都看得清清楚楚对于夯实基础至关重要。其次在资源管理和解耦方面这个组合有其优势。Spring的IOC容器负责管理所有Bean如Service、DAO的生命周期和依赖注入实现了业务逻辑层的高度解耦。Struts2则专注于控制层其强大的拦截器栈、类型转换和验证框架能很好地处理表单提交、参数校验等Web层通用逻辑。JSP虽然视图技术古老但其在服务器端直接编写Java代码尽管不推荐或使用JSTL/EL的能力对于理解服务器端渲染原理非常有帮助。MySQL作为最流行的开源关系型数据库其稳定性和生态无需多言。注意在实际的新项目启动中除非有历史包袱或特殊教学需求否则更推荐使用Spring MVC替代Struts2并使用Thymeleaf、FreeMarker等现代模板引擎替代JSP。但为了透彻理解原理从这个经典组合入手是非常有价值的。2.2 各组件核心职责与交互流程为了让这个技术栈和谐工作我们必须明确每个组件的“地盘”和它们之间的“通信协议”。Struts2扮演“交通警察”的角色。所有用户请求如/pet/list.do首先到达web.xml中配置的Struts2核心过滤器StrutsPrepareAndExecuteFilter。它根据struts.xml配置文件找到对应的Action类例如PetAction。Struts2负责初始化Action实例注意这里要与Spring整合让Action实例由Spring创建调用其中的方法如list()并处理方法的返回值如success决定下一个跳转的视图JSP页面。Spring扮演“大管家”和“服务提供商”的角色。我们通过ContextLoaderListener在Web应用启动时加载Spring的配置文件通常是applicationContext.xml。这个文件里定义了数据源DataSource、事务管理器TransactionManager、以及各种Service和DAO Bean。关键的一步是整合我们需要在struts.xml中配置让Struts2的Action不再自己new而是从Spring容器中获取。这样PetAction里需要用到的PetService就可以通过Spring的Autowired自动注入进来。JSP扮演“装修工人”的角色。它的任务是把Action传递过来的数据通常放在request或session作用域中Struts2也提供了ValueStack渲染成用户看到的HTML页面。在JSP中我们应尽量避免使用% ... %写Java代码而是使用EL表达式${pet.name}和JSTL标签c:forEach来展示数据保持页面的整洁。MySQL扮演“仓库”的角色。通过Spring配置的DataSource比如使用阿里的Druid连接池DAO层通常使用JdbcTemplate或MyBatis来执行SQL完成对宠物信息、用户、订单等数据的增删改查。它们之间的交互流程可以简化为浏览器请求 - Struts2过滤器 - Struts2根据配置找Action该Action实例来自Spring容器- Action调用Spring注入的Service - Service调用DAO - DAO通过Spring管理的数据源操作MySQL - 查询结果沿原路返回 - Action将结果数据放入值栈/请求域 - Struts2转发到对应的JSP - JSP渲染数据生成HTML - 返回浏览器。3. 宠物网站核心功能模块设计与实现3.1 数据库设计如何规划你的“宠物家园”一个宠物网站核心数据实体通常包括用户、宠物分类、宠物详情、订单、购物车、评论等。设计数据库时不仅要考虑当前功能还要为可能的扩展留有余地。以pet宠物信息表和category分类表为例这是典型的一对多关系一个分类下有多个宠物。建表SQL需要仔细考虑字段和索引-- 分类表 CREATE TABLE category ( cid INT(11) NOT NULL AUTO_INCREMENT COMMENT 分类ID, cname VARCHAR(50) NOT NULL COMMENT 分类名称如狗狗、猫咪、小宠, cdesc VARCHAR(255) DEFAULT NULL COMMENT 分类描述, state TINYINT(1) DEFAULT 1 COMMENT 状态1启用0禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (cid), UNIQUE KEY uk_cname (cname), -- 分类名唯一 KEY idx_state (state) -- 根据状态查询是常见操作 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宠物分类表; -- 宠物信息表 CREATE TABLE pet ( pid INT(11) NOT NULL AUTO_INCREMENT COMMENT 宠物ID, pname VARCHAR(100) NOT NULL COMMENT 宠物名称, cid INT(11) NOT NULL COMMENT 所属分类ID, price DECIMAL(10,2) NOT NULL COMMENT 价格, image VARCHAR(255) DEFAULT NULL COMMENT 主图路径, pdesc TEXT COMMENT 详细描述, stock INT(11) NOT NULL DEFAULT 0 COMMENT 库存, is_hot TINYINT(1) DEFAULT 0 COMMENT 是否热门1是0否, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 上架时间, PRIMARY KEY (pid), KEY idx_cid (cid), -- 外键索引关联查询用 KEY idx_is_hot (is_hot), -- 热门商品查询 KEY idx_create_time (create_time), -- 按新品排序 CONSTRAINT fk_pet_category FOREIGN KEY (cid) REFERENCES category (cid) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宠物信息表;设计心得字段注释务必写COMMENT三个月后你自己都看不懂某个字段是干嘛的。字符集统一使用utf8mb4支持存储Emoji表情避免未来出现乱码问题。外键约束像FOREIGN KEY这种在开发阶段可以帮你保持数据一致性但在超高并发的生产环境有时会因为性能考虑在应用层保证而非数据库层。毕业设计项目加上无妨。索引规划PRIMARY KEY和UNIQUE KEY是必须的。像cid、is_hot、create_time这种经常出现在WHERE或ORDER BY子句中的字段建立普通索引能极大提升查询速度。但索引不是越多越好它会降低写操作INSERT/UPDATE/DELETE的速度。3.2 后端架构Spring与Struts2的整合实战这是本项目最容易出错的环节。整合的关键在于让Struts2的Action由Spring来创建和管理从而能在Action中享受Spring的依赖注入。第一步配置web.xml这是所有配置的起点需要声明Spring的监听器和Struts2的过滤器并注意它们的顺序。?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 1. 配置Spring的监听器用于启动Spring容器 -- context-param param-namecontextConfigLocation/param-name param-valueclasspath:applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener !-- 2. 配置Struts2的核心过滤器 -- filter filter-namestruts2/filter-name filter-classorg.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter/filter-class !-- 可选解决一些兼容性问题 -- init-param param-namestruts.devMode/param-name param-valuefalse/param-value /init-param /filter filter-mapping filter-namestruts2/filter-name url-pattern/*/url-pattern /filter-mapping !-- 其他配置如字符编码过滤器 -- filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping /web-app第二步配置Spring (applicationContext.xml)这里配置数据源、事务、以及需要Spring管理的Bean。?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:txhttp://www.springframework.org/schema/tx xsi:schemaLocation http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd !-- 开启注解扫描扫描Service、DAO等组件 -- context:component-scan base-packagecom.yourdomain.pet.service, com.yourdomain.pet.dao/ !-- 配置数据源 (以Druid为例) -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource init-methodinit destroy-methodclose property nameurl valuejdbc:mysql://localhost:3306/pet_db?useUnicodetrueamp;characterEncodingutf8amp;useSSLfalseamp;serverTimezoneAsia/Shanghai/ property nameusername valueroot/ property namepassword valueyourpassword/ property nameinitialSize value5/ property nameminIdle value5/ property namemaxActive value20/ property namemaxWait value60000/ /bean !-- 配置JdbcTemplate -- bean idjdbcTemplate classorg.springframework.jdbc.core.JdbcTemplate property namedataSource refdataSource/ /bean !-- 配置事务管理器 -- bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean !-- 开启注解驱动的事务管理 -- tx:annotation-driven transaction-managertransactionManager/ !-- 特别重要将Struts2的Action也交给Spring管理scope必须是prototype -- bean idpetAction classcom.yourdomain.pet.web.action.PetAction scopeprototype property namepetService refpetService/ /bean !-- 其他Action同理 -- /beans第三步配置Struts2 (struts.xml)这里的关键是告诉Struts2Action不要自己创建去Spring容器里找。?xml version1.0 encodingUTF-8 ? !DOCTYPE struts PUBLIC -//Apache Software Foundation//DTD Struts Configuration 2.5//EN http://struts.apache.org/dtds/struts-2.5.dtd struts !-- 开发模式修改配置后无需重启 -- constant namestruts.devMode valuefalse/ !-- 指定Action的后缀如 .do 或 .action -- constant namestruts.action.extension valuedo,action,/ package namepet namespace/ extendsstruts-default !-- 关键配置告诉Struts2Action由Spring创建 -- default-class-ref classcom.opensymphony.xwork2.ActionSupport/ !-- 配置全局结果 -- global-results result nameerror/common/error.jsp/result result namelogin typeredirect/user/toLogin.do/result /global-results !-- 配置Actionclass属性写Spring中Bean的id -- action namepet_list classpetAction methodlist result namesuccess/pet/list.jsp/result result nameerror/common/error.jsp/result /action action namepet_detail classpetAction methoddetail result namesuccess/pet/detail.jsp/result /action !-- 更多Action配置... -- /package /struts整合的核心要点在applicationContext.xml中将Action的Bean的scope设置为prototype原型。这是因为Struts2每次请求都会创建一个新的Action实例而Spring默认的singleton单例会导致线程安全问题。在struts.xml中action标签的class属性不再写Action类的全限定名而是写Spring容器中对应Bean的id如petAction。确保Struts2和Spring的jar包版本兼容。这是一个经典的坑建议使用经过验证的版本组合例如 Struts2.5.x Spring 5.x。3.3 前端交互JSP页面的动态数据展示与表单处理JSP页面负责展示和收集数据。现代做法是尽可能避免在JSP中写Java脚本而是使用EL表达式和JSTL标签。列表展示页示例 (list.jsp)% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % !DOCTYPE html html head title宠物列表/title /head body h1热门宠物/h1 div classpet-list c:forEach items${petList} varpet varStatusstatus div classpet-item a href${pageContext.request.contextPath}/pet_detail.do?pid${pet.pid} img src${pageContext.request.contextPath}/images/${pet.image} alt${pet.pname} width200/ h3${pet.pname}/h3 p价格span classprice¥${pet.price}/span/p p库存${pet.stock 0 ? pet.stock : 已售罄}/p /a c:if test${pet.stock 0} button onclickaddToCart(${pet.pid})加入购物车/button /c:if /div /c:forEach /div !-- 分页 -- div classpagination 当前第${page.currentPage}页共${page.totalPage}页 c:if test${page.currentPage 1} a hrefpet_list.do?page${page.currentPage-1}上一页/a /c:if c:forEach begin1 end${page.totalPage} vari a hrefpet_list.do?page${i}${i}/a /c:forEach c:if test${page.currentPage page.totalPage} a hrefpet_list.do?page${page.currentPage1}下一页/a /c:if /div /body /html在这个JSP中${petList}就是从Action中通过request.setAttribute或Struts2默认放入值栈的数据。c:forEach标签用于循环遍历列表。分页信息也是通过类似方式传递。表单提交与Struts2 Action接收 假设有一个添加宠物的表单addPet.jsp提交到pet_add.do。form action${pageContext.request.contextPath}/pet_add.do methodpost 宠物名input typetext namepname/br/ 分类 select namecid c:forEach items${categoryList} varcat option value${cat.cid}${cat.cname}/option /c:forEach /selectbr/ 价格input typetext nameprice/br/ input typesubmit value添加/ /form对应的PetAction中Struts2可以自动将表单参数注入到Action的属性中或者注入到一个模型对象Pet对象中。package com.yourdomain.pet.web.action; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; import com.yourdomain.pet.model.Pet; import com.yourdomain.pet.service.PetService; import org.springframework.beans.factory.annotation.Autowired; // 使用ModelDriven接口可以自动将请求参数封装到Pet对象 public class PetAction extends ActionSupport implements ModelDrivenPet { private Pet pet new Pet(); // 模型对象 private PetService petService; // 列表展示 private ListPet petList; private Page page; // 分页对象 Autowired // Spring注入Service public void setPetService(PetService petService) { this.petService petService; } Override public Pet getModel() { return pet; } // 列表方法 public String list() { // 通常page参数由Struts2自动注入 page petService.findPetsByPage(page); petList page.getData(); return SUCCESS; } // 添加方法 public String add() { try { petService.addPet(pet); // pet对象已由Struts2通过setter方法填充好数据 this.addActionMessage(添加宠物成功); return SUCCESS; } catch (Exception e) { this.addActionError(添加失败 e.getMessage()); return ERROR; } } // getter and setter ... }实操心得在JSP中表单字段的name属性必须与Action中模型对象Pet的属性名严格对应或者与Action中基本属性及其setter方法对应Struts2的OGNL表达式才能正确完成数据绑定。这是初学者最容易出错的地方之一比如表单里是namepnameAction里就必须有setPname(String name)方法。4. 项目开发中的典型问题与深度排查4.1 整合配置类问题与解决方案在整合Spring和Struts2时90%的问题出在配置上。下面是一个常见问题速查表。问题现象可能原因排查步骤与解决方案启动Tomcat时报ClassNotFoundException或NoClassDefFoundError1. 缺少必要的Jar包。2. Jar包版本冲突。3. Jar包没有正确部署到WEB-INF/lib下。1. 检查WEB-INF/lib目录确保包含所有依赖。使用Maven/Gradle管理依赖可极大避免此问题。2. 检查是否有多个不同版本的相同Jar如asm-x.x.jar删除旧版本。3. 如果是IDE检查项目的Deployment AssemblyEclipse或ArtifactsIDEA确保依赖包被打包进WAR。访问.do或.action链接时报404错误1.web.xml中Struts2过滤器配置错误或URL模式不匹配。2.struts.xml文件位置不对或未被加载。3. Action的class属性配置错误。1. 确认web.xml中StrutsPrepareAndExecuteFilter的url-pattern是/*。2. 确认struts.xml在src/main/resources或WEB-INF/classes下。3. 在struts.xml的Action配置中class属性值必须是Spring容器中Bean的id且该Bean的scope是prototype。Action中注入的Service为null1. Action没有被Spring管理。2. Spring和Struts2的整合配置不正确。3. Service类上没有加Service注解或未在XML中配置。1. 确认applicationContext.xml中定义了该Action的Bean且scopeprototype。2. 确认struts.xml中Action的class指向了Spring Bean的id。3. 确认Service类已被Spring扫描到context:component-scan包路径正确或已在XML中配置。JSP页面EL表达式${}不显示内容1. 默认情况下Servlet 2.3 / JSP 1.2 可能禁用EL。2. 页面指令isELIgnored被设置为true。1. 确保web.xml头部的版本是2.4或以上支持EL。2. 在JSP页面顶部添加% page isELIgnoredfalse %。最一劳永逸的方法是在web.xml中配置jsp-config jsp-property-group url-pattern*.jsp/url-pattern el-ignoredfalse/el-ignored /jsp-property-group /jsp-config。表单提交后Action属性接收不到值1. Action中没有为表单字段提供正确的setter方法。2. 使用了ModelDriven但模型对象属性名与表单字段名不匹配。3. 类型转换失败如字符串转日期、数字。1. 检查Action中是否有与表单name属性对应的setter方法方法名必须规范如字段pname对应setPname。2. 如果实现了ModelDriven检查模型对象如Pet的属性和setter方法。3. 对于复杂类型可以在Action中自定义类型转换器或在前端使用统一格式如日期用yyyy-MM-dd。4.2 性能与安全优化要点一个能跑起来的网站只是第一步一个健壮的网站还需要考虑性能和安全。数据库连接池配置上面例子中用了DruidmaxActive最大连接数设置成20对于毕业设计和小型网站足够了。但这个值不是越大越好需要根据数据库服务器硬件和应用并发量调整。一个粗略的估算公式是maxActive ≈ (核心业务并发线程数) * (每个请求平均持有连接时间 / 平均请求处理时间)。同时要配置validationQuery如SELECT 1和testWhileIdle让连接池定期检测连接是否有效避免拿到已断开的数据库连接。SQL注入防范绝对不要在代码中拼接SQL字符串务必使用PreparedStatement。如果你用的是Spring的JdbcTemplate它内部就是使用PreparedStatement你只需要写带?占位符的SQL并传递参数即可这是最有效的防注入手段。// 错误示范存在SQL注入风险 String sql SELECT * FROM pet WHERE pname LIKE % name %; // 正确示范使用JdbcTemplate的预编译语句 String sql SELECT * FROM pet WHERE pname LIKE ?; ListPet pets jdbcTemplate.query(sql, new Object[]{% name %}, new BeanPropertyRowMapper(Pet.class));XSS跨站脚本攻击防范在JSP中直接输出用户提交的数据如评论内容是危险的。应该对输出进行转义。JSTL的c:out标签默认有转义功能或者使用${fn:escapeXml(content)}需要引入fn函数库。密码存储永远不要用明文存储用户密码必须使用哈希算法加盐Salt存储。可以使用Spring Security的BCryptPasswordEncoder它是目前公认的安全选择。Component public class PasswordUtil { Autowired private BCryptPasswordEncoder encoder; public String encode(String rawPassword) { return encoder.encode(rawPassword); } public boolean matches(String rawPassword, String encodedPassword) { return encoder.matches(rawPassword, encodedPassword); } }事务管理在Service层的方法上使用Transactional注解确保数据库操作的原子性。例如用户下单扣减库存和生成订单必须在一个事务里要么都成功要么都失败回滚。Service public class OrderServiceImpl implements OrderService { Autowired private PetDao petDao; Autowired private OrderDao orderDao; Override Transactional(rollbackFor Exception.class) // 发生任何异常都回滚 public void createOrder(Order order, ListOrderItem items) throws Exception { // 1. 检查并扣减库存悲观锁或乐观锁 for (OrderItem item : items) { int affected petDao.reduceStock(item.getPid(), item.getCount()); if (affected 0) { throw new RuntimeException(宠物库存不足或已下架ID: item.getPid()); } } // 2. 生成订单 orderDao.insert(order); // 3. 生成订单明细 orderDao.batchInsertItems(items); // 如果以上任何一步失败整个事务回滚库存恢复 } }5. 从开发到部署项目总结与进阶思考走完整个开发流程你会发现这个基于JSPSpringStruts2MySQL的宠物网站项目就像搭积木一样把Java Web开发的几个核心模块都串联实践了一遍。从最底层的数据库设计、SQL优化到中间层的业务逻辑、事务控制再到最上层的请求处理、页面渲染形成了一个完整的闭环。我个人在多次类似项目的开发中最深的一点体会是配置文件是整合项目的生命线。web.xml、applicationContext.xml、struts.xml这三个文件就像项目的“接线图”任何一根线接错了整个系统就“亮”不起来。务必保持耐心按照“从全局到局部”的顺序检查和调试。先确保Tomcat能启动看控制台日志再确保Spring容器能正确加载看是否有Bean创建失败的日志最后再测试Struts2的Action是否能被访问。这个项目作为学习终点和起点都非常合适。作为终点它让你掌握了经典Java EE项目的完整开发流程。作为起点你可以基于它进行无数种扩展和优化前端现代化把JSP替换成前后端分离架构。后端Spring Struts2或更佳选择Spring MVC / Spring Boot提供RESTful API前端使用Vue.js、React等框架用户体验和开发效率会大幅提升。引入ORM框架用MyBatis或Hibernate替换手写的JdbcTemplate能更高效、更面向对象地操作数据库。加入缓存在热门宠物查询、分类信息等读多写少的场景引入Redis能极大减轻数据库压力。引入消息队列对于像“用户下单后发送通知邮件”这类非核心、耗时的操作可以放入RabbitMQ或Kafka队列异步处理提升系统响应速度。容器化与部署学习使用Docker将你的MySQL、Redis、应用服务器打包成镜像用Docker Compose编排实现一键部署和环境一致性。最后关于视频和源码我的建议是视频是“拐杖”帮你理解流程和思路源码是“地图”告诉你具体的实现细节。但真正学会走路必须自己动手敲一遍代码把每个配置都理解透把每个bug都调试通。这个过程积累的经验远比直接复制粘贴代码要宝贵得多。当你把这个宠物网站项目从头到尾独立实现出来再去看Spring Boot、微服务这些更现代的技术你会发现底层原理都是相通的学习起来会事半功倍。本文还有配套的精品资源点击获取