SpringIOC、DI及Bean线程安全面试题解析

什么是IOC?

IOC:控制反转, 是一种设计思想,而不是一个具体的技术实现。IoC 并非 Spring 特有,在其他语言中也有应用。它是通过依赖注入(DependencyInjection)实现的。

  • 核心思想:由Spring容器管理bean的整个生命周期。通过反射实现对其他对象的控制,包括初始化、创建、销毁等,解放手动创建对象的过程,同时降低类之间的耦合度。
  • 依赖注入:通过构造器注入、setter 注入或接口注入,将对象所需的依赖传递给它,而不是让对象自行创建依赖。

为什么叫控制反转?

  • 控制:指的是对象创建(实例化、管理)的权力
  • 反转:控制权交给外部环境(Spring 框架、IoC 容器)

到底控制的是什么?其实就是控制对象的创建,IOC容器根据配置文件来创建对象,在对象的生命周期内,在不同时期根据不同配置进行对象的创建和改造,。

那什么被反转了?其实就是关于创建对象且注入依赖对象的这个动作,本来这个动作是由我们程序员在代码里面指定的,例如对象A依赖对象8,在创建对象A代码里,我们需要写好如何创建对象B,这样才能构造出一个完整的 A。而反转之后,这个动作就由 IOC 容器触发,IOC 容器在创建对象 A 的时候,发现依赖对象 B,根据配置文件,它会创建 B,并将对象 B 注入 A 中。这里要注意,注入的不一定非得是一个对象,也可以注入配置文件里面的一个值给对象 A 等等。

IOC的好处?

ioc的思想最核心的地方在于,资源不由使用资源者管理,而由不使用资源的第三方管理,这可以带来很多好处。

  1. 资源集中管理,实现资源的可配置和易管理。
  2. 降低类之间的耦合度。

比如在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,可能要每次都要搞清这个 Service 所有底层类的构造函数,这就变得复杂了。而如果使用 IoC 的话,只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度

什么是 Spring Bean?

简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象。

我们需要告诉 IoC 容器管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。

什么是依赖注入?

Dl(Dependengy Injecion,依赖注入)普遍认为是 Spng框架中用于实现控制反转(IOC)的一种机制。DI的核心思想是由容器负责对象的依赖注入,而不是由对象自行创建或查找依赖对象。

通过 Dl,Spring 容器在创建一个对象时,会自动将这个对象的依赖注入进去,这样可以让对象与其依赖的对象解耦,提升系统的灵活性和可维护性。

依赖注入主要有两种方式:构造器注入和属性注入。

Spring自动装配的方式有哪些?

  1. setter方式注入

在基于 setter 的依赖注入中,setter 方法被标注为 @Autowired。一旦使用无参数构造函数或无参数静态工厂方法实例化 Bean,为了注入 Bean 的依赖项,Spring 容器将调用这些 setter 方法。

  1. 基于属性的依赖注入

在基于属性的依赖注入中,以@Autowired(自动注入)注解注入为例,修饰符有三个属性:Constructor,byType,byName。默认按照byType注入。一旦类被实例化,Spring 容器将设置这些字段。

  • constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,那么spring会优先将bean注入到多参数的构造方法中。
  • byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。
  • byType:查找所有的set方法,将符合符合参数类型的bean注入。
  1. 构造器注入 在基于构造函数的依赖注入中,类构造函数被标注为 @Autowired,并包含了许多与要注入的对象相关的参数。

Spring Bean 注册到容器有哪些方式?

Spring Bean 注册到容器的方式主要包括以下几种:

  • 基于 XML的配置:使用 XML 文件配置 Bean,并定义 Bean 的依赖关系。
  • 基于 @Compoment 注解及其衍生注解,如 @Controller、@Repository、@Service 等进行配置。
  • 使用 @configuration 注解声明配置类,并使用@Bean 注解定义 Bean。
  • 基于 @Import 注解:可以将普通类导入到 Spring 容器中,这些类会自动被注册为 Bean。

