最全Spring常见面试题总结(超详细回答)_spring面试题,程序员35岁真的是分水岭吗

最后我们该如何学习?

1、看视频进行系统学习

这几年的Crud经历,让我明白自己真的算是菜鸡中的战斗机,也正因为Crud,导致自己技术比较零散,也不够深入不够系统,所以重新进行学习是很有必要的。我差的是系统知识,差的结构框架和思路,所以通过视频来学习,效果更好,也更全面。关于视频学习,个人可以推荐去B站进行学习,B站上有很多学习视频,唯一的缺点就是免费的容易过时。

另外,我自己也珍藏了好几套视频资料躺在网盘里,有需要的我也可以分享给你:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

2、读源码,看实战笔记,学习大神思路

“编程语言是程序员的表达的方式,而架构是程序员对世界的认知”。所以,程序员要想快速认知并学习架构,读源码是必不可少的。阅读源码,是解决问题 + 理解事物,更重要的:看到源码背后的想法;程序员说:读万行源码,行万种实践。

Spring源码深度解析:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

Mybatis 3源码深度解析:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

Redis学习笔记:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

Spring Boot核心技术-笔记:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

3、面试前夕,刷题冲刺

面试的前一周时间内,就可以开始刷题冲刺了。请记住,刷题的时候,技术的优先,算法的看些基本的,比如排序等即可,而智力题,除非是校招,否则一般不怎么会问。

关于面试刷题,我个人也准备了一套系统的面试题,帮助你举一反三:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

只有技术过硬,在哪儿都不愁就业,“万般带不去,唯有业随身”学习本来就不是在课堂那几年说了算,而是在人生的旅途中不间断的事情。

人生短暂,别稀里糊涂的活一辈子,不要将就。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • ① prepareRefresh()刷新前的预处理:
  • ② obtainFreshBeanFactory():获取在容器初始化时创建的BeanFactory:
  • ③ prepareBeanFactory(beanFactory):BeanFactory的预处理工作,向容器中添加一些组件:
  • ④ postProcessBeanFactory(beanFactory):子类重写该方法,可以实现在BeanFactory创建并预处理完成以后做进一步的设置
  • ⑤ invokeBeanFactoryPostProcessors(beanFactory):在BeanFactory标准初始化之后执行BeanFactoryPostProcessor的方法,即BeanFactory的后置处理器:
  • ⑥ registerBeanPostProcessors(beanFactory):向容器中注册Bean的后置处理器BeanPostProcessor,它的主要作用是干预Spring初始化bean的流程,从而完成代理、自动注入、循环依赖等功能
  • ⑦ initMessageSource():初始化MessageSource组件,主要用于做国际化功能,消息绑定与消息解析:
  • ⑧ initApplicationEventMulticaster():初始化事件派发器,在注册监听器时会用到:
  • ⑨ onRefresh():留给子容器、子类重写这个方法,在容器刷新的时候可以自定义逻辑
  • ⑩ registerListeners():注册监听器:将容器中所有的ApplicationListener注册到事件派发器中,并派发之前步骤产生的事件:
  • ⑪  finishBeanFactoryInitialization(beanFactory):初始化所有剩下的单实例bean,核心方法是preInstantiateSingletons(),会调用getBean()方法创建对象;
  • ⑫ finishRefresh():发布BeanFactory容器刷新完成事件:
8、BeanFactory和ApplicationContext有什么区别?

BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。

(1)BeanFactory是Spring里面最底层的接口,是IoC的核心,定义了IoC的基本功能,包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理。ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

  • 继承MessageSource,因此支持国际化。
  • 资源文件访问,如URL和文件(ResourceLoader)。
  • 载入多个(有继承关系)上下文(即同时加载多个配置文件) ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
  • 提供在监听器中注册bean的事件。

(2)①BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能提前发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。

②ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。

