Spring面试攻略:如何展现你对Spring的深入理解

这篇具有很好参考价值的文章主要介绍了Spring面试攻略:如何展现你对Spring的深入理解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是Spring?谈谈你对IOC和AOP的理解。

Spring是一种Java开发框架,旨在简化企业级应用程序的开发和部署。它具有以下优点:

  1. 对象托管:Spring能够管理和赋值所有对象,使开发人员不再需要手动管理对象的创建和依赖关系。
  2. 动态代理:Spring的动态代理功能可以实现大部分可复用的逻辑功能,从而避免了重复的代码。
  3. 强大的框架生态系统:市面上几乎所有的框架都是基于Spring构建的,Spring提供了强有力的支持和集成能力。
  4. 低侵入性:Spring的代码对于我们的应用代码几乎是无侵入的,只需要使用几个注解就能让Spring启动。

控制反转(IoC)是Spring的一个重要特性,它使得对象的创建和依赖关系的管理由Spring容器来完成。IoC有三种实现方式:注解形式、构造器形式和set方法注入。通过IoC,我们不再需要使用new关键字手动创建对象,而是将对象的创建和管理交给Spring容器处理。

面向切面编程(AOP)是Spring的另一个重要特性,它通过动态代理实现。AOP常用于日志收集、事务管理等方面。通过AOP,我们可以在被代理对象的方法执行前后,加入一些统一的业务逻辑处理,例如日志记录或权限校验。

Spring容器的启动流程是怎么样的?

启动流程几乎跟源码息息相关,如果没有看过源码可能对启动流程只能靠自己的理解去背,如果对源码右深入理解,那么这道题可以这么说:

1:初始化reader和scanner

2:使用scanner组件扫描basePackage下的所有对象,将配置类的BeanDefinition注册到容器中。

3:refresh(); 刷新容器。

可以分为以下几个步骤:

  • 定位配置文件:Spring容器首先需要定位配置文件的位置和名称。可以通过ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等类来指定配置文件的路径。
  • 加载配置文件:Spring容器会读取配置文件的内容,并将其转换为内部的数据结构,一般使用DOM或SAX解析XML文件。
  • 创建容器对象:根据配置文件中的定义,Spring容器会创建一个或多个容器对象。常用的容器对象是ApplicationContext和BeanFactory。ApplicationContext是BeanFactory的子接口,提供了更多的功能。
  • 注册Bean定义:Spring容器会解析配置文件中的Bean定义,包括Bean的名称、类型、依赖关系等,并将其注册到容器中。这些Bean定义会被封装为BeanDefinition对象。
  • 实例化Bean:根据注册的Bean定义,Spring容器会实例化相应的Bean对象,并将其放入容器中管理。实例化可以通过构造方法、工厂方法或者通过AOP代理来完成。
  • 注入依赖:Spring容器会根据Bean定义中的依赖关系,将相应的依赖注入到Bean中。可以通过构造方法、setter方法或者注解方式来完成注入。
  • 调用初始化方法:如果Bean定义中指定了初始化方法,Spring容器会在实例化和依赖注入完成后,调用Bean的初始化方法。可以通过配置文件或者注解指定初始化方法。
  • 容器就绪:当所有的Bean都被实例化、注入依赖并初始化完成后,Spring容器就处于就绪状态,可以提供相应的服务。

然后在细说自己知道的部分源码,比如我还了解到一些关于源码的细节。例如,在获取Bean定义后,Spring会在实例化之前通过合并Bean定义来进行初始化,并且AOP的逻辑是在初始化之后通过后置处理器进行动态代理。

此外,如果我们需要监听Spring的启动过程以及在启动后实现自己的业务逻辑,除了可以使用初始化对象的方法afterPropertiesSet外,还可以通过注册一个监听器来监听Spring发布的各种事件。这样,我们就可以在特定的事件触发时执行我们自己的逻辑。

Spring框架中Bean的创建过程是怎样的?

在Spring框架中,Bean的创建过程涉及到多个环节和细节。下面我将更详细地介绍每个步骤的具体内容。可以大致分为五个步骤:获取Bean定义、实例化、赋值、初始化和销毁。

  1. Bean定义是在包扫描的过程中进行注册的。通过扫描配置文件或使用注解等方式,将Bean的定义信息注册到Spring容器中。
  2. 根据Bean定义进行实例化。这一步骤会根据Bean的构造器创建对象实例,如果没有指定构造器,则使用默认的构造器。在实例化过程中,还可以应用前后实例化处理器对对象进行一些额外的处理。
  3. 在赋值阶段,会对Bean中的依赖进行赋值。这可以通过使用@Autowired注解注入依赖属性来实现。赋值完成后,Bean的属性就可以访问到依赖的对象。
  4. 通过调用init-method(@PostConstruct)方法或实现InitializingBean接口(afterPropertiesSet)来进行初始化操作。在初始化过程中,可以执行一些特定的逻辑,例如数据加载等。同样地,也可以应用前后初始化处理器对Bean进行一些额外的处理。
  5. 销毁阶段是在应用程序关闭或需要销毁Bean时执行的。通过调用destroy-method方法(@PreDestroy),可以执行对象的销毁逻辑,DisposableBean: 当Bean实现了这个接口,在对象销毁前就会调用destory()方法。

