Spring基础梳理(二):面试常见必备

很常见的知识点,也是面试题,简单列一列吧

Bean生命周期

1、Spring 容器根据配置中的 bean 定义 实例化 bean。
2、Spring 使用依赖注入填充所有属性,如 bean 中所定义的配置。
3、invokeAwareMethods:如果 bean 实现BeanNameAware 接口,则工厂通过传递 bean 的 ID 来调用setBeanName()。
如果bean实现BeanClassLoaderAware,则调用setBeanClassLoader()
4、如果 bean 实现 BeanFactoryAware 接口,工厂通过传递自身的实例来调用 setBeanFactory()。
5、如果存在与 bean 关联的任何BeanPostProcessors,则调用 preProcessBeforeInitialization() 方法
6、如果为 bean 指定了 init 方法( 的 init-method 属性),那么将调用它。
7、最后,如果存在与 bean 关联的任何 BeanPostProcessors,则将调用 postProcessAfterInitialization() 方法
8、如果 bean 实现DisposableBean 接口,当 spring 容器关闭时,会调用 destory()。
9、如果为bean 指定了 destroy 方法( 的 destroy-method 属性),那么将调用它。
在这里插入图片描述

SPI

基于接口的编程+策略模式+配置文件
在Springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个jar包中搜索所有META-INF/spring.factories

配置文件,其实这里不仅仅是去ClassPath路径下查询,而是会扫描所有路径下的jar包,只不过这个文件只会在ClassPath下的jar包中

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// spring.factories文件的格式为:key=value1,value2,value3
// 从所有的jar包中找到META-INF/spring.factories文件
// 然后从文件中解析出key=factoryClass类名称的所有value值
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    // 取得资源文件的URL
    Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    List<String> result = new ArrayList<String>();
    // 遍历所有的URL
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        // 根据资源文件URL解析properties文件,得到对应的一组@Configuration类
        Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
        String factoryClassNames = properties.getProperty(factoryClassName);
        // 组装数据,并返回
        result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
    }
    return result;
}

事务传播机制

  • PROPAGATION_REQUIRED
    支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
    比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,
    ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA
    的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
    这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被
    提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚

  • PROPAGATION_SUPPORTS
    –支持当前事务,如果当前没有事务,就以非事务方式执行。

  • PROPAGATION_MANDATORY
    –支持当前事务,如果当前没有事务,就抛出异常。

  • PROPAGATION_REQUIRES_NEW
    –新建事务,如果当前存在事务,把当前事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。

  • PROPAGATION_NOT_SUPPORTED
    –以非事务方式执行操作,如果当前存在事务,就把当前事务挂起,执行当前逻辑,结束后恢复上下文的事务。

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

  • PROPAGATION_NESTED
    如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行

Spring框架是怎么解决Bean之间的循环依赖的

先调用构造函数进行实例化,然后填充属性,再接着进行其他附加操作和初始化,正是这样的生命周期,才有了Spring的解决循环依赖,这样的解决机制是根据Spring框架内定义的三级缓存来实现的,也就是说:三级缓存解决了Bean之间的循环依赖
后面分析下代码
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述找出了前面提到的三级缓存,也就是三个Map集合类:
singletonObjects:第一级缓存,用于保存实例化、注入、初始化完成的bean实例
earlySingletonObjects:第二级缓存,用于保存实例化完成的bean实例
singletonFactories:第三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。属于提前暴露
所以当一个Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外暴露依赖的引用值(所以循环依赖问题的解决也是基于Java的引用传递),这也说明了另外一点,基于构造函数的注入,如果有循环依赖,Spring是不能够解决的。

还要说明一点,Spring默认的Bean Scope是单例的,而三级缓存中都包含singleton,可见是对于单例Bean之间的循环依赖的解决,Spring是通过三级缓存来实现的

在这里插入图片描述

  1. 创建对象A,实例化的时候把A对象工厂放入三级缓存
  2. A注入属性时,发现依赖B,转而去实例化B
  3. 同样创建对象B,注入属性时发现依赖A,一次从一级到三级缓存查询A,从三级缓存通过对象工厂拿到A,把A放入二级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入一级缓存
  4. 接着继续创建A,顺利从一级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除二级缓存中的A,同时把A放入一级缓存
  5. 最后,一级缓存中保存着实例化、初始化都完成的A、B对象

为什么要三级缓存?二级不行吗?

不可以,主要是为了生成代理对象。
因为三级缓存中放的是生成具体对象的匿名内部类,他可以生成代理对象,也可以是普通的实例对象。
使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。
假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,BeanPostProcessor去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不一致了

Spring 支持的几种 bean 的作用域

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

  • singleton : bean 在每个 Spring ioc 容器中只有一个实例。
  • prototype:一个 bean 的定义可以有多个实例。
  • request:每次 http 请求都会创建一个 bean,该作用域仅在基于 web的 Spring ApplicationContext 情形下有效。
  • session:在一个 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。
  • global-session:在一个全局的 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。缺省的 Spring bean 的作用域是 Singleton.

BeanFactory和ApplicationContext有什么区别?

ApplicationContext 继承自 BeanFactory

两者都是Spring提供的IOC容器。BeanFacotory也被称为基本IOC,而ApplicationContext被称为高级IOC。ApplicationContext包括BeanFactory的所有功能并提供了额外的功能

BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

①继承MessageSource,因此支持国际化。

②统一的资源文件访问方式。

③提供在监听器中注册bean的事件。

④同时加载多个配置文件。

⑤载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

  1. BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。

  2. ApplicationContext,它是在容器启动时,一次性创建了所有的Bean

  3. BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建

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

Spring 框架中都用到了哪些设计模式?

(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;

(2)单例模式:Bean默认为单例模式。

(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;

(4)模板方法:用来解决代码重复的问题。比如.JdbcTemplate

(5)观察者模式:ApplicationListener。

(6)适配器模式:Aop中,使用Advice(通知)来增强被代理类的功能

@Autowire和@Resource的区别

1.@Autowire是Spring开发的,而@Resource是jdk开发的

2.@Autowire是按照type来注解的,而@Resource是按照名称来的,如果名称找不到,那么就按照type

如果指定了名字查不到会报错,不会按type

使用@Autowired 注解自动装配的过程

使用@Autowired 注解来自动装配指定的 bean。在使用@Autowired 注解之前需要在 Spring 配置文

件进行配置。

在启动 spring IoC 时,容器自动装载了一个 AutowiredAnnotationBeanPostProcessor 后置处理器,

当容器扫描到@Autowied、@Resource 或@Inject 时,就会在 IoC 容器自动查找需要的 bean,并装

配给该对象的属性。在使用@Autowired 时,首先在容器中查询对应类型的 bean:

• 如果查询结果刚好为一个,就将该 bean 装配给@Autowired 指定的数据;

• 如果查询的结果不止一个,那么@Autowired 会根据名称来查找;

• 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用 required=false。

其他

  1. Spring中的单例不是线程安全的

祝你好运~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是小酒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值