③ApplicationContext启动后预载入所有的单实例Bean,所以在运行的时候速度比较快,因为它们已经创建好了。相对于BeanFactory,ApplicationContext 唯一的不足是占用内存空间,当应用程序配置Bean较多时,程序启动较慢。

(3)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

(4)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

9、Spring Bean的生命周期?

简单来说,Spring Bean的生命周期只有四个阶段:实例化 Instantiation --> 属性赋值 Populate  --> 初始化 Initialization  --> 销毁 Destruction

但具体来说,Spring Bean的生命周期包含下图的流程:

(1)实例化Bean:

对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。

对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

(2)设置对象属性(依赖注入):实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入。

(3)处理Aware接口:Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的一些资源:

  • ①如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,传入Bean的名字;
  • ②如果这个Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。
  • ②如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
  • ③如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

(4)BeanPostProcessor前置处理:如果想对Bean进行一些自定义的前置处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

(5)InitializingBean:如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。

(6)init-method:如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

(7)BeanPostProcessor后置处理:如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

(8)DisposableBean:当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

(9)destroy-method:最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

如果对bean详细加载流程的感兴趣的读者,可以阅读这篇文章:Spring的Bean加载流程_张维鹏的博客-CSDN博客

10、 Spring中bean的作用域:

(1)singleton:默认作用域,单例bean,每个容器中只有一个bean的实例。

(2)prototype:为每一个bean请求创建一个实例。

(3)request:为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。

(4)session:与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例。

(5)global-session:全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话,那么这全局变量需要存储在global-session中。

11、Spring框架中的Bean是线程安全的么?如果线程不安全,那么如何处理?

Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体情况还是要结合Bean的作用域来讨论。

(1)对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题。

(2)对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的。但是如果单例Bean是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Controller类、Service类和Dao等,这些Bean大多是无状态的,只关注于方法本身。

