面试之道

1. 项目相关

1.1 详细说明你的项目

从以下几个方面进行项目介绍
1、项目的背景,包括:是自研还是外包、什么业务、服务的客户群是谁、谁去运营等问题
2、项目的业务流程
3、项目的功能模块
4、项目的技术架构
5、个人工作职责
6、个人负责模块的详细说明,包括模块的设计,所用到的技术,技术的实现方案等

案例:

我最近参与的项目是我们公司自研的专门针对成人职业技能教育的网络课堂系统,网站提供了成人职业技能培训的相关课程,如:软件开发培训、职业资格证书培训、成人学历教育培训等课程。项目基于B2B2C的业务模式,培训机构可以在平台入驻、发布课程,我们公司作为运营方由专门的人员对发布的课程进行审核,审核通过后课程才可以发布成功,课程包括免费和收费两种形式,对于免费课程普通用户可以直接选课学习,对于收课程在选课后需要支付成功才可以继续学习。
本项目包括三个端:用户端(学生端)、机构端、运营端。
核心模块包括:内容管理、媒资管理、课程搜索、订单支付、选课管理、认证授权等。
本项目采用前后端分离架构,后端采用SpringBoot、springCloud技术栈开发,数据库使用了MySQL,还使用的Redis、消息队列、分布式文件系统、Elasticsearch等中间件系统。
划分的微服务包括:内容管理服务、媒资管理服务、搜索服务、订单支付服务、学习中心服务、系统管理服务认证授权服务、网关服务、注册中心服务、配置中心服务等。
我在这个项目中负责了内容管理、媒资管理、订单支付模块的设计与开发。

内容管理模块,是对平台上的课程进行管理,课程的相关信息比较多这里在数据库设计了课程基本信息表、课程营销表、课程计划、课程师资表进行存储,培训机构要发布一门课程需要填写课程基本信息、课程营销信息、课程计划信息、课程师资信息,填写完毕后需要提交审核,由运营人员进行课程信息的审核,整个审核过程是程序自动审核加人工确认的方式,通常24小时审核完成。课程审核通过即可发布课程,课程的相关信息会聚合到课程发布表中,这里不仅要将课程信息写到课程发布表还要将课程信息写到索引库、分布式文件系统中,所以这里存在分布式事务的问题,项目使用本地消息表加任务调度的方式去解决这里的分布式事务,保存数据的最终一致性。

1.2 你的项目流程是什么

  1. 产品人员设计产品原型。
  2. 讨论需求。
  3. 分模块设计接口。
  4. 出接口文档。
  5. 将接口文档给到前端人员,前后端分离开发。
  6. 开发完毕进行测试。
  7. 测试完毕发布项目,由运维人员进行部署安装。

1.3 系统如何处理异常?

我们自定义一个统一的异常处理器去捕获并处理异常。使用控制器增加注解@ControllerAdvice和异常处理注
解@ExceptionHandler来实现。

  • 处理自定义异常
    程序在编写代码时根据校验结果主动抛出自定义异常类对象,抛出异常时指定详细的异常信息,异常处理器
    捕获异常信息记录异常日志并响应给用户。
  • 处理未知异常
    接口执行过程中的一些运行时异常也会由异常处理器统一捕获,记录异常日志,统一应给用户500错误。
    在异常处理器中还可以针对某个异常类型进行单独处理。

2. Git

2.1 Git代码冲突怎么处理

我们在使用Git时难免会出现代码冲突的问题,出现冲突的原因是因为当本地文件的版本与目标分支中文件的版本不一致时当存在同一行的内容不同时在进行合并时会出现冲突。
代码冲突一般发生在以下情况:
1、多个分支向主分支合并时
2、同一个分支下pull或push操作时。

不使用图形化界面工具,我们要手动合并文件的内容,确定最终的版本

2.2 你是在哪个开发分支

我们不是直接在主分支开发,由技术经理创建独立的开发分支,我们是在独立的开发分支中进行开发,最后由技术经理将开发分支合并到主分支。

