跳转到内容

10:初窥后端世界

后端Banner

在前面几个题目中,你已经掌握了Java核心语法、SQL基础、网络与HTTP协议,甚至在上一题中通过HttpClient/OkHttp作为“客户端”去请求了别人的接口。

现在,角色对调!我们将进入现代Java后端开发的核心——使用Spring Boot作为“服务端”,为别人提供HTTP接口。这是一道压轴的综合实践题,只要顺着思路一步步尝试,你会发现做出一个真正的后端项目并没有那么难!

在本题中,你需要使用Maven来管理和构建你的项目,java版本使用21或更高版本(新版SpringBoot默认要求jdk21+)。

Java有一个原生的HTTP服务器实现:com.sun.net.httpserver.HttpServer,而你已经在第五题中使用过它了。而在实际开发中,SpringBootQuarkusMicronaut等第三方框架更为常用,而其中SpringBoot是应用最广泛的一个框架。本题我们将试着使用SpringBoot来搭建我们的后端应用。

部分一:概念与思维转变(思考题)

Section titled “部分一:概念与思维转变(思考题)”

SpringBoot是基于Spring的扩展框架,而Spring是一个开源的Java企业级开发框架,它的核心包括依赖注入(DI)控制反转(IoC)面向切面编程(AOP)。为了更好的学习SpringBoot,让我们先了解一下依赖注入与控制反转。

参考文章

尝试用自己的话简单回答一下(简短清晰即可,拒绝成段复制粘贴):什么是依赖注入和控制反转?和我们以前写 Java 代码时直接 new Object() 有什么区别与好处?

本题作为本次招新的最后一道题,会有很多实践内容,需要用到的相关知识可能需要你自行上网或通过AI学习解决。

  1. 试着使用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-jpamysql-connector-jspring-boot-starter-data-jpa-test这三条依赖项。

  2. SpringBoot将后端拆分成了三层架构:ControllerServiceRepository。请你了解一下这三层架构中每一层的作用。

  3. 学习编写你的第一个Controller,提供一个GET请求接口:

    • 访问/hello时,后端返回字符串"Hello glimmer!"
    • 打开浏览器或使用curl、亦或使用Postman、Apifox等工具测试,确保接口正常联通。
    • 学习使用Lombok的注解@slf4j,试着在访问/hello时,通过log.info()打印一条日志并输出到控制台。

参考:

结合你之前学到的SQL与HTTP知识,设计并实现一套简单的后端数据接口。比如,你可以设计一个用户管理系统,你的API应该至少能够支持新增用户、删除用户、修改用户信息和查询用户几种功能。

完成本任务,你需要在pom.xml中取消注释掉spring-boot-starter-data-jpamysql-connector-jspring-boot-starter-data-jpa-test这三条依赖项。

  1. 结合前几题的知识,请你部署一个MySQL数据库,并自行设计其中的表和数据字段。

  2. 尝试使用JPA让你的SpringBoot项目连接上你的MySQL数据库。

  3. 尝试在你的SpringBoot项目中设计RESTful API接口,结合HTTP动词,实现一个完整的增、删、改、查(CRUD)逻辑。

    • 新增POST /api/users(接收 JSON body)
    • 查询列表/单个GET /api/usersGET /api/users/{id}
    • 修改PUT /api/users/{id}(更新指定内容)
    • 删除DELETE /api/users/{id}
  4. 现代接口通常不会直接返回纯文本或裸数据,请保证你的所有 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>

任务要求:

  1. 编写一个切面类,用于拦截你的所有Controller接口。注意,此任务中你不能直接修改你的Controller代码。
  2. 通过你编写的切面类,打印方法执行的耗时,以及当前请求的URL和HTTP方法。
  3. 打印方法请使用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
@Slf4j
public 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切面规则:不再拦截所有接口,而是哪个接口方法加上了你的自定义注解,才为这个接口打印相关日志

从最基础的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控制器、树莓派小设备等)。

(附上你的项目介绍、代码链接、截图或心得均可!)

我们期待在面试环节与你见面,交流你的技术成长之路!加油!🚀

  1. 源码提交:保留完整的工程代码(不要包含target/等编译产物),提交至你的Git仓库。
  2. 测试证据:利用Curl/Postman/Apifox对你实现的所有接口分别进行调测,附带成功调用和完整JSON响应结果的截图。
  3. AOP日志截图:附带一张控制台输出统计耗时与请求地址的日志截图。
  4. README说明:用简单明了的语言介绍你的表结构设计,同时在上面记录任何需要你回答和思考的问题的答案。
  5. 提交URL:向我们提交仓库的URL地址。提交点这里

祝你编码愉快,我们期待在提交列表中看到你的优秀答案!

出题人:出题人头像  moriko

QQ:3580235054

邮箱:3580235054@qq.com