Spring框架中的Bean是线程安全的吗?如果线程不安全,要如何处理

Spring框架中的Bean默认是单例模式,因此不是线程安全的。如果要处理线程安全问题,可以采取以下几种方式:

  • 使用prototype作用域:通过将Bean的作用域设置为prototype,确保每次请求都创建一个新的对象,从而保证线程安全。
  • 避免使用全局资源属性:尽量避免在Bean中使用全局资源属性,而是使用无状态的属性,比如使用注解形式的Bean注入。
  • 使用ThreadLocal类:可以使用ThreadLocal类将属性与线程进行绑定,确保每个线程独有一份属性副本,从而避免线程安全问题。

Spring如何处理循环依赖问题?

大家都知道spring采用的是三级缓存,那么如何理解三级缓存处理了循环依赖问题呢?

一级缓存:缓存最终的单例池对象: private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

二级缓存:缓存初始化的对象:private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

三级缓存:缓存对象的ObjectFactory: private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

其实当一个对象实例化后就会存储在 singletonFactories三级缓存,当被引用时,会执行一个后置处理器方法,这里也是给aop创建代理对象的时机:

  protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
                exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
        return exposedObject;
    }

如果是正常的普通对象,会直接进行如二级缓存,并返回一个实例化后的对象,所以之所以使用到了三级缓存,而不是光是用二级缓存就是考虑到了循环依赖可能是一个代理对象,我们无法直接提供实例化的对象而是一个代理对象。

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // Quick check for existing instance without full singleton lock
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                synchronized (this.singletonObjects) {
                    // Consistent creation of early reference within full singleton lock
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }

Spring如何处理事务?

Spring框架提供了两种方式来处理事务:编程式事务和声明式事务。

编程式事务是通过使用TransactionTemplate来进行事务管理的方式。它需要手动在代码中显式地指定事务的开启、提交和回滚操作。这种方式对代码的侵入性较高,因为需要在每个需要进行事务管理的方法中编写事务处理的代码。一般情况下,不推荐使用编程式事务,除非在特定的场景下需要对事务进行更精细的控制。

声明式事务是通过使用注解或XML配置的方式来声明事务的行为。在Spring中,最常用的是使用注解来声明事务。通过在方法或类上添加@Transactional注解,可以指定事务的传播行为、隔离级别、超时时间等属性。事务的传播行为指的是当一个方法调用另一个带有事务注解的方法时,事务应该如何进行传播和管理。

Spring框架提供了以下几种事务的传播级别:

  • REQUIRED(默认值):如果当前存在事务,则加入该事务,如果不存在事务,则创建一个新的事务。
  • REQUIRES_NEW:无论当前是否存在事务,都创建一个新的事务。如果当前存在事务,则将当前事务挂起。
  • SUPPORTS:如果当前存在事务,则加入该事务,如果不存在事务,则以非事务方式执行。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则将当前事务挂起。
  • MANDATORY:当前必须存在事务,否则抛出异常。
  • NEVER:当前不能存在事务,否则抛出异常。
  • NESTED:如果当前存在事务,则在一个新的嵌套事务中执行。如果不存在事务,则执行与REQUIRED一样的操作。

Spring框架提供了以下几种事务的隔离级别:

  • READ_UNCOMMITTED(读未提交):此级别最低,允许读取其他事务尚未提交的数据,可能会读取到脏数据。
  • READ_COMMITTED(读已提交):在一个事务内,只能读取到已经提交的数据,避免了脏数据,但是可能会产生幻读。
  • REPEATABLE_READ(可重复读):在一个事务内,多次读取同一数据结果保持一致,解决了幻读的问题。
  • SERIALIZABLE(串行化):最高的隔离级别,所有事务都是串行执行,避免了并发问题,但并发度最低。

SpringMVC中的控制器是不是单例模式?如果是,如何保证线程安全?

在Spring MVC中,默认情况下,控制器是以单例模式创建的。这意味着在应用程序的整个生命周期中,只会创建一个控制器实例来处理所有的请求。为了保证控制器的线程安全性,可以采取以下措施:

1:保持控制器的无状态属性:控制器应该尽量避免使用实例变量来保存状态信息,尽量使用方法参数或局部变量来处理请求。这样可以确保每个请求都有独立的数据副本,避免多个线程之间的竞争和冲突。

2:设置控制器的作用域为非单例模式:可以将控制器的作用域设置为非单例模式,如prototype或request。这样每次请求都会创建一个新的控制器实例,确保每个请求都有独立的控制器对象,避免线程安全问题。

