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

spring,面试,攻略,如何,展现,深入,理解 · 浏览次数 : 495

小编点评

**Spring框架的核心概念** * **IOC(依赖注入容器)**:通过自动依赖注入来管理bean的创建和注入。 * **AOP(面向对象编程)**:通过使用注解来定义bean的创建和依赖关系。 **Bean的创建过程** * 通过 Spring容器的配置来创建bean。 * 通过 AOP注解来指定bean的创建过程。 **Bean的线程安全性** * 使用threadLocal类将属性与线程绑定。 * 使用@Transactional注解指定事务传播行为、隔离级别等属性。 **循环依赖的处理** * 通过使用 prototype 或 request 模式设置控制器的作用域。 * 通过设置控制器的作用域为非单例模式。 **事务的处理** * 通过 @Transactional注解指定事务的传播行为、隔离级别等属性。 * 使用Spring容器的事务管理机制来执行事务。 **Spring MVC中的控制器线程安全性** *默认情况下,控制器以单例模式创建的。 *通过设置控制器的作用域为非单例模式或prototype模式。

正文

什么是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框架的深入理解和应用经验。同时,也凸显了对面试题目的认真思考和清晰表达的能力。

与Spring面试攻略:如何展现你对Spring的深入理解相似的内容:

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

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

[转帖]拜托!面试请不要再问我Spring Cloud底层原理

https://www.cnblogs.com/jajian/p/9973555.html 概述# 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术。不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓。因此

京东面试:SpringBoot同时可以处理多少请求?

Spring Boot 作为 Java 开发中必备的框架,它为开发者提供了高效且易用的开发工具,所以和它相关的面试题自然也很重要,咱们今天就来看这道经典的面试题:SpringBoot同时可以处理多少个请求 ? 准确的来说,Spring Boot 同时可以处理多少个请求,并不取决于 Spring Bo

聊聊Spring Cloud Alibaba解决方案组件

在java的微服务解决方案中,最先出现目前应用比较多的就是spring cloud netfix系列,但是随着阿里的强劲支持,spring cloud alibaba解决方案逐渐可以替代前者,当然dubbo也是不容小觑的。之前面试几家公司应用的都是spring cloud alibaba,随着我自己

秋招还没Offer怎么办?

如果你是双非院线、没有实习经历、没有出众的技术(算法没刷一千道,也没做过 Spring Cloud 项目)、现在还没有面试(或只有少量的面试)、并且目前还没有 Offer,那么恭喜你,你和目前大部分同学的状态是一样的。 相信我,你并不孤单。 有人会说:“瞎扯,你去看牛客,别人都在为选阿里还是字节而发

Spring 面向切面编程AOP 详细讲解

1. Spring 面向切面编程AOP 详细讲解 @目录1. Spring 面向切面编程AOP 详细讲解每博一文案2. AOP介绍说明2.1 AOP的七大术语2.2 AOP 当中的 切点表达式3. 使用Spring 对 AOP 的实现使用3.1 准备工作3.2 Spring 基于AspectJ的AO

Java面试题:Spring框架除了IOC和AOP,还有哪些好玩的设计模式?

Spring是一个基于Java的企业级应用程序开发框架,它使用了多种设计模式来实现其各种特性和功能。本文将介绍一些在Spring中使用的常见设计模式以及相应的代码示例和说明。

Java面试题:Spring Bean线程安全?别担心,只要你不写并发代码就好了!

Spring Bean是单例模式,即在整个应用程序上下文中只有一个实例。在多线程环境下,Singleton Scope Bean可能会发生线程安全问题。Spring Bean是否线程安全取决于Bean的作用域和Bean本身的实现。在使用Singleton Scope Bean时需要特别注意线程安全问...

Java面试题:Spring中的循环依赖,给程序员带来的心理阴影

循环依赖通常发生在两个或多个Spring Bean之间,它们通过构造器、字段(使用@Autowired)或setter方法相互依赖,从而形成一个闭环。Spring通过三级缓存机制、@Lazy注解以及避免构造器循环依赖等方式来解决循环依赖问题。这些机制使得Spring容器能够更加灵活地处理bean之间...

面试官让列举Spring的事务会失效的场景,我说了8个

今天,我们就一起梳理下有哪些场景会导致Spring事务失效。