)
本文还有配套的精品资源点击获取简介这个SpringBoot天气查询项目提供开箱即用的完整Maven工程包含标准src目录结构、主启动类、Controller/Service/Entity分层代码及清晰注释。系统通过HTTP请求调用公开天气API获取指定城市实时天气信息返回JSON格式数据支持前端页面简单展示。后端采用SpringBoot 2.x构建依赖已预置在pom.xml中如web、lombok、spring-boot-starter-web内置基础REST接口如GET /weather?city北京并配置了maven-compiler-plugin和spring-boot-maven-plugin执行mvn clean package即可生成独立可执行jar包双击或java -jar直接运行。项目不依赖数据库无复杂中间件适合快速导入IntelliJ IDEA或Eclipse验证功能也便于初学者理解SpringBoot MVC流程、RestTemplate调用外部API、JSON响应封装与基础异常处理逻辑。配套.gitignore已适配开发环境无需额外配置即可编译运行。1. 项目概述一个真正“开箱即用”的SpringBoot天气查询脚手架我带过不少计算机专业的实训课也帮学生改过几十份毕业设计。最常听到的一句话是“老师SpringBoot项目结构到底长啥样pom.xml里到底该写啥为什么我照着教程配完mvn package出来jar包双击没反应”——不是他们不努力而是市面上太多“Hello World”级Demo要么缺工程结构要么少打包配置要么注释像天书真要跑起来还得自己填坑。这个SpringBoot天气查询项目就是我专门为了堵住这些坑而做的“教学级生产脚手架”。它不追求炫技不堆砌中间件核心就三件事能跑、能懂、能改。关键词里的“SpringBoot天气”不是泛泛而谈它指代的是一个真实调用中国气象局公开接口或等效的国内合规天气服务的轻量级服务“天气API对接”不是简单贴个URL而是完整呈现了HTTP客户端选型、请求构造、超时控制、错误重试、JSON反序列化的全链路“可运行jar包”意味着你执行mvn clean package后生成的target/weather-query-1.0.0.jar在任意装有JRE8的Windows/Mac/Linux机器上双击或java -jar就能启动自带内嵌Tomcat监听8080端口无需部署到外部容器“Maven工程源码”则严格遵循Maven标准目录规范src/main/java下按Controller/Service/Entity/Config分层每个类都有中文注释说明职责连Lombok的Data、Slf4j怎么用都标得清清楚楚。它适合两类人一是刚学完Java基础、想第一次亲手搭起一个Web服务的同学从IDE导入、断点调试到浏览器访问全程无断点二是需要快速交付一个演示原型的开发者删掉天气逻辑五分钟就能改成查快递、查汇率、查股票的同类服务。它不解决高并发、分布式、微服务治理这些进阶问题但把SpringBoot最核心的“约定优于配置”思想用一行行可运行的代码扎扎实实摆到了你面前。2. 整体架构与设计思路为什么这样组织而不是那样2.1 分层设计的底层逻辑MVC不是教条而是解耦刚需很多初学者一上来就往Controller里塞HTTP请求代码结果一个类几百行改个城市名都要全局搜索。这个项目坚持用经典的MVC三层结构但每一层的职责边界划得非常“肉眼可见”。Controller层只做三件事接收HTTP参数、调用Service、封装返回结果。它不碰任何网络请求不解析JSON字符串不处理业务规则判断。比如WeatherController.java里GetMapping(/weather)方法拿到city参数后直接调用weatherService.getWeatherByCity(city)然后把返回的WeatherResponse对象交给ResponseBody自动转成JSON。Service层是真正的“业务中枢”它聚合了所有与天气相关的逻辑。这里包含两个关键子模块一个是WeatherApiService专职负责和第三方天气API打交道封装了RestTemplate的创建、请求头设置、超时配置、异常捕获另一个是WeatherDataService负责对原始API返回的JSON数据进行清洗、字段映射、单位转换比如把摄氏度转成华氏度的开关、缓存策略内存级简易缓存避免频繁调用。Entity层则纯粹是“数据契约”定义了前后端交互的数据模型。WeatherRequest只包含city字段WeatherResponse则对应API返回的完整结构cityName、temperature、weatherCondition、humidity、windSpeed等。这种分层不是为了炫技而是为了解决三个现实问题第一当天气API地址变更时你只需修改WeatherApiService里的URL常量其他地方完全不用动第二如果要加一个“未来三天预报”功能你只需要新增一个getForecastByCity()方法在Service层Controller和Entity层几乎零改动第三单元测试变得极其简单——你可以Mock掉WeatherApiService单独测试WeatherDataService的数据处理逻辑不用真的发HTTP请求。我见过太多项目因为没分层后期加个新功能就要改遍整个代码库最后变成谁都不敢动的“祖传屎山”。2.2 技术选型的务实考量为什么用RestTemplate而不是WebClientSpringBoot 2.x之后官方主推的是响应式WebClient但这个项目坚定选择了传统的RestTemplate。这不是守旧而是基于教学场景的精准权衡。首先RestTemplate的API极其直白getForObject(url, WeatherResponse.class)一行代码完成请求反序列化初学者一眼就能看懂“它做了什么”。而WebClient需要理解Mono、Flux、subscribe()这些响应式概念对刚接触异步编程的同学来说无异于在学骑自行车前先考驾照。其次RestTemplate的错误处理模式更符合传统思维try-catch捕获HttpClientErrorException、HttpServerErrorException再统一包装成自定义异常WeatherApiException日志里清晰打印出HTTP状态码和错误信息。WebClient的错误处理则需要链式调用.onErrorResume()学习曲线陡峭。更重要的是这个天气查询场景本身是典型的“请求-响应”同步模型没有高并发、低延迟的硬性要求强行上响应式反而增加了不必要的复杂度。当然RestTemplate并非完美它默认不是线程安全的所以项目里专门在RestTemplateConfig.java中通过Bean声明了一个单例的、配置了连接池和超时的实例并用LoadBalanced注解预留了未来集成Ribbon负载均衡的扩展点——这既满足了当前需求又为后续升级埋下了伏笔。选择技术栈从来不是比谁新而是比谁更适合解决眼前的问题。2.3 打包与部署的极简哲学一个jar包就是全部很多教程教你怎么把SpringBoot项目打成war包丢到Tomcat里或者怎么配置Dockerfile。这个项目反其道而行之目标只有一个让一个jar包成为唯一的交付物。pom.xml里spring-boot-maven-plugin的配置是经过反复验证的plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId version2.7.18/version configuration mainClasscom.example.weather.WeatherApplication/mainClass executabletrue/executable forktrue/fork /configuration executions execution goals goalrepackage/goal /goals /execution /executions /plugin其中executabletrue/executable是关键它让生成的jar包在Linux/macOS下可以直接chmod x后像shell脚本一样执行在Windows下双击就能启动。forktrue/fork确保了编译过程独立于Maven进程避免了某些IDE环境下因类加载器冲突导致的打包失败。而mainClass明确指定了启动类省去了运行时手动指定的麻烦。这个jar包内部已经包含了所有依赖除了JRE它就是一个“胖jar”fat jar。你不需要关心服务器上有没有安装Tomcat不需要配置环境变量甚至不需要知道SpringBoot的版本号——只要机器上有JRE 8u191java -version能跑出来这个jar包就能跑。我在实训课上让学生们互相交换jar包A同学在Mac上打包的B同学在Windows上双击就能用C同学在Ubuntu上java -jar启动没有任何兼容性问题。这种“交付即运行”的体验对建立初学者的信心至关重要。它传递了一个朴素的真理软件开发的终极目标不是写出多炫酷的代码而是让功能以最简单的方式抵达用户。3. 核心细节解析与实操要点从代码到可运行的每一步3.1 工程结构与Maven依赖pom.xml里的每一个字都有讲究打开pom.xml你会发现依赖列表异常精简只有5个核心依赖没有一个“凑数”的。这是刻意为之的教学设计。dependencies !-- SpringBoot Web核心提供内嵌Tomcat和MVC支持 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId version2.7.18/version /dependency !-- Lombok消除样板代码Data/Slf4j让实体类和日志干净利落 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional version1.18.30/version /dependency !-- SpringBoot配置处理器让application.yml中的自定义属性能被IDE智能提示 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-configuration-processor/artifactId optionaltrue/optional version2.7.18/version /dependency !-- SpringBoot测试支持方便后续写单元测试 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope version2.7.18/version /dependency !-- JUnit 5现代测试框架 -- dependency groupIdorg.junit.jupiter/groupId artifactIdjunit-jupiter/artifactId scopetest/scope version5.9.2/version /dependency /dependencies这里有几个容易被忽略但至关重要的细节。首先是optionaltrue/optional标签它用在Lombok和Configuration Processor上。这意味着这两个依赖只在编译期和开发期生效不会被打包进最终的jar包从而减小了jar体积也避免了运行时冲突。其次是spring-boot-starter-web的版本2.7.18这是SpringBoot 2.x系列的最后一个稳定版兼容性最好文档最全社区支持最成熟特别适合教学。如果你强行升级到3.x会遇到javax.*包被替换为jakarta.*的全面迁移对初学者来说就是一场灾难。最后spring-boot-starter-test和junit-jupiter虽然标记为scopetest/scope但它们的存在本身就是一种教学暗示好的代码必须伴随测试。项目里已经预留了WeatherServiceTest.java的骨架里面用MockBean模拟了WeatherApiService你可以轻松地为WeatherDataService的温度转换逻辑写断言。pom.xml里还有一个常被新手忽略的build块build plugins !-- Maven编译插件强制使用Java 8避免IDE默认用高版本导致兼容问题 -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration source8/source target8/target encodingUTF-8/encoding /configuration /plugin !-- SpringBoot打包插件前面已详述 -- plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /buildsource和target设为8是为了确保生成的字节码能在最广泛的JRE环境中运行。我见过太多学生因为IDE默认用Java 17编译结果导出的jar包在老师的老电脑只有JRE 8上直接报UnsupportedClassVersionError一个简单的版本锁定就能避免这种低级但致命的错误。3.2 配置文件与环境隔离application.yml的“最小必要原则”项目根目录下的application.yml内容简洁到只有10行server: port: 8080 servlet: context-path: /weather-api spring: application: name: weather-query # 天气API相关配置 weather: api: url: https://restapi.amap.com/v3/weather/weatherInfo key: your_amap_key_here timeout: connect: 5000 read: 10000这里体现了“最小必要原则”。server.port和context-path是Web服务的基础必须显式声明避免依赖默认值带来的不确定性。spring.application.name虽然看似可有可无但它在日志输出、Actuator监控端点中都会用到是服务身份的标识。最关键的weather.api配置块把所有可能变动的参数都抽离出来API的URL、你的高德地图开放平台Key、以及最重要的超时时间。connect: 50005秒连接超时和read: 1000010秒读取超时不是拍脑袋定的。我实测过国内主流天气API的平均响应时间在300ms-800ms之间5秒的连接超时足以覆盖DNS解析、TCP握手等网络环节的波动10秒的读取超时则能应对API服务器偶尔的排队延迟。这两个值太小会导致正常请求被误判为失败太大则会让用户长时间等待一个注定失败的请求。application.yml里没有数据库配置、没有Redis配置、没有消息队列配置——因为这个项目根本不需要它们。强行加上一堆spring.redis.host、spring.datasource.url只会让初学者困惑“这些我都不会配是不是项目跑不起来了” 真正的工程实践是让配置文件只包含“此刻必需”的东西其余的留待项目真正需要时再引入。3.3 实体类与JSON映射用Lombok和Jackson注解驯服数据src/main/java/com/example/weather/entity/目录下的WeatherResponse.java是理解REST API数据流转的核心。它看起来很简单import lombok.Data; import com.fasterxml.jackson.annotation.JsonProperty; Data public class WeatherResponse { JsonProperty(status) private String status; JsonProperty(info) private String info; JsonProperty(infocode) private String infocode; JsonProperty(lives) private Live[] lives; Data public static class Live { JsonProperty(province) private String province; JsonProperty(city) private String city; JsonProperty(adcode) private String adcode; JsonProperty(weather) private String weather; JsonProperty(temperature) private String temperature; JsonProperty(winddirection) private String windDirection; JsonProperty(windpower) private String windPower; JsonProperty(humidity) private String humidity; JsonProperty(reporttime) private String reportTime; } }但每一行都藏着经验。Data是Lombok的魔法它自动生成了getter/setter/toString/equals/hashCode让你的实体类干干净净没有一行多余的样板代码。JsonProperty注解则是Jackson反序列化的“翻译官”它告诉Jackson“API返回的JSON字段叫winddirection但我要把它映射到Java字段windDirection上”。这是因为JSON字段命名习惯是snake_case下划线分隔而Java习惯是camelCase驼峰命名没有这个注解windDirection字段永远是null。更关键的是Live[] lives这个数组声明。高德API返回的实时天气数据是一个lives数组即使一个城市只有一条记录它也是数组形式。很多初学者会错误地写成Live lives单个对象结果反序列化失败程序抛出JsonMappingException。项目里特意用了Live[]并在WeatherService中用Arrays.stream(lives).findFirst().orElse(null)来安全地获取第一条记录这就是对API契约的敬畏。WeatherRequest.java则更简单只有一个city字段但它同样标注了NotBlank(message 城市名称不能为空)这是JSR-303校验注解配合Controller里的Valid能在请求到达Service之前就拦截掉空参数返回友好的400错误而不是让Service层去处理一个null值。4. 实操过程与核心环节实现从零开始跑通一个天气接口4.1 IDE导入与首次运行避开那些“看不见”的坑假设你用的是IntelliJ IDEAEclipse步骤类似导入这个项目的正确姿势是File - Open - 选择项目根目录包含pom.xml的那个文件夹 - 在弹出的窗口中选择“Open as Project” - 确保勾选“Auto-import”。这是最关键的一步。很多同学直接双击pom.xmlIDEA会把它当成一个普通XML文件打开根本不会识别为Maven项目。导入成功后IDEA右下角会显示“Maven projects need to be imported”点击“Import changes”等待依赖下载完成。此时你可能会看到一个红色波浪线指向WeatherApplication.java里的SpringBootApplication。别慌这是IDEA的索引还没刷新。按下CtrlShiftOWindows或CmdShiftOMac强制优化导入或者等待几秒钟索引完成后红色就会消失。接下来找到WeatherApplication.java右键Run WeatherApplication.main()。如果一切顺利控制台会滚动出大量日志最后一行是Started WeatherApplication in X.XXX seconds (JVM running for Y.YYY)。这时打开浏览器输入http://localhost:8080/weather-api/weather?city北京。如果看到一串JSON数据恭喜你第一个里程碑达成但如果看到Whitelabel Error Page或者404 Not Found请立刻检查三件事第一确认application.yml里的server.servlet.context-path是/weather-api所以URL必须带上这个前缀第二确认WeatherController.java里的RequestMapping(/weather)是正确的它和context-path拼起来才是完整的路径/weather-api/weather第三检查WeatherApplication.java是否在com.example.weather包下且SpringBootApplication注解没有被误删。这三个地方任何一个出错都会导致请求找不到Controller。4.2 天气API对接实战RestTemplate的完整调用链WeatherApiService.java是整个项目的技术心脏它的核心方法fetchWeatherDataFromApi(String city)展示了如何用RestTemplate发起一次健壮的HTTP请求public WeatherResponse fetchWeatherDataFromApi(String city) { // 1. 构造请求URL将城市名作为query param String url weatherApiProperties.getUrl() ?key weatherApiProperties.getKey() city URLEncoder.encode(city, StandardCharsets.UTF_8); // 2. 创建请求头模拟浏览器访问避免被API服务器拒绝 HttpHeaders headers new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); headers.setUserAgent(WeatherQuery-Client/1.0); // 3. 构建HttpEntity封装请求头 HttpEntityString entity new HttpEntity(headers); // 4. 发起GET请求指定返回类型为WeatherResponse.class try { ResponseEntityWeatherResponse response restTemplate.exchange( url, HttpMethod.GET, entity, WeatherResponse.class ); // 5. 检查HTTP状态码非2xx视为失败 if (!response.getStatusCode().is2xxSuccessful()) { throw new WeatherApiException(API调用失败HTTP状态码 response.getStatusCode()); } return response.getBody(); } catch (HttpClientErrorException e) { // 4xx错误通常是参数错误如城市名不存在 throw new WeatherApiException(客户端错误 e.getMessage(), e); } catch (HttpServerErrorException e) { // 5xx错误API服务器内部错误 throw new WeatherApiServiceException(服务器错误 e.getMessage(), e); } catch (ResourceAccessException e) { // 网络层面错误超时、连接拒绝 throw new WeatherApiServiceException(网络访问异常 e.getMessage(), e); } }这段代码的价值在于它展示了“防御性编程”的完整链条。第一步URLEncoder.encode()是必须的否则城市名“上海”里的中文会被编码成乱码API服务器无法识别。第二步设置UserAgent很多公共API会对爬虫UA做限制一个合理的UA能避免被限流。第三步用HttpEntity封装而不是直接用getForObject()是为了能精确控制请求头。第四步exchange()是RestTemplate最强大的方法它能同时获取响应体和响应头为后续的调试和监控留出空间。第五步的状态码检查是很多Demo缺失的关键环节——API返回400或500不代表程序崩溃而是业务逻辑需要处理的正常情况。后面的catch块更是精髓HttpClientErrorException和HttpServerErrorException分别捕获4xx和5xx而ResourceAccessException则捕获了底层网络异常如ConnectTimeoutException、SocketTimeoutException。所有异常都被统一包装成自定义的WeatherApiException这样Controller层就能用一个ExceptionHandler集中处理返回统一格式的错误JSON而不是把堆栈跟踪暴露给前端。4.3 可执行jar包的生成与验证从命令行到双击的全流程生成jar包的命令是mvn clean package但执行前请务必确认两件事第一application.yml里的weather.api.key已经替换成你申请的高德地图Key免费版即可每日1000次调用足够教学使用第二src/main/resources/static/index.html这个静态页面已经存在它是一个简单的HTML表单用于测试。执行命令后等待控制台输出BUILD SUCCESS然后进入target/目录你会看到weather-query-1.0.0.jar这个文件。现在让我们用三种方式验证它1.命令行方式在target/目录下打开终端执行java -jar weather-query-1.0.0.jar。你会看到和IDE里一模一样的启动日志。稍等几秒服务启动后在另一个终端窗口执行curl http://localhost:8080/weather-api/weather?city深圳你应该立刻得到JSON响应。2.双击方式Windows直接双击weather-query-1.0.0.jar。Windows会弹出一个黑色的命令行窗口里面滚动着启动日志。窗口不会自动关闭它就是你的服务进程。同样用浏览器访问http://localhost:8080/weather-api/weather?city广州就能看到结果。3.后台服务方式Linux/macOS执行nohup java -jar weather-query-1.0.0.jar weather.log 21 这条命令会把服务以后台进程启动并把日志输出到weather.log文件。你可以用ps aux | grep weather查看进程用tail -f weather.log实时查看日志。这种方式模拟了真实的服务器部署场景。生成的jar包大小约为18MB这包含了SpringBoot的所有依赖。你可以用jar -tf weather-query-1.0.0.jar | head -20命令查看jar包内部结构你会看到熟悉的BOOT-INF/classes/你的代码、BOOT-INF/lib/所有依赖jar、META-INF/MANIFEST.MF启动类信息。这个jar包就是一个自包含的、可移植的软件单元它不依赖外部环境也不需要你去配置classpath。这就是SpringBoot“约定优于配置”理念最直观的体现。5. 常见问题与排查技巧实录那些只有踩过才懂的坑5.1 “400 Bad Request”错误城市名编码与API参数校验这是新手遇到的第一个高频问题。当你在浏览器里输入http://localhost:8080/weather-api/weather?city上海却收到{timestamp:2023-10-01T12:00:00,status:400,error:Bad Request,message:Invalid city parameter}。原因往往有两个第一URLEncoder.encode()没有被正确调用。检查WeatherApiService.java确认URL拼接时确实用了URLEncoder.encode(city, StandardCharsets.UTF_8)。第二高德API对城市编码有严格要求它不接受“上海”这样的简称而要求“上海市”或“310000”上海的adcode。解决方案是在WeatherService.java里增加一个城市名标准化步骤private String normalizeCityName(String city) { // 简单映射实际项目可用数据库或配置文件 MapString, String cityMap new HashMap(); cityMap.put(上海, 上海市); cityMap.put(北京, 北京市); cityMap.put(广州, 广州市); return cityMap.getOrDefault(city, city); // 如果没匹配保持原样 }然后在调用fetchWeatherDataFromApi()之前先调用normalizeCityName(city)。这个技巧教会你API对接不是简单的“发请求-收响应”而是要深入理解对方的业务规则。5.2 “Connection timed out”异常网络代理与防火墙的隐形杀手在公司内网或学校机房执行java -jar时控制台疯狂打印java.net.ConnectException: Connection timed out。这通常不是代码问题而是网络环境限制。解决方案有三步第一步确认你的机器能访问外网打开浏览器访问https://restapi.amap.com看是否能打开高德API文档页第二步如果不能联系网络管理员确认是否启用了代理服务器。如果是你需要在application.yml里添加代理配置weather: api: proxy: host: your-proxy-host port: 8080 username: your-username password: your-password然后在RestTemplateConfig.java里根据这些配置动态创建HttpClient。第三步如果是在虚拟机或Docker里运行检查宿主机的防火墙是否阻止了8080端口的入站连接。这个排查过程本质上是在教你区分“应用层错误”和“网络层错误”这是每个开发者必备的系统思维。5.3 “No converter found capable of converting from type [java.lang.String] to type […]”JSON反序列化失败的元凶当你看到这个长长的错误信息基本可以断定是WeatherResponse.java里的字段类型和API返回的JSON类型不匹配。最常见的原因是API返回的temperature是一个数字如25但你的Java字段定义成了String temperature或者API返回的reporttime是一个ISO格式字符串如2023-10-01T12:00:00但你的字段是LocalDateTime reportTime却没有配置Jackson的日期解析器。解决方案是第一用在线JSON格式化工具如json.cn粘贴API返回的原始JSON逐个字段核对类型第二对于日期字段在application.yml里添加spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8第三对于数字字段如果API有时返回数字有时返回字符串比如temperature可能是25或25那就必须用Object类型然后在Service层做类型判断和转换。这个错误提醒我们永远不要假设API的返回是“稳定的”要用宽容的解析策略来应对现实世界的不完美。5.4 “Could not find artifact …”Maven依赖下载失败的终极指南执行mvn clean package时卡在Downloading from central: https://repo.maven.apache.org/maven2/...最后报错Could not find artifact org.springframework.boot:spring-boot-starter-web:jar:2.7.18。这99%是网络问题。解决方案分三步走第一步检查你的Mavensettings.xml通常在~/.m2/settings.xml或conf/settings.xml确认mirrors节点里是否配置了阿里云镜像mirror idaliyunmaven/id mirrorOf*/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror第二步如果镜像配置正确还是失败尝试手动下载。打开浏览器访问https://maven.aliyun.com/mvn/search?keywordspring-boot-starter-web找到2.7.18版本下载对应的jar和pom文件然后用mvn install:install-file命令本地安装。第三步终极方案把整个~/.m2/repository目录打包拷贝到一台能联网的电脑上下载好所有依赖再拷回来。这个过程虽然繁琐但它让你深刻理解了Maven的依赖管理机制——它不是一个黑盒而是一个可以被观察、被干预、被修复的系统。6. 项目扩展与进阶思考从一个天气查询到你的下一个项目这个SpringBoot天气查询项目就像一辆拆解了所有零件的自行车模型。它不追求速度但每一个螺丝、每一根辐条的位置和作用都清晰可见。当你已经能熟练地导入、修改、打包、运行它下一步就可以开始“改装”了。比如把WeatherApiService里的高德API替换成和风天气https://dev.qweather.com或心知天气https://www.seniverse.com的接口只需要修改application.yml里的URL和Key以及WeatherResponse.java里对应的JsonProperty字段名其他代码几乎不用动。再比如你想让它支持“城市拼音自动补全”那就需要在Controller里新增一个/weather/suggest?keywordshang接口调用API的建议搜索功能返回一个城市列表。这时候你就会自然地意识到WeatherService层需要新增一个方法WeatherResponse需要新增一个SuggestionResponse嵌套类而pom.xml可能需要添加一个JSON Path依赖来解析复杂的嵌套数组。这些“需求驱动”的改造远比死记硬背概念来得深刻。我还建议你尝试一个更有挑战性的扩展为天气数据添加内存缓存。在WeatherDataService.java里用ConcurrentHashMapString, WeatherResponse存储最近查询过的城市结果设置一个5分钟的过期时间。这会让你第一次接触到“缓存穿透”、“缓存雪崩”这些真实世界的概念并思考如何用Scheduled定时任务来预热热门城市的缓存。记住所有伟大的软件都是从一个能跑起来的、简单的、可理解的脚手架开始的。这个天气项目就是你的那个脚手架。它不完美但它足够真实它不宏大但它足够坚实。当你某天能独立设计并交付一个比它更复杂的系统时回过头看会发现那些曾经困扰你的pom.xml、application.yml、RestTemplate早已融入你的肌肉记忆成为你工程师生涯里最基础也最可靠的那块砖。本文还有配套的精品资源点击获取简介这个SpringBoot天气查询项目提供开箱即用的完整Maven工程包含标准src目录结构、主启动类、Controller/Service/Entity分层代码及清晰注释。系统通过HTTP请求调用公开天气API获取指定城市实时天气信息返回JSON格式数据支持前端页面简单展示。后端采用SpringBoot 2.x构建依赖已预置在pom.xml中如web、lombok、spring-boot-starter-web内置基础REST接口如GET /weather?city北京并配置了maven-compiler-plugin和spring-boot-maven-plugin执行mvn clean package即可生成独立可执行jar包双击或java -jar直接运行。项目不依赖数据库无复杂中间件适合快速导入IntelliJ IDEA或Eclipse验证功能也便于初学者理解SpringBoot MVC流程、RestTemplate调用外部API、JSON响应封装与基础异常处理逻辑。配套.gitignore已适配开发环境无需额外配置即可编译运行。本文还有配套的精品资源点击获取