基于属性的依赖注入可能存在什么问题吗?

  1. 不允许声明不可变域:基于字段的依赖注入在声明为 final/immutable 的字段上不起作用,因为这些字段必须在类实例化时实例化。声明不可变依赖项的唯一方法是使用基于构造器的依赖注入。
  2. 容易违反单一职责设计原则:使用基于字段的依赖注入,高频使用的类随着时间的推移,会在类中逐渐添加越来越多的依赖项,用着很爽,但很容易忽略类中的依赖已经太多了。但是如果使用基于构造函数的依赖注入,随着越来越多的依赖项被添加到类中,构造函数会变得越来越大,一眼就可以察觉到哪里不对劲。 有一个有超过10个参数的构造函数是一个明显的信号,表明类已经转变一个大而全的功能合集,需要将类分割成更小、更容易维护的块。 因此,尽管属性注入并不是破坏单一责任原则的直接原因,但它隐藏了信号,使我们很容易忽略这些信号。
  3. 与依赖注入容器紧密耦合: 使用基于字段的依赖注入的主要原因是为了避免 getter 和 setter 的样板代码或为类创建构造函数。最后,这意味着设置这些字段的唯一方法是通过Spring容器实例化类并使用反射注入它们,否则字段将保持 null。 依赖注入设计模式将类依赖项的创建与类本身分离开来,并将此责任转移到类注入容器,从而允许程序设计解耦,并遵循单一职责和依赖项倒置原则(同样可靠)。因此,通过自动装配(autowiring)字段来实现的类的解耦,最终会因为再次与类注入容器(在本例中是 Spring)耦合而丢失,从而使类在Spring容器之外变得无用。 这意味着,如果想在应用程序容器之外使用您的类,例如用于单元测试,将被迫使用 Spring 容器来实例化您的类,因为没有其他可能的方法(除了反射)来设置自动装配字段。
  4. 隐藏依赖关系:在使用依赖注入时,受影响的类应该使用公共接口清楚地公开这些依赖项,方法是在构造函数中公开所需的依赖项,或者使用方法(setter)公开可选的依赖项。当使用基于字段的依赖注入时,实质上是将这些依赖对外隐藏了。

为什么建议使用构造器注入

  • 依赖不可变:其实说的就是final关键字。
  • 依赖不为空:(省去了我们对其检查):当要实例化UserServiceImpl的时候,由于自己实现了有参数的构造函数,所以不会调用默认构造函数,那么就需要Spring容器传入所需要的参数,所以就两种情况:1、有该类型的参数->传入,OK 。2:无该类型的参数->报错。
  • 完全初始化的状态:这个可以跟上面的依赖不为空结合起来,向构造器传参之前,要确保注入的内容不为空,那么肯定要调用依赖组件的构造方法完成实例化。而在Java类加载实例化的过程中,构造方法是最后一步(之前如果有父类先初始化父类,然后自己的成员变量,最后才是构造方法),所以返回来的都是初始化之后的状态。

如果使用setter注入,缺点显而易见,对于IOC容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。而且将一直是个潜在的隐患,因为你不调用将一直无法发现NPE的存在

java 复制代码
// 这里只是模拟一下,正常来说我们只会暴露接口给客户端,不会暴露实现。 
UserServiceImpl userService = newUserServiceImpl(); 
userService.findUserList();// -> NullPointerException, 潜在的隐患

总结:对于必需的依赖,建议使用基于构造函数的注入,设置它们为不可变的,并防止它们为 null。对于可选的依赖项,建议使用基于 setter 的注入。

注入 Bean 的注解有哪些?

Spring 内置的 @Autowired 以及 JDK 内置的 @Resource@Inject 都可以用于注入 Bean。

Annotation Package Source
@Autowired org.springframework.bean.factory Spring 2.5+
@Resource javax.annotation Java JSR-250
@Inject javax.inject Java JSR-330

@Autowired@Resource使用的比较多一些。

@Autowired和@Resource以及@Inject等注解注的区别

  1. @Autowired是Spring自带的注解,通过AutowiredAnnotationBeanPostProcessor 类实现的依赖注入
  2. @Autowired可以作用在CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION_TYPE
  3. @Autowired默认是根据类型(byType )进行自动装配的
  4. 如果有多个类型一样的Bean候选者,需要指定按照名称(byName )进行装配,则需要配合 @Qualifier。 指定名称后,如果Spring IOC容器中没有对应的组件bean抛出NoSuchBeanDefinitionException。也可以将@Autowired中required配置为false,如果配置为false之后,当没有找到相应bean的时候,系统不会抛异常

@Qualifier 注解有什么作用

当需要创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个 bean 来消除歧义。

为什么不推荐使用 @Autowired ?

因为 Spring 官方实际上推荐的是构造器注入,而不是字段注入