3. maven

3.1 maven常用命令

  • mvn clean //清除target目录中的生成结果
  • mvn compile //编译源代码
  • mvn test //执行单元测试
  • mvn package //打包
  • mvn install //打包并把打好的包上传到本地仓库
  • mvn deploy //打包并把打好的包上传到远程仓库

3.2maven依赖版本冲突如何处理

maven依赖版本冲突一般是由于间接依赖导致一个jar包即有多个不同的版本,比如:A依赖了B的1.0版本,C依赖了B的2.0版本,项目依赖A和C从而间接依赖了B的1.0和2.0版本,此时B有两个版本引入到了项目中,当存在版本冲突时可能会出现ClassNotFoundException、NoSuchMethodError等错误。
处理版本冲突可以使用以下方法:
1、使用exclusions 排除依赖
比如:我们只依赖B的1.0版本,此时可以在依赖C时排除对B的依赖。
2、使用dependencyManagement锁定版本号。
通常在父工程对依赖的版本统一管理。
比如:我们只依赖B的1.0版本,此时可以在父工程中限定B的版本为1.0。

4. Mysql

4.1 mysql常见的存储引擎以及区别

一、InnoDB

  1. 支持事务。
  2. 使用的锁粒度默认为行级锁,可以支持更高的并发;也支持表锁。
  3. 支持外键约束;外键约束其实降低了表的查询速度,增加了表之间的耦合度。

二、MyISAM

  1. 不提供事务支持
  2. 只支持表级锁
  3. 不支持外键

三、memory

  1. 数据存储在内存中

总结:

  • MyISAM管理非事务表,提供高速存储和检索以及全文搜索能力,如果在应用中执行大量select操作,应该选择MyISAMM

  • InnoDB用于事务处理,具有ACID事务支持等特性,如果在应用中执行大量insert和update操作,应该选择InnoDB

4.2 mysql建表时要注意哪些

MySQL建表的经验有很多,下边列举一些:

  1. 注意选择存储引擎,如果要支持事务需要选择InnoDB。
  2. 注意字段类型的选择,对于日期类型如果要记录时分秒建议使用datetime,只记录年月日使用date类型,对于字符类型的选择,固定长度字段选择char,不固定长度的字段选择varchar,varchar比char节省空间但速度没有char快;对于内容介绍类的长广文本字段使用text或longtext类型;如果存储图片等二进制数据使用blob或longblob类型;对金额字段建议使用DECIMAL;对于数值类型的字段在确保取值范围足够的前提下尽量使用占用空间较小的类型,
  3. 主键字段建议使用自然主键,不要有业务意义,建议使用int unsigned类型,特殊场景使用bigint类型。
  4. 如果要存储text、blob字段建议单独建一张表,使用外键关联。
  5. 尽量不要定义外键,保证表的独立性,可以存在外键意义的字段。
  6. 设置字段默认值,比如:状态、创建时间等。
  7. 每个字段写清楚注释。
  8. 注意字段的约束,比如:非空、唯一、主键等

4.3 树型表的标记字段是什么?如何查询MySQL树型表?

树型表的标记字段是parentid即父结点的id。
查询一个树型表的方法:

  1. 当层级固定时可以用表的自链接进行查询。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    select
    one.id one_id,
    one.name one_name,
    one.parentid one_parentid,
    one.orderby one_orderby,
    one.label one_label,
    two.id two_id,
    two.name two_name,
    two.parentid two_parentid,
    two.orderby two_orderby,
    two.label two_label
    from course_category one
    inner join course_category two on one.id = two.parentid
    where one.parentid = 1
    and one.is_show = 1
    and two.is_show = 1
    order by one.orderby,
    two.orderby
  2. 如果想灵活查询每个层级可以使用mysgl递归方法,使用withRECURSIVE 实现。

    1
    2
    3
    4
    5
    6
    7
    with RECURSIVE t1  AS
    (
    SELECT 1 as n
    UNION ALL
    SELECT n + 1 FROM t1 WHERE n < 5
    )
    SELECT * FROM t1;

