面经 · 2024年3月31日 0

用户管理系统面经(持续更新)

请介绍一下本项目的完整业务流程

管理员注册 -> 管理员登陆 -> 管理员在主界面中浏览检索用户 -> 管理员可以创建、更新和删除用户

在开发过程中遇到比较复杂的技术问题和挑战?如果有,谈谈如何解决的

在项目部署时遇到跨域问题,通过配置 nginx 反向代理,请求服务器 IP(域名) + /api 时,请求转发到服务器内的实际后端服务端口

请介绍项目中使用 Spring Boot 框架的优势和试用场景

Spring Boot 的优势:

  1. 快速开发和简化配置: Spring Boot采用了约定大于配置的原则,减少了繁琐的配置,提供了自动配置和起步依赖,让开发人员能够更快速地搭建应用程序。
  2. 内嵌式Web容器: Spring Boot内置了常见的Web容器,如Tomcat、Jetty和Undertow,这意味着你可以将你的应用程序打包成一个独立的JAR文件,无需外部的Web服务器。
  3. 自动配置: Spring Boot根据项目的依赖自动配置应用程序的组件,大大减少了手动配置的工作,提高了开发效率。
  4. 生态系统支持: Spring Boot整合了Spring框架的各个模块,如Spring MVC、Spring Data、Spring Security等,可以轻松构建全栈应用。
  5. 监控和管理: Spring Boot提供了丰富的监控和管理功能,包括健康检查、性能指标、远程Shell等,有助于更好地管理和维护应用程序。
  6. 适用于微服务架构: Spring Boot是构建微服务架构的理想选择,它可以轻松地创建独立的服务,而且与Spring Cloud等微服务相关的工具集成得很好。
  7. 大型社区支持: Spring Boot有一个庞大的开发者社区,提供了丰富的文档、教程和第三方库,便于开发人员解决问题和分享经验

Spring Boot 的适用场景:

  1. Web应用程序开发:Spring Boot适用于构建各种类型的Web应用程序,包括单页应用、多页应用、RESTful API等。
  2. 微服务架构:Spring Boot可以用于构建微服务应用,每个微服务可以独立开发、部署和管理。
  3. 批处理应用程序:Spring Boot提供了对Spring Batch的支持,用于处理大规模的数据批处理任务。
  4. 云原生应用:Spring Boot与容器编排平台(如Kubernetes)和云服务(如AWS、Azure、Google Cloud)集成得很好,可以轻松部署和扩展应用程序。
  5. 企业级应用程序:Spring Boot提供了大量的企业级特性,如事务管理、安全性、缓存、消息队列等,适用于构建复杂的企业级应用。

什么是 MyBatis-Plus?它有什么作用?与 MyBatis 有什么区别?

MyBatis 是一个半 ORM 框架,它内部封装了 JDBC,使用它后,开发者只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建链接、创建 statement 等繁杂的过程,提高开发效率

MyBatis-Plus 是 MyBatis 的增强版框架,对 MyBatis 进行了二次封装,只做增强不做改变。通过提供一系列的 API 方法和代码生成器,使重复的 CRUD 操作更加简单,无需手动编写 SQL 语句,通过继承 BaseMapper 类代替 XML 文件,从而大幅提升开发效率

另外还有条件构造器、分页查询、代码生成器等功能,大大提升开发效率

如何通过继承定制通用操作模板,从而提高开发效率的?

开发过程中很多功能和代码使独立于具体的业务逻辑的,比如:

  1. 给每个请求的返回值增加 code、message和data字段
  2. 在全局异常处理器中统一处理项目的异常,防止代码错误细节暴露在外,同时优化用户体验
  3. 跨域的处理、长整数丢失问题的解决

因此可以将这些重复的共嗯那个独立抽出,作为一套通用项目模板。

在项目中如何自定义错误码?

通过自定义一个枚举类型集中定义错误码

该类提供 code、message、description 三个属性,并且提供一个默认的构造函数用于创建美剧

其中,我对错误码的整形定义是有讲究的,参考真实的 HTTP 状态码将错误进行分类,并且将错误码的前三位前缀定义为和对应含义的状态码相同,例如 HTTP 状态码 400 表示客户端请求参数错误,PARAMS_ERROR(40000, "请求承诺书错误", "具体描述")

在抛出异常时,可以传入特定的错误码枚举值作为参数,并且将错误码枚举的 message 的 description 属性作为异常类的 message,简化抛出异常的代码

为什么后端需要全局异常处理器?如何实现全局异常处理器的?

全局异常处理器能够捕获应用程序中抛出的异常,并对这些异常进行统一的处理,从而避免因未处理的异常导致系统崩溃或无法正常工作,能够提高系统的稳定性

举几个具体的应用:

  1. 错误信息统一处理:全局异常处理器可以将不同种类的异常转化为统一的错误信息格式,提供一致的错误响应给客户端,增强了用户体验。
  2. 错误日志记录:可以在全局异常处理器中记录异常信息,包括异常类型、发生时间、请求参数等,有助于排查问题和分析系统健康状况。
  3. 异常信息隐藏: 通过全局异常处理器,可以隐藏敏感信息,以防止敏感信息泄露到客户端。

如何实现