且 @Autonred 实际上 Sping提供的,导致了业务代码和框架通物定,如果更换给其他10C 框架则不适用了,而 @Resource 是 ISR-250 提供的,它是 ava 的标准,因此如果非要使用字段注入也应该使用 @Resource。

详情可以看这篇文章:基于属性的依赖注入

将一个类声明为 Bean 的方式有哪些?

  1. 使用xml方式来声明Bean的定义,Spring容器在启动会加载并解析这个xml,把bean装载到IOC容器中
  2. 使用@CompontScan注解来扫描声明了@Controller、@Service、@Repository、@Component注解的类
  3. 使用@Configuration注解声明配置类,并使用@Bean注解实现Bean的定义,这种方式其实是xml配置方式的一种演变,是Spring迈入到无xml 时代的里程碑
  4. 使用@Import注解,导入配置类或者普通的Bean
  5. 使用FactoryBean工厂bean, 动态构建一个Bean实例,Spring Cloud OpenFeign 里面的动态代理实例就是使用FactoryBean来实现的
  6. 实现ImportBeanDefinitionRegistrar接口,可以动态注入Bean实例。这个在Spring Boot里面的启动注解有用到
  7. 实现ImportSelector接口,动态批量注入配置类或者Bean对象,这个在Spring Boot里面的自动装配机制里面有用到

@Component 和 @Bean 的区别是什么?

  • @Component 注解作用于类,而@Bean注解作用于方法。
  • @Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。
  • @Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

@Component、@Controller、@Repositor和@Service 的区别?

它们本质上没区别,其它三个都是 @Component 的行生注解,之所以做了这些划分主要是为了更好地组织和管理应用的各个层次,提高代码的可读性和可维护性。

  • @Component:最普通的组件,可以被注入到spring容器进行管理。
  • @Controller:将类标记为 Spring Web MVC 控制器。
  • @Service:将类标记为业务层组件。
  • @Repository:将类标记为数据访问组件,即DAO组件。

IOC容器初始化过程?

  1. 从XML中读取配置文件。
  2. 将bean标签解析成 BeanDefinition,如解析 property 元素, 并注入到 BeanDefinition 实例中。
  3. 将 BeanDefinition 注册到容器 BeanDefinitionMap 中。
  4. BeanFactory 根据 BeanDefinition 的定义信息创建实例化和初始化 bean。

单例bean的初始化以及依赖注入一般都在容器初始化阶段进行,只有懒加载(lazy-init为true)的单例bean是在应用第一次调用getBean()时进行初始化和依赖注入。

java 复制代码
// AbstractApplicationContext
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

多例bean 在容器启动时不实例化,即使设置 lazy-init 为 false 也没用,只有调用了getBean()才进行实例化。

loadBeanDefinitions采用了模板模式,具体加载 BeanDefinition 的逻辑由各个子类完成。

Bean的生命周期

了解 Spring 生命周期的意义就在于,可以利用 Bean 在其存活期间的指定时刻完成一些相关操作 ,即扩展点。这种时刻可能有很多,但一般情况下,会在 Bean 被初始化后和被销毁前执行一些相关操作。具体扩展点的使用可以看这篇文章

简洁面试版:

  1. 实例化
    1. 通过反射去推断构造函数进行实例化
    2. 实例工厂、 静态工厂
  2. 依赖注入(DI)
    1. 解析自动装配(byname bytype constractor none @Autowired)
    2. 属性赋值(Populate Properties):Spring容器将Bean的属性值通过setter方法或其他方式进行赋值。
  3. 初始化
    1. 调用很多Aware回调方法 (初始化前的扩展机制)
    2. 调用BeanPostProcessor.postProcessBeforeInitialization (初始化前)
    3. 调用生命周期回调初始化方法
    4. 调用BeanPostProcessor.postProcessAfterInitialization, 如果bean实现aop则会在这里创建动态代理 (初始化后)
  4. 使用
  5. 销毁
    1. 在spring容器关闭的时候进行调用
    2. 调用生命周期回调销毁方法

