每天学一篇面经(第一天)

说说Java常用的框架

Spring框架:Java企业级项目的脚手架,提供IOC、DI、AOP SpringBoot框架:简化Spring的开发,不用配置一大堆XML,并且支持自动装配 MyBatis框架:常用ORM框架,解决对象关系映射问题

SpringBoot的自动装配是什么?

SpringBoot的自动装配:

  1. @SpringBootApplication开启自动装配
  2. @EnableAutoConfiguration中的@Import会触发AutoConfigurationImportSelector
  3. AutoConfigurationImportSelector去扫描META-INF中的自动装配配置
  4. 和@ConditionOnxx配合决定哪些自动装配配置生效
  5. 装配满足条件的Bean

SpringBoot启动的时候会加载什么?

Spring Boot 启动时,先进入 SpringApplication.run(),然后创建 ApplicationContext、读取配置环境、扫描启动类和组件、执行自动装配、刷新容器并实例化 Bean;如果是 Web 项目,还会启动内嵌 Tomcat,最后执行 CommandLineRunner / ApplicationRunner

Bean 的生命周期

  • 实例化:通过反射创建 Bean 对象。 Spring 容器会先根据 Bean 的定义信息创建对象,一般是通过反射来完成。这个时候对象虽然已经存在了,但里面的属性通常还没有赋值,所以它还只是一个“空对象”或者“半成品对象”。
  • 属性注入:给 Bean 注入依赖和配置。 在对象创建完成之后,Spring 会把这个 Bean 依赖的其他对象或配置值注入进去,比如常见的 @Autowired@Resource@Value 都是在这个阶段生效。经过属性注入后,Bean 才真正具备完整的依赖关系。
  • 初始化:执行 Bean 的初始化逻辑。 当 Bean 的依赖都注入完成后,Spring 会调用初始化相关的方法,比如 @PostConstructInitializingBean 接口中的 afterPropertiesSet(),或者手动配置的 init-method。这个阶段一般用来做资源准备、参数检查、连接创建等工作。
  • 使用:Bean 已经可以被业务代码正常调用。 初始化完成后,Bean 就进入可用状态了,可以被 Spring 容器提供给业务代码使用。我们平时在项目中通过 @Autowired 注入后直接调用的方法,本质上拿到的就是已经完成前面流程的 Bean。
  • 销毁:容器关闭时执行 Bean 的清理操作。 当 Spring 容器关闭时,会执行 Bean 的销毁逻辑,比如调用 @PreDestroyDisposableBeandestroy() 方法,或者配置的 destroy-method。这个阶段主要用于释放资源,例如关闭数据库连接、线程池、文件流等,避免资源泄漏。

Ioc介绍一下,怎么实现的

IOC即控制反转,是一种设计思想,将对象的创建和管理的权限交给外部容器,减少代码耦合,提高管理效率 IOC的实现原理:工厂模式+反射机制+ 配置文件或注解 详细:

  1. 读取配置信息
    Spring 启动时会读取 XML、注解、JavaConfig 等配置,解析哪些类需要交给容器管理。

  2. 实例化对象
    Spring 容器通过反射创建这些类的对象。

  3. 保存到容器中
    创建好的对象会放到 IOC 容器中,通常是一个类似 Map 的结构,方便后续获取。

  4. 依赖注入(DI)
    如果一个对象依赖另一个对象,Spring 会自动把依赖对象注入进去,而不是让开发者手动 new

  5. 统一管理生命周期
    Spring 会负责 Bean 的初始化、销毁等过程。

DI是什么?和IOC有什么关系?怎么实现的?

DI即依赖注入。由容器在运行时,将对象所依赖的其他对象注入进去,而不是由对象自己创建依赖。DI是IOC思想的一种实现

DI的实现方式

通过构造器注入依赖,优点是安全性高,依赖不可变 通过 set 方法注入,优点是灵活,可选依赖 字段注入(@Autowired、@Resouce),直接在属性上使用注解,写法简单

DI实现原理

  1. 容器启动,扫描 Bean(@Component / @Bean)
  2. 创建 Bean 实例(反射)
  3. 查找该 Bean 依赖的字段或构造器参数(如 @Autowired)
  4. 从容器中找到对应的依赖 Bean
  5. 通过反射进行注入(赋值或调用构造器)