5. Spring boot

5.1 springBoot接口开发的常用注解有哪些?

  • @Controller 标记此类是一个控制器,可以返回视图解析器指定的html页面,通过@ResponseBody可以将结果返回json、xml数据。
  • @RestController相当于@ResponseBody加 @Controler,实现rest接口开发,返回json数据,不能返回html页面。
  • @RequestMapping定义接口地址,可以标记在类上也可以标记在方法上,支持http的post、put、get等方法。
  • @PostMapping定义post接口,只能标记在方法上,用于添加记录,复杂条件的查询接口。
  • @GetMapping定义get接口,只能标记在方法上,用于查询接口的定义。
  • @PutMapping定义put接口,只能标记在方法上,用于修改接口的定义。
  • @DeleteMapping定义delete接口,只能标记在方法上,用于除接口的定义。
  • @RequestBody定义在方法上,用于将json串转成java对象。
  • @PathVarible 接收请求路径中占位符的值.
  • @ApiOperation swagger注解,对接口方法进行说明。
  • @Api wagger注解,对接口类进行说明。
  • @Autowired 基于类型注入。
  • @Resourc基于名称注入,如果基于名称注入失败转为基于类型注入。

6. MyBatis

6.1 MyBatis分页插件原理

首先分页参数放到ThreadLocal中,拦截执行的sql,根据数据库类型添加对应的分页语句重写sql,例如:(select* from table where a) 转换为 (select count(*) from table where a)和(select * from table where a limit ,),计算出了total总条数、pageNum当前第几页、pagesize每页大小和当前页的数据,是否为首页,是否为尾页,总页数等。

6.2 MyBatis的ResultType和ResultMap的区别?

  • ResultType:指定映射类型,只要查询的字段名和类型的属性名匹配可以自动映射。

  • ResuitMap:自定义映射规则,当查询的字段名和映射类型的属性不匹配时可以通过ResultMap自定义映射规则也可以实现一对多、一对一映射。

6.3 #{ } 和 ${ }有什么区别?

  • #{ }是标记一个占位符,可以防止sql注入。
  • ${ }用于在动态 sql中拼接字符串,可能导致sql!注入。

7. spring

7.1 什么情况Spring事务会失效

  1. 在方法中捕获异常没有抛出去
  2. 非事务方法调用事务方法
  3. 事务方法内部调用事务方法
  4. @Transactional标记的方法不是public,或者不是接口重写方法也不会抛异常
    • 因为CGLIB代理的代理对象 要继承被代理对象,重写里面的方法。如果是private方法 无法继承
  5. 抛出的异常与rollbackFor指定的异常不匹配,默认rollbackFor指定的异常为RuntimeException
  6. 数据库表不支持事务,比如MySQL的MyISAM
  7. Spring的传播行为导致事务失效,比如:PROPAGATION NEVER、PROPAGATION NOT SUPPORTED

8. Minio分布式文件系统

8.1 断点续传是怎么做的?

我们是基于分块上传的模式实现断点续传的需求,当文件上传一部分断网后前边已经上传过的不再上传。

  1. 前端对文件分块。
  2. 前端使用多线程一块一块上传,上传前给服务端发一个消息校验该分块是否上传,如果已上传则不再上传。
  3. 等所有分块上传完毕,服务端合并所有分块,校验文件的完整性。因为分块全部上传到了服务器,服务器将所有分块按顺序进行合并,就是写每个分块文件内容按顺序依次写入一个文件中。使用字节流去读写文件。
  4. 前端给服务传了一个md5值,服务端合并文件后计算合并后文件的md5是否和前端传的一样,如果一样则说文件完整,如果不一样说明可能由于网络丢包导致文件不完整,这时上传失败需要重新上传。

8.2 分块文件清理问题