详细版:

  1. 加载Bean定义:通过 loadBeanDefinitions 扫描所有xml配置、注解将Bean记录在beanDefinitionMap中。即IOC容器的初始化过程
  2. Bean实例化:遍历 beanDefinitionMap 创建bean,最终会使用getBean中的doGetBean方法调用 createBean来创建Bean对象
    1. 构建对象:容器通过 createBeanInstance 进行对象构造
      1. 获取构造方法(大部分情况下只有一个构造方法)
        1. 如果只有一个构造方法,无论这个构造方法有没有入参,都用这个构造方法
        2. 有多个构造方法时
          1. 先拿带有@Autowired的构造方法,但是如果多个构造方法都有@Autowired就会报错
          2. 如果没有带有@Autowired的构造方法,那就找没有入参的;如果多个构造方法都是有入参的,那也会报错
      2. 准备参数
        1. 先根据类进行查找
        2. 如果这个类有多个实例,则再根据参数名匹配
        3. 如果没有找到则报错
      3. 构造对象:无参构造方法则直接实例化
    2. 填充属性:通过populateBean方法为Bean内部所需的属性进行赋值,通常是 @Autowired 注解的变量;通过三级缓存机制进行填充,也就是依赖注入
    3. 初始化Bean对象:通过initializeBean对填充后的实例进行初始化
      1. 执行Aware:检查是否有实现者三个Aware:BeanNameAwareBeanClassLoaderAware, BeanFactoryAware;让实例化后的对象能够感知自己在Spring容器里的存在的位置信息,创建信息
      2. 初始化前:BeanPostProcessor,也就是拿出所有的后置处理器对bean进行处理,当有一个处理器返回null,将不再调用后面的处理器处理。
      3. 初始化:afterPropertiesSet,init- method;
        1. 实现了InitializingBean接口的类执行其afterPropertiesSet()方法
        2. 从BeanDefinition中获取initMethod方法
      4. 初始化后:BeanPostProcessor,;获取所有的bean的后置处理器去执行。AOP也是在这里做的
    4. 注册销毁:通过reigsterDisposableBean处理实现了DisposableBean接口的Bean的注册
      1. Bean是否有注册为DisposableBean的资格:
        1. 是否有destroyMethod。
        2. 是否有执行销毁方法的后置处理器。
      2. DisposableBeanAdapter: 推断destoryMethod
      3. 完成注册
  3. 添加到单例池:通过 addSingleton 方法,将Bean 加入到单例池 singleObjects
  4. 销毁
    1. 销毁前:如果有@PreDestory 注解的方法就执行
    2. 如果有自定义的销毁后置处理器,通过 postProcessBeforeDestruction 方法调用destoryBean逐一销毁Bean
    3. 销毁时:如果实现了destroyMethod就执行 destory方法
    4. 执行客户自定义销毁:调用 invokeCustomDestoryMethod执行在Bean上自定义的destroyMethod方法
      1. 有这个自定义销毁就会执行
      2. 没有自定义destroyMethod方法就会去执行close方法
      3. 没有close方法就会去执行shutdown方法
      4. 都没有的话就都不执行,不影响

BeanFactory和FactoryBean的区别?

BeanFactory:管理Bean的容器,Spring中生成的Bean都是由这个接口的实现来管理的。

  • 核心概念:Beanfactory负责从配置源(XML、Java 配置类、注解等)中读取 Bean 的定义,并负责创建、管理这些 Bean 的生命周期。
  • 延迟加载:Beanfactory的一个重要特性是延迟初始化,即它只会在 Bean 首次请求时才会实例化该 Bean,而不是在容器启动时就立即创建所有的 Bean。

不过 BeanFactory 本身只是一个接口,一般我们所述的 BeanFactory 指的是它实现类

  • DefaultListableBeanFactory:Beanfactory的默认实现,通常用于内部处理 Bean 的实例化和管理工作。它支持所有基本的依赖注入特性,如构造器注入、setter 注入等
  • XmlBeanFactory(已废弃):基于XML 文件配置的 Beanfactory实现,已经在 Spring 3.x中被淘汰,现推荐使用 ApplicationContext,

FactoryBean:通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,直接用xml配置比较麻烦,这时可以考虑用FactoryBean,可以隐藏实例化复杂Bean的细节。

  • 核心概念:FactoryBean 是一个实现了 FactoryBean<T>接口的 Bean,通过它可以自定义复杂对象的创建逻辑。Spring 容器会调用 getobject()方法来获取实际的 Bean 实例
  • 使用场景:FactoryBean 通常用于需要创建复杂对象或需要使用代理模式生成 Bean 的场景

当配置文件中bean标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是调用FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。如果想得到FactoryBean必须使用 '&' + beanName 的方式获取。

Mybatis 提供了 SqlSessionFactoryBean,可以简化 SqlSessionFactory的配置:

