Spring面试题-Spring Beans

什么是Spring bean?JavaBean和SpringBean和对象的区别?

至少记住这句话:构成应用程序主干并由IoC容器管理的对象称为bean。bean是一个由Spring IoC容器实例化、组装和管理的对象。

  • 例子:
    在这里插入图片描述

一个 Spring Bean 定义 包含什么?

至少记住这句话:一个Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖。

配置Bean有哪几种方式

至少记住这句话:xml、注解、import

1.xml:
2.注解:@Component(@Controller 、@Service、@Repostory) 前提:需要配置扫描包 反射调用构造方法
3.javaConfig: @Bean 可以自己控制实例化过程
4.@Import 3种方式

@Bean一般都是标注在方法上,方法返回的是对象。
-例子:
Import:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解释Spring支持的几种bean的作用域

至少记住这句话:singleton单例,prototype多例,request请求,session会话,application全局web应用

Spring框架支持以下五种bean的作用域:

  • singleton : bean在每个Spring ioc 容器中只有一个实例。
  • prototype:一个bean的定义可以有多个实例。
  • request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
  • session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情
    形下有效。
  • application:全局 Web 应用程序范围的范围标识符。

注意: 缺省的Spring bean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean
会带来很大的性能开销。
在这里插入图片描述
五种
在这里插入图片描述
在这里插入图片描述
默认的作用域是单例

单例Bean的优势是什么?

至少记住这句话:减耗,减垃圾,获取bean更快。

由于不会每次都新创建新对象所以有一下几个性能上的优势:
1.减少了新生成实例的消耗新生成实例消耗包括两方面,第一,spring会通过反射或者cglib来生成bean实例这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法。 提供服务器内存的利用率 ,减少服务器内存消耗
2.减少jvm垃圾回收由于不会给每个请求都新生成bean实例,所以自然回收的对象少了。
3.可以快速获取到bean因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的所以很快。
在这里插入图片描述
只创建一次节省内存,而且获取也快
在这里插入图片描述

Spring的Bean是线程安全的吗?

至少记住这句话:不是,默认单例模式,没有做多线程封装处理。想安全可以改成prototype。

不是,Spring框架中的单例bean不是线程安全的。

spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。

实际上大部分时候 spring bean 无状态的(比如 dao 类),所以某种程度上来说 bean 也是安全的,但如果 bean 有状
态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,
把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

有状态就是有数据存储功能(比如成员变量读写)。
无状态就是不会保存数据。
在这里插入图片描述

Spring如何处理线程并发问题?

至少记住这句话:ThreadLocal和线程同步机制。ThreadLocal空间换时间给每个线程变量副本,隔离访问冲突。线程同步时间换空间,用锁访问数据。

在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。

而ThreadLocal采用了“空间换时间”的方式。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Spring实例化Bean有哪几种方式?

至少记住这句话

  1. 构造器方式(反射);
  2. 静态工厂方式; factory-method
  3. 实例工厂方式(@Bean); factory-bean+factory-method
  4. FactoryBean方式
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

什么是Bean装配?什么是Bean的自动装配?

至少记住这句话:知道依赖关系的前提下在容器中通过依赖注入把bean组装到一起。

就是bean和bean之间互相依赖起来

装配,或bean 装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。

至少记住这句话:Spring 容器能够自动装配相互合作的bean,以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。

在Spring框架中,在配置文件中设定bean的依赖关系是一个很好的机制,Spring 容器能够自动装配相互合作的bean,
这意味着容器不需要和配置,能通过Bean工厂自动处理bean之间的协作。这意味着 Spring可以通过向Bean Factory中
注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上。

自动注入有什么限制吗?

至少记住这些话

  • 一定要声明set方法

  • 覆盖: 你仍可以用 < constructor-arg >和 < property > 配置来定义依赖,这些配置将始终覆盖自动注入。

  • 基本数据类型:不能自动装配简单的属性,如基本数据类型、字符串和类。 (手动注入还是可以注入基本数据类型的 <property value="" @Value)

  • 模糊特性:自动装配不如显式装配精确,如果有可能尽量使用显示装配。

  • 所以更推荐使用手动装配(@Autowired(根据类型、再根据名字) ref="" 这种方式 更加灵活更加清晰 )

自动装配的方式有几种?

至少记住这句话:no不自动手动ref和autowired,byName,byType,constructo(构造器的参数是通过byType装)。