上传一个文件进行分块上传,上传一半不传了,之前上传到minio的分块文件要清理吗?怎么做的?

  1. 在数据库中有一张文件表记录minio中存储的文件信息。
  2. 文件开始上传时会写入文件表,状态为上传中,上传完成会更新状态为上传完成。
  3. 当一个文件传了一半不再上传了说明该文件没有上传完成,会有定时任务去查询文件表中的记录,如果文件未上传完成则删除minio中没有上传成功的文件目录。

9. xxl-job

9.1 描述

xxl-job的工作原理是什么?
xxl-job是什么怎么工作?
XXL-J0B分布式任务调度服务由调用中心和执行器组成,调用中心负责按任务调度策略向执行器下发任务,执行器负责接收任务执行任务。

  1. 首先部署并启动xx1-job调度中心。(一个java工程)
  2. 首先在微服务添加xx1-job依赖,在微服务中配置执行器
  3. 启动微服务,执行器向调度中心上报自己。
  4. 在微服务中写一个任务方法并用xx1-job的注解去标记执行任务的方法名称。
  5. 在调度中心配置任务调度策略,调度策略就是每隔多长时间执行还是在每天或每月的固定时间去执行,比如每天0点执行,或每隔1小时执行一次等。
  6. 在调度中心启动任务。
  7. 调度中心根据任务调度策略,到达时间就开始下发任务给执行器。
  8. 执行器收到任务就开始执行任务。

9.2 如何保证任务不重复执行?

  1. 调度中心按分片广播的方式去下发任务
  2. 执行器收到作业分片广播的参数:分片总数和分片序号,计算 任务id 除以 分片总数得到一个余数,如果余数等于分片序号这时就去执行这个任务,这里保证了不同的执行器执行不同的任务
  3. 配置调度过期策略为“忽略”,避免同一个执行器多次重复执行同一个任务
  4. 配置任务阻塞处理策略为“丢弃后续调度”,注意:丢弃也没事下一次调度就又可以执行了
  5. 另外还要保证任务处理的幂等性,执行过的任务可以打一个状态标记已完成,下次再调度执行该任务判断该任务已完成就不再执行

9.3 任务幂等性如何保证?

它描述了一次和多次请求某一个资源对于资源本身应该具有同样的结果。幂等性是为了解决重复提交问题,比如:恶意刷单,重复支付等。解决幂等性常用的方案:

  1. 数据库约束,比如:唯一索引,主键。同一个主键不可能两次都插入成功。
  2. 乐观锁,常用于数据库,更新数据时根据乐观锁状态去更新。
  3. 唯一序列号,请求前生成唯一的序列号,携带序列号去请求,执行时在redis记录该序列号表示以该序列号的请求执行过了,如果相同的序列号再次来执行说明是重复执行。这里我们在数据库视频处理表中添加处理状态字段,视频处理完成更新状态为完成,执行视频处理前判断状态是否完成,如果完成则不再处理。