有状态Bean(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。

无状态Bean(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。

对于有状态的bean(比如Model和View),就需要自行保证线程安全,最浅显的解决办法就是将有状态的bean的作用域由“singleton”改为“prototype”。

也可以采用ThreadLocal解决线程安全问题,为每个线程提供一个独立的变量副本,不同线程只操作自己线程的副本变量。

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。

12、Spring基于xml注入bean的几种方式:
  • set()方法注入;
  • 构造器注入:①通过index设置参数的位置;②通过type设置参数类型;
  • 静态工厂注入;
  • 实例工厂;

详细内容请参考这篇文章:Spring中bean的注入方式

13、Spring如何解决循环依赖问题:

详细内容强烈建议参考这篇文章:Spring如何解决循环依赖问题

循环依赖问题在Spring中主要有三种情况:

  • (1)通过构造方法进行依赖注入时产生的循环依赖问题。
  • (2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
  • (3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

在Spring中,只有第(3)种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。这是因为:

  • 第一种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
  • 第二种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。

Spring在单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过二级缓存和三级缓存来解决的,其中三级缓存是主要功臣。解决的核心原理就是:在对象实例化之后,依赖注入之前,Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。

14、Spring的自动装配:

在spring中,使用autowire来配置自动装载模式,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象。

(1)在Spring框架xml配置中共有5种自动装配:

  • no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
  • byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。
  • byType:通过参数的数据类型进行自动装配。
  • constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
  • autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。

(2)基于注解的自动装配方式:

使用@Autowired、@Resource注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;

如果查询的结果不止一个,那么@Autowired会根据名称来查找;

如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

@Autowired可用于:构造函数、成员变量、Setter方法

注:@Autowired和@Resource之间的区别:

(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

15、Spring事务的实现方式和实现原理:

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。Spring只提供统一事务管理接口,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过 redo log 和 undo log实现的。Spring会在事务开始时,根据当前环境中设置的隔离级别,调整数据库隔离级别,由此保持一致。

(1)Spring事务的种类:

spring支持编程式事务管理和声明式事务管理两种方式:

①编程式事务管理使用TransactionTemplate。

②声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

(2)spring的事务传播机制:

spring事务的传播机制说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。事务传播机制实际上是使用简单的ThreadLocal实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。

① PROPAGATION_REQUIRED:(默认传播行为)如果当前没有事务,就创建一个新事务;如果当前存在事务,就加入该事务。

② PROPAGATION_REQUIRES_NEW:无论当前存不存在事务,都创建新事务进行执行。

③ PROPAGATION_SUPPORTS:如果当前存在事务,就加入该事务;如果当前不存在事务,就以非事务执行。‘

④ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

⑤ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则按REQUIRED属性执行。

⑥ PROPAGATION_MANDATORY:如果当前存在事务,就加入该事务;如果当前不存在事务,就抛出异常。

⑦ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

(3)Spring中的隔离级别:

① ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。

② ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。

③ ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。

④ ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。

⑤ ISOLATION_SERIALIZABLE:所有事务逐个依次执行。

16、Spring 框架中都用到了哪些设计模式?

Spring设计模式的详细使用案例可以阅读这篇文章:Spring中所使用的设计模式_张维鹏的博客-CSDN博客_spring使用的设计模式

(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象

(2)单例模式:Bean默认为单例模式

(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略

(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术

(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate

(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller

(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。

(8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库

17、Spring框架中有哪些不同类型的事件?

Spring 提供了以下5种标准的事件:

(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。

(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。

(3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。

(4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。

(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

18、注解的原理:

(1)什么是注解:

Java 注解就是代码中的一些特殊标记(元信息),用于在编译、类加载、运行时进行解析和使用,并执行相应的处理。它本质是继承了 Annotation 的特殊接口,其具体实现类是 JDK 动态代理生成的代理类,通过反射获取注解时,返回的也是 Java 运行时生成的动态代理对象 $Proxy1。通过代理对象调用自定义注解的方法,会最终调用 AnnotationInvocationHandler 的 invoke 方法,该方法会从 memberValues 这个Map中查询出对应的值,而 memberValues 的来源是Java常量池。

注解在实际开发中非常常见,比如 Java 原生的 @Overried、@Deprecated 等,Spring的 @Controller、@Service等,Lombok 工具类也有大量的注解,不过在原生 Java 中,还提供了元 Annotation(元注解),他主要是用来修饰注解的,比如 @Target、@Retention、@Document、@Inherited 等。

  • @Target:标识注解可以修饰哪些地方,比如方法、成员变量、包等,具体取值有以下几种:ElementType.TYPE/FIELD/METHOD/PARAMETER/CONSTRUCTOR/LOCAL_VARIABLE/ANNOTATION_TYPE/PACKAGE/TYPE_PARAMETER/TYPE_USE
  • @Retention:什么时候使用注解:SOURCE(编译阶段就丢弃) / CLASS(类加载时丢弃) / RUNTIME(始终不会丢弃),一般来说,我们自定义的注解都是 RUNTIME 级别的,因为大多数情况我们是根据运行时环境去做一些处理,一般需要配合反射来使用,因为反射是 Java 获取运行是的信息的重要手段
  • @Document:注解是否会包含在 javadoc 中;
  • @Inherited:定义该注解与子类的关系,子类是否能使用。

(2)如何自定义注解?

① 创建一个自定义注解:与创建接口类似,但自定义注解需要使用 @interface

② 添加元注解信息,比如 @Target、@Retention、@Document、@Inherited 等

③ 创建注解方法,但注解方法不能带有参数

④ 注解方法返回值为基本类型、String、Enums、Annotation 或其数组

⑤ 注解可以有默认值;

@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface CarName {
    String value() default "";
}

最后

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化

image

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

返回值为基本类型、String、Enums、Annotation 或其数组

⑤ 注解可以有默认值;

@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface CarName {
    String value() default "";
}

最后

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化

[外链图片转存中…(img-wdiKi4u7-1715584285149)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 10
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值