补充

@Resource和@Autowired的区别: 来源不同: @Resource是JDK提供的注解,@Autowired是Spring提供的注解。 注入方式不同: @Autowired默认按类型注入,@Resource默认按名称注入 注入流程不同: @Autowired:

  • 先按类型找
  • 如果有多个,再按名称匹配
  • 可以配合 @Qualifier 指定
    @Resource:
  • 先按名称找(name属性或字段名)
  • 找不到再按类型

循环依赖是怎么解决的?

Spring 通过 三级缓存 + 提前暴露对象(early exposure) 的机制,解决了 **单例 Bean 的循环依 流程:

  • 先从一级缓存找
  • 再找二级缓存
  • 再通过三级缓存创建早期对象 流程详解:
  1. 创建 A(实例化,还没注入属性)
  2. 将 A 的“早期对象”放入三级缓存(singletonFactories)
  3. A 需要注入 B → 开始创建 B
  4. 创建 B(实例化)
  5. B 需要注入 A → 从缓存中获取 A 的“早期对象”
  6. B 完成初始化
  7. 回到 A → 注入 B → A 完成初始化

三级缓存

一级缓存(singletonObjects)

  • 存放完整初始化后的 Bean
  1. 二级缓存(earlySingletonObjects)
  • 存放提前曝光的“半成品 Bean”
  1. 三级缓存(singletonFactories)
  • 存放 ObjectFactory,用于生成代理对象(解决 AOP 问题)

为什么需要三层缓存?

解决 AOP 代理问题 如果只有二级缓存:

  • 可能提前暴露的是“原始对象”
  • 但最终应该注入的是“代理对象”
  • 会导致 AOP 失效或对象不一致
    所以需要三级缓存:
  • 通过 ObjectFactory 延迟生成“代理对象”

不能解决循环依赖的场景

  1. 构造器注入的循环依赖
  • 因为对象还没创建,无法提前暴露
  1. 原型(prototype)Bean
  • Spring 不缓存 prototype Bean

登录的时候http请求怎么发的

  1. 用户在前端输入账号和密码后,前端会将登录信息封装成一个 HTTP 请求发送给后端。
  2. 请求到达后端后,由 Controller 接收请求,Service 处理登录校验逻辑,再与数据库交互验证用户信息。
  3. 如果验证通过,后端会返回 Token 或 Session 信息给前端,前端保存后,后续请求再携带这个凭证完成身份认证。

Session和JWT的区别

数据存储: Session数据保存在服务端,客户端只保存SessionId JWT的数据保存在客户端,服务端不存储,是无状态的 分布式支持: Session天然不支持分布式 JWT天然支持分布式 数据内容: Session只存SessionId JWT可以存用户信息,不如userId 性能: Session每次请求都要查看服务端存储 JWT服务端只需校验 Token

JWT的组成

  • Header(头部)
  • Payload(载荷,存放用户信息)
  • Signature(签名,防篡改)

JWT的缺点

无法主动失效,泄露后会被伪造

HTTPS加密是怎么做的?

先用非对称加密安全地协商出一个对称密钥,再用这个对称密钥加密后续通信。 详细流程:

  • 通过 TLS 握手验证服务端身份
  • 协商出一个会话密钥
  • 后续数据传输用对称加密
  • 同时配合消息认证机制,保证数据没被篡改

为什么不全程加密?

对称加密:加密和解密用同一把密钥,速度快但是需要保证密钥安全 非对称加密:有一对密钥,公钥可以公开,私钥由服务端保存。可以安全传输密钥,但是传输速度慢。

CA

CA是一个受信任的第三方机构,负责给网站签发数字证书,用来证明“某个公钥确实属于某个网站”。浏览器通过信任 CA,从而信任网站的身份。

CA 的工作流程

  1. 网站向 CA 申请证书
  2. CA 验证网站身份
  3. CA 用自己的私钥“签名”证书
  4. 浏览器内置“受信任的 CA 列表”
  5. 浏览器验证证书

怎么防中间人攻击?