在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。
在Spring框架xml配置中共有5种自动装配:

  • no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。@Autowired 来进行手动指定
    需要自动注入的属性
  • byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自
    动装配。
  • byType:通过参数的数据类型进行自动装配。
  • constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
    在这里插入图片描述

在这里插入图片描述

有哪些生命周期回调方法?有哪几种实现方式?

至少记住这句话:init、destroy。bean标签配上属性或者注解。

有两个重要的bean 生命周期方法,第一个是init , 它是在容器加载bean的时候被调用。第二个方法是 destroy 它是在
容器卸载类的时候被调用。
bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也
有相应的注解(@PostConstruct和@PreDestroy)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注解的先执行了,接口的后执行
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Spring在加载过程中Bean有哪几种形态

在这里插入图片描述

在这里插入图片描述

解释Spring框架中bean的生命周期**全记住

至少记住这段话
bean 的生命周期主要有以下几个阶段,深色底的5个是比较重要的阶段。
在这里插入图片描述

Bean生命周期:指定的就是Bean从创建到销毁的整个过程: 分4大不:

    1. 实例化
      a. 通过反射去推断构造函数进行实例化
      b. 实例工厂、 静态工厂
    1. 属性赋值
      a. 解析自动装配(byname bytype constractor none @Autowired) DI的体现
      b. 循环依赖
    1. 初始化
      a. 调用XXXAware回调方法
      b. 调用初始化生命周期回调(三种)
      c. 如果bean实现aop 创建动态代理
    1. 销毁
      a. 在spring容器关闭的时候进行调用
      b. 调用销毁生命周期回调
      在这里插入图片描述
      bean在Spring容器中从创建到销毁经历了若干阶段,每一阶段都可以针对Spring如何管理bean进行个性化定制。
      正如你所见,在bean准备就绪之前,bean工厂执行了若干启动步骤。
      我们对上图进行详细描述:
      Spring对bean进行实例化;
      Spring将值和bean的引用注入到bean对应的属性中;
      如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;
      如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
      如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上
      下文的引用传入进来;
      如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法;
      如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地,如果bean使用
      initmethod声明了初始化方法,该方法也会被调用;
      如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法;
      此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
      如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method
      声明了销毁方法,该方法也会被调用。
      现在你已经了解了如何创建和加载一个Spring容器。但是一个空的容器并没有太大的价值,在你把东西放进去之前,它
      里面什么都没有。为了从Spring的DI(依赖注入)中受益,我们必须将应用对象装配进Spring容器中。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

Spring是如何解决循环依赖的(难)**全记住

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

  1. 一级缓存:存储完整的Bean
  2. 二级缓存: 避免多重循环依赖的情况 重复创建动态代理。
  3. 三级缓存:
    a. 缓存是函数接口:通过lambda 把方法传进去( 把Bean的实例和Bean名字传进去(aop创建) )
    b. 不会立即调:(如果在实例化后立即调用的话:所有的aop 不管bean是否循环依赖都会在 实例化后创建
    proxy, 正常Bean 其实spring还是希望遵循生命周期在初始化创建动态代理, 只能循环依赖才创建)
    c. 会在 ABA (第二次getBean(A) 才会去调用三级缓存(如果实现了aop才会创建动态代理,如果没有实现
    依然返回的Bean的实例))

d. 放入二级缓存(避免重复创建)
在这里插入图片描述
夺命连环问:

    1. 二级缓存能不能解决循环依赖?
      a. 如果只是死循环的问题: 一级缓存就可以解决 :无法避免在并发下获取不完整的Bean?
      图灵课堂
      b. 二级缓存也可以解决循环依赖: 只不过如果出现重复循环依赖 会多次创建aop的动态代理
    1. Spring有没有解决多例Bean的循环依赖?
      a. 多例不会使用缓存进行存储(多例Bean每次使用都需要重新创建)
      b. 不缓存早期对象就无法解决循环
    1. Spring有没有解决构造函数参数Bean的循环依赖?
      a. 构造函数的循环依赖也是会报错
      b. 可以通过人工进行解决:@Lazy
      —i. 就不会立即创建依赖的bean了
      —ii. 而是等到用到才通过动态代理进行创建
      在这里插入图片描述
      在这里插入图片描述
      ioc加载bean创建时,会实例化,实例化完属性注入,解析注解,发现依赖,然后再去ioc容器找,然后没找到又创建bean又实例化属性注入解析注解,发现又依赖回来,如果又没拿到就去创建。。。。闭环死循环。。。
      在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为啥在这里插入图片描述
