在广州一个小公司(BOSS标注是0-20人,薪资2-3k),直接面试没有笔试,一开始就直接拿着简历问,也没有自我介绍,问题是结合场景题和八股文、基础。废话不多说,直接分享面试题目个大家做参考。
1、能讲一下IOC吗?
IOC就是控制反转,这是一种设计模式,核心思想是将对象的创建、依赖注入和生命周期管理交给IOC容器负责。在传统的编码方式中,我们一般需要在类中手动创建依赖对象,通过硬编码方式来控制对象的实例化和管理,而在Spring中,Bean以及对象之间的依赖关系都交给IOC容器负责,降低了代码之间的耦合度,也提高了系统的灵活性;
spring中主要通过XML配置和注解配置的两种方式实现。
2、数据库三范式能说一下吗?
数据库的三范式是关系型数据库设计的基本原则,旨在减少数据冗余、提高数据的一致性,并确保数据依赖的合理性;
第一范式:字段具有原子性,就是每个字段不能再拆分。比如联系方式应该拆分为电话和地址两个独立的字段;
第二范式:在满足第一范式的基础上,消除部分依赖,就是非主键字段必须完全依赖主键中的各个字段(表中使用的是复合主键,主键包含多个字段)。比如订单明细表的主键是订单ID+产品ID,字段客户姓名仅依赖订单ID,可以将客户姓名移到以订单ID为主键的订单表中;
第三范式:在满足第一、二范式的基础上,消除传递依赖,就是非主键字段不能依赖其他非主键字段。比如学生表包含学号(主键)、学院、学院电话,其中学院电话依赖学院,而学院依赖学号,应拆分为学生表(学号、学院)和学院表(学院、学院电话)
这种规则约束减少了冗余、避免了跟新异常,但过度范式化可能导致多表关联查询、降低性能;
3、数据库的优化问题?
(1)表设计优化
- 选择合适的数据类型,尽量使用最小的数据类型(比如使用INT替代BIGINT,使用CHAR替代VARCHAR)来减少存储和查询成本;
- 分区表:对于数据量极大的表可以考虑分区表,将表按某些规则分割成多个小表,提高查询效率;
- 主从复制、读写分离:如果数据库读的操作比较多,为了避免写操作所造成的性能影响,可以采用读写分离的架构;
(2)索引优化:
- 创建适当的索引,避免过多的索引,查照索引的创建原则;
- 覆盖索引:使用索引覆盖查询,避免回表,调高效率;
(3)SQL语句优化:
- 尽量明确指定需要的列,避免使用SELECT *,以减少不必要的数据传输;
- SQL语句要避免造成索引失效的写法;
- 尽量用union all 替代 union ,union多了一次过滤,效率较低;
- 避免子查询,特别是当子查询返回大量数据时,可以考虑JOIN或WITH子句替代
索引创建原则和失效场景看这篇八股文:
https://blog.csdn.net/weixin_73144915/article/details/145535602?spm=1001.2014.3001.5501
4、知道多态吗,解释一下?
多态指同一个操作作用于不同对象时,可以有不同的实现方式。核心目的是提高代码的灵活性和可扩展性;多态的两种形式如下:
1、编译时多态(静态多态),实现方法重载,在编译时根据参数类型和个数确定调用哪个方法;
2、运行时多态(动态多态),通过接口/继承和方法重写来实行,在运行时根据对象的实际类型决定调用哪个方法;
5、hashMap和hashTable的区别?
HashMap和Hashtable都是Java中用于存储键值对的哈希表类,区别如下:
(1)线程安全:HashMap不是线程安全的,如果多个线程并发访问HahsMap,并且至少有一个线程做了修改,他必须通过外部同步来保证线程安全,否则可能会导致数据不一致的情况;而Hashtable是线程安全的,他的方法都被synchronized修饰,可以在多线程环境下安全的被访问;
(2)性能:HashMap性能通常优于Hashtable,特别是在单线程环境下;而Hashtable由于方法上都有同步锁,性能较差;
(3)Null值:HashMap允许一个null值(键唯一性)和多个null值;而Hashtable不允许出现null键或null值。
6、SpringMVC的核心是什么?
SpringMVC的核心是基于前端控制器模式的请求驱动设计,将前端控制器作为中央调度器,拦截请求将请求分发给对应的处理器,并协调视图解析、数据绑定等组件完成全流程处理。其核心设计理念是解耦、模块化、可扩展。核心流程如下:
1.发送请求:用户发送请求,被前端控制器拦截;
2.映射处理器:处理器映射器根据URL找到对应的Controller层和方法;
3.调用控制器:Controller执行业务逻辑,返回ModelAndView数据;
4.解析视图:视图解析器将视图名称转化为具体视图(如HTML页面);
5.渲染视图:将模型数据填充到视图中,生成最终响应给用户;
7、怎么解决超卖问题?
解决超卖问题的核心在于保证库存扣减的原子性和一致性,尤其是在高并发场景下。以下是分层的解决方案,涵盖技术实现和业务逻辑优化:
一、技术层面解决方案
1. 数据库锁机制
-
悲观锁(Pessimistic Lock)
在事务中通过SELECT ... FOR UPDATE
锁定库存记录,防止其他事务修改。
BEGIN;
SELECT stock FROM products WHERE id=1 FOR UPDATE;
UPDATE products SET stock = stock - 1 WHERE id=1;
COMMIT;
-
缺点:性能较差,适用于低并发场景。
-
乐观锁(Optimistic Lock)
通过版本号或时间戳控制并发,仅当库存未被修改时才扣减。
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id=1 AND version = {current_version} AND stock > 0;
-
优点:性能高,适合高并发;缺点:需重试逻辑(如失败后提示用户重新下单)。
2. 分布式锁(Redis/ZooKeeper)
-
使用 Redis 的
SETNX
或 RedLock 算法,确保同一时间只有一个请求能操作库存。
示例(Redis + Lua脚本保证原子性):
local key = "product_1_stock"
local decrement = 1
local stock = tonumber(redis.call('GET', key))
if stock >= decrement then
redis.call('DECRBY', key, decrement)
return 1 -- 扣减成功
else
return 0 -- 库存不足
end
适用场景:分布式系统,需配合数据库最终一致性。
3. 缓存预扣库存(Redis + 异步队列)
步骤:
-
将库存预热到 Redis 中。
-
用户下单时,先通过
DECR
扣减 Redis 库存。 -
若扣减成功,将订单信息发送到消息队列(如 Kafka、RabbitMQ),异步更新数据库。
-
若最终数据库更新失败,需回滚 Redis 库存(如通过 TTL 自动过期或补偿事务)。
优点:扛住瞬时高并发;缺点:需处理缓存与数据库的数据一致性。
8、Mybatis中XML和注解你是怎么使用的?
配合着使用,一些简单的SQL语句可以直接用注解来写,而复杂SQL、联表查询的用XML来写
9.如果利用用户ID恶意访问接口,怎么解决?
1. 采用 Token 进行身份验证
- 使用 JWT(JSON Web Token) 或 Session 机制 进行身份认证,确保用户必须先登录后才能访问接口。
- Token 绑定用户身份,服务器通过 Token 解析出用户 ID,而不是让前端传递用户 ID。
2. 避免前端传递用户 ID
- 服务器根据 Token 自动获取当前用户 ID,而不是让前端传递
userId
参数。 - 示例:
@GetMapping("/user/profile")
public ResponseEntity<UserProfile> getUserProfile(@RequestAttribute("userId") Long userId) {
UserProfile profile = userService.getProfileById(userId);
return ResponseEntity.ok(profile);
}
这里 userId
是从 JWT 解析出来的,而不是从前端传递的参数。
3. 进行权限校验
- 后端校验数据归属权,即使用户传递
userId
,也要检查该userId
是否与当前登录用户匹配。 - 示例(Spring Boot 权限校验):
@GetMapping("/user/orders")
public ResponseEntity<List<Order>> getUserOrders(@RequestParam Long userId, @RequestAttribute("userId") Long currentUserId) {
if (!userId.equals(currentUserId)) {
throw new AccessDeniedException("非法访问");
}
return ResponseEntity.ok(orderService.getOrdersByUserId(userId));
}
这里 currentUserId
是从 Token 解析出来的,确保用户不能查询别人的订单。
4. 限制请求频率(防止暴力攻击)
- 使用 Rate Limiting(限流)机制,比如:
- Redis + 滑动窗口 进行限流
- Nginx 限流
- 使用 Spring Boot 的
Bucket4j
或Guava RateLimiter
- 示例(Spring Boot 限流):
@RateLimiter(name = "user-api", fallbackMethod = "limitExceeded")
@GetMapping("/user/data")
public ResponseEntity<?> getUserData() {
return ResponseEntity.ok("数据获取成功");
}
public ResponseEntity<String> limitExceeded(Exception e) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("请求过于频繁,请稍后再试");
}