
欢迎来到预见猿份,本站项目均为站长原创,学习中有问题可直接提交给站长老苗解决(微信:mrt_0607)。
苗润土老师,20余年一线项目经验,2014年加入黑马,星辰wms、云岚到家、学成在线项目作者,历任高级讲师、教学主管及课程研究员。 b站老苗
Java 框架(Spring Boot/Spring Cloud/SpringMVC/Spring)常见面试题
框架问的也是比较多的,但是都是一些经典问题,而且涉及一些源码流程
所以有些问题只需要记忆关键点即可
对于目前我们Java开发者来说,用的最多且无法避免的就是Spring全家桶
所以这里大家要先明白Spring、SpringMVC、SpringBoot、SpringCloud这四个框架的关系和区别
以及SpringCloud和SpringCloudAlibaba的关系
Spring:全家桶的基础框架,剩下所有全家桶都需要依赖此框架,两大核心,第一大核心是IOC控制反转、DI依赖注入。第二大核心是AOP面向切面编程。解释的时候围绕这两大核心来说
SpringMVC:帮助Java开发者快速构建一个Web应用,底层基于Servlet
SpringBoot:快速构建一个Java应用,两大核心,自动装配和起步依赖。所以围绕这两大核心来说
SpringCloud:用于构建一个微服务的项目,每个微服务还是可以基于Spring、SpringBoot、SpringMVC
SpringCloudAlibaba:由于原始SpringCloud刚开始是网飞Netflix公司开源出来的组件,而阿里这个框架吸收了 Spring Cloud Netflix 微服务框架的核心架构思想,并进行了高性能改进。自 Spring Cloud Netflix 进入停更维护后,Spring Cloud Alibaba 逐渐代替它成为主流的微服务框架。
Spring全家桶速查:https://yjoffer.com/tools/spring-cheatsheet.html
SpringCloud系统学习:https://yjoffer.com/springcloud/readme.html
1.Spring篇
IOC和DI
Spring的第一大核心,出现频率很高
IOC(Inverse of Control,控制反转)和DI(Dependency Injection,依赖注入)是两个密切相关的概念,它们是实现Spring核心功能的基础。 IOC是一种设计原则,它逆转了传统的控制流程。在传统的程序设计中,组件之间的依赖关系通常由组件自身控制,而在IOC中,这些依赖关系的控制权被反转交给了外部容器(如Spring容器)。
目的是为了Spring容器负责管理对象的生命周期和依赖关系,从而降低组件之间的耦合度,提高模块化。 DI是一种实现IOC的技术手段,它将组件之间的依赖关系注入到组件中,而不是让组件自己查找或创建依赖对象。依赖注入的目的是进一步降低组件之间的耦合度,提高代码的可维护性和可测试性。
依赖注入的几种方式
构造器注入、setter注入、字段注入 注意:Spring官方更推荐使用构造器注入。但是实际开发中使用字段注入更方便
public class MyClass {
@Autowired
private Dependency myDependency1; //字段注入
private Dependency myDependency2;
private Dependency myDependency3;
@Autowired
public MyClass(Dependency myDependency) {//构造器
this.myDependency2 = myDependency;
}
@Autowired
public void setMyDependency(Dependency myDependency) {//setter注入
this.myDependency3 = myDependency;
}
}AOP
往往跟随着场景:你们项目哪里用到了
AOP翻译为面向切面编程,是一个允许开发者将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离的技术。 简单的说就是我们可以利用AOP来增强一个特定方法,同时又不修改原方法 底层原理是动态代理 常见场景:记录操作日志、事务管理、数据脱敏、权限校验、缓存处理等。
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
// 记录日志
}
}Bean的生命周期
不用全部背出来,背几个关键单词和步骤即可
- 通过
BeanDefinition获取bean的定义信息 - 调用构造函数实例化bean
- bean的依赖注入
- 处理Aware接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware)
- Bean的后置处理器
BeanPostProcessor-前置 - 初始化方法(InitializingBean、init-method)
- Bean的后置处理器
BeanPostProcessor-后置 - 销毁bean
极简版(1 分钟快速回答)
Spring Bean 生命周期核心四步:实例化→属性注入→初始化→销毁,关键扩展点是 BeanPostProcessor(初始化前后增强),初始化优先级 @PostConstruct InitializingBean init-method,销毁优先级 @PreDestroy DisposableBean destroy-method;单例 Bean 生命周期由容器管理,原型 Bean 创建后容器不再管控。