鱼友之面

  1. 抽象类和接口的异同点:

    • 相同点:都不能实例化成对象,只能被其他类继承或实现。都包含抽象方法,这些方法只有声明没有实现。
    • 不同点:抽象类可以包含非抽象方法(具体方法),而接口中的所有方法都是抽象的。一个类可以实现多个接口,但只能继承一个抽象类。接口支持默认方法和静态方法,抽象类不支持。
  2. 线程的状态:

    • 新建(New)
    • 可运行(Runnable)
    • 阻塞(Blocked)
    • 等待(Waiting)
    • 超时等待(Timed Waiting)
    • 终止(Terminated)
    • sleep和wait的异同点:
      • sleep是Thread类的静态方法,使当前线程暂停执行指定的时间,不释放锁资源。
      • wait是Object类的方法,使当前线程等待,直到其他线程调用此对象的notify()或notifyAll()方法,会释放锁资源。
  3. 序列化是将对象的状态信息转换为字节流的过程,便于在网络上传输或存储到文件系统。序列化的目的是实现对象的持久化和远程方法调用(RMI)。

  4. finally和return的执行关系:

    • 无论try块中是否执行了return语句,finally块中的代码都会被执行。
    • 如果finally块中也包含return语句,那么它会覆盖try或catch块中的return值。
  5. Spring框架中的IoC(Inversion of Control,控制反转)和AOP(Aspect-Oriented Programming,面向切面编程):

    • IoC:通过依赖注入(DI)将对象的创建和依赖关系的管理交给Spring容器,降低了组件之间的耦合度。
    • AOP:允许开发者定义横切关注点,这些关注点独立于应用程序的主要业务逻辑,可以在不修改源代码的情况下对程序进行功能扩展,如日志记录、事务管理、安全检查等。
  6. 什么是SQL注入攻击?预防方法?

    • SQL注入攻击是通过在SQL查询中插入恶意代码来破坏、篡改或窃取数据库数据的行为。
    • 在JDBC编程中,应使用PreparedStatement代替Statement来避免SQL注入攻击,因为PreparedStatement将参数与SQL语句分开处理,不允许参数中包含SQL代码。
  7. JDBC事务概念? spring声明事务在编码中涉及哪些要点

    • JDBC事务是一系列对数据库的操作序列,要么全部成功执行,要么全部失败回滚,以保持数据的完整性和一致性。
    • Spring声明式事务管理通过注解(如@Transactional)或XML配置来简化事务管理,开发者只需关注业务逻辑,无需编写繁琐的事务控制代码。
  8. 线程死锁的原因:

    • 互斥条件:资源至少有一个是不可共享的。
    • 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
    • 不剥夺条件:线程已获得的资源,在未使用完之前,不能被其他线程强行剥夺。
    • 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
    • 规避方法:
      • 按顺序加锁,避免循环等待。
      • 设置锁的超时时间。
      • 使用tryLock()方法尝试获取锁。
      • 使用死锁检测算法。
  9. 对于一个常规的interface接口,是否可以通过反射机制创建其对象实例

    • 对于一个常规的interface接口,不能通过反射机制直接创建其对象实例,因为接口本身不能被实例化。
    • 但是,可以通过反射机制创建实现了该接口的具体类的对象实例。
  10. Mysql的数据库引擎有哪些?

    • MySQL数据库引擎主要有InnoDB、MyISAM、Memory等。
  11. new String(“abc”)创建了多少个对象?

    • new String(“abc”)创建了两个对象:一个是字符串字面量"abc"(存储在字符串常量池中),另一个是通过new关键字创建的String对象(存储在堆内存中)。
  12. 为什么重写了equals方法还要重写hashcode方法?

    • 重写equals方法后,如果两个对象相等(根据equals方法的定义),它们的hashcode也应该相等,以保证它们在哈希表中的行为一致。
    • 如果不重写hashcode方法,那么即使两个对象相等,它们的hashcode也可能不同,这会导致在使用哈希表时出现问题。
  13. Redis和MySQL如何保持数据一致性?

    • 写入时更新(Write-Through):应用程序在更新MySQL数据库后立即更新Redis。
    • 写入后更新(Write-Behind):应用程序在更新MySQL数据库后稍后再更新Redis,可以批量操作以提高性能。
    • 事务提交时更新(Write-On-Commit):应用程序在MySQL事务提交后更新Redis,确保数据的一致性。
  14. Java中有哪些常见的异常类?

    • Java中常见的异常类包括:NullPointerException、IndexOutOfBoundsException、ClassCastException、ArrayStoreException、IllegalArgumentException、IOException等。
  15. 重写(Override)和重载(Overload)的使用场景:

    • 重写:子类提供特定于其自身实现的方法版本,用于覆盖父类中的同名方法。通常用于改变方法的行为。
    • 重载:在同一类中定义多个同名方法,但参数列表不同。通常用于提供多种输入参数组合的处理方式。
  16. Redis和MySQL有安全线程吗?
    Redis和MySQL本身不提供安全线程。为了确保安全性,需要采取以下措施:

  • 使用连接池来管理数据库连接,避免频繁创建和关闭连接。
  • 对用户输入进行验证和过滤,防止SQL注入攻击。
  • 使用SSL/TLS加密通信。
  • 设置合适的权限和访问控制。