总结

本次面试涉及了Spring框架的多个方面,包括IOC和AOP的理解、Spring容器的启动流程、Bean的创建过程、Bean的线程安全性、循环依赖的处理、事务的处理以及Spring MVC中控制器的线程安全性。通过这些问题的回答,展示了对Spring框架的深入理解和应用经验。同时,也凸显了对面试题目的认真思考和清晰表达的能力。文章来源地址https://www.toymoban.com/news/detail-648895.html

到了这里,关于Spring面试攻略:如何展现你对Spring的深入理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【面试八股文】每日一题:谈谈你对线程的理解

    每日一题-Java核心-谈谈你对线程的理解【面试八股文】   Java线程是Java程序中的执行单元。一个Java程序可以同时运行多个线程,每个线程可以独立执行不同的任务。线程的执行是并发的,即多个线程可以同时执行。   Java中的线程有如下的特点 轻量级:线程的创建和销毁

    2024年02月12日
    浏览(43)
  • 前端经典面试题 | 吊打面试官系列 之 说说你对TypeScript 和 JavaScript的理解

    🖥️ 前端经典面试题 专栏:吊打面试官系列 之 说说你对TypeScript 和 JavaScript的理解 🧑‍💼 个人简介:一个不甘平庸的平凡人🍬 ✨ 个人主页:CoderHing的个人主页 🍀 格言: ☀️ 路漫漫其修远兮,吾将上下而求索☀️ 👉 你的一键三连是我更新的最大动力❤️ 目录 一、回答

    2024年01月17日
    浏览(47)
  • 说说你对堆的理解?如何实现?应用场景?

    堆(Heap)是计算机科学中一类特殊的数据结构的统称 堆通常是一个可以被看做一棵完全二叉树的数组对象,如下图: 总是满足下列性质: 堆中某个结点的值总是不大于或不小于其父结点的值 堆总是一棵完全二叉树 堆又可以分成最大堆和最小堆: 最大堆:每个根结点,都有根

    2024年04月22日
    浏览(44)
  • 面试官:服务器最大可以创建多少个tcp连接以及端口并解释下你对文件句柄的理解

    转载请注明出处: 服务器最大可以创建多少个TCP连接取决于多个因素,例如服务器的硬件配置、网络带宽、操作系统设置等。 一般来说,现代服务器的硬件资源和网络带宽都比较充足,因此可以创建大量的TCP连接。然而,服务器在创建TCP连接时也会有一些限制,例如操作系

    2023年04月12日
    浏览(42)
  • 说说你对选择排序的理解?如何实现?应用场景?

    选择排序(Selection sort)是一种简单直观的排序算法,无论什么数据进去都是  O(n²) 的时间复杂度,所以用到它的时候,数据规模越小越好 其基本思想是:首先在未排序的数列中找到最小(or最大)元素,然后将其存放到数列的起始位置 然后再从剩余未排序的元素中继续寻找最

    2024年04月23日
    浏览(43)
  • 说说你对归并排序的理解?如何实现?应用场景?

    归并排序(Merge Sort)是建立归并操作上的一种有效,稳定的排序算法,该算法是采用分治法的一个非常典型的应用 将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序 例如对于含有  n  个记录的无序表,首先默认表中每个记录各为一

    2024年04月24日
    浏览(69)
  • 说说你对二分查找的理解?如何实现?应用场景?

      在计算机科学中,二分查找算法,也称折半搜索算法,是一种在有序数组中查找某一特定元素的搜索算法 想要应用二分查找法,则这一堆数应有如下特性: 存储在数组中 有序排序 搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束 如果

    2024年04月25日
    浏览(39)
  • 网络协议报文理解刨析篇二(再谈Http和Https), 加上TCP/UDP/IP协议分析(理解着学习), 面试官都惊讶你对网络的见解

    目录 前文链接(系列助学, 也为后文学习做铺垫, 可按需读取) 一. 再谈HTTP再理解 二. HTTP对比学习HTTPS HTTP和HTTPS的区别如下: 三.TCP协议  (三次握手四次挥手细节过程理解在之前的博文中有详细图解) tcp缓冲区概念的引入  (解释流量控制): 确认应答(ACK)机制的理解 (编序号)

    2023年04月09日
    浏览(60)
  • 说说你对算法中时间复杂度,空间复杂度的理解?如何计算?

    算法(Algorithm)是指用来操作数据、解决程序问题的一组方法。对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别 衡量不同算法之间的优劣主要是通过时间和空间两个维度去考量: 时间维度:是指执行当前算

    2024年04月09日
    浏览(41)
  • 你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢?

    SPA(single-page application),翻译过来就是单页应用SPA是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,这种方法避免了页面之间切换打断用户体验在单页应用中,所有必要的代码(HTML、JavaScript和CSS)都通过单个页面的加载而检索,或者根据需要(通

    2024年02月10日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包