中间人攻击是指攻击者在客户端和服务端之间拦截并篡改通信数据。防范的核心是:身份认证 + 加密传输 + 完整性校验

攻击流程

  • 攻击者伪装成服务器和客户端通信
  • 同时伪装成客户端和服务器通信
  • 可以窃听、篡改数据

alt

HTTPS是怎么解决的?

数字证书:防止攻击者伪造服务器身份 非对称加密:中间人无法解密 对称加密:保证数据加密传输

java都有哪些锁,synchronized怎么实现的,能用在哪

Synchronized、ReentrantLock、ReadWriteLock、StampedLock

按竞争策略

类型 代表 特点
悲观锁 synchronized、ReentrantLock 先加锁再操作
乐观锁 CAS、StampedLock 乐观读 假设无冲突,失败重试

按是否可重入

类型 说明
可重入 synchronized、ReentrantLock,同线程可多次加锁
不可重入 自旋锁简单实现,容易死锁

按公平性

类型 说明
公平锁 new ReentrantLock(true),FIFO 队列,吞吐低
非公平锁 默认,允许插队,吞吐高,可能饥饿
// 1. 修饰实例方法 —— 锁是 this 对象
public synchronized void method() { ... }

// 2. 修饰静态方法 —— 锁是 Class 对象
public static synchronized void staticMethod() { ... }

// 3. 修饰代码块 —— 锁是指定对象
synchronized (lockObj) { ... }

① 偏向锁 第一个线程获取时,把 ThreadID 写入 Mark Word 再次进入直接比较 ThreadID,无 CAS 开销 → 适合:单线程反复进入同一锁

② 轻量级锁(自旋锁) 有第二个线程竞争 → 偏向锁撤销 线程在栈帧创建 Lock Record,CAS 替换 Mark Word 失败则自旋重试(JDK6+ 自适应自旋) → 适合:竞争短暂,持锁时间短

③ 重量级锁 自旋达到阈值仍失败 → 膨胀为重量级锁 依赖 OS 的 Mutex,线程挂起进入内核态 → 适合:竞争激烈,持锁时间长

alt ![[Java锁.png]]

Spring里的事务怎么用的

编程式事务(了解即可)

@Autowired
TransactionTemplate transactionTemplate;

public void transfer() {
    transactionTemplate.execute(status -> {
        accountDao.deduct(1, 100);
        accountDao.add(2, 100);
        return null;
        // 抛异常会自动回滚
    });
}

声明式事务

@Service
public class AccountService {

    @Transactional  // 加这一行,方法变成事务方法
    public void transfer(int fromId, int toId, int amount) {
        accountDao.deduct(fromId, amount);
        accountDao.add(toId, amount);
        // 抛 RuntimeException → 自动回滚
        // 正常返回 → 自动提交
    }
}

声明式事务的底层:

调用 transfer()
    ↓
[Spring AOP 代理]
    ↓
TransactionManager.begin()     // 开启事务
    ↓
执行真实方法
    ↓
成功 → commit()   异常 → rollback()

事务的传播行为

事务方法 A 调用事务方法 B,B 用哪个事务?这就是传播行为。

传播行为 含义
REQUIRED(默认) 有事务就加入,没有就新建
REQUIRES_NEW 总是新建事务,挂起外层事务
NESTED 嵌套事务,外层回滚影响内层,内层回滚不影响外层(savepoint)
SUPPORTS 有事务就加入,没有就非事务执行
NOT_SUPPORTED 非事务执行,挂起已有事务
MANDATORY 必须在事务中,否则抛异常
NEVER 不能在事务中,否则抛异常

声明式事务避坑

默认只回滚 RuntimeException(非检查异常)Error。如果抛出的是IOException就会直接提交

坑1:同类内部调用

@Service
public class OrderService {

    public void createOrder() {
        // ❌ 直接调用同类方法,绕过了 AOP 代理,事务不生效!
        this.saveOrder();
    }

    @Transactional
    public void saveOrder() {
        // ...
    }
}

// ✅ 解决方案:注入自己 或 抽到另一个 Bean
@Service
public class OrderService {
    @Autowired
    private OrderService self;  // 注入代理对象

