Java八股文系列之六(Spring)

前沿

spring是java开发日常使用最多的框架,理解并应用好spring是基础核心能力。第一版略显粗糙,持续更新中。

1.什么是Spring循环依赖。

Spring循环依赖指的是两个或多个Bean之间相互依赖,形成一个环状依赖的情况。简单来说,就是A依赖B,B依赖C,C依赖A,这样就形成了一个循环依赖的环。

Spring循环依赖通常会导致Bean无法正确地被实例化,从而导致应用程序无法正常启动或者出现异常。因此,Spring循环依赖是一种需要尽量避免的情况。

Spring循环依赖的解决方法

为了解决Spring循环依赖问题,我们可以采取以下几种方法:

  • 构造函数注入: 在构造函数注入中,Spring会检查循环依赖,并在发现循环依赖时抛出异常,避免死循环。
  • 使用@Lazy注解: @Lazy注解可以延迟Bean的实例化,从而避免循环依赖的问题。
  • 使用setter方法

Spring是如何解决Bean的循环依赖?

Spring是如何解决的循环依赖: 采用三级缓存解决的 就是三个Map ; 关键: 一定要有一个缓存保存它的早期对象作为死循环的出口

一级缓存singletonObjects存放可以使用的单例。

二级缓存earlySingletonObjects存放的是早期的bean,即半成品,此时还无法使用。

三级缓存singletonFactories是一个对象工厂,用于创建对象并放入二级缓存中。同时,如果对象有Aop代理,则对象工厂返回代理对象。

2.BeanPostProcessor作用和使用场景

BeanPostProcessor的主要作用是允许对新创建的bean实例进行一些处理,或者是对bean的某些属性进行修改。具体来说,它可以用于以下几种情况:

  1. 自定义初始化逻辑:可以在bean初始化之前或之后添加一些自定义逻辑,比如日志记录或性能监控。
  2. 代理对象:可以通过AOP(面向方面编程)技术为bean创建代理对象,在方法调用前后添加切面逻辑。
  3. 依赖注入后处理:在Spring自动注入依赖之后,可以进行进一步的处理,比如检查某些依赖是否已经注入,或者根据注入的依赖进行一些初始化操作。
  4. 属性修改:在bean完全初始化之前,可以修改bean的某些属性,或者设置某些默认值。

代码实现(自定义初始化逻辑)

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class CustomInitializationBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before Initialization : " + beanName);
        // 这里可以添加自定义的初始化逻辑
        if (bean instanceof MyBean) {
            // 对特定bean的处理
            ((MyBean) bean).customInitBefore();
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("After Initialization : " + beanName);
        // 这里可以添加自定义的初始化逻辑
        if (bean instanceof MyBean) {
            // 对特定bean的处理
            ((MyBean) bean).customInitAfter();
        }
        return bean;
    }
}


public class MyBean {

    private String name;

    // getter和setter方法

    public void customInitBefore() {
        System.out.println("Custom Init Before: " + name);
        // 在初始化之前的自定义逻辑
    }

    public void customInitAfter() {
        System.out.println("Custom Init After: " + name);
        // 在初始化之后的自定义逻辑
    }
}

3.Spring中bean的初始化流程

Spring框架中,bean的创建过程是一个复杂且多步骤的过程,涉及到多个阶段和各种机制。以下是Spring中bean创建过程的详细步骤:

  1. Bean Definition Loading

Spring通过配置文件(XML、Java配置类、注解等)加载bean定义(BeanDefinition),这些定义描述了bean的配置信息,包括bean的类名、作用域、依赖关系等。

  1. Bean Definition Registry

加载的bean定义会被注册到BeanDefinitionRegistry中。Spring使用不同的BeanDefinitionReader来解析配置并将bean定义注册到BeanFactory中。

  1. Bean Instantiation

当Spring容器需要一个bean的实例时,它会通过InstantiationStrategy实例化bean。这时只创建了对象,还没有进行依赖注入。

  1. Populate Bean Properties (Dependency Injection)