定义一个全局异常处理类,并且使用 @RestControllerAdvice (或者 @controllerAdvice) 注解该类,表示这是一个全局异常处理器类

针对每一种异常定义一个处理异常的方法,并使用 @ExceptionHandler(异常类.class) 注解标注这些方法,可以在这些方法中进行日志记录等具体的异常处理操作,并返回一个响应对象,便于前端识别并给用户有好的错误提示

项目中使用 Nginx 部署前端项目和使用 Docker 部署后端项目,这两种部署方式有什么区别?

Nginx 是一个高性能的 Web 和反向代理服务器,主要用于前端静态文件的部署和反向代理转发请求。

用 Nginx 部署前端,只需要把打包好的前端静态代码文件上传到服务器的指定目录,然后在 nginx.conf 配置中定义 location 配置,让对应的域名指向该目录即可。

Nginx 还可以作为接入层网关,负责统一接受对于前端和后端的请求,然后根据请求路径(比如 /api)转发到实际的前端静态文件或后端服务。

Docker 是一种容器技术,可以将多种不同的应用程序、组件、依赖、环境打包为镜像,整体分发和运行。对于后端项目来说,通常要包含应用程序、依赖项、数据库、中间件等组件,有时使用 Docker 可以提高部署效率,无需手动安装环境。

本项目中,我编写了 Dockerfile 来定义容器镜像,包括 java 环境、执行 maven 打包并执行 jar 包的流程,然后通过 docker build 命令构建镜像,最后通过 docker run 启动镜像。
使用 Docker 后,程序更容易水平扩展,支持更高的负载。

介绍一下你部署项目的操作流程?

以宝塔 Linux 原生部署为例:

  1. 购买云服务器
  2. 安装和初始化宝塔 Linux 面板
  3. 在宝塔面板中安装项目部署依赖软件,比如 jdk、mysql、nginx、tomcat、Java 项目管理器等
  4. 把本地的数据同步到宝塔安装的线上数据库
  5. 后端:使用 Java 项目管理器,添加 Java 项目,把本地 mvn package 命令打好的 jar 包上传到 Linux 服务器,配置启动参数和激活配置(prod),点击 “启动” 即可。
  6. 前端:上传本地打包好的 dist 网站静态文件目录到服务器,然后配置 Nginx 指向文件目录路径,即可访问前端静态文件。
  7. 在 Nginx 配置反向代理,请求服务器 IP(或域名时)+ 后端路径(/api)时,请求转发到服务器内的实际后端服务端口(比如 localhost:8080)。
  8. 在服务器运营商提供的界面和宝塔面板界面中开启防火墙的 80 端口

什么是代理和反向代理?二者有什么区别?

二者服务的对象和目的不同。

代理(正向代理)是指有一个中间服务器代替客户端发送请求到目标服务器,目标服务器不知道客户端的存在,可以用于保护客户端的安全隐私、加速访问等。

反向代理是指有一个中间服务器代替目标服务器去接受客户端的请求,客户端并不知道目标服务器的存在,可以用于负载均衡、保障服务器的安全性。

后端有哪些解决跨域的方法?你是如何在本项目中解决跨域的?

几种后端解决跨域的方法:

1)设置 CORS 响应头:后端可以在 HTTP 响应头中添加相关的 CORS 标头,允许特定的源(域名、协议、端口)访问资源。Spring Boot 项目中,可以通过配置 CorsFilter Bean 或者 Web 拦截器(实现 WebMvcConfigurer 接口)实现,不依赖第三方服务。

2)使用代理服务器:可以使用 Nginx 反向代理,通过 add_header 给后端响应添加 Access-Control-Allow-Origin 头,不改代码实现跨域。

3)@CrossOrigin 注解:Spring Boot 项目可以直接在对应的 Controller 或接口方法上添加 @CrossOrigin 注解实现跨域,但这种方式对代码的侵入性较大。

我选择第 2 种方式,不需要考虑特定的后端接口实现代码,是一种更通用的解决跨域的方法。

什么是单元测试?你是如何编写单元测试的?

单元测试是指对软件中的最小可测试单元(通常是函数、方法、类等)进行检查和验证的测试方法,可以用最细粒度的方式保证项目的质量。

在项目中,我使用 Spring Boot 整合 JUnit 单元测试框架进行单元测试。首先基于 service 创建对应的单元测试类,给测试类上添加 @SpringBootTest 注解用来测试 Spring Boot 中的 Bean;然后在类中编写具体的测试方法并打上 @Test 注解标识测试方法;在方法中调用要测试 service 的方法并通过 Assertions 注解验证测试结果是否符合预期。

什么是单元测试覆盖度?你是如何计算单元测试覆盖度的?

单元测试覆盖度(Unit Test Coverage)是一种衡量在单元测试中覆盖源代码的程度的指标。它表示在单元测试中执行了多少代码行、分支、语句或路径,以及在这些测试中检测到了多少错误。单元测试覆盖度有助于评估测试套件的质量和完整性,但并不总是反映出测试的质量。

在 Java 项目中,可以使用 JaCoCo 工具来自动生成单元测试覆盖度报告,并且在报告中查看语句覆盖、分支覆盖、路径覆盖的比例。