
写这篇东西之前我先说说为什么是这个标题组合。J2EE、IDEA、SpringBoot、Maven、Thymeleaf这套词放在一起就是目前企业里最常见的Java Web开发栈。很多刚入行的朋友在网上搜“idea安装教程”“maven是干嘛的”“springboot thymeleaf 热更新”其实就是卡在了从“会写Java”到“能开发一个完整系统”的这条路上。这篇文章就围绕这套技术栈从环境搭建到项目落地把每一步的来龙去脉、配置原理和我实际踩过的坑都写清楚。适合两类人看一是准备做毕业设计或课程项目的在校生二是刚进公司被分到老项目、需要快速上手服务端渲染项目的初级开发。1. 为什么选这套技术栈从J2EE到SpringBoot的演进1.1 J2EE在企业开发里到底承担什么角色J2EEJava 2 Platform Enterprise Edition这个名字虽然老但现在面试还经常被提起。它本质上是一套规范定义了一个企业级应用应该怎么分层、组件之间怎么协同。 Web层有Servlet和JSP业务层有EJB数据层连接JDBC、JNDI这些设施。整套体系在2000年前后非常火解决了一个大问题让Java能够处理高并发、事务、分布式这类企业级需求。但J2EE的问题是“规范”和“实现”绑得太紧配置繁琐。早年间写一个简单的Servlet要配置web.xml要部署到外置容器要处理各种XML描述符跟写业务代码的时间差不多。很多老程序员现在提到EJB还心有余悸就是被那一堆复杂配置折磨过。所以在这个项目标题里J2EE更像是一条技术路线而不是指你要真的去用EJB这些老古董它意味着你要按企业级分层的思路来设计系统Controller、Service、DAO该分的层一分不能少。1.2 SpringBoot解决了什么痛点SpringBoot的出现本质上是把原来Spring框架里繁琐的XML配置全部“规范化默认值”。按约定大于配置的思路你只需要引入一个spring-boot-starter-web它自动帮你把Spring MVC、内嵌Tomcat、Jackson这些组件都装配好main方法一启动整个Web应用就能跑了不需要再把war包丢进Tomcat里。这套机制在项目标题里的价值是“快速实现”。原来搭一个SpringMVC项目可能要花半天配置文件一大摞SpringBoot初始化项目只需要几十秒。它让开发者把精力从环境搭建中解放出来回到业务本身。热词里反复出现“springboot教程”“springboot框架介绍”也说明这是当前绝大多数入门者的第一站。对于企业内部系统、个人毕设、中小型项目来说SpringBoot足够稳定、生态足够丰富遇到问题随便一搜就有答案。1.3 Thymeleaf为什么适合做服务端渲染Thymeleaf是一个模板引擎负责在服务端把数据填充进HTML页面。它和JSP最大的区别是JSP只能在Servlet容器里被解析而Thymeleaf页面本身是合法的HTML直接用浏览器打开就能看到静态效果。这意味着前端开发人员和后端开发人员可以并行工作后端把数据填进去页面立刻就动态化。我个人的体会是对于不打算做前后端分离、又需要SEO支持或页面快速交付的项目Thymeleaf的收益非常高。它的语法就是标签属性比如th:text${name}带th:前缀后端渲染时替换文本。模板文件里没有一堆Java代码比JSP干净得多。再加上SpringBoot提供了spring-boot-starter-thymeleaf这个起步依赖配置量接近于零。你把模板放在src/main/resources/templates目录下Controller返回一个字符串SpringBoot自动找到对应的HTML文件整个过程非常顺畅。2. 环境准备IDEAMavenJDK组合的实操要点2.1 先从JDK版本聊起选择比想象中重要项目标题里没有写JDK版本但这里有个大家常忽略的坑。SpringBoot 2.x版本默认推荐JDK 8以上SpringBoot 3.x则强制要求JDK 17以上。很多人在热词里搜“springboot版本太高”就是因为创建项目时选了最新的SpringBoot 3.x而本地装的还是JDK 8启动直接报UnsupportedClassVersionError。我的建议是如果是学习或毕业设计直接用SpringBoot 2.7.x JDK 8这个组合教程最多、网上资料最全遇到问题最容易查到解决方案。如果你想跟上技术前沿用SpringBoot 3.x就必须配JDK 17而且要注意以前的javax.*包都改成了jakarta.*老代码不能直接迁移。先把这个版本对应关系理清能省下后面三天排查问题的时间。组合方案SpringBoot版本JDK版本适用场景保守稳定型2.7.xJDK 8学习、毕设、企业存量技术栈主流推荐型2.7.x / 3.2.xJDK 17新项目、团队技术栈较新尝鲜型3.3.xJDK 21纯学习新技术不考虑兼容2.2 IDEA安装与配置选对版本、配好Project StructureIDEA官方分两个版本社区版Community和旗舰版Ultimate。社区版免费对SpringBoot开发来说基础的代码提示、运行调试都够用。旗舰版需要付费订阅但支持Spring相关专门功能。网上大量流传的“idea破解版安装教程”“idea激活码”这类内容我不建议大家碰用社区版完全可以完成这个项目而且对个人长期发展更稳妥。装好IDEA之后第一步是配置JDK。打开File - Project Structure - Project把SDK设置成刚才安装的JDK路径。很多人启动项目报“No JDK specified”或者“Invalid source release: 17”根源就是这步漏了。另外Settings - Build, Execution, Deployment - Compiler - Java Compiler里Target bytecode version要和项目的JDK版本保持一致否则编译期会报错。这里再说一个细节IDEA默认会让你从“New Project”开始但创建SpringBoot项目时直接选Spring Initializr模板。它会让你选择SpringBoot版本和初始依赖比手动建Maven项目再自己往pom.xml里写依赖要靠谱得多。热词里反复出现“idea运行javaweb项目配置”其实很多人并不是不会写代码而是卡在了不知道怎么把Tomcat和项目关联起来。SpringBoot直接把Tomcat嵌进去了这是它比传统JavaWeb项目简单的重要原因。2.3 Maven安装与配置搞懂原理才不会被仓库折磨很多人把Maven当成一个“下载工具”这个理解不准确。Maven是项目构建管理工具核心做了三件事依赖管理、构建流程管理、项目信息管理。你只需要在pom.xml里声明用了哪些库Maven就从仓库把jar包拉下来并且把依赖的传递关系理清楚。maven是干嘛的这个问题可以用一句话解释它管你项目里所有外部的“零件”以及怎么把这些零件组装打包成可运行的成果。安装Maven的步骤分为三步每步都有对应的坑。第一去官网下载Maven二进制压缩包注意不要下载source版本用apache-maven-3.8.x-bin.tar.gz或apache-maven-3.8.x-bin.zip。解压到一个纯英文路径比如D:\maven。中文路径会导致某些插件异常。第二配置环境变量MAVEN_HOME指向解压目录然后在Path里加上%MAVEN_HOME%\bin。完成后在命令行执行mvn -v能输出版本信息就是安装成功。第三这一步非常关键。修改Maven安装目录下的conf/settings.xml配置本地仓库路径和阿里云镜像仓库。因为默认的中央仓库服务器在国外下载依赖极慢甚至超时。配置方法如下settings xmlnshttp://maven.apache.org/SETTINGS/1.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd !-- 本地仓库位置默认在用户目录/.m2/repository -- localRepositoryD:/maven_repository/localRepository mirrors mirror idaliyun/id namealiyun maven/name urlhttps://maven.aliyun.com/repository/public/url mirrorOfcentral/mirrorOf /mirror /mirrors /settings这里再解释一下为什么“本地仓库”这个配置很重要。Maven把下载过的jar包缓存在本地下次构建不再重复下载。但这个目录默认在C盘用户目录下时间久了能占几个G系统盘容易满了。把它迁移到D盘这类位置既能给C盘瘦身也方便以后直接拷贝仓库给同事用。我把本地仓库改到D盘后再配合阿里云镜像依赖下载速度从原来的经常失败变成了秒级完成效率提升非常明显。配置完以后在IDEA的Settings - Maven里把Maven home directory指向你刚下载的版本User settings file勾选Override并指向刚才的settings.xmlLocal repository会自动读取。这一步经常被忽略结果IDEA还在用自带的Maven导致线上能编译的代码本地报找不到依赖。3. 项目搭建与核心实现从空项目到能跑的完整系统3.1 创建SpringBoot项目并理解目录结构打开IDEA选择New Project - Spring Initializr三个关键字段要填对。Name是项目名比如manager-systemType选MavenPackage name保持和Group一致。依赖方面这个项目需要选Spring Web、Thymeleaf。如果后续要连接数据库再加Spring Data JPA或MyBatis Framework。为了展示完整用户登录流程我建议先把数据库相关依赖也选上或者用内存结构演示逻辑。创建完成后你会看到SpringBoot的标准目录结构src/main/java/com/example/managersystem - ManagersystemApplication.java // 启动类 - controller/ // 控制层 - service/ // 业务层 - mapper/repository/ // 数据访问层 - entity/ // 实体类 src/main/resources - templates/ // Thymeleaf模板页面 - static/ // css、js、图片等静态资源 - application.yml // 配置文件 src/test/java // 单元测试刚接触SpringBoot的人往往不理解这些目录为什么必须这样分层。我用装修来类比Controller是前台接待只负责收需求、给回复不做具体业务Service是项目经理知道整个业务流程怎么安排Mapper/Repository是施工队直接从数据库取材料。这样每一层只干一件事出问题容易定位也方便后期维护。ManagersystemApplication.java这个启动类很重要它有SpringBootApplication注解。这个注解是由三个注解组合而成的SpringBootConfiguration表示这是一个配置类EnableAutoConfiguration表示自动装配ComponentScan负责扫描当前包及子包下的Spring组件。所以你的Controller、Service类一定要放在启动类所在包的子包内否则扫描不到请求全部404。3.2 pom.xml依赖配置与版本管理我用SpringBoot 2.7.x版本做示例pom.xml的核心依赖如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies !-- Web开发核心包含SpringMVC和内嵌Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Thymeleaf模板引擎 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency !-- 热更新工具修改代码后自动重启 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-devtools/artifactId optionaltrue/optional /dependency !-- 参数校验等等 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency /dependencies这个parent节点的作用极其重要。它相当于一个“中央标准”把SpringBoot所有起步依赖的版本号统一管理了。你不需要在dependency里写版本号Maven会自动按照parent里定义的版本去下载。这样全家桶之间不会有版本冲突。如果把parent删掉你会发现一连串“找不到版本”的错误那是因为你没有遵守版本约定的规则。3.3 配置文件application.yml中的关键参数server: port: 8080 spring: thymeleaf: cache: false encoding: UTF-8 mode: HTML datasource: url: jdbc:mysql://localhost:3306/manager_system?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver # MyBatis相关可配置的驼峰映射、控制台SQL日志等 mybatis: configuration: map-underscore-to-camel-case: true logging: level: com.example.managersystem.mapper: debug很多人搜“springboot配置文件怎么写的”时会看到各种说法但记住三个最关键的设置就行thymeleaf.cachefalse关闭模板缓存这样修改HTML后刷新页面就能看到变化不用每次重启项目encodingUTF-8防止中文乱码datasource配置数据库连接。serverTimezoneAsia/Shanghai必须加否则MySQL驱动会因为你本机时区和数据库时区不一致而报错。如果你的环境暂时没装MySQL也可以用H2内存数据库来做演示。只需要在pom.xml里加上com.h2database:h2依赖然后把url改成jdbc:h2:mem:testdb项目启动时会自动创建一个内存数据库关掉项目数据就消失非常适合学习和测试。3.4 核心功能实现用户登录与信息列表为了让项目变现为“一个系统”我在这里实现两个最经典的功能模块用户登录和用户列表展示。先看实体类和模拟数据源。由于本篇重点是集成流程我用一个Map模拟用户数据实际项目请换成数据库// entity/User.java public class User { private Integer id; private String username; private String password; private String realName; // getter/setter省略 }// service/UserService.java Service public class UserService { private final MapInteger, User userMap new ConcurrentHashMap(); public UserService() { User admin new User(); admin.setId(1); admin.setUsername(admin); admin.setPassword(123456); admin.setRealName(系统管理员); userMap.put(admin.getId(), admin); } public User login(String username, String password) { for (User user : userMap.values()) { if (user.getUsername().equals(username) user.getPassword().equals(password)) { return user; } } return null; } public ListUser listAll() { return new ArrayList(userMap.values()); } }接着是Controller。这里要解释一个初学者容易搞混的概念Controller和RestController的区别。RestController是Controller加ResponseBody的组合它默认把返回的对象序列化成JSON。而我们用Thymeleaf做服务端渲染Controller返回的是模板名称所以必须用Controller或RestController但方法上标注ResponseBody让SpringMVC去找对应的HTML模板。Controller public class UserController { Autowired private UserService userService; GetMapping(/login) public String loginPage() { return login; // 匹配 templates/login.html } PostMapping(/login) public String doLogin(RequestParam String username, RequestParam String password, HttpSession session, Model model) { User user userService.login(username, password); if (user ! null) { session.setAttribute(loginUser, user); return redirect:/user/list; } model.addAttribute(error, 用户名或密码错误); return login; } GetMapping(/user/list) public String userList(Model model) { model.addAttribute(users, userService.listAll()); return userList; } }这段代码里有几个值得学习的点。RequestParam接收表单传过来的username和password如果参数名称不对Spring会直接抛出MissingServletRequestParameterException。登录失败时用model.addAttribute(error, 用户名或密码错误)把错误信息带回页面Thymeleaf模板会用th:text把它显示出来。登录成功用redirect:/user/list这是一个重定向好处是刷新列表页不会再次提交表单避免了重复登录操作。4. Thymeleaf页面渲染实操从静态到动态的完整过程4.1 模板放在哪里、怎么被找到Thymeleaf模板默认放在src/main/resources/templates目录下。比如你在Controller里写了userListSpringBoot就会自动找templates/userList.html。如果模板文件不存在会启动报Circular view path [userList]错误或者500错误。区分一下如果返回的是redirect:userList那叫重定向会重新发起一次请求如果是return userList那叫视图解析直接找模板渲染。有一个我见过很多次的坑在Controller里用index作为返回字符串但是templates目录下没有index.html而src/main/resources/static/下也没有index.html就导致404。SpringBoot的静态资源查找有一套优先级顺序先找Controller映射再找模板最后找静态文件。所以模板文件放错目录症状就是页面找不到排查时先确认模板路径。4.2 页面代码示例登录页与列表页先看登录页。这个页面里用了th:if判断错误信息是否存在用th:action指定表单提交地址。!DOCTYPE html html langzh-CN xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 title用户登录/title /head body h2系统登录/h2 !-- 如果error不为空就显示 -- p th:if${error} stylecolor:red th:text${error}这里显示错误信息/p form th:action{/login} methodpost div label用户名/label input typetext nameusername/ /div div label密码/label input typepassword namepassword/ /div button typesubmit登录/button /form /body /html再看列表页。列表页用th:each遍历用户集合显示每个用户的ID、用户名和姓名。!DOCTYPE html html langzh-CN xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 title用户列表/title /head body h2用户列表/h2 table border1 thead tr thID/th th用户名/th th姓名/th /tr /thead tbody tr th:eachuser : ${users} td th:text${user.id}1/td td th:text${user.username}admin/td td th:text${user.realName}系统管理员/td /tr /tbody /table /body /html4.3 常见Thymeleaf语法速查与踩坑服务端渲染最常见的需求就是这个表格模式。我自己带人做项目时经常让他们把下面这张表背熟80%的页面问题都能解决。需求语法说明输出变量值th:text${user.username}输出转义后的文本输出HTML内容th:utext${htmlContent}不转义慎用有xss风险循环遍历th:eachitem : ${list}遍历List或数组条件判断th:if${user ! null}条件为真才渲染该标签相反条件th:unless${user ! null}条件为假才渲染拼接URLth:href{/user/detail(id${user.id})}生成带参数的URL表单值回显th:value${user.username}用于input的value属性这里要特别提一下{}这个表达式它是URL表达式会自动加上应用的context-path。如果项目部署时配置了server.servlet.context-path/manager那th:href{/user/list}会正确生成/manager/user/list。如果直接在href里写死路径部署到带前缀的环境就会全部404。这个细节我见过不止一次在新人代码里出现。还有关于“热更新”的配置。热词里有一项是“springboot thymeleaf 热更新”。实现方式不难第一步引入spring-boot-devtools依赖第二步在application.yml里设置spring.thymeleaf.cachefalse。DevTools会监控classpath下文件变化当模板文件被修改时自动重启应用刷新页面就能看到新效果。不过要注意DevTools默认只监控制定路径有时候你改了HTML但它没有触发热重启可以按CtrlF10手动触发。如果重启后还是旧页面十有八九是浏览器缓存了页面试试无痕窗口。代码写法上还有一个导致页面显示原始标签的问题如果HTML文件没有在html标签上写xmlns:thhttp://www.thymeleaf.orgIDE会报警告但功能上不会出错。真正会出错的是标签属性写错比如把th:each写成th:forEach运行时会提示Could not parse as expression。报错信息会在控制台里给出非常明确的模板文件路径和行号直接定位。5. 常见问题与排查技巧实录5.1 Maven依赖下载慢、下载失败的整套解决思路这个问题排在榜首毫不意外。核心原因就是访问中央仓库太慢。解决路径我已经在前面讲了修改settings.xml加阿里云镜像。但有一个更隐蔽的问题换完镜像后明明之前的jar包下载了70%被中断留下的.lastUpdated文件会导致Maven认为这包已经下载过反复报错但不重新下载。遇到这种情况最粗暴有效的办法是去本地仓库目录里找到对应依赖路径下的.lastUpdated文件全选删除然后重新执行mvn clean install。我在多人协作时也推荐一个命令组合mvn clean install -DskipTestsclean会清掉target目录里的旧编译产物install会把项目安装到本地仓库-DskipTests跳过测试编译。这个命令在IDEA右侧的Maven工具窗口里也可以直接双击执行。如果仍然失败把IDEA里Maven的settings.xml路径再确认一遍经常有人改的是Maven安装目录的settings.xml但IDEA读的是C:\Users\用户名\.m2\settings.xml两个不是同一个文件。5.2 SpringBoot版本太高引发的兼容问题热词里有一条“springboot版本太高”这个我实在太有感触。SpringBoot版本更新快很多教程还是基于2.x写的。如果你新建项目时IDEA默认给了3.x版本会碰到三类问题每一个都够折腾一晚上。第一JDK版本要求变高。3.x必须要JDK17这是硬指标编译期就会报错。第二javax变成jakarta。2.x里导入的是javax.servlet.http.HttpSession3.x里必须换成jakarta.servlet.http.HttpSession。第三部分第三方库没有及时适配。某些企业里的内部组件或者老版本的MyBatis、MySQL驱动在SpringBoot 3.x下会出现莫名其妙的兼容问题。所以我会反复强调学习阶段用2.7.x稳定压倒一切。不要看到有新版本就往上冲生产项目求的不是新是稳定。5.3 端口占用和时区问题启动项目时报Port 8080 was already in use说明你的8080端口被别的进程占用了。两个解决办法一是找到占用进程Windows在命令行执行netstat -ano | findstr 8080然后taskkill /PID 进程号 /F。二是在application.yml里换端口比如server.port: 8081。我一般用第二种方式因为开发时并发启动多个项目是常态每个项目用不同端口避免互相干扰。还有时区问题。连接MySQL时驱动会读取数据库服务器时区如果数据库的时区和本机不一致可能报The server time zone value is unrecognized。解决办法是在url里带serverTimezoneAsia/Shanghai。如果还不行去MySQL命令行执行set global time_zone 08:00也能解决。5.4 小技巧把IDEA控制台的乱码一次性搞定这个不算什么官方问题但几乎每个Windows上跑SpringBoot的人都遇到过。控制台信息乱码通常有两个来源第一个是项目运行时的输出编码第二个是Maven构建时的日志编码。处理办法是在IDEA安装目录/bin/idea64.exe.vmoptions文件末尾加上-Dfile.encodingUTF-8再看Settings - Editor - File Encodings把Global Encoding和Project Encoding都设为UTF-8最后在Help - Edit Custom VM Options里也写上-Dfile.encodingUTF-8。三处配置齐了再用System.out.println(测试中文)验证一下。我遇到过最离谱的一次乱码是数据库查询结果里的中文字符变成问号那是数据库表本身的字符集不对。解决办法是把建表语句里的DEFAULT CHARSETutf8mb4明确写上并在连接串里加useUnicodetruecharacterEncodingUTF-8。排查乱码问题时先分清是哪一层出的问题页面乱码是模板编码控制台乱码是IDEA配置数据库乱码是驱动连接参数和库表字符集。6. 写在最后的实操复盘这套系统真正跑起来之后我建议你再做三件小事。第一给登录接口加上简单的输入校验空用户名、空密码、密码长度不够这些场景都处理掉这是从“会跑”到“能上线”的分水岭。第二把模拟数据换成真实数据库连上MySQL用Spring Data JPA或MyBatis做一次CRUD你会对数据访问层有更深的理解。第三给项目加上一个简单的操作日志记录用户登录时间、IP和操作内容这个能力在以后的企业项目里几乎是必考的。我个人在实际操作中的体会是这套技术栈最大的价值不是某一个具体框架而是让你理解“分层”这件事。Controller管请求和跳转Service管业务规则模板管展示。当你想改页面样式时不用动Java代码当你想改登录逻辑时不用去翻HTML。这种解耦能力是经历了很多个混乱项目之后才深感珍贵的。如果你现在刚开始接触这套东西建议按文章顺序完整敲一遍不要直接复制粘贴到自己的项目里手打代码的过程本身就是排查问题的最好训练。卡住的时候别气馁Java Web的花样就这么多翻过这几座山后面是一片开阔地。