    public void createOrder() {
        self.saveOrder();        // 通过代理调用,事务生效
    }

    @Transactional
    public void saveOrder() { ... }
}

坑2:方法不是 public

// ❌ AOP 代理无法拦截 private/protected 方法
@Transactional
private void doSomething() { ... }

// ✅ 必须是 public
@Transactional
public void doSomething() { ... }

坑3:异常被吞掉

// ❌ 异常被 catch 了,Spring 感知不到,不会回滚
@Transactional
public void transfer() {
    try {
        accountDao.deduct(1, 100);
        accountDao.add(2, 100);
    } catch (Exception e) {
        log.error("出错了", e);  // 异常被吃掉,事务提交!
    }
}

// ✅ 要么重新抛出,要么手动标记回滚
@Transactional
public void transfer() {
    try {
        accountDao.deduct(1, 100);
        accountDao.add(2, 100);
    } catch (Exception e) {
        log.error("出错了", e);
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // 手动标记
        // 或者 throw new RuntimeException(e);
    }
}

面经链接

https://www.nowcoder.com/feed/main/detail/0dfe5838ae16439f9b2dfa3effb08580

#面经#
每日面经记录 文章被收录于专栏

记录每天Java和Agent面经学习

全部评论

相关推荐

点赞 评论 收藏
分享
点赞 评论 收藏
分享
04-20 22:20
已编辑
门头沟学院 golang
27届,bg为四非本211硕,如题,导师不放实习,且每周至少一次线下组会(工作日),从研一上开始实习,然后我组在研一下引入了打卡机五段大厂分别是:美团到店、美团服务零售、快手电商、字节TikTok、字节CapCut。目前要结束我的第五段实习了(不会再刷第六段,好好搞学校的事,还有秋招)本来一直告诉自己的是“所有委屈到了终点再说”,过去告诉自己的终点自然还没到,但我觉得自己仿佛已经到了另一个终点,有感而发,写了这篇文章也许你会觉得为啥不尝试问问导师能不能实习,或者用其他让自己舒服的手段,我只能说,这很复杂,有导师的人自然会懂,这种一开始就把“利益冲突”摆明面上的招几乎就是不可能成功———————————————————我到底是怎么实习的?骗hr自己满勤,然后没有捷径,就是每周往返,第一段去的是北京美团,而学校在江苏,因此需要一周一次北京江苏往返,因为实习钱少,所以坐的基本是绿皮,难以入睡,下车后就是长达2小时的地铁去公司,地铁站上靠着人睡觉周末做什么?基本在做导师的科研or横向,学习的话很多时候就是尽力在晚上回到出租屋的时候学,这很难维持,但只能不断push自己如何破解打卡机?直接把打卡机偷了,或者使用指纹膜(当然我很早就做好了无法破解的准备,那就是找个长三角实习,每天早起去打卡完坐高铁去实习,从每周高铁往返变成每天)导师会压力吗?非常压力,实习的时候非常害怕微信弹出他的消息,PTSD了,有时候一周要往返两次学校,每次都跟要死了一样,之前真是情绪崩溃好几次,哈哈哈哈平时往返怎么平衡工作?我本来很晕车,为了不耽误公司和导师的进度,从车上一看电脑就头晕、吐,到后面可以随意在高铁、地铁、出租车上Coding,甚至不会再因为往返感到心累了,哈哈哈哈这一路已经淬炼出比较坚强的内心了,已经数不清多少次坐末班高铁从学校回公司,多少次凌晨6点爬起来赶车过去我会把这些当作是我人生的弯路,但现在,这些已经成为我宝贵的经验了。往后,我想我也能真正允许各种不好的情况出现了,因为我会真正把它当作我要解决的问题,而非抱怨,这又何尝不是终点呢?要照顾好身体,我不管怎么往返,一直非常在乎身体,会让自己睡够8小时,最近几星期培养早睡早起到公司健身后去工作的习惯,我觉得好身体很关键
gtgt..:很佩服,但是很恐怖,感觉已经从人类异化到高度运转的机器了
美团工作强度 2457人发布
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
今天 00:23
長谷川育美offic...:一般没有10w我是要紫砂的
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务