10:初窥后端世界
在前面几个题目中,你已经掌握了Java核心语法、SQL基础、网络与HTTP协议,甚至在上一题中通过HttpClient/OkHttp作为“客户端”去请求了别人的接口。
现在,角色对调!我们将进入现代Java后端开发的核心——使用Spring Boot作为“服务端”,为别人提供HTTP接口。这是一道压轴的综合实践题,只要顺着思路一步步尝试,你会发现做出一个真正的后端项目并没有那么难!
在本题中,你需要使用Maven来管理和构建你的项目,java版本使用21或更高版本(新版SpringBoot默认要求jdk21+)。
Java有一个原生的HTTP服务器实现:com.sun.net.httpserver.HttpServer,而你已经在第五题中使用过它了。而在实际开发中,SpringBoot、Quarkus、Micronaut等第三方框架更为常用,而其中SpringBoot是应用最广泛的一个框架。本题我们将试着使用SpringBoot来搭建我们的后端应用。
部分一:概念与思维转变(思考题)
Section titled “部分一:概念与思维转变(思考题)”SpringBoot是基于Spring的扩展框架,而Spring是一个开源的Java企业级开发框架,它的核心包括依赖注入(DI)、控制反转(IoC) 和 面向切面编程(AOP)。为了更好的学习SpringBoot,让我们先了解一下依赖注入与控制反转。
尝试用自己的话简单回答一下(简短清晰即可,拒绝成段复制粘贴):什么是依赖注入和控制反转?和我们以前写 Java 代码时直接 new Object() 有什么区别与好处?
部分二:任务实践
Section titled “部分二:任务实践”本题作为本次招新的最后一道题,会有很多实践内容,需要用到的相关知识可能需要你自行上网或通过AI学习解决。
任务1:Hello World
Section titled “任务1:Hello World”-
试着使用Spring Initializr或IDE的应用生成工具,快速创建一个SpringBoot工程。其中Project选择Maven,Spring Boot选不带SNAPSHOT字样的最新版,Packaging选择Jar,需要的依赖如下:
- Web
- Spring Web
- SQL
- Spring Data JPA
- MySQL Driver
- Developer Tools
- Lombok
然后在pom.xml中先注释掉spring-boot-starter-data-jpa、mysql-connector-j、spring-boot-starter-data-jpa-test这三条依赖项。
- Web
-
SpringBoot将后端拆分成了三层架构:Controller、Service、Repository。请你了解一下这三层架构中每一层的作用。
-
学习编写你的第一个Controller,提供一个GET请求接口:
- 访问/hello时,后端返回字符串"Hello glimmer!"。
- 打开浏览器或使用curl、亦或使用Postman、Apifox等工具测试,确保接口正常联通。
- 学习使用Lombok的注解@slf4j,试着在访问/hello时,通过log.info()打印一条日志并输出到控制台。
参考:
任务2:数据库联动与RESTful CRUD
Section titled “任务2:数据库联动与RESTful CRUD”结合你之前学到的SQL与HTTP知识,设计并实现一套简单的后端数据接口。比如,你可以设计一个用户管理系统,你的API应该至少能够支持新增用户、删除用户、修改用户信息和查询用户几种功能。
完成本任务,你需要在pom.xml中取消注释掉spring-boot-starter-data-jpa、mysql-connector-j、spring-boot-starter-data-jpa-test这三条依赖项。
-
结合前几题的知识,请你部署一个MySQL数据库,并自行设计其中的表和数据字段。
-
尝试使用JPA让你的SpringBoot项目连接上你的MySQL数据库。
-
尝试在你的SpringBoot项目中设计RESTful API接口,结合HTTP动词,实现一个完整的增、删、改、查(CRUD)逻辑。
- 新增:POST /api/users(接收 JSON body)
- 查询列表/单个:GET /api/users 或 GET /api/users/{id}
- 修改:PUT /api/users/{id}(更新指定内容)
- 删除:DELETE /api/users/{id}
-
现代接口通常不会直接返回纯文本或裸数据,请保证你的所有 API 接口返回统一的JSON数据结构,以达到统一响应格式的规范要求。
以下为一个示例:
{"code": 200,"message": "操作成功","data": {"id": 1,"name": "Atri","gender": "female","birthday": "8.28"}}
加分项(选做):简单的基础参数校验
尝试使用Spring Validation(如@NotNull,@NotBlank等)去校验用户调用POST请求时传来的 JSON 参数是否合法(例如:创建用户时,用户名不能为空)。
Spring Validation的依赖已给出,请在pom.xml中加入此依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId></dependency>部分三:无侵入的魔法——面向切面编程
Section titled “部分三:无侵入的魔法——面向切面编程”有的时候,我们希望记录每一个接口是谁调用的、耗费了多长时间,但不希望在每个方法里面手动写System.currentTimeMillis()这样的重复代码。你有什么想法吗?你可能会想到上一题中提到的责任链模式和Okhttp的Interceptor。幸运的是,SpringBoot也有类似的设计。不过今天,我们不用它们,让我们换一种方法来试一试。
面向切面编程(AOP) 是Spring的核心特性之一。如果说面向对象(OOP)是把业务逻辑按照“对象”切分成一个个模块,那么AOP就是把各个模块中共同需要的非业务逻辑(横切关注点)抽取出来,像一块“切面”一样统一织入到业务代码中。
请了解一下面向切面编程,思考一下,在什么场景下你会推荐使用AOP?它解决了传统面向对象(OOP)编程中的什么痛点?
参考文章:
接下来就来尝试一下,在你上面的CRUD的代码基础上使用AOP来统一记录一下每次执行方法的耗时,并打印当前请求的URL和HTTP方法(GET、POST等)吧!
请在你的pom.xml中添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aspectj</artifactId></dependency>任务要求:
- 编写一个切面类,用于拦截你的所有Controller接口。注意,此任务中你不能直接修改你的Controller代码。
- 通过你编写的切面类,打印方法执行的耗时,以及当前请求的URL和HTTP方法。
- 打印方法请使用slf4j,不能使用System.out.println()等方法。
以下是一个示例,但仅实现了打印方法执行的耗时。你可以自己从头编写一个切面类,或者修改和补全下面的示例,并把这个实例类放到你的SpringBoot项目中的合适位置。
package com.example.demo.aspect;
import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect@Component@Slf4jpublic class LogAspect { @Pointcut("within(com.example.demo.controller..*)") public void logLayer() {}
@Around("logLayer()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { String targetClass = joinPoint.getTarget().getClass().getSimpleName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); log.info("即将执行{}.{}(), args={}", targetClass, methodName, Arrays.toString(args)); long startTime = System.currentTimeMillis(); Object result = joinPoint.proceed(); log.info("执行结束: {}.{}(), 耗时{}ms, result={}", targetClass, methodName, System.currentTimeMillis() - startTime, result); return result; }}如果你能够很好地完成以上内容,可以尝试挑战一下(非必须,仅作为加分项):
使用自定义注解改造你的AOP,并修改你的AOP切面规则:不再拦截所有接口,而是哪个接口方法加上了你的自定义注解,才为这个接口打印相关日志。
致敬一路坚持的你
Section titled “致敬一路坚持的你”从最基础的Java语法、面向对象,到计算机网络、SQL、Linux,再到现在手握Spring Boot独立跑通一套Web增删改查——能够一口气完成这十道题,你已经超越了绝大多数同龄人。
请一定要给一路坚持到这里的自己鼓个掌!遇到报错时的迷茫、翻阅文档时的枯燥、反复调试接口直至看到"code": 200那一刻的喜悦,都是软件工程师真实生活的一部分。无论你完成了多少题,这份动手解决问题的勇气和耐心,已经是你最大的收获。
如果你想在后端开发走得更远…
Section titled “如果你想在后端开发走得更远…”压轴题实现一个简单的CRUD只是后端体系的起点。现代服务端工程体系庞大而浪漫,未来如果你想在后端工程开发这条路上继续深耕,这些有趣而强大的工具/技术正等着你去探索:
- 持久层进阶:从JPA走到底层,去探究MyBatis/MyBatis-Plus的灵活性,学习MySQL的底层索引优化与事务处理。
- 高并发与缓存:为系统加上Redis缓存,体验在内存中达到每秒数十万次读写操作的极速。
- 解耦与异步:引入消息队列(如RabbitMQ/Kafka),看看淘宝双十一的秒杀体系是如何靠异步削峰来拯救服务器的。
- 分布式与架构:走进Spring Cloud/微服务与Docker/Kubernetes的世界,体会如何像搭积木一样管理成百上千台服务器。
不要被庞大的技术体系吓到!没有任何一个工程师是把它学完了才开始干活的,在项目中动手实践,才是成长最快的方式。
彩蛋环节:展示你心中的“火花”(自由交流项)
Section titled “彩蛋环节:展示你心中的“火花”(自由交流项)”我们相信,一个优秀的工程师往往是一个爱折腾的实践者。除了上面规定的题目,如果你曾经自己鼓捣过任何有趣的“小玩意”,欢迎在本部分尽情向我们炫耀/展示!
无论它看起来多么微小、无论技术栈是什么,我们都超级期待看到:
- 你用 Python 写的某个自动化的爬虫或小工具、批量修改文件的脚本;
- 你自己搭建过的个人网页、小博客,或者是尝试过的小游戏;
- 甚至是你在硬件/物联网领域的DIY作品(比如有趣的Arduino控制器、树莓派小设备等)。
(附上你的项目介绍、代码链接、截图或心得均可!)
我们期待在面试环节与你见面,交流你的技术成长之路!加油!🚀
- 源码提交:保留完整的工程代码(不要包含target/等编译产物),提交至你的Git仓库。
- 测试证据:利用Curl/Postman/Apifox对你实现的所有接口分别进行调测,附带成功调用和完整JSON响应结果的截图。
- AOP日志截图:附带一张控制台输出统计耗时与请求地址的日志截图。
- README说明:用简单明了的语言介绍你的表结构设计,同时在上面记录任何需要你回答和思考的问题的答案。
- 提交URL:向我们提交仓库的URL地址。提交点这里
祝你编码愉快,我们期待在提交列表中看到你的优秀答案!
出题人:
moriko
QQ:3580235054