java 复制代码
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");
    this.sqlSessionFactory = buildSqlSessionFactory();
  }

  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
	//复杂逻辑
  }
    
  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }
    return this.sqlSessionFactory;
  }
}

在 xml 配置 SqlSessionFactoryBean:

xml 复制代码
<bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="trade" />
    <property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />
    <property name="configLocation" value="classpath:mybatis-config.xml" />
    <property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" />
</bean>

Spring 将会在应用启动时创建 SqlSessionFactory,并使用 sqlSessionFactory 这个名字存储起来。

Spring 中的 ObjectFactory 是什么?

ObjectFactory是 Spring 框架中的一个接口,主要用于延迟获取 Bean 实例。

ObjectFactory 提供了一种延迟加载的机制,它通过 getObject() 方法返回一个 Bean的实例,使用 ObjectFactory 可以避免在容器启动时立即创建所有 Bean,即只有在真正需要使用 Bean 时才会从 Spring容器中获取Bean 实例,有助于优化性能。

Spring 中的 ApplicationContext 是什么?

ApplicationContext 是多个底层接口组合后的接口。它主要提供了五大功能.

  1. 核心容器 BeanFactory
  2. 国际化 MessageSource
  3. 资源获取 ResourceLoader
  4. 环境信息 EnvironmentCapable
  5. 事件发布 ApplicationEventPublisher

BeanFactory和ApplicationContext有什么区别?

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

两者区别如下:

1、功能上的区别。BeanFactory是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。

ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能,如继承MessageSource、支持国际化、统一的资源文件访问方式、同时加载多个配置文件等功能。

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

而ApplicationContext是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单例Bean,那么在需要的时候,不需要等待创建bean,因为它们已经创建好了。

相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

3、创建方式的区别。BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

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

Bean的作用域

  1. singleton:单例,Spring中的bean默认都是单例的。
  2. prototype:每次请求都会创建一个新的bean实例。
  3. request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
  4. session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。
  5. global-session:全局session作用域。
  6. websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

Bean 是线程安全的吗?

Spring 框架中的 Bean 是否线程安全,取决于其作用域和状态。

我们这里以最常用的两种作用域 prototype 和 singleton 为例介绍。几乎所有场景的 Bean 作用域都是使用默认的 singleton ,重点关注 singleton 作用域即可。

prototype 作用域下,每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,所以不存在线程安全问题。singleton 作用域下,IoC 容器中只有唯一的 bean 实例,可能会存在资源竞争问题(取决于 Bean 是否有状态)。如果这个 bean 是有状态的话,那就存在线程安全问题(有状态 Bean 是指包含可变的成员变量的对象)。

不过,大部分 Bean 实际都是无状态(无状态就是不包含可变的成员变量)的(比如 Dao、Service),这种情况下,虽然说它本身不是线程安全的,但是只是调用了其中的方法,不会造成线程安全问题。

对于有状态单例 Bean 的线程安全问题,常见的有两种解决办法:

  1. 在 Bean 中尽量避免定义可变的成员变量:确保单例 Bean 是无状态的或仅使用线程安全的数据结构
  2. 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。
  3. 加锁:如果需要在单例 Bean 中管理共享资源,可以通过 synchronized 关键字或其他线程同步机制(使用线程安全的数据结构)来确保线程安全。
相关推荐
TitosZhang2 小时前
BIO、NIO、AIO详解
java·redis·nio
Arva .2 小时前
Spring Boot 配置文件
java·spring boot·后端
IT_Octopus2 小时前
https私人证书 PKIX path building failed 报错解决
java·spring boot·网络协议·https
程序员清风3 小时前
网易三面:Java中默认使用的垃圾回收器及特点分版本说说?
java·后端·面试
这周也會开心3 小时前
本地部署javaweb项目到Tomcat的三种方法
java·tomcat
小蒜学长3 小时前
jsp基于JavaWeb的原色蛋糕商城的设计与实现(代码+数据库+LW)
java·开发语言·数据库·spring boot·后端
摇滚侠3 小时前
Spring Boot中使用线程池来优化程序执行的效率!笔记01
java·spring boot·多线程
毕设源码-江学长3 小时前
计算机毕业设计java共享茶室预约微信小程序 微信小程序中的共享茶室预订平台 茶室共享预约小程序的设计与开发
java·微信小程序·课程设计
卡布奇诺-海晨4 小时前
2025版本的idea解决Git冲突
java·git·intellij-idea