才创建动态代理呢?如果实例化之后直接调用创建动态代理,不存函数接口
在这里插入图片描述
人家循环依赖B线程在那里创建动态代理是被迫的,如果那时赋的是实例那就有问题了。正常的bean还是在初始化严格遵循bean的生命周期在初始化创建动态代理。
在这里插入图片描述
为啥要三级缓存呢?二级缓存行不行呢?
可以啊,直接在这里动态代理,也就是不再存函数接口了这里,
在这里插入图片描述
在这里插入图片描述
多例bean不会用缓存进行存储
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Spring是如何帮我们在并发下避免获取不完整的Bean?

至少记住这句:双重检查锁

  • 为什么一级缓存不加到锁里面:
    性能:避免已经创建好的Bean阻塞等待
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
初始化有生命周期回调,aop,初始化之前获取到的对象是没有创建动态代理的对象。
实例化之后没有属性注入、没有初始化的Bean都是不完整的
在这里插入图片描述
在这里插入图片描述
两个线程进来线程一实例化完放进三级缓存,线程二从三级缓存就直接拿走
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
如何避免呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
不锁一级是为了已经创建好的直接给线程2拿走啊,别影响了性能,不然创建好的你也锁全都等你没创建的创建啊?

描述BeanDefinition的加载过程

至少记住这句话
BeanDefinition的加载过程就是将 概念态的Bean注册为定义态的Bean
不同的Spring上下文会有不同的注册过程,但是会用共同的api步骤:

  1. 通过BeanDefinitionReader 将配置类(AnnotatedBeanDefinitionReader)(xml文件:XmlBeanDefinitionReader) 注
    册为BeanDefinition
  2. 解析配置类ConfigurationClassParser(xml文件:BeanDefinitionDocumentReader)
  3. 不同的注解(xml节点)有不同的解析器
    a. 比如ComponentScan 需要通过ClassPathBeanDefinitionScanner扫描所有类找到类上面有
    @Import的类
  4. 将读取到的Bean定义信息通过BeanDefinitionRegistry注册为一个BeanDefinition
    在这里插入图片描述
    在这里插入图片描述
    先扫描再解析吧???
    在这里插入图片描述

如何在所有BeanDefinition注册完后做扩展?

至少记住这句:通常可以使用beanFactoryPostProcessor 对已注册的BeanDefinition进行修改、或者通过它的子接口BeanDefinitionRegistryPostProcessor 再进行注册
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以就可以在这里插入图片描述
进行扩展

如何在Spring所有Bean创建完后做扩展?

哪里才算所有的Bean创建完: new ApplicationContext()---->refresh()
---->finishBeanFactoryInitialization(循环所有的BeanDefinition ,通过BeanFactory.getBean()生成所有的Bean) 这个循环结束之后所有的bean也就创建完了

至少记住这句
1.方式一 基于SmartInitializingSingleton接口Source在创建所有单例Bean的方法中:
finishBeanFactoryInitialization(beanFactory);
SmartInitializingSingleton接口是在所有的Bean实例化完成以后,Spring回调的方法, 所以这里也是一个扩展点,可以在单例bean全部完成实例化以后做处理。

2.方式二 基于Spring事件监听Source生命周期的最后一步是finishRefresh();这里面中有一个方法是publishEvent
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
循环所有BeanDefinition创建完Bean之后会调用Smart扩展接口,然后发布refreshevent的事件,ioc加载就是在refresh方法里体现的,说明ioc加载完了,加载完的事件就是这个。
在这里插入图片描述

Spring容器启动时,为什么先加载BeanFactoryPostProcess

至少记得这句
1.因为BeanDefinition会在ioc容器加载的先注册, 而BeanFactoryPostProcess就是在所有的BeanDefinition注册完后做扩展的,所以要先加载BeanFactoryPostProcess
2. 解析配置类的组件 它就实现BeanFactoryPostProcess, 所以要先去加载BeanFactoryPostProcess
在这里插入图片描述
在这里插入图片描述

Bean的生产顺序是由什么决定的?(BeanDefination的注册顺序是由什么决定的?)

至少记住这句话
Bean的创建顺序是由BeanDefinition的注册顺序来决定的, 当然依赖关系也会影响Bean创建顺序 (A-B)。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

BeanDefinition的注册顺序由什么来决定的?

至少记住这句话
主要是由注解(配置)的解析顺序来决定:
在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值