最近开始面试了,410面试了一家公司 针对自己薄弱的面试题库,深入了解下,也应付下面试。在这里先祝愿大家在现有公司好好沉淀,定位好自己的目标,在自己的领域上发光发热,在自己想要的领域上(技术管理、项目管理、业务管理等)越走越远!希望各位面试都能稳过,待遇都是杠杠的!因为内心还是很慌,所以先整理所有高频的面试题出来,包含一些业务场景及java相关的所有面试。
Spring相关面试题
1.Spring AOP底层实现原理是什么?
- Spring AOP(面向切面编程)的底层实现基于动态代理技术,主要通过两种实现方式:JDK动态代理和CGLIB字节码生成。当目标类实现了接口,Spring默认使用了JDK动态代理,否则使用CGLIB方式,而spring-boot选择了VCGLIB方式来实现。
- JDK动态代理(基于接口)
- 适用条件:目标类实现了至少一个接口
- 代理对象通过JDK直接生成,实现目标类的接口,并通过反射调用目标类
- 特点:
- 运行时生成接口的代理类
- 通过InvocationHandler拦截方法调用
- 性能较好,但只能代理接口方法
- CGLIB字节码生成(基于子类)
- 适用条件:不能使用final
- 适用对象:目标类没有实现接口
- CGLIB通过CFLIB-ASM操作框架生成,继承目标类,通过子类调用父类的方式进行调用目标方法
- 特点:
- 通过ASM库直接生成目标类的子类(Enhancer)
- 可以代理普通类方法(包括非public方法)
- 创建代理对象速度较慢,但执行效率高
- 代理创建流程
- Spring AOP创建代理的核心流程:
- 解析切面配置
- 通过@Aspect注解或者XML配置识别切面
- 解析切入点表达式(Pointcut)
- 创建代理工厂(proxyFactory)
- 选择代理机制
- 生成代理对象
- JDK代理:proxy.newProxyInstance()
- CGLIB:enhance.create()
- 拦截器链执行
- Spring AOP通过责任链模式执行增强逻辑
- 增强类型与顺序执行
- spring aop支持了五种通知类型
- @Aroud环绕通知
- @before前置通知
- 目标方法执行
- @AfterReturing(返回通知,正常返回时执行)
- @After(后置通知,finally块中执行)
- @AfterThrowing(异常通知,抛出异常时执行)
- 性能优化与实现
- Spring对AOP进行了多项优化
- 缓冲机制
- 代理类缓冲DefaultAopProxyFactory
- 拦截器链缓冲AdvisedSupport
- 预过滤
- 预先排除不可能匹配的方法
- 选择性代理
- 支队匹配切入点的方法生成代理逻辑
- 其他方法直接调用目标方法
- 与AspectJ的关系
特性 | Spring AOP | AspectJ |
---|
实现方式 | 运行时动态代理 | 编译时/加载时织入 |
性能 | 较好 | 最优(编译期完成) |
功能范围 | 仅方法级别 | 字段、构造器、静态块等 |
依赖 | 仅Spring核心 | 需要AspectJ编译器/织入器 |
适用场景 | 简单切面需求 | 复杂切面需求 |
- 总结:
- Spring AOP的底层实现本质上是基于代理模式的运行时增强,其核心特点是
- 非侵入性:通过代理实现,不修改原始代码
- 灵活性:支持多种通知类型和切入点表达式
- 可扩展性:可与AspectJ部分功能集成
- 性能平衡:通过缓存和优化策略保证运行时效率
组合面试题
1.如何有效的阻止订单重复提交和支付

理论上只会在用户在下单的这个动作可能因为网络抖动、RPC重试等进行多次下单的操作,其他步骤确认订单只是修改订单状态,跳转支付和确认支付这些不会出现多次支付的问题。所以该题主要是针对用户多次调用下单接口怎么处理即下单接口的幂等性问题。
- 订单重复提交问题
- 前端防重复提交方案
- 按钮置灰等操作
- PRG模式:post/redirect/get模式,用户点击表单时重定向跳转到其他页面。
- token机制,在用户进入订单界面前生成固定的token,前端限制一个token调用时,后端拦截token的一次性,做请求拦截限制
- 请求拦截:通过axios拦截器拦截信息
- 后端接口设计
- 幂等性设计
- 每次请求先配合客户端生成一个唯一id,可由订单id+用户id+确认标识做绑定,同一接口,每次调用的id一致则不生成新的订单,注意标识符的失效时间
- 请求参数中带有时间戳与当前时间对比,若时间太长则默认为重复请求
- 请求状态检查,根据日志查询、用户订单关联查询是否有重复数据
- 数据库唯一约束
- redis原子操作
- setnx操作,对同一个订单id+用户id+确认标识做绑定,设置失效时间,进行处理
- 支付方重复方案
- 分布式系统解决方案
- 异常处理机制
- 监控与报警
- 建议:
- 多层次防御:前端+后端+数据库约束等
- 核心原则:所有写操作必须实现幂等性接口
- 关键数据:订单号、用户ID、时间戳组合防重复
- 状态管理:严格的状态机控制流程
- 补偿机制:自动核对+人工干预双重保险
- 技术选择
场景 | 推荐方案 | 优点 |
---|
简单单体系统 | 本地锁+数据库唯一约束 | 实现简单 |
分布式高并发 | Redis分布式锁+消息队列 | 扩展性好 |
金融级支付系统 | 状态机+定时核对+人工干预 | 可靠性最高 |
旧系统改造 | 前端Token+后端幂等接口 | 侵入性最小 |
2.RestTemplate 如何优化连接池
- restTemplate默认是没有连接池,他的调用原理是每次都会创建一个HTTP连接,默认使用simpleClientHttpRequestFactory去创建连接,底层通过HttpURLConnection创建连接。在高并发的条件下,会无上限的创建连接,消耗系统资源。所以需要通过连接池来限制最大连接数,当请求的域不是很多且不随机的情况下,还可以复用同一个域的HTTP连接;
- HTTP请求流程:在我们发起一个http请求连接的时候,会对域名解析,连接之前的三次握手,如果是HTTPS还需要传递安全证书,以及请求完成之后的4次挥手。但是我们真正请求和响应只在其中的一小环节,所以我们通过一个连接池就可以对同一个域来建立一个长链接,就无需执行每次的无关业务请求的动作,这样就实现了连接的复用。
- 通过resttemplate来配置连接池的话有HttpClient和OkHttp.
- 具体实现:
- 代码上引入httpclient连接池的包,修改RestTemplate请求工厂,将默认工厂的simpleClientHttpRequestFactory换成HttpClientFactory,在为这个工厂配置请求bean。最后去设置连接池参数信息。设置最大连接数,根据QPS的响应时间平均值来设置,设置某个域的长链接复用,但是如果该值太小,比如设置2,那么只会创建两个长链接,这样后续的接口会进行阻塞等待。
- 在实现连接池的方法时,需要注意以下几点:
- 路由区分:对重要API设置独立的路由连接数
- 异常处理:配置重试机制
- DNS刷新:避免DNS缓冲问题
- 连接存活时间
参数 | 建议值 | 说明 |
---|
setMaxTotal | 100-500 | 最大连接数,根据服务器配置调整 |
setDefaultMaxPerRoute | 50-100 | 每个路由(host:port)的最大连接数 |
setConnectTimeout | 3000-5000ms | 建立TCP连接的超时时间 |
setSocketTimeout | 5000-10000ms | 数据传输超时时间 |
setConnectionRequestTimeout | 1000-2000ms | 从连接池获取连接的超时时间 |
evictIdleConnections | 30-60s | 空闲连接回收时间 |