Spring会通过依赖注入(DI)机制将需要的依赖注入到bean中。可以使用以下几种方式进行注入:

  • 构造器注入
  • Setter方法注入
  • 注解(如@Autowired@Inject@Resource
  1. BeanPostProcessor - Before Initialization

在bean初始化之前,Spring会调用所有注册的BeanPostProcessorpostProcessBeforeInitialization方法,对bean进行处理。

  1. Bean Initialization
  • 如果bean实现了InitializingBean接口,Spring会调用其afterPropertiesSet方法。
  • 如果在bean定义中指定了init-method,Spring会调用该自定义初始化方法。
  1. BeanPostProcessor - After Initialization

在bean初始化之后,Spring会调用所有注册的BeanPostProcessorpostProcessAfterInitialization方法,对bean进行进一步处理。

  1. Bean Ready for Use

至此,bean已经完全初始化并可以被应用程序使用。

  1. Bean Destruction (For Singleton and Prototype Scopes)

当Spring容器关闭时,会对singleton作用域的bean进行销毁处理。

  • 如果bean实现了DisposableBean接口,Spring会调用其destroy方法。
  • 如果在bean定义中指定了destroy-method,Spring会调用该自定义销毁方法。

4.什么是Spring IOC?

Spring IOC(Inversion of Control)是Spring框架的核心特性之一,翻译为"控制反转"。它是一种设计原则,用于将对象的创建和依赖关系的管理从应用程序代码中分离出来,由Spring容器(也称为IOC容器)负责管理。

核心概念

  1. 控制反转(Inversion of Control)

控制反转的基本思想是将对象的控制权从应用程序代码中移交给Spring容器。传统的编程方式中,对象是通过代码直接创建和管理的,而在IOC容器中,对象的创建和依赖关系的注入都是由容器来管理的。

  1. 依赖注入(Dependency Injection,DI)

依赖注入是实现控制反转的一种方式。它通过构造函数、Setter方法或字段注入将依赖关系注入到对象中,而不是由对象自己创建或查找依赖。

主要优点

  • 解耦:通过依赖注入,对象之间的耦合度大大降低,使代码更容易维护和测试。
  • 易于测试:由于依赖是注入的,可以轻松地使用Mock对象进行单元测试。
  • 灵活性:可以通过配置文件或注解灵活地配置和管理对象的依赖关系。

5.Spring Bean的生命周期

  • 实例化(Instantiation):Spring容器通过反射机制创建bean的实例。

  • 属性注入(Populating Bean Properties):Spring容器将配置中的属性(例如通过XML配置、注解或Java配置类)注入到bean的相应属性中。

  • 设置BeanName (Setting Bean Name):如果bean实现了BeanNameAware接口,Spring容器会调用setBeanName方法,将该bean在容器中的名称传递给它。

  • 设置Bean工厂 (Setting Bean Factory):如果bean实现了BeanFactoryAware接口,Spring容器会调用setBeanFactory方法,将BeanFactory实例传递给它。

  • 设置ApplicationContext (Setting ApplicationContext):如果bean实现了ApplicationContextAware接口,Spring容器会调用setApplicationContext方法,将ApplicationContext实例传递给它。

  • Bean的初始化(Bean Initialization):这一阶段包括两部分:

    • 调用BeanPostProcessorpostProcessBeforeInitialization方法 :如果bean实现了BeanPostProcessor接口,Spring容器会在bean初始化之前调用它的postProcessBeforeInitialization方法。
    • 调用自定义的初始化方法 :Spring容器会调用bean的初始化方法。如果bean实现了InitializingBean接口,会调用其afterPropertiesSet方法。此外,如果bean在配置中指定了init-method属性,也会调用这个自定义初始化方法。
  • 调用BeanPostProcessorpostProcessAfterInitialization方法 :如果bean实现了BeanPostProcessor接口,Spring容器会在bean初始化之后调用它的postProcessAfterInitialization方法。

  • Bean的就绪状态(Bean Ready):此时bean已经完全初始化并且可以使用。

  • Bean的销毁(Bean Destruction):当Spring容器关闭时,会执行bean的销毁过程。这一阶段包括:

    • 调用DisposableBeandestroy方法 :如果bean实现了DisposableBean接口,Spring容器会调用它的destroy方法。
    • 调用自定义的销毁方法:如果bean在配置中指定了destroy-method属性,Spring容器会调用这个自定义销毁方法。

6.Spring的事物传播模型?

Spring事务传播(Transaction Propagation)定义了一个事务中方法的执行方式,以及是否应该加入到现有事务中。Spring提供了多种事务传播行为,以满足不同的业务需求。以下是Spring支持的主要事务传播行为:

  • REQUIRED

这是默认的传播行为。如果当前存在一个事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

  • REQUIRES_NEW

每次都会新建一个事务。如果当前存在事务,则挂起该事务,直到新事务完成。

  • SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。

  • NOT_SUPPORTED

总是以非事务方式执行。如果当前存在事务,则挂起该事务,直到该方法执行完成。

  • NEVER

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

  • MANDATORY

必须在一个已有事务中运行。如果当前没有事务,则抛出异常。

  • NESTED

如果当前存在事务,则在嵌套事务中执行。如果当前没有事务,则创建一个新的事务。嵌套事务在内部使用保存点(savepoint),这意味着它们可以独立于外部事务进行回滚。

相关推荐
理想不理想v6 分钟前
使用JS实现文件流转换excel?
java·前端·javascript·css·vue.js·spring·面试
hutaotaotao8 分钟前
c语言用户不同命令调用不同函数实现
c语言·开发语言
抱走江江8 分钟前
SpringCloud框架学习(第二部分:Consul、LoadBalancer和openFeign)
学习·spring·spring cloud
huangjiazhi_9 分钟前
QTcpSocket 服务端和客户端
开发语言·qt
ac-er888824 分钟前
ThinkPHP中的MVC分层是什么
开发语言·php·mvc
惜.己26 分钟前
Jmeter中的配置原件(四)
java·前端·功能测试·jmeter·1024程序员节
yava_free1 小时前
JVM这个工具的使用方法
java·jvm
shinelord明1 小时前
【再谈设计模式】建造者模式~对象构建的指挥家
开发语言·数据结构·设计模式
黑不拉几的小白兔1 小时前
PTA部分题目C++重练
开发语言·c++·算法
写bug的小屁孩1 小时前
websocket身份验证
开发语言·网络·c++·qt·websocket·网络协议·qt6.3