Bean的作用域
还会问Spring里的Bean是单例的吗
还会问这个bean是线程安全的吗?参考并发编程篇
默认是单例的,作用域singleton,也就是整个容器里就这么一个bean 可以配置成多例,使用注解@Socpe("prototype"),那么这个bean每次用到都会创建一个新的
循环依赖以及三级缓存
具体流程比较复杂,同学们还是需要自己去把流程看一下,不管是视频还是ppt,纯文字不太好解释
循环依赖的现象指的是A注入B,然后B页注入了A,形成了一个圈。 解决方案:使用@Lazy注解,或者使用spring.main.allow-circular-references=true 但是Spring本身是允许循环依赖并且可以解决的 循环依赖在spring中是允许存在,spring框架依据三级缓存已经解决了大部分的循环依赖
- 一级缓存(Singleton Objects):
- 简单来说放的就是成品Bean
- 二级缓存(Early Singleton Objects):
- 简单来说就是放的早期Bean,已经实例化但是还没设置属性
- 三级缓存(Singleton Factories):
- 存放Bean工厂对象,即ObjectFactory,用于创建Bean的实例。三级缓存是Spring 4.0引入的,它允许Spring容器提前暴露一个Bean的引用,即使该Bean还没有完全初始化。 Spring通过以下步骤使用三级缓存解决循环依赖:
- Bean创建:Spring容器开始创建Bean A。
- 属性注入:在Bean A的属性注入过程中,需要引用Bean B。
- Bean B创建:Spring容器开始创建Bean B,但在Bean B的属性注入过程中,又需要引用Bean A,形成循环依赖。
- 提前引用:在Bean A的属性注入过程中,如果Spring容器发现需要Bean B,但Bean B尚未完全初始化,它会检查三级缓存中是否有Bean B的ObjectFactory。
- ObjectFactory调用:如果三级缓存中有Bean B的ObjectFactory,Spring容器将使用它来创建Bean B的一个早期引用,并注入到Bean A中。
- Bean A初始化:此时Bean A可以继续初始化过程,因为已经获得了Bean B的引用。
- Bean B完成初始化:当Bean B的完整初始化过程完成后,它将从二级缓存移动到一级缓存。
- Bean A完全初始化:随后,Bean A也完成初始化过程,从二级缓存移动到一级缓存。 通过三级缓存,Spring容器能够在Bean完全初始化之前,提供一个代理的引用,从而打破循环依赖,确保每个Bean都能被成功创建和初始化。
@Transactional事务的原理以及失效的场景
用注解的事务管理方式:声明式事务
还有一个编程式事务:TransactionalTemplate
Spring中声明式事务(也就是注解)底层依赖的是AOP,而AOP底层是动态代理,动态代理又分两种方式:JDK和Cglib 事务失效的场景:
- 提前捕获异常
- 注解默认捕获
RuntimeException,而抛出了一个非运行时异常 - 方法没有用public修饰(动态代理)
- 调用类的内部方法(动态代理) 其中第四个最为重要,也是问的最多,具体如下代码
methodA方法没有加事务注解,而methodB方法加了事务,那么methodB方法上的事务注解就会失效,原因是因为调用methodB其实是走的this.methodB(),注意前面这个this指的是当前对象,而AOP是基于动态代理实现的,所以想要增强就得使用代理对象(平时我们注入的service其实都是代理对象) 解决:我们只要想办法获取代理对象,这里我们可以借助AspectJ来实现。 通过AopContext.currentProxy()拿到当前bean的代理对象,再去调用方法 或者使用TransactionalTemplate来实现编程式事务(不用注解)
@Service
public class OrderService {
public void methodA(){
// ... 准备订单数据
// 生成订单并扣减库存
methodB();
}
@Transactional
public void methodB(){
// 生成订单
insertOrder();
// 扣减库存
reduceStock();
}
}@Resource和@Autowired的区别
@Autowired是Spring框架自己的注解。默认根据类型(byType)进行自动装配。如果存在多个同类型的Bean,可以通过@Qualifier注解来指定具体需要装配哪个Bean。@Resource是Java EE的注解,Spring对其做了增强,使其在Spring中也可以使用。默认通过名称(byName)进行自动装配。这意味着它会查找与属性名称相匹配的Bean。如果找不到,则回退到byType进行自动装配。
2.SpringMVC篇
SpringMVC的常用注解
因为SpringMVC是处理Web开发的,所以基本都是Controller层负责处理请求响应的
@RequestBody、@RequestParam、@PathVariable、@RequestMapping、@GetMapping、@PostMapping、@ResponseBody
SpringMVC的执行流程
还会问成SpringMVC的工作原理
其实问的就是为什么发个请求到tomcat,这边SpringMVC的controller就能直接收到请求 核心就是DispatcherServlet,它本身就是Servlet,Servlet就是负责接收请求处理响应,同时还有一个urlPattern指定接受的路径,而DispathcerServlet的路径是/*,所以能接收所有的请求


3.SpringBoot篇
SpringBoot自动装配原理
出现频率最高的面试题,务必掌握。本质上还是基于SpringBoot的"约定大于配置"的思想
先说作用以及好处 简单的说就是SpringBoot可以自动将一些Bean对象加载到IOC容器而不用我们手动配置。如果只看这句话同学们可能没什么感觉,可以参考下面没有SpringBoot的代码感受一下。 再说流程
- 首先SpringBoot项目有一个启动类,启动类上有一个注解
@SpringBootApplication - 这个注解里包含三个核心注解,对于自动装配来说最重要的就是
@EnableAutoConfiguration - 这个注解里又有一个
@Import({AutoConfigurationImportSelector.class})制定了selector类 - 这个类通过一系列方法最终找到了一个
META-INF/spring文件夹下的一个org.springframework.boot.autoconfigure.AutoConfiguration.imports文件(2.7版本之前叫spring.factories) - 在这个文件里,定义了许多
Configuration配置类,配置类里面定义了需要自动加载到IOC容器的Bean对象 - 这些对象还有
@Conditional类型的注解,表示某些条件下才会加载,比如@ConditionalOnClass就是有某个字节码文件的情况下才会去加载
没有用SpringBoot之前,自己加载MyBatis相关配置Bean。用了SpringBoot,下面的配置类通通不需要写,里面的SqlSessionFactoryBean和MapperScannerConfigurer都会通过自动装配的特性自动加载到IOC容器。所以我们只要引入了MyBatis的starter依赖,就可以直接使用MyBatis,非常方便
public class MyBatisConfig {//自定项目中要定义MyBatis的配置类,定义之后才能正常使用MyBatis框架
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.itheima.domain");
return factoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}SpringBoot起步依赖原理
基于Maven的依赖传递 完整的说就是我们引入一个starter依赖后,由于starter中替我们打包好了所有需要的其他依赖,所以只需引入这一个starter依赖,就可以直接使用该框架
SpringBoot内置服务器是什么
Tomcat 追问:能否替换?能替换成那些服务器? 可以替换成jetty(更轻量级)、undertow(并发性能更好) 在pom文件中把web的的起步依赖先排除掉Tomcat的起步依赖,在引入jetty的起步依赖
SpringBoot的常用注解
@SpringBootApplication:SpringBoot框架的核心注解,通常写在启动类上,算是一个复合注解,其中包含了三个子注解: @SpringBootConfiguration 用来声明当前类是一个配置类 @EnableAutoConfiguration springboot实现自动化配置的核心注解,通过这个注解把spring应用所需的bean注入容器中 @ComponentScan 用来自动扫描注解标识的类,生成ioc容器里的bean,默认扫描范围是@ComponentScan注解所在配置类包及子包的类 @ConfigurationProperties:可以把配置文件内容绑定到bean的字段上 Condition条件注解: @ConditionalOnClass classpath 下存在某个 class 时,条件才成立 @ConditionalOnMissingBean beanFactory 内不存在某个 bean 时,条件才成立 @ConditionalOnProperty 配置文件中存在某个 property(键、值)时,条件才成立
4.SpringCloud篇
SpringCloud五大组件
注意要区分是原版springcloud还是Alibaba版本的,我们课上用的都是Alibaba的版本
注册中心:Nacos/Eureka 网关:Gateway/Zuul 熔断器:Hystrix/Sentinel 远程调用:OpenFeign 负载均衡:Ribbon/LoadBalancer
Nacos和Eureka的区别
Nacos是SpringCloudAlibaba的组件,Eureka是SpringCloud原版Netflix的组件 二者都是做为注册中心的存在,提供服务的注册和发现。 目前国内使用Nacos更多,Nacos还可以做为配置中心来使用
熔断降级如何配置?
注意区分你们用的是sentinel还是hystrix,我们项目里用的都是阿里的sentinel
sentinel
- 限流 在sentinel的控制台找到对应的服务和对应的接口路径,可以对该路径配置限流,失败的请求就会走降级逻辑
- 降级
- 方式一:FallbackClass,无法对远程调用的异常做处理
- 方式二:FallbackFactory,可以对远程调用的异常做处理,我们一般选择这种方式。
- 熔断
Sentinel中的断路器不仅可以统计某个接口的慢请求比例,还可以统计异常请求比例。当这些比例超出阈值时,就会熔断该接口,即拦截访问该接口的一切请求,降级处理;当该接口恢复正常时,再放行对于该接口的请求。可以在控制台配置熔断策略,如下图

5.MyBatis&MyBatisPlus
#和$的区别
#用于预编译的sql,能够提升sql执行的性能,把where后面的条件换成?而做到相同sql结构不同条件的语句提前编译。还可以防止sql注入$就是字符串拼接,有sql注入的风险,不安全,但是如果表名、字段名是变量的话,就只能用$
MyBatis的执行流程
- 读取MyBatis配置文件:mybatis-config.xml加载运行环境和映射文件(SpringBoot时代已经融合到application.yml里)
- 构造会话工厂SqlSessionFactory
- 会话工厂创建SqlSession对象(包含了执行SQL语句的所有方法)
- 操作数据库的接口,Executor执行器,同时负责查询缓存的维护
- Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息
- 输入参数映射
- 输出结果映射
MyBatis的多级缓存
也叫一级二级缓存
SqlSession 大家可以简单理解成每一次HTTP请求,MyBatis都会帮我们创建一个SqlSession,并且这个对开发者是透明的,也就是我们其实并不需要知道它的存在。
而Mapper其实就是咱们平时写的OrderMapper、UserMapper这些接口,它的范围要比SqlSession更大,不同的SqlSession可能调用的都是同一个Mapper
一级缓存是 SqlSession 级别的缓存,同一个 SqlSession 对同一条 SQL 的查询结果会被缓存起来,下次查询相同条件时直接从缓存中获取,减少数据库访问次数。 二级缓存是 Mapper 级别的缓存,可以在多个 SqlSession 之间共享,不同的 SqlSession 执行相同的 Mapper 查询时,如果二级缓存中有数据则可以直接使用,进一步提高查询效率。 追问:Mybatis的二级缓存什么时候会清理缓存中的数据 当某一个作用域(一级缓存 Session/二级缓存mapper)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear。
MyBatisPlus有什么好处
有代码生成器,直接提供了单表增删改查的语句,还有一些好用的插件,逻辑删除,生成雪花算法ID,写简单sql语句可以直接在代码层面去写,不用写xml语句
MyBatisPlus有哪些常用插件
参考官网:https://baomidou.com/plugins/ 分页插件(最常用的,务必掌握怎么配置) 乐观锁插件(多加一个version版本号字段) 多租户插件(先了解什么叫多租户) 动态表名插件(分表) 防全表更新删除插件
6.场景题
我想让一段代码在bean被创建的时候执行
通常用于数据准备,比如我有一个工具类的bean,需要启动的时候加载一些配置文件的数据
- 使用
@PostConstruct注解修饰Bean内部的一个方法,该方法内部的代码就会在bean初始化的时候执行 - 实现
InitializingBean接口,重写afterPropertiesSet方法,该方法内部的代码也会初始化的时候执行
怎么自己实现一个starter实现自动装配
首先介绍SpringBoot自动装配原理,这里不赘述,参考上面 然后我们首先新建一个starter,在该项目中,我们在resources目录下新建META-INF/spring文件夹,在该文件夹下新建org.springframework.boot.autoconfigure.AutoConfiguration.imports文件(2.7版本之前叫spring.factories)。 在这个文件中指定自己的配置类,比如XXConfiguration,在这个配置类中写入需要加载的Bean对象,比如XXUtils。 然后别的项目依赖你这个starter之后,你之前写的XXUtils就会自动加载到它的IOC容器,从而实现自动装配
你们微服务是怎么拆分的
一般来说是按照业务拆分,正常情况10个左右的微服务偏多,当然数量不固定,也可以5、6个。最好针对自己的项目提前分好,比如系统服务(System)、用户服务(User)、订单服务(Order)、。。。
你们项目中有没有用到AOP?哪里用到了
肯定有用到,结合自身实际项目来回答 最常见的就是记录操作日志,注意这里的操作日志不是我们平时使用log.info打印的那种日志,而是需要把用户的操作记录存储到数据库中的日志。比如用户增删改操作,我们就对应往数据库里插入三条记录 具体实现方式: 1.定义一个自定义注解,对所有需要记录日志的操作方法上加入该注解 2.实现一个切面类并使用@Around环绕通知,切所有注解修饰的方法 3.在切面类中把这次操作存储到数据库
为什么现在都用SpringBoot而不是SSM了
主要是因为它方便我们快速开发,具体参考SpringBoot自动装配原理
除了MyBatis你还用过哪些持久层框架?
可以没用过,但是要听说过了解过 Hibernate,ORM框架,简单地说就是把操作数据库当成操作对象来看待,现在国内很少用 JPA,Spring官方的持久层框架,国内还是有部分占用率,要自行去了解
MyBatis支持延迟加载吗?
延迟加载的意思是:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。 Mybatis支持一对一关联对象和一对多关联集合对象的延迟加载 在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false,默认是关闭的
听说过Dubbo吗
也是一个微服务框架,一般来说使用dubbo+zookeeper的方式就不用springcloud全家桶了 具体使用方式自行了解
使用过低代码开发框架吗
我们公司之前使用过jeecgboot,ruoyi也使用过,感觉都挺方便的 注意:如果要回答,必须看一下大概的视频或者文档
Spring/SpringBoot/SpringCloud中用到哪些设计模式?
单例模式(Singleton Pattern)
Spring 中的应用:Spring 容器默认将 Bean 以单例模式创建,即一个 Bean 在整个应用中只有一个实例。可以通过 @Scope("singleton") 注解(默认情况)来指定 Bean 的作用域为单例。
工厂模式(Factory Pattern) 原理:工厂模式定义一个创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。 Spring 中的应用:Spring 的 BeanFactory 和 ApplicationContext 就是工厂模式的典型应用。BeanFactory 是 Spring 容器的基础接口,它负责创建和管理 Bean 实例;ApplicationContext 是 BeanFactory 的子接口,提供了更多的企业级特性。
代理模式(Proxy Pattern)
原理:代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。 Spring 中的应用:Spring AOP(面向切面编程)使用了代理模式。Spring AOP 可以在不修改原有业务逻辑的基础上,对方法进行增强,如日志记录、事务管理等。Spring AOP 有两种代理方式:JDK 动态代理和 CGLIB 代理。
观察者模式(Observer Pattern)
原理:观察者模式定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。 Spring 中的应用:Spring 的事件驱动机制使用了观察者模式。Spring 定义了一系列的事件(如 ApplicationEvent)和监听器(如 ApplicationListener),当某个事件发生时,会通知所有注册的监听器。
模板方法模式(Template Method Pattern)
原理:模板方法模式定义一个操作中的算法骨架,将一些步骤延迟到子类中实现。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 Spring 中的应用:Spring 的 JdbcTemplate、HibernateTemplate 等模板类使用了模板方法模式。这些模板类封装了数据访问的通用逻辑,将具体的数据库操作步骤留给子类实现。
策略模式(Strategy Pattern)
原理:策略模式定义了一系列的算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。 Spring 中的应用:Spring 的事务管理使用了策略模式。Spring 提供了多种事务管理器(如 DataSourceTransactionManager、JtaTransactionManager 等),可以根据不同的业务需求选择不同的事务管理策略。
