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),这意味着它们可以独立于外部事务进行回滚。

相关推荐
如影随从几秒前
04-ArcGIS For JavaScript的可视域分析功能
开发语言·javascript·arcgis·可视域分析
XiaoCCCcCCccCcccC4 分钟前
C语言实现双向链表
c语言·开发语言·链表
十年一梦实验室7 分钟前
【C++】相机标定源码笔记- RGB 相机与 ToF 深度传感器校准类
开发语言·c++·笔记·数码相机·计算机视觉
Tech Synapse12 分钟前
Java循环创建对象内存溢出怎么解决
java·开发语言·jvm
IT·陈寒12 分钟前
Kotlin vs Java:深入解析两者之间的最新差异与优劣(全面指南)
java·python·kotlin
蜉蝣之翼❉17 分钟前
c++ 简单线程池
开发语言·c++
行动π技术博客23 分钟前
spring中IOC相关介绍
java·spring·rpc
WHYBIGDATA28 分钟前
Scala中高级的函数编程
开发语言·后端·scala
吃青椒的小新33 分钟前
独一无二的设计模式——单例模式(Java实现)
java·后端·单例模式·设计模式
知识分享小能手34 分钟前
从新手到高手:Scala函数式编程完全指南,Scala 访问修饰符(6)
大数据·开发语言·后端·python·数据分析·scala·函数式编程